Olá pessoal! No artigo anterior , prometi falar sobre o lançamento do Docker no Docker e os aspectos práticos da aplicação desta lição. É hora de cumprir nossa promessa. Um devedor experiente, talvez, argumentaria que aqueles que precisam do Docker dentro do Docker apenas jogam o soquete demônio do Docker do host dentro do contêiner e isso é suficiente em 99% dos casos. Mas não se apresse em jogar biscoitos para mim, porque falaremos sobre o lançamento real do Docker dentro do Docker. Esta solução tem muitos campos de aplicação possíveis e este artigo é sobre um deles; portanto, sente-se e endireite os braços à sua frente.
Iniciar
Tudo começou em uma noite chuvosa de setembro, quando eu estava limpando um carro alugado pela Digital Ocean por US $ 5, o que ficou estagnado porque o Docker preencheu todos os 24 gigabytes de espaço disponível em disco com suas imagens e contêineres. A ironia era que todas essas imagens e contêineres eram transitórios e precisavam apenas testar o desempenho do meu aplicativo toda vez que uma nova versão de uma biblioteca ou estrutura era lançada. Tentei escrever scripts de shell e configurar a programação das coroas para limpeza de lixo, mas isso não salvou: toda vez que, inevitavelmente, o espaço em disco do meu servidor acabava sendo consumido e o servidor travava (na melhor das hipóteses). Em algum momento, deparei com um artigo sobre como executar o Jenkins em um contêiner e como ele pode criar e excluir pipelines de montagem através do soquete do daemon do docker lançado nele. Gostei da ideia, mas decidi seguir em frente e tentar experimentar o lançamento direto do Docker dentro do Docker. Pareceu-me então uma decisão completamente lógica extrair imagens do docker e criar contêineres de todos os aplicativos necessários para testar dentro de outro contêiner (vamos chamá-lo de contêiner intermediário). A ideia era executar um contêiner intermediário com o sinalizador -rm, que exclui automaticamente todo o contêiner com todo o seu conteúdo quando ele para. Eu vasculhei a imagem do docker do próprio Docker ( https://hub.docker.com/_/docker ), mas acabou sendo muito volumoso e não consegui fazê-lo funcionar conforme necessário e queria seguir todo o caminho.
Prática. Solavancos
Decidi fazer o contêiner funcionar conforme necessário e continuei minhas experiências, o que resultou em uma infinidade de cones. O resultado da minha auto-tortura foi o seguinte algoritmo:
Iniciamos o contêiner do Docker em um modo interativo.
docker run --privileged -it docker:18.09.6
Preste atenção à versão do recipiente, um passo para a direita ou para a esquerda e seu DinD se transforma em abóbora. De fato, tudo se quebra com bastante frequência com o lançamento de uma nova versão.
Devemos entrar imediatamente na concha.
Tentando descobrir quais contêineres estão sendo executados (Resposta: nenhuma), mas vamos executar o comando de qualquer maneira:
docker ps
Você ficará um pouco surpreso, mas acontece que o daemon do 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 host
Vamos executá-lo você mesmo:
dockerd &
Outra surpresa desagradável:
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: Iptables not found
Instale os pacotes iptables e bash (é mais agradável trabalhar no bash do que no sh):
apk add --no-cache iptables bash
Começamos o bash. Finalmente, estamos de volta ao habitual
tente iniciar o docker novamente:
dockerd &
Deveríamos ver uma longa folha de registro terminando:
INFO[2019-11-25T19:51:19.448080400Z] Daemon has completed initialization INFO[2019-11-25T19:51:19.474439300Z] API listen on /var/run/docker.sock
Pressione Enter. Estamos de volta à festa.
A partir de agora, podemos tentar lançar outros contêineres dentro do contêiner do Docker, mas e se quisermos levantar outro contêiner do Docker dentro do contêiner do Docker ou se algo der errado e o contêiner "voar para fora"? Comece tudo de novo.
Contêiner DinD próprio e novas experiências
Para não repetir as etapas acima, criei meu próprio contêiner DinD:
https://github.com/alekslitvinenk/dind
A solução de trabalho DinD me deu a oportunidade de executar o Docker dentro do Docker recursivamente e realizar experiências mais ousadas.
Um desses experimentos (bem-sucedidos) com a execução de MySQL e Nodejs, vou descrever agora.
O mais impaciente pode ver como foi aqui
Então, vamos começar:
Inicie o DinD interativamente. 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/dind
Nós nos encontramos em uma festa, de onde podemos começar imediatamente a lançar contêineres subsidiários.
Iniciamos o MySQL:
docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysql
Nós nos conectamos ao banco de dados da mesma maneira que nos conectamos localmente. Verifique se tudo funciona.
Lançamos o segundo contêiner:
docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-server
Observe que o mapeamento da porta aqui será exatamente 8080: 8080 , pois já mapeamos a porta 80 do host para o contêiner pai na porta 8080.
Nós vamos para o host local no navegador, estamos convencidos de que o servidor responde "Olá, mundo!".
No meu caso, o experimento com os contêineres do docker fechados foi bastante positivo, e continuarei desenvolvendo o projeto e usá-lo para preparação. Parece-me que esta é uma solução muito mais leve que a mesma Kubernetes e Jenkins X. Mas esta é a minha opinião subjetiva.
Eu acho que isso é tudo para o artigo de hoje. No próximo artigo, descreverei mais detalhadamente experimentos com o lançamento recursivo do Docker no Docker e montagem de diretórios em contêineres aninhados.
PS Se você acha este projeto útil, dê um asterisco no GitHub, bifurque-se e conte a seus amigos.
Edit1 Bugs corrigidos , focados em 2 vídeos