Здравейте на всички! В моя , обещах да разкажа за стартиране на Docker в Docker и за практическите аспекти на това занимание. Време е да изпълня своето обещание. Опитен DevOps специалист вероятно би възразил, че за тези, на които им е нужен Docker в Docker, просто пробиват сокета на Docker демона от хоста в контейнера и това е достатъчно в 99% от случаите. Но не бързайте да ме обсипвате с бисквити, защото ще говорим за истинското стартиране на Docker вътре в Docker. Това решение има много възможни области на приложение и една от тях е темата на тази статия, така че се настанете удобно и изправете ръцете си пред себе си.

Начало
Всичко започна в дъждовната септемврийска вечер, когато чистех наетата за $5 машинка в Digital Ocean, която замръзна, защото Docker запълни всичките 24 гигабайта налично дисково пространство с образи и контейнери. Иронията беше, че всички тези образи и контейнери бяха преходни и бяха нужни единствено за тестване на работоспособността на моето приложение всякога, когато излизаше нова версия на някоя библиотека или фреймворк. Опитах се да пиша shell скриптове и да настройвам cron задачи за почистване на боклука, но това не помогна: всеки път всичко неминуемо завършваше с това, че дисковото пространство на сървъра ми се изсмукваше, а сървърът оставаше замръзнал (в най-добрия случай). В някой момент попаднах на статия за стартиране на Jenkins в контейнер и как той може да създава и изтрива билд пайплайни чрез прокаран в него сокет на Docker демона. Идеята ми допадна, но реших да отида по-далеч и да експериментирам с директното стартиране на Docker вътре в Docker. Тогава ми се струваше съвсем логично решение да изтеглям Docker образи и да създавам контейнери на всички приложения, които са ми нужни за тестване, вътре в друг контейнер (нека го наречем staging контейнер). Идеята беше да стартирам staging контейнера с флага -rm, който автоматично изтрива целия контейнер с всичкото му съдържание при спиране. Пробвах Docker образа на самия Docker (), но той се оказа твърде обемист и не успях да го накарам да работи, както ми трябваше и исках да премина целия път сам.
Практика. Уроци
Поставих си за цел да накарам контейнера да работи така, както ми беше нужно, и продължих експериментите си, резултатът от които бе безброй шишарки. Резултатът от самонаказанието ми е следният алгоритъм:
Стартираме Docker контейнера в интерактивен режим.
docker run --privileged -it docker:18.09.6Обърнете внимание на версията на контейнера, стъпка надясно или наляво и вашият DinD се превръща в тиква. Всъщност, всичко често се чупи с излизането на нова версия.
Трябва веднага да влезем в shell.Опитваме да разберем какви контейнери са стартирани (Отговор: никакви), но нека все пак изпълним командата:
docker psЩе се изненадате малко, но се оказва, че Docker демонът дори не е стартиран:
error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 192.168.65.1:53: no such hostНека го стартираме сами:
dockerd &Още едно неприятно изненадващо:
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: Iptables not foundИнсталираме пакетите iptables и bash (в bash всичко е по-приятно, отколкото в sh):
apk add --no-cache iptables bashСтартираме bash. Най-накрая отново сме в познатия shell.
опитваме да стартираме Docker отново:
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Натискаме Enter. Отново сме в bash.
Започвайки от този момент, можем да опитваме да стартираме други контейнери в нашия Docker контейнер, но какво, ако искаме да стартираме още един Docker контейнер в нашия Docker контейнер или нещо не върви и контейнерът "изпадне"? Започваме отначало.
Собственият DinD контейнер и нови експерименти.
За да не повтарям описаните по-горе стъпки отново и отново, създадох собствен DinD контейнер:
Работещото DinD решение ми позволи да стартирам Docker вътре в Docker рекурсивно и да провеждам по-смели експерименти.
Сега ще опиша един такъв (успешен) експеримент с MySQL и Nodejs.
Най-нетърпеливите могат да видят как е било тук.

И така, нека започнем:
Стартираме DinD в интерактивен режим. В тази версия на DinD трябва ръчно да мапнем всички портове, които могат да бъдат използвани от нашите подчинени контейнери (аз вече работя по това).
docker run --privileged -it -p 80:8080 -p 3306:3306 alekslitvinenk/dindПопаднахме в контейнер, откъдето можем незабавно да започнем стартирането на дъщерни контейнери.
Стартиране на MySQL:
docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysqlСвързваме се с базата данни, както бихме го направили локално. Убедете се, че всичко работи.
Стартиране на втори контейнер:
docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-serverОбърнете внимание, че мапингът на порта тук ще бъде точно 8080:8080, тъй като вече сме мапнали порт 80 от хоста в родителския контейнер на порт 8080.
Отиваме на localhost в браузъра и се уверяваме, че сървърът отговаря с «Hello World!».
В моя случай експериментът с вложените Docker контейнери се оказа доста положителен и аз ще продължа да развивам проекта и да го използвам за стейджинг. Смятам, че това е много по-лесно решение в сравнение с Kubernetes и Jenkins X. Но това е моето субективно мнение.
Мисля, че за днешната статия това е всичко. В следващата статия ще опиша по-подробно експериментите с рекурсивното стартиране на Docker в Docker и монтиране на директории дълбоко в вложените контейнери.
P.S. Ако смятате, че този проект е полезен, моля дайте му звезда в GitHub, направете fork и разкажете на приятелите си.
Edit1 Поправих грешките, фокусирах се върху 2 видеа.
Източник: habr.com
