Vários comandos na diretiva Docker CMD

39

Não entendo o que está acontecendo quando tento executar dois comandos em tempo de execução via diretiva CMD no `Dockerfile. Eu assumi que isso deveria funcionar:

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

Mas não está funcionando. O contêiner não foi iniciado. Então eu tive que fazer assim:

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

Eu não entendo Por que é que? Por que a primeira linha não é o caminho certo? Alguém pode me explicar essas coisas "formato de shell CMD vs formato JSON, etc". Em palavras simples.

Só para nota - a mesma estava com command:directiva em docker-compose.yml, como esperado.

Vladan
fonte

Respostas:

33

Eu acredito que a diferença pode ter a ver com, o segundo comando faz o processamento do shell, enquanto o primeiro não. Pela documentação oficial , existe o formulário exece shell, seu primeiro comando é um formulário exec e, por exemplo, não expande variáveis ​​de ambiente, enquanto o segundo. Portanto, é possível que, usando o formulário exec, o comando possa falhar devido à dependência do processamento do shell. Você pode verificar isso executandodocker logs CONTAINERID

Seu segundo comando, a forma do shell, é equivalente a -

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

Trechos da documentação -

Nota: Ao contrário do formulário do shell, o formulário exec não chama um shell de comando. Isso significa que o processamento normal do shell não acontece. Por exemplo, CMD [ "echo", "$HOME" ]não fará substituição de variável em $HOME. Se você quiser processamento de shell, em seguida, use o formulário de shell ou executar um shell diretamente, por exemplo: CMD [ "sh", "-c", "echo", "$HOME" ].

Daniel t.
fonte
Provavelmente o comando falhou devido a variáveis ​​de ambiente. Ainda devo usar este execformulário, pois é o preferido? Por que é preferido? Ou devo usar uma shellforma mais simples ?
Vladan
Falha porque executar um comando após o outro é uma função shell. Variáveis ​​de ambiente é um arenque vermelho.
21715 Bryan
Se você estiver executando vários serviços no Docker, recomendo o uso de um gerenciador de processos como supervisor. Dessa forma, você inicia apenas o supervisord na seção CMD e ele cuidará do início dos serviços. Você pode verificar os detalhes aqui - docs.docker.com/articles/using_supervisord
Daniel t.
Este é um artigo exato que eu estava lendo :) Obrigado.
Vladan
Ainda não entendo por que você precisa fazer CMD [ "sh", "-c", "echo", "$HOME"]. Por que não CMD ["sh", "-c", "echo $HOME"]ou, nesse caso CMD ["sh -c echo $HOME"]?
sixty4bit
4

Não torne difícil para si mesmo. Basta criar um arquivo bash "start.sh":

#!/bin/bash

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

no seu Dockerfile, faça:

ADD start.sh /

CMD ["/start.sh"]
Digital Human
fonte
2

A sintaxe json de CMD(e RUNe ENTRYPOINT) passa os argumentos para o kernel diretamente como um syscall exec. Não há separação do comando dos argumentos por espaços, escape de aspas, redirecionamento de E / S, substituição de variável, canalização entre comandos, execução de vários comandos etc. no syscall exec. O syscall leva apenas o executável para executar e a lista de argumentos a serem transmitidos para esse executável e o executa.

Caracteres gostam $de expandir variáveis, ;separar comandos, (espaço) para separar argumentos &&e ||encadear comandos, >para redirecionamento de saída, |canalizar entre comandos, etc., são todos recursos do shell e precisam de algo como /bin/shou /bin/bashpara interpretá-los e implementá-los.


Se você alternar para a sintaxe da cadeia de caracteres CMD, o docker executará seu comando com um shell:

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

Caso contrário, sua segunda sintaxe faz exatamente a mesma coisa:

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

Observe que eu não recomendo executar vários comandos dessa maneira dentro de um contêiner, pois não há tratamento de erros se o seu primeiro comando falhar, especialmente se for executado em segundo plano. Você também deixa um shell executando o pid 1 dentro do contêiner, o que interrompe o manuseio do sinal, resultando em um atraso de 10 segundos e na morte desagradável do contêiner pelo docker. A manipulação do sinal pode ser atenuada usando o execcomando shell :

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

No entanto, lidar com processos que falham silenciosamente em segundo plano exige que você alterne para algum tipo de gerenciador de processos múltiplos, como supervisord, ou, de preferência, quebre seu aplicativo em vários contêineres e implante-os com algo como docker-compose.

BMitch
fonte
1

Eu acho que o primeiro comando falha porque, no formato DOCKER CMD, apenas o primeiro parâmetro é executado, o restante é alimentado nesse comando.

O segundo formulário funciona porque todos os comandos separados por ";" são alimentados no comando sh, que os executa.

devrimbaris
fonte
1

Eu não acho que você deve colocar semi-vírgula depois de "start"

ao invés de usar

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

experimentar

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

como o docker usa "sh -c", o comando acima será executado como abaixo

/etc/init.d/nullmailer start
/etc/init.d/nullmailer /usr/sbin/php5-fpm
vedat
fonte
A sintaxe json não executa comandos com um shell, não existe sh -cnesse cenário.
BMitch 7/08