Vários comandos na Diretiva Docker CMD

Não entendendo o que está acontecendo quando tento executar dois comandos em tempo de execução por meio da Diretiva CMD em `Dockerfile. Presumi que isso deveria funcionar:

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

Mas não está a funcionar. Container não começou. Então eu tive que fazer assim:

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

Não compreendo. Porquê? 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.

Apenas para notar - o mesmo foi com command: directiva em docker-compose.yml, como esperado.

Acredito que a diferença pode ser porque o segundo comando faz o processamento do shell enquanto o primeiro não. De acordo com o documentação oficial, há o exec e shell formulario. Seu primeiro comando é um exec formulario. O exec o formulário não expande variáveis de ambiente enquanto o shell a forma faz. É possível que usando o exec forme o comando está falhando devido à sua dependência do processamento do shell. Você pode verificar isso executando docker logs CONTAINERID

Seu segundo comando, o formulário shell, é equivalente a -

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

Trechos da documentação -

Nota: ao contrário do formulário shell, o formulário exec não invoca 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 variável em $HOME. Se você deseja o processamento do shell, use o formulário do shell ou execute um shell diretamente, por exemplo: CMD [ "sh", "-c", "echo", "$HOME" ].

Não torne isso difícil para si mesmo. Basta criar um arquivo bash & quot; start. sh & quot;:

#!/bin/bash/usr/bin/command2 param1/usr/bin/command1

em seu Dockerfile fazer:

ADD start.sh /RUN chmod +x /start.shCMD ["/start.sh"]

A sintaxe json de CMD (e RUN e ENTRYPOINT) passe 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 IO, substituição de variáveis, tubulação entre comandos, execução de vários comandos, etc, no exec syscall. O syscall leva apenas o executável para executar e lista de argumentos para passar para esse executável, e ele o executa.

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


Se você mudar para a sintaxe de string de CMD, 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 não recomendo executar vários comandos dessa maneira dentro de um contêiner, pois não há tratamento de erros se seu primeiro comando falhar, especialmente se for executado em segundo plano. Você também deixa um shell funcionando como pid 1 dentro do contêiner, o que quebrará o manuseio do sinal, resultando em um atraso de 10 segundos e uma eliminação ingrata do contêiner pelo docker. O manuseio do sinal pode ser mitigado usando o shell exec comando:

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

No entanto, lidar com processos que falham silenciosamente em segundo plano requer que você mude para algum tipo de Gerenciador de vários processos, como supervisord, ou, de preferência, divida seu aplicativo em vários contêineres e implemente-os com algo como docker-compose.

Acho que o primeiro comando falha porque no formulário CMD do DOCKER, apenas o primeiro parâmetro é executado, o restante é alimentado neste comando.

A segunda forma funciona porque todos os comandos separados com"; " são alimentados no comando sh, que os executa.

Por exemplo, imagine que você tem dois comandos python para executar python init_reset.py e python app.py. Em seguida, usando CMD, você pode combinar os dois comandos com o único comando

CMD python init_reset.py ; python app.py

No Docker compose, isso pode ser feito como o exemplo a seguir:

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

O exec irá mudar o contexto do executável principal para apache2-forground.

Eu não acho que você deve colocar semi vírgula após "começar"

em vez de usar

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

tentar

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