Zpět na blog
DevOps19. prosince 2024Michael Hettwer12 min čtení

Nasazení bez výpadku s Nginx a Systemd

Průvodce krok za krokem k dosažení skutečného nasazení bez výpadku pomocí přepínání upstream v Nginx a socket aktivace systemd – bez Kubernetes.

Nasazení bez výpadku jsou často prezentována jako schopnost výhradně pro Kubernetes. Nejsou. S Nginx jako reverzním proxy a socket aktivací systemd můžete dosáhnout skutečných aktualizací aplikací bez výpadku na jednom serveru bez orchestrátoru kontejnerů – a přesně pochopit, co se děje v každém kroku.

Základní myšlenka

Strategie má dvě části: (1) spustit dvě verze vaší aplikace současně po krátkou dobu během nasazení a (2) použít Nginx k atomickému přesunu provozu ze staré verze na novou. Socket aktivace systemd zajišťuje, že nová připojení jsou pozastavena – ne zahozena – během přepínání.

Krok 1: Socket aktivace Systemd

Socket aktivace umožňuje systemd vlastnit naslouchající socket. Když se vaše aplikace restartuje, socket zůstane otevřený – příchozí připojení se řadí do fronty v jádře – a jsou předána novému procesu, jakmile je připraven. Žádná připojení nejsou odmítnuta.

ini
# /etc/systemd/system/myapp.socket
[Unit]
Description=MyApp socket

[Socket]
ListenStream=127.0.0.1:3000
Accept=no

[Install]
WantedBy=sockets.target
ini
# /etc/systemd/system/myapp.service
[Unit]
Description=MyApp application server
Requires=myapp.socket
After=myapp.socket

[Service]
ExecStart=/usr/bin/node /srv/myapp/current/server.js
WorkingDirectory=/srv/myapp/current
User=myapp
Group=myapp
Restart=on-failure

# Tell the service to use the socket passed by systemd
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Krok 2: Konfigurace Nginx upstream

nginx
# /etc/nginx/conf.d/myapp.conf
upstream myapp {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://myapp;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Allow Nginx to hold connections briefly during restart
        proxy_next_upstream error timeout;
        proxy_connect_timeout 5s;
        proxy_read_timeout 60s;
    }
}

Krok 3: Deployment skript

bash
#!/bin/bash
# deploy.sh — zero-downtime deployment
set -euo pipefail

APP_DIR=/srv/myapp
RELEASE=$(date +%Y%m%d%H%M%S)
RELEASE_DIR="$APP_DIR/releases/$RELEASE"

echo "==> Creating release directory: $RELEASE_DIR"
mkdir -p "$RELEASE_DIR"

echo "==> Pulling latest code"
git clone --depth 1 git@github.com:yourorg/myapp.git "$RELEASE_DIR"

echo "==> Installing dependencies"
cd "$RELEASE_DIR"
npm ci --production

echo "==> Running database migrations"
npm run migrate

echo "==> Switching symlink atomically"
ln -sfn "$RELEASE_DIR" "$APP_DIR/current"

echo "==> Reloading application (socket remains open)"
# systemd will restart the service while keeping the socket alive
systemctl reload-or-restart myapp.service

echo "==> Verifying health check"
sleep 2
curl -sf http://127.0.0.1:3000/health || { echo "Health check failed!"; exit 1; }

echo "==> Cleaning up old releases (keeping last 5)"
ls -dt "$APP_DIR/releases"/* | tail -n +6 | xargs rm -rf

echo "==> Deployment complete: $RELEASE"
Tip:

Klíčem je ln -sfn, který atomicky nahrazuje symbolický odkaz. Od okamžiku změny symbolického odkazu budou všechny nové worker procesy spuštěné systemd obsluhovat nový kód. Stávající požadavky v průběhu zpracování pokračují na starých workerech, dokud neskončí.

Krok 4: Ověření nulového výpadku

bash
# Run this in a second terminal while deploying
# It will report immediately if any request fails
while true; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://example.com/health)
  echo "$(date +%H:%M:%S) — HTTP $STATUS"
  sleep 0.2
done

Měli byste vidět nepřetržitý proud řádků „HTTP 200" – i během nasazení. Pokud uvidíte 502 nebo 503, zkontrolujte nastavení proxy_next_upstream a ujistěte se, že vaše aplikace začne rychle přijímat připojení (v rámci okna proxy_connect_timeout).

Kdy přejít na Kubernetes

Tento přístup funguje výborně pro týmy provozující jeden až několik serverů s jednou aplikací. Když potřebujete multi-node nasazení, automatické horizontální škálování nebo komplexní service meshe, Kubernetes si svoji komplexnost zaslouží. Do té doby je Nginx + systemd jednodušší, rychlejší pro ladění a nevyžaduje žádný cluster k údržbě.