¿Cómo mantener el contenedor Docker en ejecución después de iniciar los servicios?

195

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 se cierran. 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 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, accedo a la imagen con -i -t / bin / bash), y todo parece que se ejecuta correctamente cuando ejecuto la imagen, pero sale una vez termina de poner en marcha mis procesos. Me gustaría que los procesos se ejecutaran indefinidamente y, según tengo entendido, el contenedor debe 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é es emocionante? 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?

3
  • 1
    ¿está exponiendo los puertos de los servicios al exterior (opción -p para ejecutar la ventana acoplable)? (por supuesto, esto no les impedirá salir)
    ribamar
    18 de febrero de 2016 a las 17:04
  • 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 todavía se está ejecutando en segundo plano. 12 de mayo de 2018 a las 16:03
  • ¿Responde esto a tu pregunta? El contenedor de Docker se detendrá automáticamente después de "docker run -d"
    shmuels
    10 nov.20 a las 18:38
57

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

Al diseñar un contenedor Docker, se supone que debe compilarlo de manera que solo haya un proceso en ejecución (es decir, debe tener un contenedor para Nginx y otro para supervisord o la aplicación que se está ejecutando); además, ese proceso debería ejecutarse en primer plano.

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


Sin embargo, si realmente necesita (o desea) ejecutar varios servicios en su contenedor Docker, considere comenzar desde "Imagen base de Docker" , que se usa runitcomo un proceso pseudo-init ( runitpermanecerá en línea mientras se ejecuten 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 con razonable facilidad.

7
  • 3
    ¿Puede explicar por qué debería tener un solo servicio en ejecución? Podría agregar nginx al supervisor si es necesario, pero no estoy seguro de por qué debería ser necesario.
    Eli
    10 de septiembre de 2014 a las 21:36
  • 3
    @Eli La respuesta corta es que así es como funciona Docker. Docker solo ejecutará un proceso (y sus hijos) por contenedor. Se recomienda que este proceso sea un proceso de aplicación real (de modo que, si sale, Docker lo sepa), pero de hecho puede usar el 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. 10 de septiembre de 2014 a las 21:40
  • 1
    @Eli Esta publicación de blog de Docker argumenta que ejecutar múltiples procesos (y, en términos generales, ver un contenedor como un "pequeño VPS") es subóptimo. En su caso, el hilo de comentarios probablemente será más relevante que la publicación del blog real. 10 de septiembre de 2014 a las 22:45
  • 1
    La imagen base de Docker es una solución terrible para muchos problemas empresariales porque pocas empresas serias usan ubuntu y prefieren el árbol RHEL / Centos. 5/10/2016 a las 11:26
  • 17
    "Pocas empresas 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, (lamento ser quisquilloso), pero este tipo de comentario parece ser el tipo que difunde información altamente obstinada sin un argumento de por qué. 10/10/2018 a las 12:22
260

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 ...)

6
  • 9
    Estaba usando CMD["sleep", "1d"]pero tu solución parece mejor 28/04/18 a las 8:55
  • @GeorgiosPligoropoulos esto quedará atascado en esa línea; tal vez funcionar en segundo plano 20/06/19 a las 11:01
  • 20
    También se puede utilizar CMD["sleep", "infinity"].
    Romain
    27/07/19 a las 8:49
  • dieciséis
    o 'gato', pero la gente podría decir que es abuso animal. xD 16/09/19 a las 13:27
  • 1
    Puede terminar su guión de punto de entrada con, exec tail -f /dev/nullpero usarlo tailcomo punto de entrada es una respuesta incorrecta. 19 de mayo de 2020 a las 10:12
110

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

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. -dsolo le permite ejecutar el contenedor en segundo plano.

9
  • 3
    No puedo reproducir esto. ¿Podría darnos un ejemplo? ¿Hay algo específico (por ejemplo: CMD) sobre Dockerfile que necesitemos para que esto funcione? 21/06/2017 a las 20:18
  • 2
    Esto no funcionó para mí. Usé el comando docker logs <image>para asegurarme de que se trataba de un error que hacía que mi contenedor docker saliera. El estado de salida es 0y el último resultado es la confirmación de que mi lighttpdservidor se está ejecutando:[ ok ] Starting web server: lighttpd.
    ob1
    25/07/2017 a las 15:39
  • No he estado trabajando con Docker por un tiempo. Entonces, es posible que la interfaz de línea de comandos haya cambiado y que este comando ya no funcione.
    arne.z
    25/07/2017 a las 18:31
  • 6
    Puedo confirmar que esto realmente funciona con la última versión de Docker. Si desea adjuntarlo más tarde a esta sesión, el uso de -dit también funcionará. 18 de septiembre de 2017 a las 7:33
  • 1
    @Largo un script no aceptará un tty, agregar exec basho exec shsi bash no está instalado, al final de start.sh. Entonces puedes usar la bandera -t
    123
    6/12/19 a las 11:24
45

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 PID 1 lo es.

