¿Cómo mantener el contenedor Docker ejecutándose después de iniciar los servicios?

156

He visto un montón de tutoriales que parecen hacer lo mismo que estoy tratando de hacer, pero por alguna razón mis contenedores Docker salen. Básicamente, estoy configurando un servidor web y algunos demonios dentro de un contenedor Docker. Hago las partes finales de esto a través de un script de bash llamado run-all.shque ejecuto a través de CMD en mi Dockerfile. run-all.shSe ve como esto:

service supervisor start
service nginx start

Y lo inicio dentro de mi Dockerfile de la siguiente manera:

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

Puedo ver que todos los servicios se inician correctamente cuando ejecuto las cosas manualmente (es decir, acceder a la imagen con -i -t / bin / bash), y todo parece funcionar correctamente cuando ejecuto la imagen, pero se cierra una vez termina de iniciar mis procesos. Me gustaría que los procesos se ejecuten indefinidamente, y hasta donde yo entiendo, el contenedor tiene que seguir ejecutándose para que esto suceda. Sin embargo, cuando corro docker ps -a, veo:

➜  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

¿Lo que da? ¿Por qué está saliendo? Sé que podría poner un bucle while al final de mi script bash para mantenerlo, pero ¿cuál es la forma correcta de evitar que salga?

Eli
fuente
1
¿Está exponiendo los puertos de los servicios al exterior (opción -p para ejecutar Docker)? (por supuesto, esto no impedirá que salgan)
ribamar
1
Estaba usando ENTRYPOINT en mi Dockerfile, y después de que se ejecutó el script definido en ENTRYPOINT (mi script de inicio), apareció en los registros pero mi contenedor parecía estar saliendo. Entonces, en lugar de ENTRYPOINT, usé el comando RUN para ejecutar el script y el contenedor aún se está ejecutando en segundo plano.
ypahalajani

Respuestas:

50

Realmente no es así como debe diseñar sus contenedores Docker.

Al diseñar un contenedor Docker, se supone que debe construirlo de manera que solo se esté ejecutando un proceso (es decir, debe tener un contenedor para Nginx y otro para el supervisor o la aplicación que se está ejecutando); Además, ese proceso debe ejecutarse en primer plano.

El contenedor "saldrá" cuando el proceso mismo salga (en su caso, ese proceso es su script bash).


Sin embargo, si realmente necesita (o desea) ejecutar múltiples servicios en su contenedor Docker, considere comenzar desde "Docker Base Image" , que se utiliza runitcomo un proceso pseudoinicial ( runitpermanecerá en línea mientras se ejecutan Nginx y Supervisor), que permanecerá en primer plano mientras sus otros procesos hacen lo suyo.

Tienen documentos sustanciales, por lo que debería poder lograr lo que está tratando de hacer de manera razonablemente fácil.

Thomas Orozco
fuente
1
¿Puede explicar por qué solo debería tener un servicio en ejecución? Podría agregar nginx al supervisor si fuera necesario, pero no estoy seguro de por qué esto debería ser necesario.
Eli
3
@Eli La respuesta corta es que así es como funciona Docker. Docker solo ejecutará un proceso (y sus elementos secundarios) por contenedor. Se recomienda que este proceso sea un proceso de solicitud real (de modo que si existe, Docker lo sabe), pero de hecho puede usar supervisor como ese proceso. Tenga en cuenta que tendrá que configurar el supervisor para que se ejecute en primer plano (es decir, no demonizar), lo que se hace a través de la --nodaemonopción.
Thomas Orozco
1
@Eli Esta publicación de blog de Docker explica que ejecutar múltiples procesos (y, en términos generales, ver un contenedor como un "VPS pequeño") es subóptimo. En su caso, el hilo de comentarios probablemente será más relevante que la publicación de blog real.
Thomas Orozco
1
La imagen base de Docker es una solución terrible para muchos problemas empresariales porque pocas compañías serias usan ubuntu, prefiriendo en cambio el árbol RHEL / Centos.
Ingeniero de software
9
"Pocas compañías serias" parece indefendible. La elección del sistema operativo parece basarse completamente en el caso de uso. Cualquier empresa tiene muchos entornos diferentes, incluido el uso interno del desarrollador, el uso interno de los empleados, el soporte de ventas, la puesta en escena, los POC y, finalmente, la producción (e incluso ese es un término vago). No creo que el OP haya mencionado su caso de uso, (perdón por ser quisquilloso), pero este tipo de comentario parece ser el tipo que difunde información muy obstinada sin ningún argumento de por qué.
John Carrell
155

Si está utilizando un Dockerfile, intente:

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

(Obviamente, esto es solo para fines de desarrollo, no debería necesitar mantener vivo un contenedor a menos que esté ejecutando un proceso, por ejemplo, nginx ...)

Balas suaves
fuente
55
Estaba usando CMD["sleep", "1d"]pero su solución parece mejor
George Pligoropoulos
@GeorgiosPligoropoulos esto se atascará en esa línea; tal vez correr en segundo plano funcionará
Prashanth Sams
55
También se puede usar CMD["sleep", "infinity"].
Romain
55
o 'gato' pero la gente podría decir que es abuso de animales. xD
lawphotog
Puede terminar su secuencia de comandos de punto de entrada exec tail -f /dev/nullpero usar tailcomo punto de entrada es una respuesta incorrecta.
Torsten Bronger
86

