Come eseguire più servizi in un contenitore Docker
Pubblicato: 2022-06-30Docker è una tecnologia per imballare i componenti della tua pila come contenitori isolati. È pratica comune eseguire ciascuno dei processi nel proprio contenitore, creando una netta divisione tra i componenti. Ciò migliora la modularità e consente di accedere ai vantaggi di scalabilità della containerizzazione.
Possono ancora verificarsi situazioni in cui desideri eseguire più servizi all'interno di un singolo contenitore. Sebbene ciò non sia naturale nell'ecosistema Docker, mostreremo alcuni approcci diversi che puoi utilizzare per creare contenitori con più di un processo di lunga durata.
Identificazione del problema
I contenitori Docker eseguono un unico processo in primo piano. Questo è definito dalle istruzioni ENTRYPOINT
e CMD
dell'immagine. ENTRYPOINT
viene impostato all'interno del Dockerfile
di un'immagine mentre CMD
può essere sovrascritto durante la creazione di contenitori. I contenitori si arrestano automaticamente quando il processo in primo piano termina.
Puoi avviare altri processi dal CMD
ma il contenitore rimarrà in esecuzione solo mentre il processo in primo piano originale è attivo. Mantenere il container operativo attraverso la durata combinata di due servizi indipendenti non è direttamente possibile utilizzando il meccanismo ENTRYPOINT/CMD
.
Avvolgimento di più processi in un unico punto di ingresso
Gli script wrapper sono la soluzione più semplice al problema. Puoi scrivere uno script che avvii tutti i tuoi processi e attenda che finiscano. L'impostazione dello script come Docker ENTRYPOINT
lo eseguirà come processo in primo piano del contenitore, mantenendo il contenitore in esecuzione fino all'uscita di uno degli script racchiusi.
#!/bin/bash /opt/primo processo & /opt/secondo processo & aspetta -n uscita $?
Questo script avvia i file binari /opt/first-process
e /opt/second-process
all'interno del contenitore. L'uso di &
consente allo script di continuare senza attendere l'uscita di ogni processo. wait
viene utilizzato per sospendere lo script fino al termine di uno dei processi. Lo script esce quindi con il codice di stato emesso dallo script finito.
Questo modello fa sì che il contenitore esegua sia il first-process
che second-process
fino all'uscita di uno di essi. A quel punto, il contenitore si arresterà, anche se l'altro processo potrebbe essere ancora in esecuzione.
Per utilizzare questo script, modifica ENTRYPOINT
e CMD
dell'immagine Docker per renderla il processo in primo piano del contenitore:
ENTRYPOINT ["/bin/sh"] CMD [./percorso/di/script.sh"]
L'opzione del contenitore --init
Una sfida nella gestione dei processi dei container è la pulizia efficace quando escono. Docker esegue il tuo CMD
come ID processo 1, rendendolo responsabile della gestione dei segnali e dell'eliminazione degli zombi. Se il tuo script non ha queste capacità, potresti ritrovarti con processi figlio orfani persistenti all'interno del tuo contenitore.
Il comando docker run
ha un flag --init
che modifica il punto di ingresso per utilizzare tini
come PID 1. Questa è un'implementazione minima del processo init che esegue il tuo CMD
, gestisce l'inoltro del segnale e raccoglie continuamente zombi.
Vale la pena usare --init
se si prevede di generare molti processi e non si desidera gestire manualmente la pulizia. Tini è un gusto leggero init progettato per i contenitori. È molto più piccolo di alternative a tutti gli effetti come systemd
e upstart
.
Utilizzo di un Process Manager dedicato
Lo scripting manuale diventa rapidamente non ottimale quando hai molti processi da gestire. L'adozione di un process manager è un altro modo per eseguire diversi servizi all'interno dei contenitori Docker. Il responsabile del processo diventa il tuo ENTRYPOINT
e ha la responsabilità di avviare, mantenere e pulire dopo i processi di lavoro.
Ci sono diverse opzioni disponibili quando si implementa questo approccio. supervisord
è una scelta popolare che può essere facilmente configurata tramite un file /etc/supervisor/conf.d/supervisord.conf
:
[programma: apache2] comando=/usr/sbin/apache2 -DFOREGROUND [programma: mysqld] comando=/usr/sbin/mysqld_safe
Questo file di configurazione configura supervisord
per avviare Apache e MySQL. Per utilizzarlo in un contenitore Docker, aggiungi tutti i pacchetti richiesti all'immagine, quindi copia il file di configurazione del supervisord
nella posizione corretta. Imposta supervisord
come CMD
dell'immagine per eseguirlo automaticamente all'avvio dei contenitori.
DA ubuntu: più recente ESEGUI apt-get install -y apache2 mysql-server supervisor COPIA supervisord.conf /etc/supervisor/conf.d/supervisord.conf ENTRYPOINT ["/bin/sh"] CMD ["/usr/bin/supervisore"]
Poiché supervisord
viene eseguito continuamente, non è possibile arrestare il contenitore quando uno dei processi monitorati si chiude. Un'opzione alternativa è s6-overlay
che ha questa capacità. Utilizza un modello di servizio dichiarativo in cui inserisci gli script di servizio direttamente in /etc/services.d
:
# Aggiungi s6-overlay alla tua immagine AGGIUNGI https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp ESEGUI tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz ESEGUI printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run ESEGUI chmod +x /etc/services.d/first-service/run # Usa s6-overlay come punto di ingresso della tua immagine ENTRYPOINT ["/init"]
È possibile aggiungere uno script di finish
eseguibile all'interno delle directory del servizio per gestire l'arresto del contenitore con docker stop
. s6-overlay
eseguirà automaticamente questi script quando il suo processo riceve un segnale TERM
dovuto al comando di stop
.
Gli script Finish ricevono il codice di uscita del loro servizio come primo argomento. Il codice è impostato su 256 quando il servizio viene interrotto a causa di un segnale non rilevato. Lo script deve scrivere il codice di uscita finale in /run/s6-linux-init-container-results/exitcode
; s6-overlay legge questo file ed esce con il valore all'interno, facendo sì che quel codice venga utilizzato come codice di arresto del contenitore.
#!/bin/sh echo "$1" > /run/s6-linux-init-container-results/exitcode
Quando dovresti eseguire più processi in un container?
Questa tecnica viene utilizzata al meglio con processi strettamente accoppiati che non è possibile separare per essere eseguiti come contenitori indipendenti. Potresti avere un programma che si basa su un'utilità di supporto in background o un'applicazione monolitica che esegue la propria gestione dei singoli processi. Le tecniche mostrate sopra possono aiutarti a containerizzare questi tipi di software.
L'esecuzione di più processi in un contenitore dovrebbe comunque essere evitata ove possibile. Attenersi a un unico processo in primo piano massimizza l'isolamento, impedisce ai componenti di interferire tra loro e migliora la tua capacità di eseguire il debug e testare pezzi specifici. Puoi ridimensionare i componenti individualmente utilizzando gli agenti di orchestrazione dei contenitori, offrendoti la flessibilità per eseguire più istanze dei processi che richiedono più risorse.
Conclusione
I contenitori di solito hanno un processo in primo piano e funzionano per tutto il tempo in cui è attivo. Questo modello si allinea alle best practice di containerizzazione e ti consente di trarre il massimo vantaggio dalla tecnologia.
In alcune situazioni potrebbe essere necessario eseguire più processi in un contenitore. Poiché tutte le immagini hanno in definitiva un unico punto di ingresso, è necessario scrivere uno script wrapper o aggiungere un gestore di processi che si assuma la responsabilità di avviare i file binari di destinazione.
I gestori di processo ti danno tutto ciò di cui hai bisogno ma gonfiano le tue immagini con pacchetti e configurazioni extra. Gli script wrapper sono più semplici ma potrebbe essere necessario associarli al flag --init
di Docker per prevenire la proliferazione del processo zombie.