Como manter o contêiner do Docker em execução após o início dos serviços?

155

Eu já vi vários tutoriais que parecem fazer a mesma coisa que estou tentando fazer, mas por algum motivo meus contêineres do Docker saem. Basicamente, estou configurando um servidor Web e alguns daemons dentro de um contêiner do Docker. Eu faço as partes finais disso por meio de um script bash chamado run-all.shque eu corro pelo CMD no meu Dockerfile. run-all.shse parece com isso:

service supervisor start
service nginx start

E inicio-o dentro do meu Dockerfile da seguinte maneira:

CMD ["sh", "/root/credentialize_and_run.sh"]

Percebo que todos os serviços iniciam corretamente quando executo as coisas manualmente (por exemplo, acessando a imagem com -i -t / bin / bash), e tudo parece funcionar corretamente quando executo a imagem, mas sai uma vez termina iniciando meus processos. Eu gostaria que os processos fossem executados indefinidamente e, pelo que entendi, o contêiner precisa continuar em execução para que isso aconteça. No entanto, quando corro docker ps -a, vejo:

➜  docker_test  docker ps -a
CONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS                      PORTS               NAMES
c7706edc4189        some_name/some_repo:blah   "sh /root/run-all.sh   8 minutes ago       Exited (0) 8 minutes ago                        grave_jones

O que da? Por que está saindo? Eu sei que eu poderia colocar um loop while no final do meu script bash para continuar, mas qual é a maneira certa de impedir que ele saia?

Eli
fonte
1
você está expondo as portas dos serviços para fora (opção -p para executar o docker)? (é claro que isso não vai impedi-los para sair)
Ribamar
1
Eu estava usando ENTRYPOINT no meu Dockerfile e, após a execução do script definido em ENTRYPOINT (meu script de inicialização), ele apareceu nos logs, mas meu contêiner parecia ter saído. Portanto, em vez de ENTRYPOINT, usei o comando RUN para executar o script e o contêiner ainda está sendo executado em segundo plano.
Ypahalajani

Respostas:

49

Não é assim que você deve projetar seus contêineres do Docker.

Ao projetar um contêiner do Docker, você deve construí-lo para que exista apenas um processo em execução (ou seja, você deve ter um contêiner para o Nginx e outro para o supervisord ou o aplicativo em execução); Além disso, esse processo deve ser executado em primeiro plano.

O contêiner "sairá" quando o processo em si for encerrado (no seu caso, esse processo é seu script bash).


No entanto, se você realmente precisar (ou desejar) executar vários serviços no seu contêiner Docker, considere iniciar a partir de "Imagem Base do Docker" , que usa runitcomo um processo pseudo-init ( runitpermanecerá online enquanto o Nginx e o Supervisor executam), que permanecerá em primeiro plano, enquanto seus outros processos fazem as coisas deles.

Eles têm documentos substanciais; portanto, você deve conseguir o que está tentando fazer razoavelmente facilmente.

Thomas Orozco
fonte
1
Você pode explicar por que eu deveria ter apenas um serviço em execução? Eu poderia adicionar o nginx ao supervisor, se necessário, mas não sei por que isso seria necessário.
Eli
3
@ Eli A resposta curta é que é assim que o Docker funciona. O Docker executará apenas um processo (e seus filhos) por contêiner. É recomendável que esse processo seja um processo de aplicativo real (para que, caso exista, o Docker saiba), mas você pode realmente usar o supervisor nesse processo. Observe que você precisará configurar o supervisor para executar em primeiro plano (ou seja, não daemonize), o que é feito através da --nodaemonopção
Thomas Orozco
1
@Eli Esta postagem do blog do Docker defende que a execução de vários processos (e, em geral, a exibição de um contêiner como um "pequeno VPS") é subótima. No seu caso, o segmento de comentários provavelmente será mais relevante do que a postagem do blog real.
Thomas Orozco
1
A imagem base do Docker é uma solução terrível para muitos problemas empresariais, porque poucas empresas sérias usam o ubuntu, preferindo a árvore RHEL / Centos.
Engenheiro de software
9
"Poucas empresas sérias" parecem indefensáveis. A escolha do SO parece basear-se inteiramente no caso de uso. Qualquer empresa possui muitos ambientes diferentes, incluindo o uso interno do desenvolvedor, o uso interno do funcionário, suporte a vendas, preparo, POCs e, finalmente, produção (e mesmo que seja um termo vago). Não acredito que o OP tenha mencionado o caso de uso deles, (desculpe-me por não ter certeza), mas esse tipo de comentário parece ser do tipo que divulga informações altamente opinativas, sem argumentos sobre o porquê.
quer
154