Acabo de tener el mismo problema y descubrí que si está ejecutando su contenedor con la bandera -ty -d, sigue ejecutándose.

docker run -td <image>

Esto es lo que hacen las banderas (según docker run --help):

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

El más importante es la -tbandera. -dsimplemente te permite ejecutar el contenedor en segundo plano.

arne.z
fuente
3
No puedo reproducir esto. ¿Podría dar un ejemplo? ¿Hay algo específico (por ejemplo: CMD) sobre Dockerfile que necesitamos para que esto funcione?
Matheus Santana
2
Esto no funcionó para mí. Utilicé el comando docker logs <image>para asegurarme de que era un error que causaba que mi contenedor acoplable saliera. El estado de salida es 0y la última salida es la confirmación de que mi lighttpdservidor se está ejecutando:[ ok ] Starting web server: lighttpd.
ob1
No he estado trabajando con Docker por un tiempo ahora. Por lo tanto, es posible que la interfaz de la línea de comandos haya cambiado y que este comando ya no funcione.
arne.z
44
Puedo confirmar que esto realmente está funcionando con la última versión de Docker. Si desea adjuntar más tarde a esta sesión, usar -dit también funcionará.
John Hamilton
1
@Largo un script no aceptará un tty, agregar exec basho exec shsi bash no está instalado, al final de start.sh. Entonces puede usar la bandera -t
123
43

La razón por la que sale es porque el script de shell se ejecuta primero como PID 1 y cuando se completa, el PID 1 desaparece y la ventana acoplable solo se ejecuta mientras que el PID 1 lo está.

Puede usar el supervisor para hacer todo, si se ejecuta con el indicador "-n" se le dice que no demonice, por lo que permanecerá como el primer proceso:

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

Y tu supervisor.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

Entonces puede tener tantos otros procesos como desee y el supervisor se encargará de reiniciarlos si es necesario.

De esa forma, podría usar la supervisión en casos en los que necesite nginx y php5-fpm y no tenga mucho sentido separarlos.

Phazei
fuente
¿En qué parte de los documentos dice si el PID 1 finaliza y el contenedor acoplable deja de funcionar?
8oh8
@ 8oh8 Así es como funcionan los espacios de nombres de proceso; no es tan específico de Docker como "lo que subyace en todos los contenedores". De 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

puede ejecutarse catsin ningún argumento como lo menciona bro @ Sa'ad para simplemente mantener el contenedor funcionando [en realidad no haciendo más que esperar la entrada del usuario] (el complemento Docker de Jenkins hace lo mismo)

Serge Velikanov
fuente
además de mi respuesta: pero entiendo que docker-compose (no daemonized) se usa para mostrarle el flujo de trabajo de su contenedor, por lo que puede ser útil ajustar los archivos de registro de sus servicios iniciados. aplausos
Serge Velikanov
1
o cat. El complemento de Docker de Jenkin lo hace.
Sa'ad
12

Asegúrese de agregar daemon off;nginx.conf o ejecutarlo CMD ["nginx", "-g", "daemon off;"]según la imagen oficial de nginx

Luego use lo siguiente para ejecutar el supervisor como servicio y nginx como proceso en primer plano que evitará que el contenedor salga

service supervisor start && nginx

En algunos casos, necesitará tener más de un proceso en su contenedor, por lo que obligar al contenedor a tener exactamente un proceso no funcionará y puede crear más problemas en la implementación.

Por lo tanto, debe comprender las compensaciones y tomar su decisión en consecuencia.

iTech
fuente
7

Motivación:

No hay nada de malo en ejecutar múltiples procesos dentro de un contenedor acoplable . Si a uno le gusta usar Docker como una máquina virtual ligera, que así sea. A otros les gusta dividir sus aplicaciones en micro servicios. Pienso: ¿una pila de LÁMPARAS en un contenedor? Simplemente genial.

La respuesta:

Quédese con una buena imagen base como la imagen base de phusion . Puede haber otros. Por favor comenta.

Y esto es solo otra petición de supervisión. Porque la imagen base de phusion proporciona un supervisor además de algunas otras cosas como la configuración de cron y locale. Cosas que te gustaría tener configuradas cuando ejecutas una máquina virtual tan liviana. Por lo que vale, también proporciona conexiones ssh en el contenedor.

La imagen de phusion en sí misma solo comenzará y seguirá ejecutándose si emite esta declaración básica de ejecución de docker:

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 muy simple:

Si una imagen base no es para ti ... Para que la CMD rápida la siga ejecutando, supongo que algo así para bash:

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

O esto para busybox:

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

Esto es bueno, porque saldrá inmediatamente en un docker stop. Simplemente sleepo cattardará unos segundos antes de que salga el contenedor.

itsafire
fuente
Personalicé la imagen base de centos7 para cargar PostgreSQL 11. Comienza con una llamada a / usr / pgsql-11 / bin / pg_ctl pero pg_ctl sale una vez que el servidor se está ejecutando. Su sugerencia de usar trap funcionó muy bien; es la última línea de mi script pgstartwait.sh
Alchemistmatt
6

Capture el PID del proceso ngnix en una variable (por ejemplo $ NGNIX_PID) y al final del archivo de punto de entrada haga

wait $NGNIX_PID 

De esa manera, su contenedor debería funcionar hasta que ngnix esté vivo, cuando ngnix se detenga, el contenedor también se detendrá

usuario2825611
fuente