Добре обмислете, преди да използвате 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 флаг –привилегирован и написах първата версия 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 ядра) 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 100TB от $199 в Нидерландия! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — от $99! Четете за това Как да изградим инфраструктура от корпоративен клас с прилагане на сървъри Dell R730xd Е5-2650 v4 на стойност 9000 евро за копейки?

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster