Docker y Rust: contenedorización de aplicaciones en Rust

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

Introducción: Por qué Docker y Rust son la pareja ideal

Rust es un lenguaje de programación de sistemas moderno, conocido por su rendimiento y seguridad de memoria. Sin embargo, para entregar una aplicación en Rust al usuario final o a la nube, es necesario resolver el problema del entorno. Aquí es donde Docker viene al rescate. La contenerización permite empaquetar una aplicación Rust junto con todas sus dependencias en un contenedor ligero y aislado. En este artículo, analizaremos cómo crear una imagen Docker eficiente y mínima para un proyecto Rust, utilizando las mejores prácticas.



1. Fundamentos de Docker para desarrolladores Rust

Docker es una plataforma para desarrollar, enviar y ejecutar aplicaciones en contenedores. Un contenedor incluye la propia aplicación y todo lo necesario para su funcionamiento: bibliotecas, utilidades del sistema y configuraciones. Para Rust, esto es especialmente relevante, ya que el binario compilado no depende de un runtime (a diferencia de Python o Node.js), lo que permite crear imágenes muy pequeñas.

Los componentes principales que necesitarás son:

  • Dockerfile — un archivo de texto con instrucciones para construir la imagen.
  • .dockerignore — un archivo que excluye archivos innecesarios del contexto de construcción.
  • Docker Compose (opcional) — para orquestar múltiples contenedores.

Antes de comenzar, asegúrate de tener instalado Docker Desktop o Docker Engine.



2. Creación de un Dockerfile para una aplicación Rust

Consideremos un ejemplo simple de un servidor web en Rust utilizando el framework actix-web. Nuestro archivo principal main.rs se ve así:

use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder { "Hello, Docker and Rust!"}

#[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}

Ahora crearemos un Dockerfile. La técnica más importante aquí es la construcción multi-etapa (multi-stage build). Permite primero compilar la aplicación en un entorno con el conjunto completo de herramientas de Rust, y luego copiar el binario listo en una imagen final mínima.

// ---- Primera etapa: compilación ----FROM rust:1.75 as builder

WORKDIR /app

// Copiamos los manifiestos para cachear las dependenciasCOPY Cargo.toml Cargo.lock ./

// Creamos un main.rs vacío para compilar las 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

// Compilamos el binario final (el caché de dependencias ya existe)RUN cargo build --release

// ---- Segunda etapa: imagen final ----FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*

WORKDIR /app

// Copiamos el binario desde la primera etapaCOPY --from=builder /app/target/release/my_rust_app .

EXPOSE 8080

CMD ["./my_rust_app"]

Observa el truco con el main.rs vacío. Esto permite que Docker almacene en caché las dependencias compiladas (crates) y no las recompile cada vez que solo cambias el código fuente. Esto acelera significativamente las compilaciones repetidas.



2.1. Optimización del tamaño de la imagen

La imagen final basada en debian:bookworm-slim pesa alrededor de 80-100 MB. Sin embargo, si deseas obtener una imagen realmente mínima, puedes usar scratch (imagen vacía) o alpine. Para aplicaciones Rust compiladas estáticamente, esta es la opción ideal. Para ello, necesitas cambiar el target de compilación:

// En la primera etapa (builder):RUN rustup target add x86_64-                

Blogs

Book Recommendations