Termux — удобная среда на Android для разработки: здесь можно собирать проекты, прогонять тесты и готовить артефакты для контейнеризации. Rust хорошо подходит для микросервисов благодаря безопасности памяти, производительности и удобной системе сборки cargo. В этой статье рассмотрим практичный путь от локальной разработки до публикации Docker-образа в реестре: кросс‑компиляция, запуск тестов и сборка контейнеров.
Цель материала — сделать процесс воспроизводимым: чтобы “проект на телефоне” превращался в образ, который корректно запускается в среде вашего окружения (CI/CD, тестовый стенд, прод). Все шаги будут описаны без “магии”, с фокусом на проверяемые команды.
Предпосылки и требования
Перед началом уточните:
- Телефон с Android, установленный Termux.
- Доступ к интернету (для загрузки зависимостей и публикации образов).
- Аккаунт в Docker‑реестре (Docker Hub, GitLab Registry, Harbor, ECR и т.д.).
- Понимание целевой архитектуры контейнера (чаще
linux/amd64илиlinux/arm64).
Вариант с локальной сетью возможен при помощи VPN, но только для создания локальной сети и доступа к ресурсам разработки внутри вашей сети — без обхода блокировок.
Подготовка Termux: базовые пакеты и Rust toolchain
Сначала обновим пакеты и поставим инструменты, которые пригодятся для сборки и упаковки.
pkg update -y
pkg upgrade -y
pkg install -y git curl ca-certificates tar openssl-tool clang make pkg-config
Установите Rust. На практике удобно использовать официальный установщик rustup (или пакетный вариант, если выбран он). Рекомендую rustup для гибкости версий.
curl https://sh.rustup.rs -sSf | sh -s -- -y
. $HOME/.cargo/env
rustc --version
cargo --version
Проверьте, что ~/.cargo/bin добавлен в PATH.
echo $PATH
Создание каркаса микросервиса на Rust
Для примера возьмём типичный стек: HTTP‑сервер на axum (или actix-web, без принципиальной разницы). Создадим проект и добавим зависимости.
cargo new ms-hello --bin
cd ms-hello
В Cargo.toml добавим зависимости. Пример ниже показывает минимальный набор для HTTP и тестов.
cat > Cargo.toml <<'EOF'
[package]
name = "ms-hello"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[dev-dependencies]
reqwest = { version = "0.12", features = ["json"] }
tower = "0.5"
EOF
Содержимое src/main.rs для запуска простого эндпоинта:
cat > src/main.rs <<'EOF'
use axum::{routing::get, Router};
async fn health() -> &'static str {
"ok"
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/health", get(health));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080")
.await
.expect("bind");
println!("listening on http://0.0.0.0:8080");
axum::serve(listener, app).await.unwrap();
}
EOF
Запустим на телефоне:
cargo run
Проверка из другого терминала (или через браузер):
curl -s http://127.0.0.1:8080/health
Локальные тесты в Termux: качество до сборки
Прежде чем переходить к контейнеризации, убедитесь, что тесты стабильны. Добавим интеграционный тест с запуском сервера или тестируем роутинг напрямую. Пример — тест маршрута через axum (упрощённый вариант).
Создадим тестовый файл:
mkdir -p tests
cat > tests/health.rs <<'EOF'
use axum::{routing::get, Router};
async fn health() -> &'static str {
"ok"
}
#[tokio::test]
async fn health_returns_ok() {
let app = Router::new().route("/health", get(health));
// В минимальном примере проверим обработчик напрямую не поднимая реальный listener.
// Для сложных сценариев используйте axum::Server на тестовом порту.
let response = app
.oneshot(())
.await;
let _ = response; // placeholder, чтобы фокус был на подходе.
}
EOF
Запуск тестов:
cargo test
Если вы предпочитаете полноценные HTTP‑интеграционные тесты (через reqwest и запуск сервера на локальном порту), напишите их по месту под вашу архитектуру. Ключевой принцип: тест должен подтверждать бизнес‑контракты, а не “только компиляцию”.
Кросс‑компиляция: собрать под Docker‑архитектуру
Если вы собираете образ для linux/amd64 или linux/arm64, а компилируете в Termux на Android, вам понадобится кросс‑компиляция. В Rust это решается целевыми тройками (target triples) и установкой соответствующих инструментов.
Сначала определим, какие target тройки вам нужны. Для примера возьмём:
x86_64-unknown-linux-gnu(linux/amd64)aarch64-unknown-linux-gnu(linux/arm64)
Добавьте target'ы:
rustup target add x86_64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-gnu
Дальше — самый важный момент: вам нужно обеспечить линковщик и необходимые инструменты для этих целей. На Android это может быть нетривиально “в лоб”, поэтому практичный путь — собирать в Docker билд‑среде (чуть ниже это будет рассмотрено), либо использовать готовые toolchain‑решения.
Ниже приведён общий шаблон команд сборки под target:
cargo build --release --target x86_64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu
Если сборка падает из‑за линковщика/системных библиотек, корректный промышленный подход — не пытаться “дотягивать” libc и линковку на устройстве, а переносить финальную сборку в контейнер (см. секцию “Docker: multi-stage сборка и воспроизводимость”). Это лучше для консистентности и повторяемости.
Оптимизация релизной сборки: размер и детерминизм
В микросервисах часто важны время старта и размер бинарника. Используйте настройку профилей в Cargo.toml:
cat >> Cargo.toml <<'EOF'
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
EOF
Затем собирайте релиз:
cargo build --release
Для воспроизводимости полезно фиксировать зависимости версионным диапазоном и контролировать Cargo.lock (commit его в репозиторий).
Подготовка Dockerfile: multi-stage сборка
Чтобы гарантировать, что итоговый бинарник подходит к нужной платформе, лучше использовать multi-stage Dockerfile. Такой подход также облегчает multi-arch сборку (например, amd64/arm64) через buildx.
Создайте Dockerfile в корне проекта:
cat > Dockerfile <<'EOF'
# syntax=docker/dockerfile:1.6
# 1) Builder
FROM rust:1.78-bookworm AS builder
WORKDIR /app
# Кэш зависимостей
COPY Cargo.toml Cargo.lock ./
RUN mkdir -p src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
# Собираем реальный проект
COPY src ./src
RUN cargo build --release
# 2) Runtime
FROM debian:bookworm-slim
WORKDIR /app
# Копируем бинарник
COPY --from=builder /app/target/release/ms-hello ./ms-hello
# Минимальные настройки (безопасность)
USER 65532:65532
EXPOSE 8080
CMD ["./ms-hello"]
EOF
Важно:
- В образ переносится только бинарник — это снижает поверхность атаки и размер.
USER 65532:65532повышает безопасность (при корректной поддержке в базовом образе).
Сборка образа локально в Termux
Для сборки Docker‑образов из Termux у вас есть несколько вариантов: сборка на CI, сборка на хосте с Docker или использование окружения, где Docker доступен из Termux. Практично и надёжно — выполнять docker buildx на рабочей станции/сервере или в CI.
Если Docker на вашей стороне доступен, базовая команда:
docker build -t rybinsklab/ms-hello:dev .
Проверка запуска (пример):
docker run --rm -p 8080:8080 rybinsklab/ms-hello:dev
Затем проверка:
curl -s http://127.0.0.1:8080/health
Публикация Docker-образа в реестре
Ниже — универсальный сценарий. Замените registry.example.com, USERNAME, IMAGE_NAME на свои значения.
1) Логин в реестр:
docker login registry.example.com -u USERNAME
2) Тегирование:
IMAGE=registry.example.com/IMAGE_NAME
TAG=0.1.0
docker tag rybinsklab/ms-hello:dev $IMAGE:$TAG
3) Публикация:
docker push $IMAGE:$TAG
Рекомендуется также вести “latest” или версионные теги согласно вашей стратегии (семвер, git‑sha, date‑tags). Для микросервисов чаще удобнее версионность и неизменяемость артефакта.
Multi-arch публикация (amd64/arm64) через buildx
Если ваш реестр и инфраструктура поддерживают multi-arch, можно публиковать единый тег, внутри которого будут варианты под разные архитектуры.
Пример команд (на машине, где доступен buildx):
docker buildx create --use --name rybxs
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t $IMAGE:$TAG \
--push \
.
Это избавляет от ручной кросс‑компиляции и повышает воспроизводимость: сборка происходит в стандартизированных средах Docker.
CI/CD: как встроить Termux‑процесс в промышленную цепочку
Хотя Termux удобен для локальной разработки, обычно “финальный” пайплайн лучше переносить в CI. Типовой сценарий:
- На этапе PR:
cargo test(быстро, без контейнеров). - На этапе merge/release: сборка релиза и docker build (в CI) с multi-arch.
- Публикация в реестр и деплой на целевую среду.
Termux в таком случае остаётся отличным инструментом для черновой разработки, отладки, быстрой проверки и подготовки исходников.
Типовые проблемы и как их диагностировать
1) “Не собирается под target”
Кросс‑сборка на Android часто упирается в линковку и наличие совместимых библиотек. Практический совет: используйте Docker multi-stage сборку для финального бинарника.
2) Тесты “зависят от сети”
Сделайте тесты детерминированными: подменяйте внешние сервисы через mock или локальные тестовые серверы. Идеально — чтобы cargo test всегда давал одинаковый результат.
3) Бинарник не запускается в runtime‑образе
Проверьте базовый образ runtime (glibc/musl), совместимость ABI и наличие нужных переменных окружения. Multi-stage + одинаковая toolchain‑база обычно решает проблему.
Заключение
Разработка микросервисов на Rust в Termux позволяет быстро создавать и отлаживать сервисы прямо на устройстве: вы запускаете cargo run, прогоняете cargo test и готовите проект к контейнеризации. Для надёжной кросс‑платформенной публикации в Docker‑реестре практичнее использовать multi-stage сборку в Docker (и при необходимости buildx для multi‑arch), чтобы финальные бинарники были воспроизводимыми и соответствовали целевой архитектуре.
Нужна помощь с настройкой репозитория, пайплайна CI/CD, Docker‑архитектурой или подбором стратегии тэгирования/версий для микросервисов? Обратитесь в РыбинскЛАБ — поможем спроектировать процесс разработки и публикации под ваши цели.