Se você estiver usando um Dockerfile, tente:

ENTRYPOINT ["tail", "-f", "/dev/null"]

(Obviamente, isso é apenas para fins de desenvolvimento, você não precisa manter um contêiner vivo, a menos que esteja executando um processo, por exemplo, nginx ...)

Balas macias
fonte
5
Eu estava usando CMD["sleep", "1d"], mas sua solução parece melhor
George Pligoropoulos
@GeorgiosPligoropoulos, isso ficará preso nessa linha; talvez executado em segundo plano vai funcionar
Prashanth Sams
5
Também pode usar CMD["sleep", "infinity"].
Romain
5
ou "gato", mas as pessoas podem dizer que é abuso de animais. xD
lawphotog 16/09/19
Você pode concluir o script do ponto de entrada com exec tail -f /dev/nullmas usar tailcomo um ponto de entrada é uma resposta errada.
Torsten Bronger
86

Acabei de ter o mesmo problema e descobri que, se você estiver executando seu contêiner com o sinalizador -te -d, ele continuará sendo executado.

docker run -td <image>

Aqui está o que as bandeiras fazem (de acordo com docker run --help):

-d, --detach=false         Run container in background and print container ID
-t, --tty=false            Allocate a pseudo-TTY

O mais importante é a -tbandeira. -dpermite executar o contêiner em segundo plano.

arne.z
fonte
3
Eu não posso reproduzir isso. Você poderia fornecer um exemplo? Existe algo específico (por exemplo: CMD) sobre o Dockerfile que precisamos para que isso funcione?
Matheus Santana
2
Isto não funcionou para mim. Usei o comando docker logs <image>para garantir que foi um erro que causou a saída do contêiner do Docker. O estado de saída é 0ea última saída é confimation que meu lighttpdservidor está em execução:[ ok ] Starting web server: lighttpd.
OB1
Não trabalho com o Docker há um tempo. Portanto, é possível que a interface da linha de comandos tenha mudado e que esse comando não funcione mais.
Arne.z 25/07/19
4
Posso confirmar que isso realmente está funcionando com a versão mais recente do docker. Se você deseja anexar posteriormente a esta sessão, usar -dit também funcionará.
John Hamilton
1
@ Um script longo não aceita um tty, add exec bashou exec shse o bash não estiver instalado, até o final do start.sh. Então você pode usar a bandeira -t
123
43

A razão pela qual ele sai é porque o script do shell é executado primeiro como PID 1 e, quando concluído, o PID 1 desaparece e o docker é executado apenas enquanto o PID 1 está.

Você pode usar o supervisor para fazer tudo; se for executado com o sinalizador "-n", é solicitado que você não daemonize, portanto, ele permanecerá como o primeiro processo:

CMD ["/usr/bin/supervisord", "-n"]

E seu supervisord.conf:

[supervisord]
nodaemon=true

[program:startup]
priority=1
command=/root/credentialize_and_run.sh
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=false
startsecs=0

[program:nginx]
priority=10
command=nginx -g "daemon off;"
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.log
autorestart=true

Em seguida, você poderá ter quantos outros processos desejar e o supervisor cuidará da reinicialização deles, se necessário.

Dessa forma, você pode usar o supervisord nos casos em que possa precisar de nginx e php5-fpm e não faz muito sentido separá-los.

