Introduction : pourquoi Docker et Rust forment un duo idéal
Le développement logiciel moderne nécessite non seulement d'écrire du code efficace, mais aussi de créer un environnement fiable pour son exécution. Rust — un langage de programmation système réputé pour ses performances et sa sécurité mémoire — est parfaitement adapté à la création de services à forte charge. Docker, quant à lui, fournit des conteneurs légers et isolés, permettant d'empaqueter l'application avec toutes ses dépendances.
La combinaison de ces deux technologies offre aux développeurs un outil puissant : un fichier binaire Rust compilé, placé dans un conteneur minimal, fonctionne rapidement, en toute sécurité et de manière prévisible sur n'importe quelle plateforme. Dans cet article, nous passerons d'un exemple simple à une solution prête pour la production, analyserons les erreurs typiques et apprendrons à optimiser les images.
Les bases : création d'une application Rust simple dans Docker
Étape 1. Préparation du projet Rust
Pour commencer, créons une application web simple en Rust en utilisant le framework populaire actix-web. Ce n'est pas obligatoire, mais cela donne un exemple concret de travail avec le réseau à l'intérieur d'un conteneur.
Créez un nouveau projet :
cargo new rust-docker-appcd rust-docker-appAjoutez la dépendance dans Cargo.toml :
[dependencies]actix-web = "4"Remplacez le contenu de src/main.rs par :
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}Étape 2. Dockerfile simple
Un Dockerfile de base pour une application Rust ressemble à ceci :
FROM rust:latest
WORKDIR /appCOPY . .
RUN cargo build --release
EXPOSE 8080
CMD ["./target/release/rust-docker-app"]Cette approche fonctionne, mais présente de sérieux inconvénients : l'image est très volumineuse (plus de 1 Go) et chaque reconstruction recharge toutes les dépendances.
Optimisation : construction multi-étapes et réduction de la taille de l'image
Construction multi-étapes (Multi-stage build)
La technique clé pour les développeurs Rust est l'utilisation de la construction multi-étapes. L'idée est simple : dans la première étape, nous utilisons une image complète avec le compilateur et les outils, et dans la seconde, une image minimale dans laquelle nous copions uniquement le fichier binaire final.
# Étape 1 : compilationFROM 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
# Étape 2 : image minimaleFROM debian:bookworm-slim
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]Pourquoi cela fonctionne-t-il ? La première commande RUN cargo build met en cache les dépendances. Lorsque vous modifiez uniquement le code source, Docker ne reconstruit pas les dépendances, ce qui accélère considérablement la compilation.
Utilisation d'Alpine pour une compression maximale
Si vous avez besoin d'une taille d'image minimale, utilisez alpine comme étape finale. Les binaires Rust sont liés statiquement, donc aucune bibliothèque supplémentaire n'est nécessaire.
# Étape finale avec AlpineFROM alpine:latest
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]La taille de l'image peut alors être de seulement 5 à 15 Mo au lieu de 1 Go+ !
Techniques avancées : mise en cache et sécurité
Mise en cache des dépendances avec sccache
Pour les grands projets, vous pouvez utiliser