Cómo ejecuté Docker dentro de Docker y qué salió de él

¡Hola a todos! En su artículo anteriorPrometí hablar sobre cómo ejecutar Docker en Docker y los aspectos prácticos del uso de esta lección. Es hora de cumplir tu promesa. Un desarrollador experimentado probablemente objetará que aquellos que necesitan Docker dentro de Docker simplemente reenvían el socket del demonio Docker desde el host al contenedor y esto será suficiente en el 99% de los casos. Pero no se apresure a arrojarme cookies, porque hablaremos sobre cómo ejecutar Docker dentro de Docker. Esta solución tiene muchas aplicaciones posibles y este artículo trata sobre una de ellas, así que siéntate y estira los brazos frente a ti.

Cómo ejecuté Docker dentro de Docker y qué salió de él

principio

Todo empezó una tarde lluviosa de septiembre cuando estaba limpiando la máquina que alquilé por 5 dólares en Digital Ocean y que estaba congelada porque Docker había llenado los 24 gigabytes de espacio disponible en el disco con sus imágenes y contenedores. La ironía era que todas estas imágenes y contenedores eran transitorios y sólo eran necesarios para probar el rendimiento de mi aplicación cada vez que se lanzaba una nueva versión de una biblioteca o marco. Intenté escribir scripts de shell y configurar una programación cron para limpiar la basura, pero no sirvió de nada: cada vez terminaba inevitablemente con el espacio en el disco de mi servidor consumido y el servidor colgado (en el mejor de los casos). En algún momento, me encontré con un artículo sobre cómo ejecutar Jenkins en un contenedor y cómo puede crear y eliminar canalizaciones de compilación a través de un socket de demonio acoplable reenviado a él. Me gustó la idea, pero decidí ir más allá e intentar experimentar ejecutando Docker directamente dentro de Docker. En ese momento, me pareció una solución completamente lógica descargar imágenes de Docker y crear contenedores para todas las aplicaciones que necesitaba para probar dentro de otro contenedor (llamémoslo contenedor provisional). La idea era iniciar un contenedor provisional con el indicador -rm, que elimina automáticamente todo el contenedor y todo su contenido cuando se detiene. Jugué con la imagen de Docker desde el propio Docker (https://hub.docker.com/_/docker), pero resultó ser demasiado engorroso y nunca logré que funcionara como lo necesitaba y quise llegar hasta el final yo mismo.

Práctica. Conos

Me propuse hacer que el contenedor funcionara como necesitaba y continué mis experimentos, lo que resultó en una gran cantidad de cogollos. El resultado de mi autotortura fue el siguiente algoritmo:

  1. Lanzamos el contenedor Docker en modo interactivo.

    docker run --privileged -it docker:18.09.6

    Presta atención a la versión del contenedor, da un paso hacia la derecha o hacia la izquierda y tu DinD se convierte en una calabaza. De hecho, las cosas se estropean con bastante frecuencia cuando se lanza una nueva versión.
    Debemos meternos inmediatamente en el caparazón.

  2. Estamos intentando averiguar qué contenedores se están ejecutando (Respuesta: ninguno), pero ejecutemos el comando de todos modos:

    docker ps

    Te sorprenderá un poco, pero resulta que el demonio Docker ni siquiera se está ejecutando:

    error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 
    192.168.65.1:53: no such host

  3. Ejecutémoslo nosotros mismos:

    dockerd &

    Otra sorpresa desagradable:

    failed to start daemon: Error initializing network controller: error obtaining controller instance: failed 
    to create NAT chain DOCKER: Iptables not found

  4. Instale los paquetes iptables y bash (todo es más agradable de trabajar en bash que en sh):

    apk add --no-cache iptables bash

  5. Iniciemos bash. Finalmente volvemos al caparazón habitual.

  6. Intentemos iniciar Docker nuevamente:

    dockerd &

    Deberíamos ver una larga hoja de registros que termina con:

    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

  7. Presione Entrar. Estamos de vuelta en la fiesta.

De ahora en adelante, podemos intentar lanzar otros contenedores dentro de nuestro contenedor Docker, pero ¿qué pasa si queremos lanzar otro contenedor Docker dentro de nuestro contenedor Docker o algo sale mal y el contenedor falla? Empezar de nuevo.

Contenedor DinD propio y nuevos experimentos.

Cómo ejecuté Docker dentro de Docker y qué salió de él
Para evitar repetir los pasos anteriores una y otra vez, creé mi propio contenedor DinD:

https://github.com/alekslitvinenk/dind

La solución DinD funcional me dio la posibilidad de ejecutar Docker dentro de Docker de forma recursiva y realizar experimentos más aventureros.
Ahora voy a describir uno de esos experimentos (exitosos) con la ejecución de MySQL y Nodejs.
Los más impacientes podrán comprobar cómo fue aquí.

Entonces, comencemos:

  1. Lanzamos DinD en modo interactivo. En esta versión de DinD, necesitamos mapear manualmente todos los puertos que nuestros contenedores secundarios pueden usar (ya estoy trabajando en esto)

    docker run --privileged -it 
    -p 80:8080 
    -p 3306:3306 
    alekslitvinenk/dind

    Entramos en bash, desde donde podemos comenzar inmediatamente a lanzar contenedores secundarios.

  2. Inicie MySQL:

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysql

  3. Nos conectamos a la base de datos de la misma manera que lo haríamos localmente. Asegurémonos de que todo funcione.

  4. Inicie el segundo contenedor:

    docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-server

    Tenga en cuenta que la asignación de puertos será exactamente 8080:8080, ya que ya hemos asignado el puerto 80 desde el host al contenedor principal al puerto 8080.

  5. Vamos a localhost en el navegador, nos aseguramos de que el servidor responda “¡Hola mundo!”

En mi caso, el experimento con contenedores Docker anidados resultó bastante positivo y continuaré desarrollando el proyecto y utilizándolo para la puesta en escena. Me parece que esta es una solución mucho más liviana que Kubernetes y Jenkins X. Pero esta es mi opinión subjetiva.

Creo que eso es todo por el artículo de hoy. En el próximo artículo, describiré con más detalle experimentos con la ejecución recursiva de Docker en Docker y el montaje de directorios en contenedores anidados.

PS Si encuentra útil este proyecto, dele una estrella en GitHub, bifurquelo y dígaselo a sus amigos.

Edit1 Errores corregidos, centrados en 2 vídeos.

Fuente: habr.com

Añadir un comentario