Добре подумайте, перш ніж використовувати Docker-in-Docker для CI або тестового середовища

Добре подумайте, перш ніж використовувати Docker-in-Docker для CI або тестового середовища

Docker-in-Docker являє собою віртуалізоване середовище Docker-демон, запущене в самому контейнері для збирання образів контейнера. Основною метою створення Docker-in-Docker була допомога у розробці самого Docker. Багато людей використовують його для запуску Jenkins CI. Спочатку це здається нормальним, але потім виникають проблеми, яких можна уникнути, встановивши Docker у контейнер Jenkins CI. У цій статті розповідається, як це зробити. Якщо вас цікавить підсумкове рішення без подробиць, просто прочитайте останній розділ статті «Вирішення проблеми».

Добре подумайте, перш ніж використовувати Docker-in-Docker для CI або тестового середовища

Docker-in-Docker: "Хороший"

Більше двох років тому я вставив у Docker прапор –privileged та написав першу версію dind. Ціль полягала в тому, щоб допомогти основній команді швидше розробляти Docker. До появи Docker-in-Docker типовий цикл розробки був таким:

  • hackity hack;
  • складання (build);
  • зупинка запущеного Docker-демон;
  • запуск нового Docker демон;
  • тестування;
  • повторення циклу.

Якщо ж ви хотіли зробити красиву, відтворювану збірку (тобто в контейнері), то вона ставала більш хитромудрою:

  • hackity hack;
  • переконатися, що запущена працездатна версія Docker;
  • зібрати новий Docker зі старим Docker;
  • зупинити Docker демон;
  • запустити новий Docker-демон;
  • протестувати;
  • зупинити новий Docker демон;
  • повторити.

З появою Docker-in-Docker процес спростився:

  • hackity hack;
  • складання + запуск в один етап;
  • повторення циклу.

Чи не так, так набагато краще?

Добре подумайте, перш ніж використовувати Docker-in-Docker для CI або тестового середовища

Docker-in-Docker: "Поганий"

Однак, всупереч поширеній думці, Docker-in-Docker не складається на 100% із зірочок, поні та єдинорігів. Я маю на увазі, що є кілька проблем, про які розробнику потрібно знати.

