Introducción: por qué Docker y Rust son la pareja ideal
El desarrollo de software moderno no solo requiere escribir código eficiente, sino también crear un entorno confiable para su ejecución. Rust —un lenguaje de programación de sistemas conocido por su rendimiento y seguridad de memoria— es ideal para crear servicios de alta carga. Docker, por su parte, proporciona contenedores ligeros y aislados que permiten empaquetar la aplicación con todas sus dependencias.
La combinación de estas dos tecnologías brinda a los desarrolladores una herramienta poderosa: un archivo binario compilado de Rust, colocado en un contenedor mínimo, funciona de manera rápida, segura y predecible en cualquier plataforma. En este artículo, recorreremos el camino desde un ejemplo simple hasta una solución lista para producción, analizaremos errores típicos y aprenderemos a optimizar las imágenes.
Fundamentos: creación de una aplicación Rust simple en Docker
Paso 1. Preparación del proyecto Rust
Para empezar, crearemos una aplicación web simple en Rust utilizando el popular framework actix-web. Esto no es obligatorio, pero proporciona un ejemplo claro de trabajo con red dentro del contenedor.
Cree un nuevo proyecto:
cargo new rust-docker-appcd rust-docker-appAgregue la dependencia en Cargo.toml:
[dependencies]actix-web = "4"Reemplace el contenido 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}Paso 2. Dockerfile simple
Un Dockerfile básico para una aplicación Rust se ve así:
FROM rust:latest
WORKDIR /appCOPY . .
RUN cargo build --release
EXPOSE 8080
CMD ["./target/release/rust-docker-app"]Este enfoque funciona, pero tiene serias desventajas: la imagen resultante es muy grande (más de 1 GB) y cada recompilación vuelve a descargar todas las dependencias.
Optimización: compilación multi-etapa y reducción del tamaño de la imagen
Compilación multi-etapa (Multi-stage build)
La técnica clave para los desarrolladores de Rust es el uso de la compilación multi-etapa. La idea es simple: en la primera etapa usamos una imagen completa con el compilador y las herramientas, y en la segunda, una imagen mínima a la que copiamos solo el archivo binario compilado.
# Etapa 1: compilaciónFROM 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
# Etapa 2: imagen mínimaFROM debian:bookworm-slim
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]¿Por qué funciona esto? El primer RUN cargo build almacena en caché las dependencias. Cuando solo cambia el código fuente, Docker no reconstruye las dependencias desde cero, lo que acelera la compilación varias veces.
Uso de Alpine para máxima compresión
Si necesita un tamaño de imagen mínimo, use alpine como etapa final. Los binarios de Rust se enlazan estáticamente, por lo que no se requieren bibliotecas adicionales.
# Etapa final con AlpineFROM alpine:latest
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]¡El tamaño de la imagen puede ser de solo 5–15 MB en lugar de 1+ GB!
Técnicas avanzadas: almacenamiento en caché y seguridad
Almacenamiento en caché de dependencias con sccache
Para proyectos grandes, puede usar