Как я запускал Докер внутри Докера и что из этого получилось

Всем привет! В своей предыдущей статье, я обещал рассказать про запуск Докера в Докере и о практических аспектах применения этого занятия. Настало время выполнить свое обещание. Опытный девопс, пожалуй, возразит, что тем кому нужен Докер внутри Докера, просто пробрасывают сокет Докер демона из хоста внутрь контейнера и этого хватит в 99% случаев. Но не спешите кидать в меня печеньки, в речь пойдет о реальном запуске Докера внутри Докера. У этого решения много возможных областей применения и об одном из них эта статья, так что усаживайтесь поудобнее и выпрямите руки перед собой.


image


Начало

Все началось дождливым сентябрьским вечером, когда я чистил арендованную за $5 машинку на Digital Ocean, которая намертво повисла из-за того что Докер заполонил своими образами и контейнерами все 24 гигабайта доступного дискового пространства. Ирония была в том, все все эти образы и контейнеры были транзиентными и нужны были лишь для того чтобы тестировать работоспособность моего приложения каждый раз когда выходила новая версия какой-либо библиотеки или фреймворка. Я пробовал писать шелл-срипты и настраивать расписание крон для очистки мусора, но это не спасло: каждый раз все неминуемо заканчивалось тем, что дисковое пространство моего сервера оказывалось съеденым, а сервер подвисшим (в лучшем случае). В какой-то момент мне наткнулась статья про то как запускать Jenkins в контейнере и как он может создавать и удалять сборочные конвееры через проброшенный в него сокет докер демона. Идея мне приглянулась, но я решил пойти дальше и попробовать поэкспериментировать с непосредственным запуском Докера внутри Докера. Мне тогда казалось вполне логичным решением выкачивать докер образы и создавать контейнеры всех приложений которые мне нужны для тестирования внутри другого контейнера (давайте назовем его staging контейнер). Идея заключалась в том, чтобы запускать staging контейнер с флагом -rm, что автоматически удаляет весь контейнер со всем его содержимым при его остановке. Я покопался с докер образом от самого Докера (https://hub.docker.com/_/docker), но оно оказалось слишком громоздким и мне так и не удалось заставить его работать так как мне нужно и мне хотелось пройти весь путь самому.


Практика. Шишки

Я задался целью заставить контейнер работать так как мне было нужно и продолжал свои эксперименты, результатом которых стало несметное количество шишек. Итогом моего самоистязания стала следующий алгоритм:


  1. Запускаем Докер контейнер в интерактивном режиме.

    docker run --privileged -it docker:18.09.6

    Обратите внимание на версию контейнера, шаг вправо или в лево и ваш ДинД превращается в тыкву. На самом деле, все ломается довольно часто с выходом новой версии.
    Мы должны сразу попасть в шелл.


  2. Пробуем узнать, какие контейнеры запущены (Ответ: никакие), но давайте выполним команду все-равно:

    docker ps

    Вы будете немного удивлены, но оказывается Докер демон даже не запущен:

    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. Давайте запустим его самостоятельно:

    dockerd &

    Еще одна неприятная неожиданность:

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

  4. Устанавливаем пакеты iptables и bash (в баше всяко работать приятнее чем в sh):

    apk add --no-cache iptables bash

  5. Запускаем bash. Наконец-то мы снова в привычном шелле


  6. попробуем запустить Докер еще раз:

    dockerd &

    Мы должны увидеть длинную простыню логов заканчивающуюся:

    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. Нажимаем Enter. Мы снова в баше.


Понаблюдать за этим действом можно здесь (английский):


Начиная с этого момента мы можем пробовать запускать другие контейнеры внутри нашего Докер контейнера, но что если мы хотим поднять еще один Докер контейнер внутри нашего Докер контейнера или что-то пойдет не так и контейнер «вылетит»? Начинать все с начала.


Собственный DinD контейнер и новые эксперименты


8c743e3fa9173abdf6916f4e02b349ac.png


Чтобы не повторять вышеописанные шаги снова и снова я создал собственный DinD контейнер:

https://github.com/alekslitvinenk/dind

Рабочее DinD решение дало мне возможность запускать Докер внутри Докера рекурсивно и проводить более смелые эксперименты.
Один такой (удачный) эксперимент с запуском MySQL и Nodejs я собираюсь сейчас описать.
Самые нетерпеливые могут посмотреть как это было здесь


Итак, начнем:


  1. Запускаем DinD в интерактивном режиме. В данной версии DinD нам нужно вручную замапить все порты которые могут использовать наши дочерние контейнеры (я над этим уже работаю)

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

    Мы попадаем в баш, откуда можем сразу приступать к запуску дочерних контейнеров.


  2. Запускаем MySQL:

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

  3. Подключаемся к базе данных так же как мы бы подключались к ней локально. Убеждаемся что все работает.


  4. Запускаем второй контейнер:

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

    Обратите внимание, что порт мапинг здесь будет именно 8080:8080, так как мы уже замапили порт 80 из хоста в родительский контейнер на порт 8080.


  5. Идем на localhost в браузере, убеждаемся что сервер отвечает «Hello World!».


В моем случае эксперимент с вложенными Докер контейнерами оказался довольно положительным и я продолжу развивать проект и использовать его для стейджинга. Мне кажется, что это гораздо более легковесное решение чем тот же Kubernetes и Jenkins X. Но это мое субъективное мнение.

Я думаю, что для сегодняшней статьи — это все. В следующей статье я более подробно опишу эксперименты с рекурсивным запуском Докера в Докере и монтирование директорий вглубь вложенных контейнеров.

P.S. Если вы считаете данный проект полезным, то пожалуйста поставьте ему звездочку на ГитХабе, сделайте форк и расскажите друзьям.

© Habrahabr.ru