Einleitung: Warum Docker und Rust ein ideales Paar sind
Moderne Softwareentwicklung erfordert nicht nur das Schreiben von effizientem Code, sondern auch die Schaffung einer zuverlässigen Umgebung für dessen Ausführung. Rust – eine Systemprogrammiersprache, bekannt für ihre Leistung und Speichersicherheit – eignet sich ideal für die Erstellung hochbelasteter Dienste. Docker wiederum bietet leichte und isolierte Container, mit denen Sie die Anwendung samt aller Abhängigkeiten verpacken können.
Die Kombination dieser beiden Technologien gibt Entwicklern ein mächtiges Werkzeug: Eine kompilierte Rust-Binärdatei, platziert in einem minimalen Container, läuft schnell, sicher und vorhersagbar auf jeder Plattform. In diesem Artikel gehen wir den Weg von einem einfachen Beispiel bis zu einer Production-Ready-Lösung, analysieren typische Fehler und lernen, Images zu optimieren.
Grundlagen: Erstellen einer einfachen Rust-Anwendung in Docker
Schritt 1: Vorbereitung des Rust-Projekts
Erstellen wir zunächst eine einfache Webanwendung in Rust mit dem beliebten Framework actix-web. Dies ist nicht zwingend erforderlich, bietet aber ein anschauliches Beispiel für die Arbeit mit dem Netzwerk innerhalb eines Containers.
Erstellen Sie ein neues Projekt:
cargo new rust-docker-appcd rust-docker-appFügen Sie die Abhängigkeit in Cargo.toml hinzu:
[dependencies]actix-web = "4"Ersetzen Sie den Inhalt von src/main.rs durch:
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}Schritt 2: Einfaches Dockerfile
Ein einfaches Dockerfile für eine Rust-Anwendung sieht so aus:
FROM rust:latest
WORKDIR /appCOPY . .
RUN cargo build --release
EXPOSE 8080
CMD ["./target/release/rust-docker-app"]Dieser Ansatz funktioniert, hat aber gravierende Nachteile: Das Image wird sehr groß (über 1 GB) und jeder Neubuild lädt alle Abhängigkeiten erneut herunter.
Optimierung: Multi-Stage-Build und Reduzierung der Image-Größe
Multi-Stage-Build
Die Schlüsseltechnik für Rust-Entwickler ist die Verwendung eines Multi-Stage-Builds. Die Idee ist einfach: In der ersten Stufe verwenden wir ein vollständiges Image mit Compiler und Tools, in der zweiten Stufe ein minimales Image, in das wir nur die fertige Binärdatei kopieren.
# Stufe 1: BuildFROM 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
# Stufe 2: Minimales ImageFROM debian:bookworm-slim
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]Warum funktioniert das? Der erste RUN cargo build cached die Abhängigkeiten. Wenn Sie nur den Quellcode ändern, baut Docker die Abhängigkeiten nicht neu, was den Build erheblich beschleunigt.
Verwendung von Alpine für maximale Komprimierung
Wenn Sie eine minimale Image-Größe benötigen, verwenden Sie alpine als finale Stufe. Rust-Binärdateien werden statisch gelinkt, daher sind keine zusätzlichen Bibliotheken erforderlich.
# Finale Stufe mit AlpineFROM alpine:latest
WORKDIR /appCOPY --from=builder /app/target/release/rust-docker-app .
EXPOSE 8080CMD ["./rust-docker-app"]Die Image-Größe kann dabei nur 5–15 MB betragen, statt 1+ GB!
Fortgeschrittene Techniken: Caching und Sicherheit
Caching von Abhängigkeiten mit sccache
Für große Projekte kann