Pourquoi Deployer Rust avec Docker ?
Le déploiement de Rust avec Docker est une pratique courante dans l'industrie moderne, en particulier pour les applications qui nécessitent une exécution standardisée et une isolation sécurisée. Un contexte réel est le développement d'applications web qui doivent être déployées sur différents environnements. Par exemple, un développeur peut avoir besoin de déployer une application Rust sur un serveur Linux en production, mais également sur une machine virtuelle ou même sur un ordinateur portable pendant le développement.
Un cas concret est la mise en place d'un service API de blog, où chaque environnement doit être configuré exactement de la même manière pour garantir la cohérence et éviter les erreurs liées à l'environnement.
Prerequis
- Connaissance approfondie de Rust
- Familiarité avec le terminal Unix/Linux (Linux, macOS)
- Installation de Docker (version recommandée : 20.10.x ou plus tard)
Concepts fondamentaux
1. Images et Conteneurs Docker
Image Docker : Un template contenant tous les fichiers nécessaires pour exécuter une application.
Conteneur Docker : Une instance d'une image Docker en cours d'exécution.
// Exemple de création d'un fichier Dockerfile
## Dockerfile
FROM rust:latest
## Copie le code source dans le conteneur
COPY . /usr/src/myapp
## Change le répertoire de travail
WORKDIR /usr/src/myapp
## Construit le projet Rust
RUN cargo build --release
## Exécute l'application lors du démarrage du conteneur
CMD ["./target/release/myapp"]
2. Docker Compose
Docker Compose : Un outil pour définir et exécuter des applications multi-conteneurs.
## docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "8080:8080"
3. Variables d'environnement
Variables d'environnement : Permettent de configurer des valeurs à l'intérieur du conteneur.
## Créer un fichier .env pour définir les variables
APP_PORT=8080
DATABASE_URL=postgres://user:password@localhost/dbname
4. Docker Hub
Docker Hub : Un service de stockage en ligne d'images Docker et un registre de conteneurs.
## Login à Docker Hub
docker login
## Construire une image et la publier sur Docker Hub
docker build -t myusername/myapp:latest .
docker push myusername/myapp:latest
Mise en pratique : Projet fil rouge
1. Création du projet
cargo new rust_docker_app
cd rust_docker_app
2. Ajout d'une fonctionnalité simple (ex : une API web)
// src/main.rs
use actix_web::{get, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, world!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
actix_web::App::new()
.service(hello)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
3. Création du Dockerfile
// Dockerfile
FROM rust:latest as builder
## Copier le code source dans le conteneur
COPY . /usr/src/myapp
## Change le répertoire de travail
WORKDIR /usr/src/myapp
## Construit le projet Rust
RUN cargo build --release
## Créer une image finale pour l'exécution
FROM alpine:latest
## Copier la dépendance du builder
COPY --from=builder /usr/src/myapp/target/release/rust_docker_app /usr/local/bin/
## Exécute l'application lors du démarrage du conteneur
CMD ["rust_docker_app"]
4. Création de docker-compose.yml
// docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "8080:8080"
5. Exécution du projet
docker-compose up --build
Erreurs fréquentes et debugging
1. Erreur : error[E0463]: can't find crate for 'actix-web'
Code incorrect
// Cargo.toml
[dependencies]
## actix-web = "2.0"
Correction
// Cargo.toml
[dependencies]
actix-web = "4.0"
2. Erreur : error: failed to start process: No such file or directory (os error 2)
Code incorrect
## Dockerfile
CMD ["./myapp"]
Correction
## Dockerfile
CMD ["rust_docker_app"]
3. Erreur : error: could not compile 'myapp' due to previous error
Code incorrect
// src/main.rs
use actix_web::{get, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, world!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
actix_web::App::new()
.service(hello)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Correction
// src/main.rs
use actix_web::{get, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello, world!")
}
#[actix_web::main]
async fn main() {
HttpServer::new(|| {
actix_web::App::new()
.service(hello)
})
.bind("127.0.0.1:8080")
.unwrap()
.run()
.await
}
Pour aller plus loin
1. Utiliser Docker Secrets pour les variables sensibles
Docker Secrets est un moyen sécurisé de gérer des variables d'environnement sensibles.
2. Optimisation du Dockerfile
Utilisez une image base plus légère et optimisez le processus de construction.
3. Utiliser Docker Swarm pour la gestion de l'échelle
Docker Swarm est un outil de orchestration pour gérer une flotte de machines Docker.
Défi pratique à réaliser seul
- Créer une application CLI : Utilisez Rust pour créer une application CLI qui prend des arguments et affiche un message personnalisé.
- Déployer sur Docker Hub : Construisez une image Docker de votre application CLI et la publiez sur Docker Hub.
- Utiliser Docker Compose avec plusieurs services : Créez un fichier
docker-compose.ymlpour déployer deux services (un backend Rust et un frontend Node.js) qui communiquent entre eux.
Bon apprentissage! N'hésitez pas à poser des questions si vous avez besoin d'aide supplémentaire.