Commandes multiples dans la directive Docker CMD

39

Ne comprenant pas ce qui se passe lorsque j'essaie d'exécuter deux commandes au moment de l'exécution via la directive CMD dans `Dockerfile. J'ai supposé que cela devrait fonctionner:

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

Mais ça ne marche pas. Le conteneur n'a pas commencé. Donc je devais le faire comme ça:

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

Je ne comprends pas. Pourquoi donc? Pourquoi la première ligne n'est pas la bonne façon? Quelqu'un peut-il m'expliquer ces trucs "format shell CMD vs format JSON, etc."? En mots simples.

Juste pour noter - le même était avec command:directive dans docker-compose.yml, comme prévu.

Vladan
la source

Réponses:

33

Je crois que la différence pourrait avoir à faire avec, la deuxième commande effectue le traitement du shell, tandis que la première ne le fait pas. Selon la documentation officielle , il existe le formulaire execet shell, votre première commande est un formulaire exec et, par exemple, elle ne développe pas les variables d'environnement, contrairement à la seconde. Il est donc possible qu'en utilisant le formulaire exec, la commande échoue en raison de sa dépendance au traitement du shell. Vous pouvez vérifier cela en exécutantdocker logs CONTAINERID

Votre seconde commande, la forme shell, est équivalente à -

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

Extraits de la documentation -

Remarque: contrairement au formulaire shell, le formulaire exec n'invoque pas un shell de commande. Cela signifie que le traitement shell normal ne se produit pas. Par exemple, CMD [ "echo", "$HOME" ]ne fera pas de substitution de variable sur $HOME. Si vous souhaitez que le traitement shell puis utilisez soit la forme de la coque ou exécuter un shell directement, par exemple: CMD [ "sh", "-c", "echo", "$HOME" ].

Daniel t.
la source
La commande a probablement échoué à cause de variables d'environnement. Devrais-je toujours utiliser ce execformulaire, car c'est le formulaire préféré? Pourquoi c'est préféré? Ou devrais-je utiliser un shellformulaire plus simple ?
Vladan
Il a échoué car l'exécution d'une commande après l'autre est une fonction shell. Les variables d'environnement sont un hareng rouge.
Bryan
Si vous utilisez plusieurs services dans Docker, je vous recommanderais d'utiliser un gestionnaire de processus tel que supervisor. De cette façon, vous ne lancez que supervisord dans la section CMD, qui se chargera de lancer les services. Vous pouvez vérifier les détails ici - docs.docker.com/articles/using_supervisord
Daniel t.
Ceci est un article exact que je lisais :) Merci.
Vladan
Je ne comprends toujours pas pourquoi tu dois le faire CMD [ "sh", "-c", "echo", "$HOME"]. Pourquoi pas CMD ["sh", "-c", "echo $HOME"]ou d'ailleurs CMD ["sh -c echo $HOME"]?
Sixty4bit
4

Ne rend pas les choses difficiles pour toi. Il suffit de créer un fichier bash "start.sh":

#!/bin/bash

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

dans votre Dockerfile faire:

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

CMD ["/start.sh"]
Humain numérique
la source
2

La syntaxe json de CMD(et RUNet ENTRYPOINT) transmet les arguments au noyau directement en tant qu'exec syscall. Il n'y a pas de séparation de la commande des arguments par des espaces, d'échappements de guillemets, de redirection IO, de substitution de variable, de canalisation entre les commandes, d'exécution de plusieurs commandes, etc., dans le syscall de exec. L'appel système ne prend que l'exécutable à exécuter et la liste des arguments à transmettre à cet exécutable, et il l'exécute.

Les caractères tels que $développer les variables, ;séparer les commandes, (espace) séparer les arguments &&et ||chaîner les commandes, >rediriger la sortie, |diriger entre les commandes, etc., sont autant de fonctionnalités du shell et nécessitent quelque chose comme /bin/shou /bin/bashpour les interpréter et les implémenter.


Si vous passez à la syntaxe de chaîne de CMD, docker exécutera votre commande avec un shell:

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

Sinon, votre deuxième syntaxe fait exactement la même chose:

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

Notez que je ne recommande pas d'exécuter plusieurs commandes de cette manière à l'intérieur d'un conteneur, car il n'y a pas de traitement d'erreur si votre première commande échoue, surtout si elle s'exécute en arrière-plan. Vous laissez également un shell s'exécutant en tant que pid 1 à l'intérieur du conteneur, ce qui interrompra la gestion des signaux, ce qui entraînera un délai de 10 secondes et une destruction inutile de votre conteneur par le menu fixe. Le traitement du signal peut être atténué en utilisant la execcommande shell :

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

Cependant, la gestion silencieuse des processus en arrière-plan nécessite que vous passiez à une sorte de gestionnaire multi-processus tel que supervisord ou, de préférence, divisez votre application en plusieurs conteneurs et les déployez avec un logiciel comme docker-compose.

BMitch
la source
1

Je suppose que la première commande échoue car sous la forme DOCKER CMD, seul le premier paramètre est exécuté, le reste est introduit dans cette commande.

La seconde forme fonctionne car toutes les commandes séparées de ";" sont introduits dans la commande sh, qui les exécute.

devrimbaris
la source
1

Je ne pense pas que vous devriez mettre une virgule après "commencer"

à la place d'utiliser

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

essayer

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

comme docker utilise "sh -c", la commande ci-dessus sera exécutée comme ci-dessous

/etc/init.d/nullmailer start
/etc/init.d/nullmailer /usr/sbin/php5-fpm
vedat
la source
La syntaxe json n'exécute pas de commandes avec un shell, il n'y en a pas sh -cdans ce scénario.
BMitch