Perché combinare sistemato con Docker per i dislocamenti di produzione

Mentre Docker fornisce le politiche di riavvio (), queste politiche funzionano solo fino a quando il daemon Docker è in esecuzione. Systemd – il sistema init utilizzato da Ubuntu, Debian, Fedora, CentOS, e la maggior parte delle distribuzioni Linux moderne – lo prende ulteriormente gestendo il ciclo di vita del sistema Docker daemon disponibile e può iniziare i container.

  • Ordine di avvio garantito tramite direttive di dipendenza (ad esempio, dopo network.target, dopo docker.service)
  • Unified logging via , rendendo il debugging semplice
  • Controllo a grana fine sui limiti delle risorse (CPU, memoria, I/O) utilizzando direttive di unità di sistema
  • Riavviare automaticamente il guasto con i limiti di ritardo configurabili e di scoppio
  • Supporto per l'attivazione delle prese e l'avvio del timed

Incarnando ogni contenitore Docker in un file di servizio systemd, i team di operazioni ottengono un'interfaccia coerente per avviare, arrestare e monitorare i container, riducendo l'affidabilità agli script ad-hoc e all'intervento manuale.

Creazione di un servizio di sistema per un contenitore singolo Docker

L'approccio standard prevede la scrittura di un file di unità di servizio che chiama Docker comandi per eseguire e fermare il contenitore.

Passo 1: Scrivere il file Unità di servizio

Creare un file chiamato []]. Utilizzare il seguente modello come punto di partenza:

[Unit]
Description=My Application Container
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Restart=always
RestartSec=10
StartLimitBurst=3
ExecStartPre=-/usr/bin/docker kill myapp
ExecStartPre=-/usr/bin/docker rm myapp
ExecStart=/usr/bin/docker run --rm --name myapp \
 -e DB_HOST=10.0.1.50 \
 -e DB_PORT=5432 \
 -v /data/myapp:/app/data \
 -p 8080:8080 \
 myregistry/myapp:latest
ExecStop=/usr/bin/docker stop -t 10 myapp
ExecStopPost=-/usr/bin/docker rm myapp

[Install]
WantedBy=multi-user.target

Spiegazione delle direttive chiave:

  • [][]] – assicura che il demone di Docker stia correndo prima di iniziare il contenitore.
  • ][] – se Docker viene fermato, questo servizio si ferma pure.
  • [][[]]] – pulisce ogni contenitore di avamposto da un precedente run (il [ prefisso significa che i guasti qui non sono grassi).
  • []][]] – usa [] per rimuovere automaticamente il contenitore quando si ferma.
  • [] – con grazia ferma il contenitore con un timeout (10 secondi).
  • ][] – riavvia il contenitore indipendentemente dal codice di uscita.
  • ][] – aspetta 10 secondi prima di riavviare.
  • [][]] – i limiti riavviano a 3 tentativi per intervallo (default 10 secondi) per evitare i loop di riavvio.

Fase 2: Attivare e avviare il servizio

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service

] dice sistemato per rileggere i file di servizio. crea il collegamento simlink in modo che il servizio inizia sul boot.

Gestione del Servizio con Comandi Standard Systemd

Una volta che il servizio è in esecuzione, si controlla come qualsiasi altro servizio di sistema:

  • Inizio:
  • Smettete:
  • Riavviare ]
  • Status:
  • Logs: (segue i registri in diretta)

Modelli di configurazione avanzata

