Olá a todos! Em minha , eu prometi falar sobre a execução do Docker dentro do Docker e os aspectos práticos dessa atividade. Chegou a hora de cumprir minha promessa. Um DevOps experiente pode argumentar que para quem precisa do Docker dentro do Docker, a solução é simplesmente passar o socket do daemon do Docker do host para dentro do container, e isso é suficiente em 99% dos casos. Mas não se apresse em me jogar cookies, pois aqui falaremos sobre a execução real do Docker dentro do Docker. Essa solução tem muitas áreas de aplicação possíveis e este artigo aborda uma delas, então, acomode-se e estique as mãos à sua frente.

Início
Tudo começou numa chuvosa noite de setembro, quando eu estava limpando uma máquina alugada por $5 na Digital Ocean, que congelou completamente porque o Docker ocupou todos os 24 gigabytes de espaço em disco disponível com suas imagens e containers. A ironia era que todas essas imagens e containers eram transitórios e precisavam apenas para testar a funcionalidade do meu aplicativo toda vez que uma nova versão de alguma biblioteca ou framework era lançada. Eu tentei escrever scripts shell e configurar agendamentos cron para limpar a bagunça, mas isso não funcionou: sempre acabava resultando que o espaço em disco do meu servidor estava consumido e o servidor estava travado (na melhor das hipóteses). Em algum momento, me deparei com um artigo sobre como executar o Jenkins em um container e como ele pode criar e remover pipelines de build através de um socket do daemon Docker passado para ele. A ideia me agradou, mas decidi ir além e experimentar a execução direta do Docker dentro do Docker. Naquele momento, parecia uma solução bem lógica baixar imagens do Docker e criar containers de todos os aplicativos que eu precisava para testes dentro de outro container (vamos chamá-lo de container de staging). A ideia era iniciar o container de staging com a flag -rm, que remove automaticamente todo o container com todo o seu conteúdo ao ser parado. Eu brinquei com a imagem do Docker do próprio Docker (), mas era muito complexo e eu não consegui fazê-lo funcionar da maneira que eu precisava, e eu queria seguir todo o caminho sozinho.
Prática. Aprendizados.
Eu tinha o objetivo de fazer o contêiner funcionar como eu precisava e continuei meus experimentos, resultado dos quais foram incontáveis obstáculos. O resultado da minha autocrítica foi o seguinte algoritmo:
Iniciamos o contêiner Docker em modo interativo.
docker run --privileged -it docker:18.09.6Preste atenção na versão do contêiner; um pequeno erro e seu DinD se transforma em abóbora. Na verdade, tudo quebra com bastante frequência quando uma nova versão é lançada.
Devemos entrar diretamente no shell.Tentamos descobrir quais contêineres estão em execução (Resposta: nenhum), mas vamos executar o comando de qualquer forma:
docker psVocê ficará um pouco surpreso, mas parece que o daemon Docker nem está em execução:
error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 192.168.65.1:53: no such hostVamos iniciá-lo manualmente:
dockerd &Mais uma desagradável surpresa:
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: Iptables not foundInstalamos os pacotes iptables e bash (no bash é muito mais agradável trabalhar do que no sh):
apk add --no-cache iptables bashIniciamos o bash. Finalmente estamos de volta ao shell familiar.
vamos tentar iniciar o Docker mais uma vez:
dockerd &Devemos ver uma longa lista de logs terminando com:
INFO[2019-11-25T19:51:19.448080400Z] Daemon has completed initialization INFO[2019-11-25T19:51:19.474439300Z] API listen on /var/run/docker.sockPressione Enter. Estamos de volta ao bash.
A partir deste momento, podemos tentar iniciar outros contêineres dentro do nosso contêiner Docker, mas e se quisermos levantar mais um contêiner Docker dentro do nosso contêiner Docker ou se algo der errado e o contêiner "sair"? Começar tudo de novo.
Contêiner DinD próprio e novas experiências
Para não repetir os passos descritos acima vez após vez, criei meu próprio contêiner DinD:
A solução DinD funcional me permitiu executar o Docker dentro do Docker recursivamente e realizar experiências mais ousadas.
Um experimento (bem-sucedido) com a execução do MySQL e Nodejs que vou descrever agora.
Os mais impacientes podem ver como isso foi feito aqui.

Então, vamos começar:
Iniciamos o DinD em modo interativo. Nesta versão do DinD, precisamos mapear manualmente todas as portas que nossos contêineres filhos podem usar (já estou trabalhando nisso).
docker run --privileged -it -p 80:8080 -p 3306:3306 alekslitvinenk/dindEntramos no bash, de onde podemos começar a iniciar contêineres filhos imediatamente.
Iniciando o MySQL:
docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysqlConectamo-nos ao banco de dados da mesma forma que nos conectaríamos localmente. Garantimos que tudo está funcionando.
Iniciando o segundo contêiner:
docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-serverObserve que o mapeamento de porta aqui será exatamente 8080:8080, pois já mapeamos a porta 80 do host para o contêiner pai na porta 8080.
Vamos para localhost no navegador e certificamo-nos de que o servidor responde "Hello World!".
No meu caso, a experiência com contêineres Docker aninhados foi bastante positiva e continuarei a desenvolver o projeto e usá-lo para staging. Parece-me uma solução muito mais leve do que o Kubernetes e Jenkins X. Mas essa é a minha opinião subjetiva.
Acho que, para o artigo de hoje, é isso. No próximo artigo, descreverei em mais detalhes os experimentos com a execução recursiva do Docker dentro do Docker e o montante de diretórios dentro dos contêineres aninhados.
P.S. Se você achar este projeto útil, por favor, dê uma estrela no GitHub, faça um fork e conte aos amigos.
Edit1 Corrigi os erros, foquei em 2 vídeos
Fonte: habr.com
