Docker y Rust: contenerización de aplicaciones de nivel avanzado

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

Docker y Rust: Contenerización de aplicaciones de nivel avanzado



La contenerización de aplicaciones Rust con Docker no es solo empaquetar código en una imagen. A nivel avanzado, es el arte de crear contenedores mínimos, rápidos y seguros que son perfectos para entornos de producción. Rust, como lenguaje de programación de sistemas, se compila a código nativo, lo que brinda oportunidades únicas para optimizar imágenes Docker. En este artículo, exploraremos técnicas que van más allá del docker build básico y mostraremos cómo crear contenedores eficientes para aplicaciones Rust.



1. Compilación multi-etapa (Multi-stage build) para Rust



La compilación multi-etapa es la piedra angular de la contenerización avanzada de Rust. Permite separar el entorno de compilación (con compilador, dependencias y herramientas) de la imagen final, que contiene solo el archivo binario compilado y las dependencias de runtime necesarias.



Consideremos un ejemplo de Dockerfile para un servidor web con actix-web:



# Etapa 1: CompilaciónFROM rust:1.75-slim-bullseye AS builder

WORKDIR /app

# Copiamos solo los archivos de dependencias para cachear capasCOPY Cargo.toml Cargo.lock ./

# Creamos un main.rs vacío para compilar dependenciasRUN mkdir src && echo "fn main() {}" > src/main.rsRUN cargo build --release 2>/dev/null || true

# Ahora copiamos el código fuente realCOPY src ./src

# Recompilamos solo los archivos modificados (el caché de dependencias no se toca)RUN cargo build --release

# Etapa 2: Imagen finalFROM debian:bullseye-slim

RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ libssl1.1 \ && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/my_app /usr/local/bin/my_app

USER nobodyEXPOSE 8080

CMD ["/usr/local/bin/my_app"]


Optimizaciones clave:

  • Cacheo de dependencias: Primero se copian solo Cargo.toml y Cargo.lock, y luego se compilan las dependencias. Esto permite que Docker use el caché de capas si los archivos de manifiesto no han cambiado.
  • Imagen final mínima: Se usa debian:bullseye-slim en lugar de la imagen completa de Rust. Esto reduce el tamaño de ~1.5 GB a ~150 MB.
  • Seguridad: La aplicación se ejecuta como el usuario nobody, no como root.


2. Minimización del tamaño de la imagen: desde Alpine hasta scratch



Las aplicaciones Rust no requieren un entorno de runtime como una máquina virtual Java o un intérprete de Python. Esto permite usar imágenes base mínimas.



Opción con Alpine Linux:

FROM alpine:3.19 AS builderRUN apk add --no-cache musl-dev# ... compilación ...

FROM alpine:3.19RUN apk add --no-cache ca-certificatesCOPY --from=builder /app/target/release/my_app /my_appCMD ["/my_app"]


Opción con scratch (imagen vacía):

FROM alpine:3.19 AS builderRUN apk add --no-cache musl-dev# Compilación con enlace estáticoRUN cargo build --release --target x86_64-unknown-linux-musl

FROM scratchCOPY --from=builder /app/target/x86_64-unknown-linux-musl/release/my_app /my_appCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/CMD ["/my_app"]


Comparación de tamaños de imágenes:

  • debian:bullseye-slim: ~80-150 MB (depende de la cantidad de bibliotecas del sistema)
  • alpine:3.19: ~10-20 MB (usa musl libc en lugar de glibc)
  • scratch: ~5-10 MB (solo binario y certificados)


Importante: Al usar scratch, es necesario asegurarse de que su aplicación no requiera bibliotecas dinámicas. Para ello, use el enlace estático con el objetivo

Blogs

Book Recommendations