Múltiples comandos en la directiva Docker CMD

39

No entiendo lo que sucede cuando intento ejecutar dos comandos en tiempo de ejecución a través de la directiva CMD en `Dockerfile. Supuse que esto debería funcionar:

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

Pero no está funcionando. El contenedor no ha comenzado. Así que tuve que hacerlo así:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

No entiendo. ¿Porqué es eso? ¿Por qué la primera línea no es la correcta? ¿Alguien puede explicarme estas cosas de "formato de shell CMD vs formato JSON, etc."? En palabras simples

Solo para tener en cuenta: lo mismo command:sucedió con la directiva docker-compose.yml, como se esperaba.

Vladan
fuente

Respuestas:

33

Creo que la diferencia podría tener que ver con que el segundo comando realiza el procesamiento de shell, mientras que el primero no. Según la documentación oficial , existe el formulario execy shell, su primer comando es un formulario exec y, por ejemplo, no expande las variables de entorno, mientras que el segundo sí lo hace. Por lo tanto, es posible que, al usar el formulario exec, el comando falle debido a su dependencia del procesamiento de shell. Puede verificar esto ejecutandodocker logs CONTAINERID

Su segundo comando, la forma de shell, es equivalente a -

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

Extractos de la documentación -

Nota: a diferencia del formulario de shell, el formulario exec no invoca un shell de comando. Esto significa que el procesamiento de shell normal no ocurre. Por ejemplo, CMD [ "echo", "$HOME" ]no hará la sustitución de variables en $HOME. Si desea que el proceso de shell use el formulario de cáscara o ejecutar una cáscara directamente, por ejemplo: CMD [ "sh", "-c", "echo", "$HOME" ].

Daniel t.
fuente
Probablemente el comando falló debido a las variables de entorno. ¿Debo seguir usando este execformulario, ya que es el preferido? ¿Por qué se prefiere? ¿O debería usar una shellforma más simple ?
Vladan
Falló porque ejecutar un comando después de otro es una función de shell. Las variables de entorno son un arenque rojo.
Bryan
Si está ejecutando múltiples servicios en Docker, recomendaría usar un administrador de procesos como supervisor. De esa forma, solo se inicia la supervisión en la sección CMD y se encargará de iniciar los servicios. Puede consultar los detalles aquí: docs.docker.com/articles/using_supervisord
Daniel t.
Este es un artículo exacto que estaba leyendo :) Gracias.
Vladan
Todavía no entiendo por qué necesitas hacerlo CMD [ "sh", "-c", "echo", "$HOME"]. ¿Por qué no CMD ["sh", "-c", "echo $HOME"]o, para el caso CMD ["sh -c echo $HOME"],?
sixty4bit
4

No te lo pongas difícil. Simplemente cree un archivo bash "start.sh":

#!/bin/bash

/usr/bin/command2 param1
/usr/bin/commnad1

en su Dockerfile hacer:

ADD start.sh /
RUN chmod +x /start.sh

CMD ["/start.sh"]
Humano digital
fuente
2

La sintaxis JSON de CMD(y RUNy ENTRYPOINT) pasan los argumentos al kernel directamente como un syscall exec. No hay separación del comando de los argumentos por espacios, escape de comillas, redirección de E / S, sustitución de variables, canalización entre comandos, ejecución de comandos múltiples, etc., en la llamada al sistema ejecutivo. El syscall solo toma el ejecutable para ejecutarse y la lista de argumentos para pasar a ese ejecutable, y lo ejecuta.

Los caracteres como $expandir variables, ;separar comandos, (espacio) para separar argumentos &&y ||encadenar comandos, >para redireccionar resultados, |canalizar entre comandos, etc., son todas características del shell y necesitan algo como /bin/sho /bin/bashpara interpretarlas e implementarlas.


Si cambia a la sintaxis de cadena de CMD, Docker ejecutará su comando con un shell:

CMD /etc/init.d/nullmailer start ; /usr/sbin/php5-fpm

De lo contrario, su segunda sintaxis hace exactamente lo mismo:

CMD ["sh", "-c", "/etc/init.d/nullmailer start ; /usr/sbin/php5-fpm"]

Tenga en cuenta que no recomiendo ejecutar múltiples comandos de esta manera dentro de un contenedor ya que no hay manejo de errores si su primer comando falla, especialmente si se ejecuta en segundo plano. También deja un shell ejecutándose como pid 1 dentro del contenedor, lo que interrumpirá el manejo de la señal, lo que dará como resultado un retraso de 10 segundos y la destrucción de su contenedor por parte del acoplador. El manejo de la señal se puede mitigar utilizando el execcomando de shell :

CMD /etc/init.d/nullmailer start ; exec /usr/sbin/php5-fpm

Sin embargo, el manejo de procesos que fallan silenciosamente en segundo plano requiere que cambie a algún tipo de administrador multiproceso como supervisor, o preferiblemente divida su aplicación en múltiples contenedores y los implemente con algo como docker-compose.

BMitch
fuente
1

Supongo que el primer comando falla porque en la forma DOCKER CMD, solo se ejecuta el primer parámetro, el resto se alimenta a este comando.

La segunda forma funciona porque todos los comandos están separados por ";" son alimentados al comando sh, que los ejecuta.

devrimbaris
fuente
1

No creo que debas poner una coma después del "inicio"

En lugar de usar

CMD ["/etc/init.d/nullmailer", "start", ";", "/usr/sbin/php5-fpm"]

tratar

CMD ["/etc/init.d/nullmailer", "start", "/usr/sbin/php5-fpm"]

como Docker usa "sh -c", el comando anterior se ejecutará de la siguiente manera

/etc/init.d/nullmailer start
/etc/init.d/nullmailer /usr/sbin/php5-fpm
vedat
fuente
La sintaxis json no ejecuta comandos con un shell, no existe sh -cen ese escenario.
BMitch