Docker-in-Docker é un entorno de daemon Docker virtualizado que se executa no propio contenedor para crear imaxes do contenedor. O propósito principal de crear Docker-in-Docker era axudar a desenvolver o propio Docker. Moitas persoas úsano para executar Jenkins CI. Isto parece normal ao principio, pero despois xorden problemas que se poden evitar instalando Docker nun contenedor Jenkins CI. Este artigo indícase como facelo. Se estás interesado na solución final sen detalles, só tes que ler a última sección do artigo, "Resolver o problema".
Docker-in-Docker: "Bo"
Hai máis de dous anos que puxen en Docker
- hackity hack;
- construír;
- deter un daemon Docker en execución;
- lanzando un novo daemon Docker;
- probas;
- repetir o ciclo.
Se querías facer un conxunto fermoso e reproducible (é dicir, nun recipiente), volveuse máis complicado:
- hackity hack;
- asegúrese de que se está a executar unha versión de Docker que funcione;
- construír un novo Docker co antigo Docker;
- parar o demo Docker;
- iniciar un novo daemon Docker;
- proba;
- deter o novo daemon Docker;
- repetir.
Coa chegada de Docker-in-Docker, o proceso fíxose máis sinxelo:
- hackity hack;
- montaxe + lanzamento nunha etapa;
- repetir o ciclo.
Non é moito mellor deste xeito?
Docker-in-Docker: "Malo"
Non obstante, ao contrario da crenza popular, Docker-in-Docker non é 100% estrelas, pôneis e unicornios. O que quero dicir é que hai varios problemas dos que un desenvolvedor debe ter en conta.
Un deles refírese aos LSM (módulos de seguridade de Linux) como AppArmor e SELinux: ao executar un contedor, o "Docker interno" pode tentar aplicar perfís de seguridade que entrarán en conflito ou confundirán ao "Docker externo". Este é o problema máis difícil de resolver cando se intenta combinar a implementación orixinal da bandeira –privileged. Os meus cambios funcionaron e todas as probas pasarían á miña máquina Debian e ás máquinas virtuales de proba de Ubuntu, pero fallaban e arderían na máquina de Michael Crosby (que recordo que tiña Fedora). Non lembro a causa exacta do problema, pero puido ser porque Mike é un tipo sabio que traballa con SELINUX=enforce (eu usei AppArmor) e os meus cambios non tiveron en conta os perfís de SELinux.
Docker-in-Docker: "Malvado"
O segundo problema é cos controladores de almacenamento Docker. Cando executa Docker-in-Docker, o Docker externo execútase enriba dun sistema de ficheiros normal (EXT4, BTRFS ou o que teña) e o Docker interno execútase nun sistema de copia en escritura (AUFS, BTRFS, Device Mapper). , etc.). , dependendo do que estea configurado para usar Docker externo). Isto crea moitas combinacións que non funcionarán. Por exemplo, non poderá executar AUFS enriba de AUFS.
Se executas BTRFS enriba de BTRFS, debería funcionar ao principio, pero unha vez que haxa subvolumes aniñados, fallará ao eliminar o subvolume pai. O módulo Device Mapper non ten espazo de nomes, polo que se o executan varias instancias de Docker na mesma máquina, todas poderán ver (e influír) as imaxes entre si e nos dispositivos de copia de seguridade do contedor. Isto é malo.
Hai solucións para resolver moitos destes problemas. Por exemplo, se queres usar AUFS no Docker interno, só tes que converter o cartafol /var/lib/docker nun volume e estarás ben. Docker engadiu algúns espazos de nomes base aos nomes de destino do Device Mapper para que se se executan varias chamadas de Docker na mesma máquina, non se pisarán entre si.
Non obstante, tal configuración non é nada sinxela, como se pode ver nestes
Docker-in-Docker: empeora
Que pasa coa caché de compilación? Isto tamén pode ser bastante difícil. Moitas veces, a xente pregúntame "se estou a executar Docker-in-Docker, como podo usar imaxes aloxadas no meu servidor en lugar de devolver todo ao meu Docker interno"?
Algunhas persoas emprendedoras intentaron vincular /var/lib/docker desde o host a un contedor Docker-in-Docker. Ás veces comparten /var/lib/docker con varios contedores.
Queres corromper os teus datos? Porque isto é exactamente o que danará os teus datos.
O daemon Docker foi deseñado claramente para ter acceso exclusivo a /var/lib/docker. Nada máis debería "tocar, pinchar ou tocar" ningún ficheiro Docker situado neste cartafol.
Por que é así? Porque este é o resultado dunha das leccións máis difíciles aprendidas ao desenvolver dotCloud. O motor de contedores dotCloud funcionou ao ter varios procesos que acceden a /var/lib/dotcloud simultaneamente. Trucos astutos como a substitución de ficheiros atómicos (en lugar da edición no lugar), o código peppering con bloqueos consultivos e obrigatorios e outros experimentos con sistemas seguros como SQLite e BDB non sempre funcionaron. Cando estabamos a redeseñar o noso motor de contedores, que finalmente se converteu en Docker, unha das grandes decisións de deseño foi consolidar todas as operacións de contedores baixo un único daemon para eliminar todas as tonterías de concorrencia.
Non me malinterpretes: é totalmente posible facer algo bo, fiable e rápido que implique múltiples procesos e control paralelo moderno. Pero pensamos que é máis sinxelo escribir e manter código usando Docker como único reprodutor.
Isto significa que se compartes o directorio /var/lib/docker entre varias instancias de Docker, terás problemas. Por suposto, isto pode funcionar, especialmente nas primeiras fases das probas. "Escoita, mamá, podo executar ubuntu como un docker!" Pero proba algo máis complexo, como tirar a mesma imaxe de dúas instancias diferentes, e verás que o mundo arde.
Isto significa que se o teu sistema de CI realiza compilacións e reconstrucións, cada vez que reinicias o teu contedor Docker-in-Docker, corres o risco de soltar unha bomba nuclear na súa caché. Isto non é xenial!
A solución
Imos dar un paso atrás. Necesitas realmente Docker-in-Docker ou só queres poder executar Docker e construír e executar contedores e imaxes desde o teu sistema CI mentres ese propio sistema CI está nun contedor?
Aposto que a maioría da xente quere esta última opción, o que significa que queren que un sistema de CI como Jenkins poida executar contedores. E a forma máis sinxela de facelo é simplemente inserir un socket Docker no seu contenedor CI e asocialo coa bandeira -v.
Simplemente, cando executes o teu contenedor CI (Jenkins ou outro), en lugar de piratear algo xunto con Docker-in-Docker, iníciao coa liña:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
Este contedor agora terá acceso ao socket Docker e, polo tanto, poderá executar contedores. Excepto que en lugar de executar contedores "nenos", lanzará contedores "irmáns".
Proba isto usando a imaxe oficial de Docker (que contén o binario de Docker):
docker run -v /var/run/docker.sock:/var/run/docker.sock
-ti docker
Parece e funciona como Docker-in-Docker, pero non é Docker-in-Docker: cando este contedor cree contedores adicionais, crearanse no Docker de nivel superior. Non experimentarás os efectos secundarios do aniñamento e a caché de montaxe compartirase en varias chamadas.
Nota: As versións anteriores deste artigo recomendaban ligar o binario de Docker do host ao contedor. Agora non é fiable xa que o motor Docker xa non abarca bibliotecas estáticas ou case estáticas.
Entón, se queres usar Docker de Jenkins CI, tes dúas opcións:
instalando a CLI de Docker usando o sistema básico de empaquetado de imaxes (é dicir, se a súa imaxe está baseada en Debian, use paquetes .deb), usando a API de Docker.
Algúns anuncios 🙂
Grazas por estar connosco. Gústanche os nosos artigos? Queres ver máis contido interesante? Apóyanos facendo un pedido ou recomendando a amigos,
Dell R730xd 2 veces máis barato no centro de datos Equinix Tier IV en Amsterdam? Só aquí
Fonte: www.habr.com