26 November 2019

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

Open sourceIT InfrastructureVirtualizationAPIDevOps

Всем привет! В своей предыдущей статье, я обещал рассказать про запуск Докера в Докере и о практических аспектах применения этого занятия. Настало время выполнить свое обещание. Опытный девопс, пожалуй, возразит, что тем кому нужен Докер внутри Докера, просто пробрасывают сокет Докер демона из хоста внутрь контейнера и этого хватит в 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 контейнер и новые эксперименты



Чтобы не повторять вышеописанные шаги снова и снова я создал собственный 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. Если вы считаете данный проект полезным, то пожалуйста поставьте ему звездочку на ГитХабе, сделайте форк и расскажите друзьям.


Edit1 Исправил ошибки, сделал фокус на 2 видео

Tags:dockerdocker-in-dockervirtualizationopen sourceexperimentmysqlnodejsdevopscicdpipelinecontainerskubernetesjenkins x
Hubs: Open source IT Infrastructure Virtualization API DevOps
+5
7k 61
Comments 6