phazei
fonte
Onde, nos documentos, diz se o PID 1 termina o contêiner do docker para de ser executado?
8oh8
@ 8oh8 É essencialmente assim que os namespaces de processo funcionam; não é específico do Docker, mas "a coisa subjacente a todos os contêineres". De man7.org/linux/man-pages/man7/pid_namespaces.7.html :If the "init" process of a PID namespace terminates, the kernel terminates all of the processes in the namespace via a SIGKILL signal. This behavior reflects the fact that the "init" process is essential for the correct operation of a PID namespace.
dannysauer
40

você pode executar catsem nenhum argumento, como mencionado por bro @ Sa'ad, para simplesmente manter o contêiner funcionando (na verdade, nada mais é do que aguardar a entrada do usuário) (o plugin Docker do Jenkins faz a mesma coisa)

Serge Velikanov
fonte
além da minha resposta: mas entenda que o docker-compose (não daemonized) é usado para mostrar o fluxo de trabalho do seu contêiner, portanto, pode ser útil personalizar os arquivos de log dos seus serviços iniciados. Cheers
Serge Velikanov
1
ou cat. O plugin docker de jenkin faz isso.
Sa'ad
12

Certifique-se de adicionar daemon off;o nginx.conf ou executá-lo CMD ["nginx", "-g", "daemon off;"]conforme a imagem oficial do nginx

Em seguida, use o seguinte para executar o processo de supervisor como serviço e nginx como primeiro plano que impedirá a saída do contêiner

service supervisor start && nginx

Em alguns casos, você precisará ter mais de um processo em seu contêiner, portanto, forçar o contêiner a ter exatamente um processo não funcionará e poderá criar mais problemas na implantação.

Portanto, você precisa entender as compensações e tomar sua decisão de acordo.

iTech
fonte
7

Motivação:

Não há nada de errado em executar vários processos dentro de um contêiner de docker . Se alguém gosta de usar o docker como uma VM leve - que assim seja. Outros gostam de dividir seus aplicativos em microsserviços. Eu pensa: Uma pilha LAMP em um contêiner? Apenas ótimo.

A resposta:

Fique com uma boa imagem de base, como a imagem de base do phusion . Pode haver outros. Por favor comente.

E este é apenas mais um pedido de supervisor. Como a imagem base do phusion está fornecendo supervisor, além de outras coisas, como cron e configuração de localidade. Coisas que você gosta de configurar ao executar uma VM tão leve. Pelo que vale a pena, também fornece conexões ssh no contêiner.

A própria imagem do phusion será iniciada e continuará sendo executada se você emitir esta instrução básica de execução do docker:

moin@stretchDEV:~$ docker run -d phusion/baseimage
521e8a12f6ff844fb142d0e2587ed33cdc82b70aa64cce07ed6c0226d857b367
moin@stretchDEV:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS
521e8a12f6ff        phusion/baseimage   "/sbin/my_init"     12 seconds ago      Up 11 seconds

Ou simplesmente simples:

Se uma imagem base não é para você ... Para o CMD rápido para mantê-lo em execução, eu suponho algo assim para o bash:

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"

Ou isso para o busybox:

CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

Isso é bom, porque sairá imediatamente em um docker stop. Simplesmente sleepou catlevará alguns segundos antes que o contêiner saia.

itsafire
fonte
Eu personalizei a imagem base do centos7 para carregar o PostgreSQL 11. Você inicia isso com uma chamada para / usr / pgsql-11 / bin / pg_ctl, mas o pg_ctl sai quando o servidor está em execução. Sua sugestão de usar armadilha funcionou muito bem; é a última linha do meu script pgstartwait.sh
Alchemistmatt
6

Capture o PID do processo ngnix em uma variável (por exemplo, $ NGNIX_PID) e, no final do arquivo do ponto de entrada,

wait $NGNIX_PID 

Dessa forma, seu contêiner deve ser executado até que o ngnix esteja ativo; quando o ngnix para, o contêiner também pára

user2825611
fonte