Zero-Downtime-Deployment von Rust-Diensten: Neustart ohne Ausfallzeiten

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Zero-Downtime-Deployment von Rust-Diensten: Wie Sie Ihre Anwendung neu starten, ohne Kunden zu verlieren



Stellen Sie sich folgende Situation vor: Sie haben eine neue Version Ihres Rust-Dienstes ausgerollt, aber zum Zeitpunkt des Neustarts erhalten 50 Benutzer einen 502-Fehler. Jemand hat Daten verloren, jemand Geld und jemand das Vertrauen in Ihr Produkt. Ein bekanntes Problem? Heute werden wir durchgehen, wie Sie ein Zero-Downtime-Deployment von Rust-Diensten einrichten, sodass Updates für Ihre Kunden unbemerkt bleiben.



Zero-Downtime (oder Deployment ohne Ausfallzeiten) ist keine Magie, sondern eine Reihe technischer Methoden: Graceful Shutdown, korrektes Verbindungsmanagement und Traffic-Balancing. Rust mit seinem Typsystem und asynchronem Runtime (Tokio) gibt uns die Werkzeuge, um diese Aufgabe elegant zu lösen. Los geht's!



Das Problem: Warum ein normaler Neustart die Produktion zerstört



Wenn Sie einfach den Prozess töten (kill -9) oder std::process::exit() aufrufen, schließt das Betriebssystem sofort alle Sockets. Kunden, die in diesem Moment Anfragen senden, erhalten Connection reset oder Timeout. Für HTTP-Dienste ist das eine Katastrophe: Browser zeigen Fehler an, mobile Apps stürzen ab und Hintergrundaufgaben verlieren ihren Zustand.



In Rust können wir das beheben. Wir müssen:

  • Aufhören, neue Anfragen anzunehmen (aber alte zu Ende bearbeiten).
  • Warten, bis alle aktiven Handler abgeschlossen sind.
  • Erst dann den Prozess beenden.


Dies nennt man Graceful Shutdown – die Grundlage für Zero-Downtime-Deployment von Rust-Diensten.



Schritt 1. Graceful Shutdown in Rust mit Tokio



Tokio bietet integrierte Mechanismen für Graceful Shutdown. Betrachten wir ein Beispiel eines HTTP-Servers mit axum (einem beliebten Web-Framework in Rust).



Basis-Server ohne Graceful Shutdown



use axum::{Router, routing::get};use std::net::SocketAddr;

#[tokio::main]async fn main() { let app = Router::new().route("/", get(|| async { "Hello, World!" }));

let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap();}


Wenn Sie Strg+C drücken, stürzt der Server einfach ab – alle aktiven Anfragen werden abgebrochen. Nicht gut.



Graceful Shutdown hinzufügen



use axum::{Router, routing::get};use std::net::SocketAddr;use tokio::signal;

async fn shutdown_signal() { // Warten auf SIGTERM oder SIGINT (Strg+C) let ctrl_c = async { signal::ctrl_c() .await .expect("failed to install Ctrl+C handler"); };

#[cfg(unix)] let terminate = async { signal::unix::signal(signal::unix::SignalKind::terminate()) .expect("failed to install signal handler") .recv() .await; };

#[cfg(not(unix))] let terminate = std::future::pending::<()>();

tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, }

println!("Beendigungssignal empfangen, starte Graceful Shutdown...");}

#[tokio::main]async fn main() { let app = Router::new().route("/", get(|| async { "Hello, World!" }));

let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); axum::Server::bind(&addr) .serve(app.into_make_service()) .with_graceful_shutdown(shutdown_signal()) .await .unwrap();

println!("Server hat die Arbeit beendet.");}


Jetzt, beim Empfang von SIGTERM oder Strg+C, wird der Server:

  • Keine neuen Verbindungen mehr annehmen.
  • Warten, bis alle aktuellen Anfragen abgeschlossen sind (mit einem Standard-Timeout).
  • Den Prozess beenden.


Dies ist der erste Schritt zum Zero-Downtime-Deployment von Rust-Diensten. Aber wenn Sie nur einen Server haben, können sich während der Wartezeit auf den Abschluss der Anfragen keine neuen Clients verbinden. Es ist notwendig

Blogs

Buchempfehlungen