Складання docker контейнерів за допомогою docker контейнерів

docker

З моменту початку роботи з Docker ми зіткнулися з чималою кількістю проблем. Одна з них — організація складання додатків та пакування їх у контейнери. Ми вирішили це завдання за допомогою введення концепції складального контейнера. Про те, що це таке, навіщо потрібно і як ми до цього дійшли і йтиметься у цьому пості.

Що таке складальний контейнер (builder container)?

Складальний контейнер - це спеціально зібраний docker image, завдання якого виконувати якусь роботу над вихідними кодами програми, наприклад встановити залежності, скомпілювати вихідні коди, статичний аналіз коду і т.д. Визначення досить велике, тому розглянемо з прикладу.

У нас є проект. Щоб його зібрати та запустити потрібно:

  1. встановити всі необхідні npm пакети
  2. за допомогою bower встановити всі клієнтські залежності
  3. потім запустити grunt, щоб він зібрав нам все це
Ми користуємося для цього npm-builder - складальний контейнер з nodejs, npm, g++ і make всередині, для наочності виконаємо всі кроки окремо:

Отже, у кілька команд ми отримали зібраний проект.

Розберемо кроки, що ж відбувається. Ми запускаємо docker контейнер npm-builder у кореневій директорії проекту та монтуємо в /data поточну директорію (кореневу директорію проекту з усіма вихідними даними), /data ми вказуємо як робочу директорію контейнера, щоб усі команди в контейнері виконувались у ній. Першою командою, npm install, в директорію node_modules встановляться всі модулі nodejs перераховані в package.json, включаючи bower. Наступною командою bower install --allow-root ми встановлюємо в директорію bower_components всі залежності, перераховані в bower.json. І останньою командою ми запускаємо складання проекту grunt'ом.Додатково при запуску контейнерів вказуємо опцію --rm, щоб контейнери видалялися після зупинки.

На прикладі проекту на php, ми можемо використовувати складальний контейнер composer для встановлення залежностей:

Навіщо все це?

Поріг входження в проект.Використання складальних контейнерів дозволяє позбавитися необхідності установки різноманітного програмного забезпечення на машині розробника. Все, що потрібно для початку роботи над проектом - схилювати репозиторій проекту, встановити docker, docker-compose і виконати "docker-compose up -d", всі необхідні для збирання та запуску проекту контейнери підтягнуться з docker hub. Більше не потрібно встановлювати на машину розробника різноманітний необхідний для збирання та запуску софт.

Час збирання.Якщо всі інструкції для збирання вказувати в Dockerfile, то час збирання контейнера починає зростати, пропорційно кількості кроків збирання.

Єдиноманітність виконуваного середовища.Складання програми та її запуск тепер речі незалежні, упаковка в контейнер — лише COPY зібраного додатка. Значить, незалежно від оточення, контейнери в яких програми виконуються завжди одні й самі, наприклад, будь-який додаток на php виконується в контейнері з php. Значить, якщо щось раптом ламається, то ламається воно скрізь і однаково.

Повторне використання етапів складання.Якщо збірка описана в Dockerfile, то всі етапи складання кожен раз доведеться описувати заново, вирішується використанням інструкції ONBUILD, але в цьому випадку ми втрачаємо певний рівень гнучкості, наприклад, якщо знадобляться додаткові кроки складання їх все одно доведеться описувати окремо, також частково можна позбутися прозорості збирання, щоб зрозуміти що ж станеться при збиранні потрібно дивитисявихідник батьківського контейнера

Предсказаемость складання.Коли ми почали використовувати docker, то спочатку деякі кроки складання проектів ми проводили на хостовій машині, наприклад запуск grunt. Не робіть так;)

Єдиноманітність процесу складання, щоб не доводилося розбиратися з деталями Dockerfile, щоб docker виступав в першу чергу як засіб пакування, доставки та запуску додатків, а не як засіб керування конфігурацією. Виділивши процес збирання з Dockerfile ми змогли досягти того, що більшість Dockerfile'ів виглядають приблизно так:

Використання вже існуючих інструментів.В екосистемі докера вже існує маса інструментів, вистачає і корисних і не дуже, винаходити велосипед не хотілося. Наприклад, docker-compose, складальні контейнери підходять і тут.

Конвенції при використанні складальних контейнерів

У процесі застосування ідеї складальних контейнерів, ми виділили для себе кілька угод щодо розробки та використання.

Загальний “інтерфейс”. Із загального ми виділили volume'и. У складальних контейнерах є volume / data - для монтування вихідних програм і / cache - для монтування директорії з кешами складального контейнера, це можуть бути, наприклад, сторонні залежності (кеш composer, npm, bower). Для складальних контейнерів ми використовуємо один базовий образ, який виконує роль інтерфейсу.

Наприклад, ми не використовуємо окремі складальні контейнери з phpunit, тому що phpunit підключається через composer і як його версія, так і його наявність визначається безпосередньо самим додатком.

Спосіб запуску.За способом запуску ми поділяємо контейнера на:

  • One shotконтейнери, що запускаються один раз, виконують команду і завершують свою роботу. Зручно використовуватиме встановлення залежностей, клонування вихідників, генерації документації до апі.
  • Long running контейнери, довгоживучі, відслідковують зміни у вихідниках і перезбирають проект - перескладання SCSS при змінах стилів, складання JS, проганяють тести при змінах вихідників, проганяють статичний аналізатор і т.д.
Назва контейнерів.До назви складального контейнера ми додаємо суфікс “-builder”, наприклад, npm-builder, erlnag-builder і т.д.

Використання з docker-compose

Складальні контейнери у зв'язці з docker-compose зручно використовувати в оточенні розробника, коли потрібно, наприклад, робити деякі дії при зміні вихідних джерел.

Із docker-compose все просто, але є нюанси. Використовувати 1 shot контейнери можливо тільки якщо docker-compose up виконується з опцією “-d”, в іншому випадку після завершення роботи такого контейнера, compose зупиняє всі інші контейнери.

Ось так виглядає встановлення залежностей php програми з використанням composer:

Long running контейнери працюють без проблем. Ось так виглядає запуск установки nodejs, bower залежностей та запуск grunt'a:

Використання з make

Make у зв'язці з докером та складальними контейнерами ми використовуємо для складання релізів, місцями ми їм замінюємо docker-compose.

Ось приклад makefile'а, який використовує підхід зі складальними контейнерами:

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

На цьому все. Дякую за увагу.

Дякую своєму колегі, cnam812, за допомогу у написанні статті.

Ви можете допомогти і перевести небагато коштів на розвиток сайту