Одна з них стосується LSM (модулів безпеки Linux), таких як AppArmor та SELinux: під час запуску контейнера «внутрішній Docker» може спробувати застосувати профілі безпеки, які конфліктуватимуть або заплутуватимуть «зовнішній Docker». Це найскладніша проблема, яку потрібно було вирішити при спробі поєднати вихідну реалізацію прапора – privileged. Мої зміни працювали, і всі тести теж пройшли б на моїй машині Debian і тестових віртуальних машинах Ubuntu, але вони б впали і згоріли на машині Майкла Кросбі (наскільки я пам'ятаю, у нього була Fedora). Я не можу згадати точну причину проблеми, але можливо, вона виникала тому, що Майк - мудра людина, яка працює з SELINUX = enforce (я використовував AppArmor), і мої зміни не враховували профілі SELinux.

Docker-in-Docker: "Злий"

Друга проблема пов'язана із драйверами сховища Docker. Коли ви запускаєте Docker-in-Docker, зовнішній Docker працює поверх звичайної файлової системи (EXT4, BTRFS або будь-який інший, яку ви маєте), а внутрішній Docker працює поверх системи копіювання під час запису (AUFS, BTRFS, Device Mapper і т.д.). , в залежності від того, що налаштовано використовувати зовнішній Docker). При цьому виникає безліч комбінацій, які не працюватимуть. Наприклад, ви не зможете запускати AUFS поверх AUFS.

Якщо ви запускаєте BTRFS поверх BTRFS, спочатку це має працювати, але як тільки з'являться вкладені підрозділи, видалити батьківський підрозділ parent subvolume не вдасться. Модуль Device Mapper не має простору імен, тому якщо кілька екземплярів Docker використовують його на одній машині, всі вони зможуть бачити (і впливати) на образи один на одного і на пристрої резервного копіювання контейнерів. Це погано.

Є обхідні шляхи для вирішення багатьох цих проблем. Наприклад, якщо ви хочете використовувати AUFS у внутрішньому Docker, просто перетворите папку /var/lib/docker в тому, і все буде гаразд. Docker додав деякі базові простори імен до цільових імен Device Mapper, тому якщо кілька викликів Docker будуть виконуватися на одній машині, вони не стануть «наступати» один на одного.

Тим не менш, така настройка зовсім не проста, як можна побачити з цих статей у репозиторії dind на GitHub.

Docker-in-Docker: стає ще гірше

А як щодо кешу збирання? Це також може бути досить складно. Люди часто запитують мене “якщо я запускаю Docker-in-Docker, як я можу використати образи, розташовані на моєму хості, замість того, щоб знову витягувати все у моєму внутрішньому Docker”?

Деякі підприємливі люди намагалися прив'язати /var/lib/docker із хоста до контейнера Docker-in-Docker. Іноді вони спільно використовують /var/lib/docker із кількома контейнерами.

Добре подумайте, перш ніж використовувати Docker-in-Docker для CI або тестового середовища
Бажаєте пошкодити дані? Тому що це саме те, що зашкодить вашим даним!

Docker-демон був розроблений для того, щоб мати ексклюзивний доступ до /var/lib/docker. Ніщо інше не повинно "торкатися, тикати або мацати" будь-які файли Docker, що знаходяться в цій папці.

Чому це так? Тому що це результат одного з найважчих уроків, отриманих під час розробки dotCloud. Контейнерний двигун dotCloud працював, маючи кілька процесів, що одночасно звертаються до /var/lib/dotcloud. Хитрі трюки, такі як атомарна заміна файлів (замість редагування на місці), «складання» коду рекомендаційними та обов'язковими блокуваннями та інші експерименти з безпечними системами, такими як SQLite та BDB, спрацьовували не завжди. Коли ми переробляли наш контейнерний двигун, який врешті-решт перетворився на Docker, одним з головних дизайнерських рішень було зібрати всі операції з контейнерами під єдиним демоном, щоб покінчити з цією нісенітницею одночасного доступу.

Не зрозумійте мене неправильно: цілком можливо зробити щось хороше, надійне і швидке, що включатиме кілька процесів і сучасне паралельне управління. Але ми думаємо, що простіше і легше писати та підтримувати код, використовуючи Docker як єдиний гравець.

Це означає, що якщо ви поділяєте каталог /var/lib/docker між кількома екземплярами Docker, у вас будуть проблеми. Звісно, ​​це може спрацювати, особливо на ранніх стадіях тестування. "Слухай, Ма, я можу "докером" запустити ubuntu!" Але спробуйте зробити щось складніше, наприклад, витягнути той самий образ із двох різних екземплярів, і ви побачите, як палає світ.

Це означає, що якщо ваша система CI виконує складання та перескладання, то щоразу при перезапуску контейнера Docker-in-Docker ви ризикуєте скинути в його кеш ядерну бомбу. Це зовсім не круто!

Рішення проблеми

Давайте зробимо крок назад. Вам дійсно потрібний Docker-in-Docker або ви просто хочете мати можливість запускати Docker, а саме збирати та запускати контейнери та образи з вашої системи CI, тоді як сама ця система CI знаходиться у контейнері?

Б'юся об заклад, що більшості людей потрібен останній варіант, тобто вони хочуть, щоб система CI, така як Jenkins, могла запускати контейнери. І найпростіший спосіб зробити це просто вставити сокет Docker у ваш CI-контейнер, зв'язавши його з прапором -v.

Простіше кажучи, коли ви запускаєте свій CI-контейнер (Jenkins або інший), замість того, щоб зламувати щось разом з Docker-in-Docker, почніть його з рядка:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Тепер цей контейнер матиме доступ до сокету Docker і, отже, зможе запускати контейнери. За винятком того, що замість запуску "дочірніх" контейнерів він запускатиме "родинні" контейнери.

Спробуйте це, використовуючи офіційний образ docker (який містить двійковий файл Docker):

docker run -v /var/run/docker.sock:/var/run/docker.sock 
           -ti docker

Це виглядає і працює як Docker-in-Docker, але це не Docker-in-Docker: коли цей контейнер буде створювати додаткові контейнери, вони будуть створені в Docker найвищого рівня. Ви не будете відчувати побічні ефекти вкладеності, і кеш збірки буде спільно використовуватися для кількох викликів.

Примітка: попередні версії цієї статті рекомендували прив'язати двійковий файл Docker від хоста до контейнера. Тепер це стало ненадійним, оскільки механізм Docker більше не поширюється на статичні чи майже статичні бібліотеки.

Таким чином, якщо ви хочете використовувати Docker з Jenkins CI, у вас є 2 варіанти:
встановлення Docker CLI з використанням базової системи упаковки образу (тобто якщо ваш образ заснований на Debian, використовуйте пакети .deb), використання Docker API.

Небагато реклами 🙂

Дякую, що залишаєтеся з нами. Вам подобаються наші статті? Бажаєте бачити більше цікавих матеріалів? Підтримайте нас, оформивши замовлення або порекомендувавши знайомим, хмарні VPS для розробників від $4.99, унікальний аналог entry-level серверів, який був винайдений нами для Вас: Вся правда про VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps від $19 чи як правильно ділити сервер? (Доступні варіанти з RAID1 і RAID10, до 24 ядер і до 40GB DDR4).

Dell R730xd вдвічі дешевше в дата-центрі Equinix Tier IV в Амстердамі? Тільки в нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТБ від $199 у Нідерландах! Dell R420 – 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB – від $99! Читайте про те Як побудувати інфраструктуру корп. класу із застосуванням серверів Dell R730xd Е5-2650 v4 вартістю 9000 євро за копійки?

Джерело: habr.com

Додати коментар або відгук