Posso executar vários programas em um contêiner do Docker?

150

Estou tentando entender o Docker a partir do ponto de implantar um aplicativo que se destina a ser executado nos usuários na área de trabalho. Minha aplicação é simplesmente uma aplicação web de balão e banco de dados mongo. Normalmente eu instalaria em uma VM e encaminharia uma porta de host para o aplicativo Web convidado. Gostaria de experimentar o Docker, mas não tenho certeza de como devo usar mais de um programa. As documentações dizem que só pode haver ENTRYPOINT; portanto, como posso ter o Mongo e meu aplicativo de balão. Ou eles precisam estar em contêineres separados; nesse caso, como eles se comunicam e como isso facilita a distribuição do aplicativo?

nickponline
fonte
2
Spot on: me faz perguntar por janela de encaixe eram tão populares .. (processo único ..?) - mas vamos ver o que as respostas nos dizer ..
javadba

Respostas:

120

Pode haver apenas um ENTRYPOINT, mas esse destino geralmente é um script que inicia quantos programas forem necessários. Além disso, você pode usar, por exemplo, Supervisord ou similar para cuidar de iniciar vários serviços em um único contêiner. Este é um exemplo de um contêiner de docker executando mysql, apache e wordpress em um único contêiner .

Digamos que você tenha um banco de dados usado por um único aplicativo da web. Então é provavelmente mais fácil executar os dois em um único contêiner.

Se você tiver um banco de dados compartilhado usado por mais de um aplicativo, será melhor executar o banco de dados em seu próprio contêiner e os aplicativos, cada um em seus próprios contêineres.

Há pelo menos duas possibilidades de como os aplicativos podem se comunicar quando estão em execução em contêineres diferentes:

  1. Use portas IP expostas e conecte-se através delas.
  2. As versões recentes do docker oferecem suporte à vinculação .
vesako
fonte
1
Parece que as novas versões do Docker agora suportam redes de contêineres do Docker .
jpierson
Docker agora suporta a correr Supervisor, permitindo-lhe sepcify comportamento para cada processo, como AutoRestart = true, stdout_logfile, stderr_logfile etc Dê uma olhada docs.docker.com/engine/admin/using_supervisord
Andreas Lundgren
4
Definitivamente, eu não recomendaria tentar executar o aplicativo Web e o mongodb no mesmo contêiner neste exemplo. Existem bons casos de uso de supervisord ou processos similares a init no Docker, mas este não faz parte deles. É muito mais simples usar o docker-compose para executar apenas os dois serviços em contêineres separados.
nicolas-van
@ nicolas-van Por que é mais simples? É porque se o db morre, posso reiniciar o contêiner do db em vez de ter que reiniciar tudo?
brillout 24/02/19
Os aplicativos na mesma máquina também podem se comunicar por soquetes de domínio Unix . Maior desempenho garantido.
Cético Jule
21

Eu tinha requisitos similares de executar uma pilha LAMP, Mongo DB e meus próprios serviços

O Docker é uma virtualização baseada em SO, e é por isso que isola seu contêiner em torno de um processo em execução; portanto, requer pelo menos um processo em execução no FOREGROUND.

Para que você forneça seu próprio script de inicialização como ponto de entrada, seu script de inicialização se tornará um script de imagem estendido do Docker, no qual você pode empilhar qualquer número de serviços até que INICIAR PELO MENOS UM SERVIÇO PRÉVIO, O QUE DEMAIS TAMBÉM ESTÁ NO FINAL

Portanto, meu arquivo de imagem do Docker tem duas linhas abaixo no final:

COPY myStartupScript.sh /usr/local/myscripts/myStartupScript.sh
CMD ["/bin/bash", "/usr/local/myscripts/myStartupScript.sh"]

No meu script, eu executo todo o MySQL, MongoDB, Tomcat etc. No final, eu executo o meu Apache como um thread em primeiro plano.

source /etc/apache2/envvars
/usr/sbin/apache2 -DFOREGROUND

Isso me permite iniciar todos os meus serviços e manter o contêiner ativo, com o último serviço começando em primeiro plano

Espero que ajude

ATUALIZAÇÃO : Desde a última vez em que respondi a essa pergunta, surgiram coisas novas, como o Docker composite , que pode ajudá-lo a executar cada serviço em seu próprio contêiner, mas unir todos eles como dependências entre esses serviços, tente saber mais sobre o docker-compose e use-o, é uma maneira mais elegante, a menos que sua necessidade não corresponda a ele.

Basav
fonte
6

Discordo totalmente de algumas soluções anteriores que recomendavam a execução de ambos os serviços no mesmo contêiner. É claramente indicado na documentação que não é recomendado :

Geralmente, é recomendável que você separe as áreas de preocupação usando um serviço por contêiner. Esse serviço pode entrar em vários processos (por exemplo, o servidor da web Apache inicia vários processos de trabalho). Não há problema em ter vários processos, mas para tirar o máximo proveito do Docker, evite que um contêiner seja responsável por vários aspectos do seu aplicativo geral. Você pode conectar vários contêineres usando redes definidas pelo usuário e volumes compartilhados.

Existem bons casos de uso para supervisord ou programas similares, mas a execução de um aplicativo Web + banco de dados não faz parte deles.

