Docker y Rust: contenerización de aplicaciones para producción

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course
```html

Introducción: ¿por qué un desarrollador de Rust necesita Docker?

Rust es famoso por su rendimiento y seguridad de memoria, pero incluso el binario más eficiente requiere un entorno adecuado para ejecutarse. Docker resuelve este problema empaquetando la aplicación junto con todas sus dependencias en un contenedor aislado. En este artículo, analizaremos cómo contenerizar aplicaciones Rust con Docker, utilizando técnicas modernas, incluyendo la compilación multi-etapa y la minimización del tamaño de la imagen.

La contenerización de proyectos Rust es especialmente ventajosa porque Rust se compila en un binario estático — esto permite crear imágenes mínimas (alpine o incluso scratch) que se ejecutan más rápido y son más seguras que sus equivalentes en Python o Node.js.

1. Fundamentos: Dockerfile para una aplicación Rust simple

Empecemos con un servidor web simple en Rust utilizando el framework actix-web. Crearemos un Dockerfile básico.

1.1. Estructura del proyecto

my_rust_app/
├── Cargo.toml
├── Cargo.lock
└── src/
    └── main.rs

1.2. Ejemplo de código (main.rs)

use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("¡Hola desde el contenedor Rust!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(hello))
        .bind("0.0.0.0:8080")?
        .run()
        .await
}

1.3. Dockerfile simple

FROM rust:1.77 AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
COPY src/ src/
RUN cargo build --release

FROM debian:bookworm-slim
COPY --from=builder /app/target/release/my_rust_app /usr/local/bin/
EXPOSE 8080
CMD ["my_rust_app"]

Explicación: la primera etapa (builder) compila la aplicación, la segunda etapa copia solo el binario a una imagen Debian limpia. Esto reduce el tamaño de ~2 GB a ~100 MB.

2. Compilación multi-etapa: optimización del tamaño de la imagen

La compilación multi-etapa (multi-stage build) es una técnica clave para Rust. Permite separar el entorno de compilación (con el compilador, dependencias) del entorno de ejecución.

2.1. Dockerfile avanzado con caché

Para acelerar las compilaciones repetidas, almacenamos en caché las dependencias:

FROM rust:1.77 AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release 2>/dev/null || true
COPY src/ src/
RUN cargo build --release && \
    cp target/release/my_rust_app /app/rust_app

FROM gcr.io/distroless/cc-debian12
COPY --from=builder /app/rust_app /app/rust_app
EXPOSE 8080
CMD ["/app/rust_app"]

¿Por qué distroless? La imagen distroless contiene solo glibc y bibliotecas básicas — sin shell, apt, ls. Esto aumenta la seguridad y reduce el tamaño a ~30 MB.

2.2. Imagen mínima con scratch

Si tu aplicación no usa libc (por ejemplo, escrita con #![no_std]), puedes usar scratch:

FROM rust:1.77 AS builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM scratch
COPY --from=builder /app/target/release/my_rust_app /app
EXPOSE 8080
CMD ["/app"]

¡Esta imagen pesa menos de 10 MB!

3. Docker Compose: aplicación multi-contenedor

A menudo, una aplicación Rust funciona junto con PostgreSQL, Redis o Nginx. Usamos docker-compose.yml:

3.1. Ejemplo de docker-compose.yml

version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
      - RUST_LOG=info
    depends_on:
      - db

  db:
    image: postgres:16-alpine
```                

Blogs

Book Recommendations