Pourquoi ne puis-je pas utiliser Docker CMD plusieurs fois pour exécuter plusieurs services?

96

J'ai construit une image de base à partir de Dockerfile nommée centos + ssh. Dans Dockerfile de centos + ssh, j'utilise CMD pour exécuter le service ssh.

Ensuite, je veux créer une image et exécuter un autre service nommé rabbitmq, le Dockerfile:

FROM centos+ssh
EXPOSE 22
EXPOSE 4149
CMD /opt/mq/sbin/rabbitmq-server start

Pour démarrer rabbitmq container , run :

docker run -d -p 222:22 -p 4149:4149 rabbitmq

mais le service ssh ne fonctionne pas, il semble que le Dockerfile CMD de rabbitmq écrase le CMD de centos.

  1. Comment CMD fonctionne-t-il dans l'image Docker?
  2. Si je veux exécuter plusieurs services, comment faire? Vous utilisez un superviseur?
edwardsbean
la source

Réponses:

62

Même si CMD est écrit dans le Dockerfile, ce sont vraiment des informations d'exécution. Tout comme EXPOSE, mais contrairement à par exemple RUN et ADD. Par cela, je veux dire que vous pouvez le remplacer plus tard, dans un Dockerfile étendu, ou simplement dans votre commande d'exécution, ce que vous rencontrez. À tout moment, il ne peut y avoir qu'un seul CMD.

Si vous souhaitez exécuter plusieurs services, j'utiliserais en effet superviseur. Vous pouvez créer un fichier de configuration du superviseur pour chaque service, les AJOUTER dans un répertoire, et exécuter le superviseur avec supervisord -c /etc/supervisorpour pointer vers un fichier de configuration du superviseur qui charge tous vos services et ressemble à

[supervisord]
nodaemon=true