Você definitivamente deve usar o docker-composite para fazer isso e orquestrar vários contêineres com responsabilidades diferentes.

nicolas-van
fonte
2
Este é um comentário, não uma resposta. Por favor, considere adicionar uma explicação e / ou links para apoiar esta posição. Caso contrário, não é útil.
Ivan Ivanov
1
Essa é uma resposta no sentido de que a melhor recomendação que posso oferecer nesse caso de uso é usar o docker-compose. Enfim, você está certo de que eu poderia dar mais links para recomendações oficiais. Vou atualizar isso.
nicolas-van
A pergunta é sobre a execução de 2 processos em um contêiner, portanto, não se preocupa com as práticas recomendadas. Vou dar um exemplo: tive que executar o rabbitmq na imagem baseada no PhotonOS e no processo java também ... Então, usei um script de entrada e usei como ENTRYPOINT :)
Vallerious
A pergunta original não é genérica sobre a viabilidade técnica de executar dois processos em um contêiner do Docker. Ele indica um caso de uso específico, que é a implantação de um aplicativo Python junto com um banco de dados MongoDB. E, para esse caso de uso, a melhor recomendação é desencorajar o uso de um único contêiner e recomendar o uso do docker-compose.
nicolas-van
5

Eles podem estar em contêineres separados e, de fato, se o aplicativo também foi projetado para ser executado em um ambiente maior, provavelmente o seriam.

Um sistema com vários contêineres exigiria um pouco mais de orquestração para poder exibir todas as dependências necessárias, embora no Docker v0.6.5 +, haja um novo recurso para ajudar com o incorporado no próprio Docker - Linking . No entanto, com uma solução para várias máquinas, ainda é algo que precisa ser organizado fora do ambiente do Docker.

Com dois contêineres diferentes, as duas partes ainda se comunicam por TCP / IP, mas, a menos que as portas tenham sido bloqueadas especificamente (não recomendado, pois você não conseguiria executar mais de uma cópia), teria que passar pela nova porta que o banco de dados foi exposto quanto ao aplicativo, para que ele pudesse se comunicar com o Mongo. Novamente, é algo que o Linking pode ajudar.

Para uma instalação pequena e mais simples, em que todas as dependências estão no mesmo contêiner, é possível ter o banco de dados e o tempo de execução do Python iniciados pelo programa chamado inicialmente como ENTRYPOINT. Isso pode ser tão simples quanto um script de shell ou algum outro controlador de processo - o Supervisord é bastante popular e existem vários exemplos nos Dockerfiles públicos.

Alister Bulman
fonte
3

Concordo com as outras respostas de que é preferível o uso de dois contêineres, mas se você quiser empacotar vários serviços em um único contêiner, poderá usar algo como supervisord.

em Hipache por exemplo, o Dockerfile incluído executa o supervisord, e o arquivo supervisord.conf especifica a execução do hipache e do redis-server.

ben schwartz
fonte
2

O Docker fornece alguns exemplos de como fazê-lo. A opção leve é:

Coloque todos os seus comandos em um script de wrapper, completo com informações de teste e depuração. Execute o script do wrapper como seu CMD. Este é um exemplo muito ingênuo. Primeiro, o script do wrapper:

#!/bin/bash

# Start the first process
./my_first_process -D
status=$?
if [ $status -ne 0 ]; then
  echo "Failed to start my_first_process: $status"
  exit $status
fi

# Start the second process
./my_second_process -D
status=$?
if [ $status -ne 0 ]; then
  echo "Failed to start my_second_process: $status"
  exit $status
fi

# Naive check runs checks once a minute to see if either of the processes exited.
# This illustrates part of the heavy lifting you need to do if you want to run
# more than one service in a container. The container will exit with an error
# if it detects that either of the processes has exited.
# Otherwise it will loop forever, waking up every 60 seconds

while /bin/true; do
  ps aux |grep my_first_process |grep -q -v grep
  PROCESS_1_STATUS=$?
  ps aux |grep my_second_process |grep -q -v grep
  PROCESS_2_STATUS=$?
  # If the greps above find anything, they will exit with 0 status
  # If they are not both 0, then something is wrong
  if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
    echo "One of the processes has already exited."
    exit -1
  fi
  sleep 60
done

Em seguida, o Dockerfile:

FROM ubuntu:latest
COPY my_first_process my_first_process
COPY my_second_process my_second_process
COPY my_wrapper_script.sh my_wrapper_script.sh
CMD ./my_wrapper_script.sh
OzNetNerd
fonte
2

Você pode executar 2 processos em primeiro plano usando wait. Basta criar um script bash com o seguinte conteúdo. Ex . start.sh:

# runs 2 commands simultaneously:

mongod & # your first application
P1=$!
python script.py & # your second application
P2=$!
wait $P1 $P2

No seu Dockerfile, inicie-o com

CMD bash start.sh
Sam
fonte
0

Se um script dedicado parecer sobrecarregar demais, você poderá gerar processos separados explicitamente sh -c. Por exemplo:

CMD sh -c 'mini_httpd -C /my/config -D &' \
 && ./content_computing_loop
Rafael
fonte