Pense com cuidado antes de usar o Docker-in-Docker para IC ou ambiente de teste.



O Docker-in-Docker é um daemon Docker virtualizado em execução no próprio contêiner para criar imagens de contêiner. O principal objetivo da criação do Docker-in-Docker era ajudar a desenvolver o próprio Docker. Muitas pessoas o usam para executar o Jenkins CI. No início, isso parece normal, mas há problemas que podem ser evitados com a instalação do Docker no contêiner do Jenkins CI. Este artigo descreve como fazer isso. Se você estiver interessado na solução final sem detalhes, basta ler a última seção do artigo "Resolvendo o problema".



Docker-in-Docker: bom


Mais de dois anos atrás, inseri o sinalizador –privileged no Docker e escrevi a primeira versão do dind . O objetivo era ajudar a equipe principal a desenvolver o Docker mais rapidamente. Antes do Docker-in-Docker, um ciclo de desenvolvimento típico era assim:

  • hackity hack;
  • montagem
  • parando um daemon do docker em execução;
  • iniciar um novo daemon do docker;
  • teste;
  • repetição de loop.

Se você queria fazer uma montagem bonita e reproduzível (ou seja, em um contêiner), ficou mais complicado:

  • hackity hack;
  • verifique se uma versão funcional do Docker está sendo executada;
  • construir uma nova janela de encaixe com uma janela de encaixe antiga;
  • pare o daemon do docker;
  • iniciar um novo daemon do docker;
  • testar;
  • interrompa o novo daemon do Docker;
  • repetir.

Com o advento do Docker-in-Docker, o processo foi simplificado:

  • hackity hack;
  • montagem + lançamento em uma etapa;
  • repetição de loop.

Isso não é muito melhor?



Docker-in-Docker: Ruim


No entanto, contrariamente à crença popular, o Docker-in-Docker não é 100% composto por estrelas, pôneis e unicórnios. Quero dizer, existem vários problemas que um desenvolvedor precisa conhecer.

Um deles diz respeito a LSMs (módulos de segurança Linux), como AppArmor e SELinux: quando o contêiner é iniciado, o “Docker interno” pode tentar aplicar perfis de segurança que entrarão em conflito ou confundirão o “Docker externo”. Esse é o problema mais difícil que precisava ser resolvido ao tentar combinar a implementação original do sinalizador –privileged. Minhas alterações funcionaram e todos os testes passaram na minha máquina Debian e o Ubuntu também testou as máquinas virtuais, mas elas travavam e queimavam na máquina de Michael Crosby (tanto quanto me lembro, ele tinha o Fedora). Não me lembro da causa exata do problema, mas provavelmente aconteceu porque Mike é uma pessoa sábia que trabalha com SELINUX = enforce (usei o AppArmor) e minhas alterações não levaram em consideração os perfis do SELinux.

Docker-in-Docker: irritado


O segundo problema está relacionado aos drivers de armazenamento do Docker. Quando você inicia o Docker-in-Docker, o Docker externo é executado sobre o sistema de arquivos normal (EXT4, BTRFS ou o que você tiver) e o Docker interno é executado sobre o sistema de cópia e gravação (AUFS, BTRFS, Mapeador de Dispositivos etc.) , dependendo do que está configurado para usar um Docker externo). Nesse caso, existem muitas combinações que não funcionarão. Por exemplo, você não pode executar o AUFS sobre o AUFS.

Se você executar o BTRFS em cima do BTRFS, isso funcionará primeiro, mas assim que as subchaves aparecerem, o subvolume pai não poderá ser excluído. O módulo Mapeador de Dispositivos não possui um espaço para nome; portanto, se várias instâncias do Docker o usarem na mesma máquina, todas poderão ver (e influenciar) as imagens umas nas outras e nos dispositivos de backup do contêiner. Isto é mau.

Existem soluções alternativas para resolver muitos desses problemas. Por exemplo, se você deseja usar o AUFS no Docker interno, basta transformar a pasta / var / lib / docker em uma e tudo ficará bem. O Docker adicionou alguns espaços de nomes básicos aos nomes de destino do Mapeador de Dispositivos, para que, se várias chamadas ao Docker forem feitas na mesma máquina, elas não se "afastem".

No entanto, essa configuração está longe de ser simples, como você pode ver nesses artigos no repositório dind no GitHub.

Docker-in-Docker: piorando


E o cache de compilação? Isso também pode ser bastante difícil. As pessoas costumam me perguntar "se eu executo o Docker-in-Docker, como posso usar as imagens localizadas no meu host, em vez de recuperar tudo no meu Docker interno"?

Algumas pessoas empreendedoras tentaram vincular / var / lib / docker do host ao contêiner do Docker-in-Docker. Às vezes, eles compartilham / var / lib / docker com vários contêineres.