Le distribuzioni di produzione richiedono spesso più di un semplice [. Di seguito sono i miglioramenti comuni che è possibile aggiungere ai file di servizio systemd.

Variabili dell'ambiente di passaggio

Non è consigliabile utilizzare segreti o configurazione di codifica rigida nel file di servizio, ma utilizzare un file di ambiente separato:

[Service]
EnvironmentFile=-/etc/myapp/env.conf
ExecStart=/usr/bin/docker run --rm --name myapp \
 --env-file /etc/myapp/env.conf \
 myregistry/myapp:latest

Il prefisso prima del percorso significa che il servizio inizierà anche se il file non esiste (utile durante la configurazione iniziale).

Collegamenti di rete e porta

Per i contenitori che devono comunicare tra loro sullo stesso host, considerare l'utilizzo o delle reti di ponti definite dall'utente.

ExecStart=/usr/bin/docker run --rm --name web \
 --network=my-net \
 -p 443:443 \
 -v /etc/ssl/certs:/etc/ssl/certs:ro \
 myregistry/web:latest

Se si utilizza una rete personalizzata, assicurarsi che la rete esista prima dell'avvio del servizio. È possibile aggiungere un comando per crearlo:

ExecStartPre=/usr/bin/docker network create my-net

Dipendenze intercontainer

Quando un contenitore richiede un altro essere pronto prima di iniziare (ad esempio, un'app web in attesa di un database), sistemato può far rispettare l'ordine.

[Unit]
Description=Web App Container
After=network-online.target docker.service mydb.service
BindsTo=mydb.service

lega il ciclo di vita dell'app web al contenitore del database – se il database si ferma, anche l'app web viene fermata.

Controllo della salute e disponibilità

I controlli sanitari del docker possono essere integrati con il sistema per prevenire la disponibilità di servizi prematuri.

ExecStartPost=/usr/local/bin/wait-for-health.sh http://localhost:8080/health 30

Lo script dovrebbe uscire 0 solo quando il contenitore è sano. Se non riesce, sistemato segna l'unità come fallito.

Limiti di risorse tramite Systemd

È possibile limitare la CPU e la memoria di un contenitore a livello di cgroup senza bandiere di risorse proprie di Docker. Ciò è particolarmente utile quando si esegue più contenitori su un unico host:

[Service]
MemoryMax=512M
CPUQuota=50%

Queste impostazioni creano un limite difficile che sistemato fa rispettare indipendentemente da Docker.

Gestione di contenitori multipli: Sistemato vs. Docker Compose

Per un piccolo numero di contenitori (ad esempio, 2-5), i singoli file di servizio sistemati sono semplici e manutenbili. Tuttavia, quando un progetto coinvolge molti servizi interconnessi, Docker Compose diventa più conveniente. È ancora possibile utilizzare sistemato per orchestrare l'intero stack Docker Compose creando un'unica unità di servizio che chiama . Esempio:

[Unit]
Description=My Application Stack
After=network-online.target docker.service
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/docker-compose up -d
ExecStop=/usr/local/bin/docker-compose down

[Install]
WantedBy=multi-user.target

Questo approccio ti dà la semplicità di Compose per definire i servizi combinati con la gestione del ciclo di vita di sistema. Si noti che [] viene utilizzato perché esce immediatamente. mantiene l'unità in uno stato “attivo” fino a quando è chiamato.

Quale metodo si dovrebbe scegliere?

  • Servizi sistemati individuali[[] – migliori per applicazioni legacy, servizi con ordini di avvio rigorosi, o quando hai bisogno di limiti di risorse per-container.
  • Docker Compose con systemd[] – ideale per le pile di microservizi dove le dipendenze vengono gestite internamente da Compose, e si desidera che un'unità singola gestisca l'intero gruppo.

Risoluzione dei problemi Problemi comuni

Anche con un'attenta configurazione, si possono incontrare problemi. Di seguito sono frequenti insidie e le loro soluzioni.

Servizio falli con “Cannot collegare al demone Docker”

Questo significa che il servizio inizia prima che la presa Docker sia pronta. Assicurare che la vostra unità contenga e []. Verificare anche che il daemon Docker sia abilitato: .

Riavviiamento contenitore in un Loop

Se il contenitore esce immediatamente, sistemato continuerà a riavviarlo secondo [] e []. Controllare i registri dei container con [. Aumentare ] (ad esempio, 30 secondi) e impostare ] per evitare un loop occupato.

Servizio non smette di pulire

] può lasciare il contenitore in esecuzione. Verificare che [[] utilizza il nome del contenitore corretto.

Variabili dell'ambiente non caricati

Se si utilizza , confermare il file esiste ed è leggibile per root. Evitare di citare i problemi – righe sistemate citazioni da valori variabili. Per iniezione segreta, prendere in considerazione l'utilizzo di credenziali di sistema o un manager segreto dedicato.

Considerazioni di sicurezza

Eseguire contenitori Docker attraverso systemd solleva alcuni punti di sicurezza:

  • Eseguire sempre il servizio systemd come utente non root se possibile (utilizzare [] e [[] direttive, ma assicurarsi che l'utente abbia accesso alla presa Docker o eseguito in modalità rootless).
  • Evitare di utilizzare in unità di sistema, a meno che non sia assolutamente necessario.
  • Utilizzare i supporti di leghe in sola lettura ([) ogni volta che il contenitore non ha bisogno di scrivere all'host.
  • Leverage systemd’s e ] per indurire l’unità contro le fughe.
[Service]
ProtectSystem=strict
ReadWritePaths=/var/log/myapp
PrivateTmp=true
User=myappuser

Risorse esterne

Per ulteriori informazioni, consultare i riferimenti ufficiali:

Conclusioni

Integrando i container Docker, puoi creare un robusto meccanismo di avvio automatico che si integra perfettamente con il resto del tuo sistema Linux. La scrittura di file di unità di servizio ben strutturati, puoi controllare l'ordine di avvio, gestire le dipendenze, impostare i limiti delle risorse e monitorare i log utilizzando strumenti che già conosce il tuo team operativo.

Inizia con un semplice file di unità, testalo accuratamente, quindi sovrapponi le opzioni avanzate come i file di ambiente, i controlli sanitari e l'indurimento della sicurezza. Con questo approccio, i contenitori Docker sopravvivranno ai riavviiiimenti, agli crash e alle modifiche di configurazione senza intervento manuale, liberando il tuo team di concentrarsi sulle applicazioni di costruzione.