105 votes

Commandes multiples dans la directive Docker CMD

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

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

Mais ça ne fonctionne pas. Le conteneur n'a pas démarré. Donc j'ai dû faire comme ça :

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

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

Juste pour noter - la même chose s'est produite avec command: dans la directive docker-compose.yml comme prévu.

77voto

user254047 Points 1

Ne rendez pas les choses difficiles pour vous. Créez simplement un fichier bash "start.sh" : 

#!/bin/bash

/usr/bin/command2 param1
/usr/bin/command1

dans votre Dockerfile :

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

CMD ["/start.sh"]

73voto

ggustafsson Points 1908

Je pense que la différence pourrait être due au fait que la seconde commande effectue un traitement Shell alors que la première ne le fait pas. Selon le documentation officielle il y a les exec y shell des formulaires. Votre première commande est un exec forme. Le site exec ne développe pas les variables d'environnement alors que le formulaire shell le fait. Il est possible qu'en utilisant le exec forme la commande échoue en raison de sa dépendance au traitement de Shell. Vous pouvez vérifier cela en exécutant docker logs CONTAINERID

Votre deuxième commande, la forme Shell, est équivalente à -

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

Extraits de la documentation -

Note : Contrairement au formulaire Shell, le formulaire exec n'invoque pas une commande Shell. Cela signifie que le traitement normal de Shell ne se produit pas. Par exemple , CMD [ "echo", "$HOME" ] ne fera pas de substitution de variable sur $HOME . Si vous voulez un traitement Shell alors utilisez le formulaire Shell ou exécutez directement un Shell, par exemple : CMD [ "sh", "-c", "echo", "$HOME" ] .

13voto

BMitch Points 4794

La syntaxe json de CMD (et RUN y ENTRYPOINT ) passent les arguments au noyau directement comme un syscall exec. Il n'y a pas de séparation de la commande des arguments par des espaces, d'échappement des guillemets, de redirection d'E/S, de substitution de variable, de tuyauterie entre les commandes, d'exécution de commandes multiples, etc. dans l'appel système d'exécution. Le syscall prend seulement l'exécutable à exécuter et la liste des arguments à passer à cet exécutable, et il l'exécute.

Des personnages comme $ pour développer les variables, ; pour séparer les commandes, (espace) pour séparer les arguments, && y || à la chaîne de commandement, > pour la redirection des sorties, | pour passer d'une commande à l'autre, etc., sont toutes des caractéristiques du Shell et requièrent quelque chose comme /bin/sh o /bin/bash pour les interpréter et les mettre en œuvre.


Si vous passez à la syntaxe de chaîne de caractères 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 façon à l'intérieur d'un conteneur car il n'y a pas de gestion des erreurs si votre première commande échoue, surtout si elle s'exécute en arrière-plan. Vous laissez également un Shell s'exécuter en tant que pid 1 à l'intérieur du conteneur, ce qui brisera la gestion des signaux, ce qui entraînera un délai de 10 secondes et un kill non gracieux de votre conteneur par docker. La gestion des signaux peut être atténuée en utilisant le Shell. exec commandement :

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

Cependant, pour gérer les processus qui échouent silencieusement en arrière-plan, il faut passer à une sorte de gestionnaire multi-processus comme supervisord, ou de préférence décomposer votre application en plusieurs conteneurs et les déployer avec quelque chose comme docker-compose.

2voto

TreDubZedd Points 2070

Dans Docker compose, cela peut être fait comme dans l'exemple suivant :

command: ["sh", "-c", "
    apt update && apt install -y libldap-common;
    cp /ca.crt /usr/local/share/ca-certificates/;
    update-ca-certificates;
    exec apache2-foreground
  "]

El exec changera le contexte de l'exécutable principal en apache2-forground.

1voto

Mateng Points 2026

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

La deuxième forme fonctionne car toutes les commandes séparées par " ;" sont introduites dans la commande sh, qui les exécute.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X