Introdução: por que Docker e Rust são a dupla ideal
O desenvolvimento moderno de software exige não apenas escrever código eficiente, mas também criar um ambiente confiável para sua execução. Rust — uma linguagem de programação de sistemas, conhecida por seu desempenho e segurança de memória — é ideal para criar serviços de alta carga. Docker, por sua vez, fornece contêineres leves e isolados, permitindo empacotar a aplicação com todas as suas dependências.
A união dessas duas tecnologias oferece aos desenvolvedores uma ferramenta poderosa: um binário compilado em Rust, colocado em um contêiner mínimo, executa de forma rápida, segura e previsível em qualquer plataforma. Neste artigo, percorreremos o caminho desde um exemplo simples até uma solução pronta para produção, analisaremos erros típicos e aprenderemos a otimizar as imagens.
Fundamentos: criando uma aplicação Rust simples no Docker
Passo 1. Preparação do projeto Rust
Para começar, vamos criar uma aplicação web simples em Rust usando o popular framework actix-web. Isso não é obrigatório, mas fornece um exemplo prático de trabalho com rede dentro de um contêiner.
Crie um novo projeto:
cargo new rust-docker-appcd rust-docker-appAdicione a dependência no Cargo.toml:
[dependencies]actix-web = "4"Substitua o conteúdo de src/main.rs por:
use actix_web::{web, App, HttpServer, Responder};
async fn index() -> impl Responder { "Hello from Rust in Docker!"}
#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().route("/", web::get().to(index)) }) .bind("0.0.0.0:8080")? .run() .await}Passo 2. Dockerfile simples
Um Dockerfile básico para uma aplicação Rust se parece com isto:
FROM rust:latest
WORKDIR /appCOPY . .
RUN cargo build --release
EXPOSE 8080
CMD ["./target/release/rust-docker-app"]Essa abordagem funciona, mas tem sérias desvantagens: a imagem fica muito grande (mais de 1 GB) e cada reconstrução baixa todas as dependências novamente.
Otimização: compilação multi-estágio e redução do tamanho da imagem
Compilação multi-estágio (Multi-stage build)
A técnica chave para desenvolvedores Rust é usar a compilação multi-estágio. A ideia é simples: no primeiro estágio, usamos uma imagem completa com o compilador e ferramentas; no segundo, uma imagem mínima para a qual copiamos apenas o binário pronto.
# Estágio 1: compilaçãoFROM rust:latest AS builder
WORKDIR /appCOPY Cargo.toml Cargo.lock ./RUN mkdir src && echo "fn main() {}" > src/main.rsRUN cargo build --release 2>/dev/null || true
COPY src/ src/RUN cargo build --release
# Estágio 2: imagem mínimaFROM debian:bookworm-slim
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]Por que isso funciona? O primeiro RUN cargo build armazena em cache as dependências. Quando você altera apenas o código-fonte, o Docker não reconstrói as dependências do zero, acelerando a compilação várias vezes.
Usando Alpine para máxima compressão
Se você precisa de um tamanho mínimo de imagem, use alpine como estágio final. Os binários Rust são linkados estaticamente, portanto, bibliotecas adicionais não são necessárias.
# Estágio final com AlpineFROM alpine:latest
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]O tamanho da imagem pode ser de apenas 5–15 MB em vez de 1+ GB!
Técnicas avançadas: cache e segurança
Cache de dependências com sccache
Para projetos grandes, você pode usar