Deseja corromper dados? Porque é exatamente isso que danificará seus dados!

O daemon docker foi claramente projetado para ter acesso exclusivo ao / var / lib / docker. Nada mais deve "tocar, cutucar ou tocar" qualquer arquivo do Docker nesta pasta.

Porque isto é assim? Porque é o resultado de uma das lições mais difíceis aprendidas com o desenvolvimento do dotCloud. O mecanismo de contêiner dotCloud trabalhou com vários processos acessando / var / lib / dotcloud ao mesmo tempo. Truques complicados, como a substituição atômica de arquivos (em vez de a edição no local), "empoleirar" o código com bloqueios obrigatórios e consultivos e outras experiências com sistemas seguros como SQLite e BDB, nem sempre funcionavam. Quando redesenhamos nosso mecanismo de contêiner, que acabou se transformando no Docker, uma das principais decisões de design foi coletar todas as operações de contêineres em um único daemon para eliminar toda essa bobagem de acesso simultâneo.

Não me entenda mal: é bem possível fazer algo bom, confiável e rápido, que incluirá vários processos e controle paralelo moderno. Mas achamos mais simples e fácil escrever e manter o código usando o Docker como o único player.

Isso significa que, se você compartilhar o diretório / var / lib / docker entre várias instâncias do Docker, terá problemas. Obviamente, isso pode funcionar, especialmente nos estágios iniciais do teste. "Escute, mãe, eu posso executar o ubuntu como uma janela de encaixe!" Mas tente fazer algo mais complexo, por exemplo, retire a mesma imagem de duas instâncias diferentes e você verá como o mundo queima.

Isso significa que, se o sistema de IC executar montagens e remontagens, sempre que você reiniciar o contêiner do Docker-in-Docker, você corre o risco de soltar uma bomba nuclear em seu cache. Isso não é nada legal!

Solução para o problema


Vamos dar um passo atrás. Você realmente precisa de um Docker-in-Docker ou apenas deseja poder executar o Docker, ou seja, criar e executar contêineres e imagens do seu sistema de IC, enquanto esse sistema de CI estiver no contêiner?

Aposto que muitas pessoas precisam da última opção, ou seja, desejam um sistema de IC como o Jenkins para executar contêineres. E a maneira mais fácil de fazer isso é simplesmente inserir o soquete do Docker no seu contêiner de IC, associando-o ao sinalizador -v.

Simplificando, quando você inicia seu contêiner de IC (Jenkins ou outro), em vez de invadir algo com o Docker-in-Docker, inicie-o da linha:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Agora, esse contêiner terá acesso ao soquete do Docker e, portanto, poderá iniciar contêineres. Exceto que, em vez de iniciar contêineres "filhos", ele executará contêineres "relacionados".

Tente fazer isso usando a imagem oficial da janela de encaixe (que contém o binário do Docker):

docker run -v /var/run/docker.sock:/var/run/docker.sock \
           -ti docker

Parece e funciona como um Docker-in-Docker, mas não é um Docker-in-Docker: quando esse contêiner cria contêineres adicionais, eles serão criados no Docker de nível superior. Você não experimentará os efeitos colaterais do aninhamento e o cache de compilação será compartilhado por várias chamadas.

Nota: as versões anteriores deste artigo recomendavam a ligação do binário do Docker do host ao contêiner. Isso agora não é confiável, pois o mecanismo do Docker não se estende mais às bibliotecas estáticas ou quase estáticas.

Portanto, se você deseja usar o Docker do Jenkins CI, você tem 2 opções:
instalar a CLI do Docker usando o sistema básico de empacotamento de imagens (ou seja, se sua imagem for baseada no Debian, use os pacotes .deb), usando a API do Docker.

Um pouco de publicidade :)


Obrigado por ficar com a gente. Você gosta dos nossos artigos? Deseja ver materiais mais interessantes? Ajude-nos fazendo um pedido ou recomendando aos seus amigos o VPS baseado em nuvem para desenvolvedores a partir de US $ 4,99 , um analógico exclusivo de servidores básicos que foi inventado por nós para você: Toda a verdade sobre o VPS (KVM) E5-2697 v3 (6 núcleos) 10GB DDR4 480GB SSD 1Gbps de 10GB de US $ 19 ou como dividir o servidor? (as opções estão disponíveis com RAID1 e RAID10, até 24 núcleos e até 40GB DDR4).

Dell R730xd 2 vezes mais barato no data center Equinix Tier IV em Amsterdã? Somente nós temos 2 TVs Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV a partir de US $ 199 na Holanda!Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - a partir de US $ 99! Leia sobre como construir infraestrutura classe c usando servidores Dell R730xd E5-2650 v4 que custam 9.000 euros por um centavo?

All Articles