[include]
files = /etc/supervisor/conf.d/*.conf

Si vous souhaitez plus de détails, j'ai écrit un blog sur ce sujet ici: http://blog.trifork.com/2014/03/11/using-supervisor-with-docker-to-manage-processes-supporting-image- héritage/

qkrijger
la source
Merci, superviseur est une bonne idée, mais je me demande comment CMD fonctionne dans l'image docker
edwardsbean
2
Vous avez posé deux questions, 2. sur l'exécution de plusieurs services. En vous demandant comment fonctionne CMD, veuillez préciser ce que vous voulez savoir précisément. J'ai déjà mentionné qu'il s'agissait d'informations d'exécution et d'être écrasé par tout nouveau CMD.
qkrijger
118

Vous avez raison, le deuxième Dockerfile écrasera la CMDcommande du premier. Docker exécutera toujours une seule commande, pas plus. Donc à la fin de votre Dockerfile, vous pouvez en spécifier un commande à exécuter. Pas plus.

Mais vous pouvez exécuter les deux commandes sur une seule ligne:

FROM centos+ssh
EXPOSE 22
EXPOSE 4149
CMD service sshd start && /opt/mq/sbin/rabbitmq-server start

Ce que vous pouvez également faire pour rendre votre Dockerfile un peu plus propre, vous pouvez placer vos commandes CMD dans un fichier supplémentaire:

FROM centos+ssh
EXPOSE 22
EXPOSE 4149
CMD sh /home/centos/all_your_commands.sh

Et un fichier comme celui-ci:

service sshd start &
/opt/mq/sbin/rabbitmq-server start
Thomas Uhrig
la source
1
merci, je pense que l'utilisation du superviseur est meilleure.Mais pourquoi docker n'exécute qu'un seul CMD? Que se passe-t-il à l'intérieur?
edwardsbean
1
Je ne sais pas ce qui se passe à l'intérieur. Mais je pense que c'est juste conçu comme ça. Lorsque vous avez une image et que vous exécutez une commande (par exemple avec CMD), cela démarre un conteneur. Le conteneur s'exécute tant que la commande s'exécute. Et dès que la commande se termine, le conteneur s'arrête également. Ainsi, chaque conteneur représente une seule commande (en cours d'exécution).
Thomas Uhrig
Je pense que c'est peut-être à cause de lcx ou de la limite de quelque chose
edwardsbean
2
@ Tyguy7 .. parce que ................?
StartupGuy
1
&&technique ne fonctionnera qu'avec des services non interactifs (qui peuvent démarrer en arrière-plan) sinon seul le premier fonctionnera.
noraj
26

Bien que je respecte la réponse de qkrijger expliquant comment vous pouvez contourner ce problème, je pense que nous pouvons en apprendre beaucoup plus sur ce qui se passe ici ...

Pour répondre réellement à votre question de " pourquoi " ... Je pense qu'il serait utile pour vous de comprendre comment fonctionne la docker stopcommande et que tous les processus doivent être arrêtés proprement pour éviter les problèmes lorsque vous essayez de les redémarrer (corruption de fichier, etc.).

Problème: si docker a fait SSH début de la commande de ce et a commencé RabbitMQ de votre fichier Docker? " La commande docker stop tente d'abord d'arrêter un conteneur en cours d'exécution en envoyant un signal SIGTERM au processus racine (PID 1) dans le conteneur. " Quel processus est le suivi de docker en tant que PID 1 qui obtiendra le SIGTERM? Sera-ce SSH ou Rabbit ?? "Selon le modèle de processus Unix, le processus d'initialisation - PID 1 - hérite de tous les processus enfants orphelins et doit les récolter. La plupart des conteneurs Docker n'ont pas de processus d'initialisation qui le fait correctement, et par conséquent, leurs conteneurs se remplissent de processus zombies au fil du temps. "

Réponse: Docker prend simplement ce dernier CMD comme celui qui sera lancé en tant que processus racine avec le PID 1 et qui obtiendra le SIGTERM docker stop.

Solution suggérée: vous devez utiliser (ou créer) une image de base spécialement conçue pour exécuter plus d'un service, tel que phusion / baseimage

Il devrait être important de noter que tini existe exactement pour cette raison, et à partir de Docker 1.13 et plus, tini fait officiellement partie de Docker, ce qui nous dit que l'exécution de plus d'un processus dans Docker EST VALIDE .. donc même si quelqu'un prétend être plus habile en ce qui concerne Docker, et insiste sur le fait que vous êtes absurde de penser à faire cela, sachez que vous ne l'êtes pas. Il existe des situations parfaitement valables pour cela.

Bon à savoir:

StartupGuy
la source
3

La réponse officielle du docker à Exécuter plusieurs services dans un conteneur .

Il explique comment vous pouvez le faire avec un système init (systemd, sysvinit, upstart), un script ( CMD ./my_wrapper_script.sh) ou un superviseur comme supervisord.

La &&solution de contournement ne peut fonctionner que pour les services qui démarrent en arrière-plan (démons) ou qui s'exécutent rapidement sans interaction et libèrent l'invite. Faire cela avec un service interactif (qui garde l'invite) et seul le premier service démarrera.

Noraj
la source
0

Pour expliquer pourquoi CMD est conçu pour n'exécuter qu'un seul service par conteneur, réalisons simplement ce qui se passerait si les serveurs secondaires exécutés dans le même conteneur ne sont pas triviaux / auxiliaires mais «majeurs» (par exemple, le stockage fourni avec l'application frontend). Pour commencer, cela décomposerait plusieurs fonctionnalités de conteneurisation importantes telles que la mise à l'échelle horizontale (automatique) et la replanification entre les nœuds, qui supposent toutes deux qu'il n'y a qu'une seule application (source de charge du processeur) par conteneur. Ensuite, il y a le problème des vulnérabilités - plus de serveurs exposés dans un conteneur signifie des correctifs plus fréquents des CVE ...

Admettons donc qu'il s'agit d'un `` coup de pouce '' des concepteurs de Docker (et Kubernetes / Openshift) vers les bonnes pratiques et nous ne devons pas réinventer les solutions de contournement (SSH n'est pas nécessaire - nous avons docker exec / kubectl exec / oc rshconçu pour le remplacer).

  • Plus d'informations

/devops/447/why-it-is-recommended-to-run-only-one-process-in-a-container

mirekphd
la source