Come mantenere in esecuzione il contenitore Docker dopo l'avvio dei servizi?


156

Ho visto un sacco di tutorial che sembrano fare la stessa cosa che sto cercando di fare, ma per qualche ragione i miei contenitori Docker escono. Fondamentalmente, sto installando un web server e alcuni demoni all'interno di un container Docker. Faccio le parti finali di questo tramite uno script bash chiamato run-all.shche corro attraverso CMD nel mio Dockerfile. run-all.shSomiglia a questo:

service supervisor start
service nginx start

E lo avvio all'interno del mio Dockerfile come segue:

CMD ["sh", "/root/credentialize_and_run.sh"]

Vedo che tutti i servizi si avviano correttamente quando eseguo le cose manualmente (ovvero accedendo all'immagine con -i -t / bin / bash) e tutto sembra funzionare correttamente quando eseguo l'immagine, ma esce una volta finisce di avviare i miei processi. Vorrei che i processi funzionassero indefinitamente e, per quanto ho capito, il contenitore deve continuare a funzionare perché ciò avvenga. Tuttavia, quando corro docker ps -a, vedo:

➜  docker_test  docker ps -a
CONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS                      PORTS               NAMES
c7706edc4189        some_name/some_repo:blah   "sh /root/run-all.sh   8 minutes ago       Exited (0) 8 minutes ago                        grave_jones

Cosa dà? Perché sta uscendo? So che potrei semplicemente mettere un ciclo while alla fine del mio script bash per continuare, ma qual è il modo giusto per impedirgli di uscire?


1
stai esponendo le porte dei servizi all'esterno (opzione -p per eseguire la finestra mobile)? (ovviamente questo non impedisce loro di uscire)
ribamar,

1
Stavo usando ENTRYPOINT nel mio Dockerfile e dopo l'esecuzione dello script definito in ENTRYPOINT (il mio script init), è apparso nei log ma il mio contenitore sembrava essere uscito. Quindi, invece di ENTRYPOINT, ho usato il comando RUN per eseguire lo script e il contenitore è ancora in esecuzione in background.
ypahalajani,

Risposte:


51

Non è proprio così che dovresti progettare i tuoi contenitori Docker.

Quando si progetta un contenitore Docker, è necessario crearlo in modo che sia in esecuzione un solo processo (ovvero è necessario disporre di un contenitore per Nginx e uno per supervisord o l'app in esecuzione); inoltre, tale processo dovrebbe essere eseguito in primo piano.

Il contenitore "uscirà" quando termina il processo stesso (nel tuo caso, quel processo è il tuo script bash).


Tuttavia, se hai davvero bisogno (o desideri) di eseguire più servizi nel tuo contenitore Docker, prendi in considerazione di iniziare da "Immagine base Docker" , che utilizza runitcome processo pseudo-init ( runitrimarrà online mentre Nginx e Supervisor sono in esecuzione), che rimarrà in primo piano mentre gli altri tuoi processi fanno la loro parte.

Hanno documenti sostanziali, quindi dovresti essere in grado di ottenere ciò che stai cercando di fare ragionevolmente facilmente.


1
Puoi spiegare perché dovrei avere un solo servizio in esecuzione? Potrei aggiungere nginx al supervisore se necessario, ma non sono sicuro del perché questo dovrebbe essere necessario.
Eli,

3
@Eli La risposta breve è che funziona così Docker. Docker eseguirà solo un processo (e i relativi figli) per container. Si raccomanda che questo processo sia un vero e proprio processo di applicazione (in modo che se si chiude, Docker lo sa), ma è possibile utilizzare il supervisore come tale processo. Nota che dovrai configurare il supervisore per l'esecuzione in primo piano (cioè non daemonize), che viene fatto tramite l' --nodaemonopzione.
Thomas Orozco,

1
@Eli Questo post sul blog Docker sostiene che l'esecuzione di più processi (e, in generale, la visualizzazione di un contenitore come un "piccolo VPS") non è ottimale. Nel tuo caso, il thread dei commenti sarà probabilmente più pertinente del post sul blog effettivo.
Thomas Orozco,

1
L'immagine di base Docker è una soluzione terribile per molti problemi aziendali perché poche aziende serie usano Ubuntu, preferendo invece l'albero RHEL / Centos.
Software Engineer

9
"Poche aziende serie" sembrano indifendibili. La scelta del sistema operativo sembrerebbe basarsi interamente sul caso d'uso. Ogni data azienda ha molti ambienti diversi tra cui l'utilizzo interno da parte degli sviluppatori, l'utilizzo interno dei dipendenti, il supporto alle vendite, la gestione temporanea, i POC e infine la produzione (e anche questo è un termine vago). Non credo che l'OP abbia menzionato così il loro caso d'uso (mi dispiace essere pignolo) ma questo tipo di commento sembra essere il tipo che diffonde informazioni altamente ponderate senza alcun motivo sul perché.
John Carrell,

157

Se si utilizza un file Docker, provare:

ENTRYPOINT ["tail", "-f", "/dev/null"]

(Ovviamente questo è solo a scopo di sviluppo, non dovresti aver bisogno di mantenere in vita un contenitore a meno che non stia eseguendo un processo ad es. Nginx ...)


5
Stavo usando CMD["sleep", "1d"]ma la tua soluzione sembra migliore
George Pligoropoulos,

@GeorgiosPligoropoulos questo rimarrà bloccato in quella linea; forse correre in background funzionerà
Prashanth Sams,

5
Può anche usare CMD["sleep", "infinity"].
Romain,

6
o "gatto" ma la gente potrebbe dire che è un abuso di animali. xD
Lawphotog,

È possibile completare lo script del punto di accesso con exec tail -f /dev/nullma l'utilizzo tailcome punto di accesso è una risposta errata.
Torsten Bronger,

86

Ho appena avuto lo stesso problema e ho scoperto che se stai eseguendo il tuo container con il -te -dflag, continua a funzionare.

docker run -td <image>

Ecco cosa fanno le bandiere (secondo docker run --help):

-d, --detach=false         Run container in background and print container ID
-t, --tty=false            Allocate a pseudo-TTY

La più importante è la -tbandiera. -dti consente di eseguire il contenitore in background.


3
Non riesco a riprodurre questo. Potresti fornire un esempio? C'è qualcosa di specifico (es: CMD) su Dockerfile di cui abbiamo bisogno per far funzionare tutto ciò?
Matheus Santana,

2
Questo non ha funzionato per me. Ho usato il comando docker logs <image>per assicurarmi che fosse un errore a causare l'uscita del mio contenitore mobile. Lo stato di uscita è 0e l'ultimo output è la conferma che il mio lighttpdserver è in esecuzione:[ ok ] Starting web server: lighttpd.
ob1

Non lavoro con Docker da un po 'di tempo ormai. Quindi è possibile che l'interfaccia della riga di comando sia cambiata e che questo comando non funzioni più.
arne.z,

4
Posso confermare che questo funziona davvero con l'ultima versione della finestra mobile. Se vuoi collegarti in seguito a questa sessione, anche usare -dit funzionerà.
John Hamilton,

1
@Lungo uno script non accetterà un tty, aggiungere exec basho exec shse bash non è installato, alla fine di start.sh. Quindi puoi usare il flag -t
123

43

Il motivo per cui esce è perché lo script della shell viene eseguito prima come PID 1 e quando è completo, PID 1 è sparito e la finestra mobile viene eseguita solo mentre lo è PID 1.

Puoi usare il supervisore per fare tutto, se eseguito con il flag "-n" viene detto di non demonizzare, quindi rimarrà il primo processo:

CMD ["/usr/bin/supervisord", "-n"]

E il tuo supervisord.conf:

[supervisord]
nodaemon=true

[program:startup]
priority=1
command=/root/credentialize_and_run.sh
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=false
startsecs=0

[program:nginx]
priority=10
command=nginx -g "daemon off;"
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.log
autorestart=true

Quindi è possibile avere tutti gli altri processi desiderati e il supervisore gestirà il riavvio di essi, se necessario.

In questo modo potresti usare supervisord nei casi in cui potresti aver bisogno di nginx e php5-fpm e non ha molto senso separarli.


Dove nei documenti si dice se PID 1 termina l'esecuzione del contenitore docker?
8oh8,

@ 8oh8 Ecco come funzionano gli spazi dei nomi dei processi; non è specifico per Docker tanto quanto "la cosa alla base di tutti i contenitori". Da man7.org/linux/man-pages/man7/pid_namespaces.7.html :If the "init" process of a PID namespace terminates, the kernel terminates all of the processes in the namespace via a SIGKILL signal. This behavior reflects the fact that the "init" process is essential for the correct operation of a PID namespace.
dannysauer,

40

puoi eseguire il tutto catsenza alcun argomento, come menzionato da bro @ Sa'ad per mantenere il contenitore funzionante [in realtà non facendo altro che aspettare l'input dell'utente] (il plugin Docker di Jenkins fa la stessa cosa)


oltre alla mia risposta: ma capisci che docker-compose (non demone) viene usato per mostrarti il ​​flusso di lavoro del tuo contenitore, quindi potrebbe essere utile adattare i file di registro dei tuoi servizi avviati. salute
Serge Velikanov,

1
o cat. il plugin docker di jenkin lo fa.
Sa'ad,

12

Assicurati di aggiungere daemon off;nginx.conf o eseguirlo CMD ["nginx", "-g", "daemon off;"]come da immagine nginx ufficiale

Quindi utilizzare quanto segue per eseguire sia supervisore come servizio che nginx come processo in primo piano che impedirà la chiusura del contenitore

service supervisor start && nginx

In alcuni casi dovrai avere più di un processo nel tuo contenitore, quindi forzare il contenitore ad avere esattamente un processo non funzionerà e può creare più problemi nella distribuzione.

Quindi devi capire i compromessi e prendere la tua decisione di conseguenza.


7

Motivazione:

Non c'è nulla di sbagliato nell'esecuzione di più processi all'interno di un contenitore finestra mobile . Se a uno piace usare la finestra mobile come una VM leggera, così sia. Ad altri piace dividere le loro applicazioni in micro servizi. Me pensa: uno stack LAMP in un contenitore? Semplicemente fantastico.

La risposta:

Stick con una buona immagine di base come l' immagine di base di fusione . Potrebbero essercene altri. Per favore, commenta.

E questo è l'ennesimo appello per il supervisore. Perché l'immagine di base di phusion fornisce supervisore oltre ad alcune altre cose come cron e impostazioni locali. Roba che ti piace avere configurato quando esegui una VM così leggera. Per quello che vale, fornisce anche connessioni ssh nel contenitore.

L'immagine di phusion stessa si avvierà e continuerà a funzionare se si emette questa istruzione di esecuzione docker di base:

moin@stretchDEV:~$ docker run -d phusion/baseimage
521e8a12f6ff844fb142d0e2587ed33cdc82b70aa64cce07ed6c0226d857b367
moin@stretchDEV:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS
521e8a12f6ff        phusion/baseimage   "/sbin/my_init"     12 seconds ago      Up 11 seconds

O morto semplice:

Se un'immagine di base non fa per te ... Per la rapida CMD per mantenerla in esecuzione suppongo qualcosa di simile per bash:

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"

O questo per busybox:

CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

Questo è carino, perché uscirà immediatamente su a docker stop. Semplicemente sleepo catimpiegherà qualche secondo prima che il contenitore esca.


Ho personalizzato l'immagine di base centos7 per caricare PostgreSQL 11. Si inizia con una chiamata a / usr / pgsql-11 / bin / pg_ctl ma pg_ctl esce quando il server è in esecuzione. Il tuo suggerimento di usare trap ha funzionato alla grande; è l'ultima riga della mia sceneggiatura pgstartwait.sh
Alchemistmatt

6

Cattura il PID del processo ngnix in una variabile (ad esempio $ NGNIX_PID) e alla fine del file entrypoint do

wait $NGNIX_PID 

In questo modo, il contenitore dovrebbe funzionare fino a quando ngnix è in vita, quando ngnix si ferma, anche il contenitore si ferma


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.