Puede usar el supervisor para hacer todo, si se ejecuta con la bandera "-n", se le dice que no haga demonización, por lo que permanecerá como el primer proceso:

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

Y su 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

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

De esa manera, podría usar supervisord en los casos en que necesite nginx y php5-fpm y no tiene mucho sentido separarlos.

2
  • ¿En qué parte de los documentos dice si PID 1 finaliza el contenedor de la ventana acoplable deja de ejecutarse?
    8oh8
    21 oct 2018 a las 20:48
  • 1
    @ 8oh8 Así es esencialmente como funcionan los espacios de nombres de procesos; no es tanto específico de Docker como "lo que subyace a 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. 11/12/18 a las 21:42
44

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

2
  • además de mi respuesta: pero entienda que docker-compose (no demonizado) se usa para mostrarle el flujo de trabajo de su contenedor, por lo que podría ser útil rastrear los archivos de registro de sus servicios iniciados. salud 21/10/2016 a las 12:02
  • 1
    o cat. El complemento Docker de Jenkin lo hace.
    Sa'ad
    15 de marzo de 2018 a las 12:54
19

Motivación:

No hay nada de malo en ejecutar varios procesos dentro de un contenedor de ventana acoplable . Si a uno le gusta usar Docker como una máquina virtual liviana, que así sea. A otros les gusta dividir sus aplicaciones en microservicios. Yo 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 esta es solo otra petición para el supervisor. 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 le gusta configurar cuando ejecuta una máquina virtual tan liviana. Por lo que vale, también proporciona conexiones ssh en el contenedor.

La imagen de phusion en sí se iniciará y seguirá ejecutándose si emite esta declaración básica de ejecución de la ventana acoplable:

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

O simplemente muerto:

Si una imagen base no es para usted ... Para que el CMD rápido la mantenga funcionando, supongo que algo como esto 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 a docker stop.

Simplemente, sleepo cattomará unos segundos antes de que el contenedor sea asesinado por la fuerza por el acoplador .

Actualizaciones

Como respuesta a Charles Desbiens sobre la ejecución de múltiples procesos en un contenedor:

Ésta es una opinión. Y los doctores apuntan en esta dirección. Una cita: "Está bien tener varios procesos, pero para obtener el máximo beneficio de Docker, evite que un contenedor sea responsable de varios aspectos de su aplicación general". Seguro que obviamente es mucho más poderoso dividir su complejo servicio en múltiples contenedores. Pero hay situaciones en las que puede ser beneficioso seguir la ruta de un contenedor. Especialmente para electrodomésticos. La imagen de GitLab Docker es mi ejemplo favorito de contenedor multiproceso. Facilita la implementación de este complejo sistema. No hay forma de configuración incorrecta. GitLab conserva todo el control sobre su dispositivo. Ganar-ganar.

3
  • Personalicé la imagen base de centos7 para cargar PostgreSQL 11. Empiece con una llamada a / usr / pgsql-11 / bin / pg_ctl pero pg_ctl sale una vez que el servidor se está ejecutando. Tu sugerencia de usar trampa funcionó muy bien; es la última línea de mi script pgstartwait.sh 31/08/19 a las 4:29
  • Es un poco extraño decir que no hay nada de malo en ejecutar múltiples procesos en un solo contenedor, y luego usar esa oración para vincular a documentos que comienzan diciendo que no es la mejor idea ... 4 de agosto a las 20:44
  • @CharlesDesbiens Gracias por tu aporte. Consulte mi respuesta actualizada. 5 de agosto a las 8:19
13

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 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 forzar 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 una decisión en consecuencia.

5

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 ejecutarse hasta que ngnix esté vivo, cuando ngnix se detiene, el contenedor también se detiene

1

Además de tener algo como: ENTRYPOINT ["tail", "-f", "/dev/null"]en su archivo de la ventana acoplable, también debe ejecutar el contenedor de la ventana acoplable con la -tdopción. Esto es particularmente útil cuando el contenedor se ejecuta en un m / c remoto. Piense en ello más como si hubiera entrado en un m / c remoto que tiene la imagen y ha iniciado el contenedor. En este caso, cuando salga de la sesión ssh, el contenedor se eliminará a menos que se inicie con la -tdopción. El comando de muestra para ejecutar su imagen sería:docker run -td <any other additional options> <image name>

Esto es válido para la versión de Docker. 20.10.2

0

Hay algunos casos durante el desarrollo en los que aún no hay servicio, pero desea simularlo y mantener vivo el contenedor.

Es muy fácil escribir un marcador de posición de bash que simule un servicio en ejecución:

while true; do
  sleep 100
done

Reemplaza esto por algo más serio a medida que avanza el desarrollo.

-1

¿Qué tal si se utiliza la forma de servicio de supervisión, si está disponible?

servicio YOUR_SERVICE supervisar

Once supervise is successfully running, it will not exit unless it is killed or specifically asked to exit.

Ahorra tener que crear un supervisord.conf