Збирати образи Docker в werf тепер можна і по звичайному Dockerfile

Краще пізно ніж ніколи. Або як ми мало не припустилися серйозної помилки, не маючи підтримки звичайних Dockerfiles для складання образів програми.

Збирати образи Docker в werf тепер можна і по звичайному Dockerfile

Йтиметься про werf — GitOps-утиліту, яка інтегрується з будь-якою CI/CD-системою та забезпечує керування всім життєвим циклом програми, дозволяючи:

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


Філософія проекту – зібрати низькорівневі інструменти в єдину уніфіковану систему, яка дає DevOps-інженерам контроль над додатками. По можливості повинні бути задіяні вже існуючі утиліти (на зразок Helm та Docker). Якщо ж вирішення якогось завдання немає — ми можемо створити та підтримувати все необхідне для цього.

Передісторія: свій збирач образів

Так і сталося зі збирачем образів у werf: звичного Dockerfile нам не вистачало. Якщо швидко поринути в історію проекту, то ця проблема виявилася вже в перших версіях werf (тоді ще відомого як dapp).

Створюючи інструмент для складання додатків у Docker-образи, ми швидко зрозуміли, що Dockerfile нам не підходить для деяких конкретних завдань:

  1. Необхідність збирати типові невеликі веб-застосунки за наступною стандартною схемою:
    • встановити загальносистемні залежності програми,
    • встановити bundle бібліотек залежностей програми,
    • зібрати асети,
    • і найважливіше – оновлювати код в образі швидко та ефективно.
  2. При змінах файлів проекту збирач повинен швидко створювати новий шар шляхом накладання патча на змінені файли.
  3. Якщо змінилися певні файли, необхідно перезбирати відповідну залежну стадію.

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

Загалом, недовго думаючи, ми озброїлися мовою програмування, що використовується. (див. нижче) і вирушили в дорогу - реалізовувати власний DSL! Відповідаючи поставленим завданням, він був призначений для опису процесу збирання по стадіях та визначення залежностей цих стадій від файлів. А доповнював його власний збирач, який перетворював DSL на кінцеву мету — зібраний образ. Спочатку DSL був на Ruby, а в міру переходу на Golang - Конфіг нашого збирача став описуватися в YAML-файлі.

Збирати образи Docker в werf тепер можна і по звичайному Dockerfile
Старий конфіг для dapp на Ruby

Збирати образи Docker в werf тепер можна і по звичайному Dockerfile
Актуальний конфіг для werf на YAML

Механізм роботи збирача теж змінювався з часом. Спочатку ми просто генерували на льоту якийсь тимчасовий Dockerfile з нашої конфігурації, а потім почали запускати складальні інструкції у тимчасових контейнерах і робити commit.

NB: На даний момент наш збирач, який працює зі своїм конфігом (у YAML) і називається Stapel-збирачем, вже розвинувся в досить потужний інструмент. Його розгорнутий опис заслуговує на окремі статті, а основні подробиці можна дізнатися з документації.

Усвідомлення проблеми

Але ми зрозуміли, причому не відразу, що зробили одну помилку: не додали можливості збирати образи через стандартний Dockerfile та інтегрувати їх у ту ж інфраструктуру комплексного управління додатком (тобто збирати образи, деплоїти та чистити їх). Як можна було зробити інструмент для деплою в Kubernetes і реалізувати підтримку Dockerfile, тобто. стандартного способу опису образів для більшості проектів?

Замість відповіді на таке запитання ми пропонуємо її вирішення. Що робити, якщо ви вже маєте Dockerfile (або набір Dockerfile'ів) і ви хочете використовувати werf?

NB: До речі, з чого б вам взагалі захотіти використати werf? Основні фічі зводяться до:

  • повний цикл управління додатком, включаючи очищення образів;
  • можливість керувати збиранням відразу кількох образів з єдиного конфіга;
  • покращений процес деплою чартів, сумісних з Helm.

З більш повним їх списком можна ознайомитись на сторінці проекту.

Отже, якщо раніше ми запропонували б переписати Dockerfile на наш конфіг, то тепер з радістю скажемо: «Дозвольте werf зібрати ваші Dockerfile'и!»

Як використовувати?

Повна реалізація цієї можливості з'явилася у релізі werf v1.0.3-beta.1. Загальний принцип простий: користувач вказує шлях до існуючого Dockerfile у конфізі werf, після чого запускає команду werf build… і все – werf збере образ. Розглянемо абстрактному прикладі.

Оголосимо наступний Dockerfile в корені проекту:

FROM ubuntu:18.04
RUN echo Building ...

І оголосимо werf.yaml, який використовує цей Dockerfile:

configVersion: 1
project: dockerfile-example
---
image: ~
dockerfile: ./Dockerfile

Всі! Залишилось запустити werf build:

Збирати образи Docker в werf тепер можна і по звичайному Dockerfile

Крім того, можна оголосити наступний werf.yaml для складання відразу кількох образів з різних Dockerfile'ів:

configVersion: 1
project: dockerfile-example
---
image: backend
dockerfile: ./dockerfiles/Dockerfile-backend
---
image: frontend
dockerfile: ./dockerfiles/Dockerfile-frontend

Нарешті, підтримується і передача додаткових параметрів збирання таких як --build-arg и --add-host - Через конфіг werf. Повний опис конфігурації Dockerfile image доступний на сторінці документації.

Як це працює?

У процесі складання функціонує стандартний кеш локальних шарів Docker. Однак, що важливо, werf також інтегрує конфігурацію Dockerfile у свою інфраструктуру. Що це означає?

  1. Кожен образ, зібраний з Dockerfile, складається з одного stage під назвою dockerfile (Докладніше про те, що таке stages в werf, можна почитати тут).
  2. Для stage'а dockerfile werf розраховує сигнатуру, яка залежить від конфігураційного вмісту Dockerfile. При зміні конфігурації Dockerfile відбувається зміна сигнатури стадії dockerfile і werf ініціює перескладання цієї стадії з новим конфігом Dockerfile. Якщо ж сигнатура не змінюється, то werf бере образ із кешу (детальніше про використання сигнатур у werf розповідалося у цій доповіді).
  3. Далі зібрані образи можна опублікувати командою werf publish (або werf build-and-publish) і використовувати для деплою в Kubernetes. Опубліковані образи Docker Registry будуть чиститися стандартними засобами очищення werf, тобто. відбудеться автоматичне очищення старих образів (старше N днів), образів, пов'язаних з неіснуючими Git-гілками, та за іншими політиками.

Докладніше про описані тут моменти можна дізнатися з документації:

Примітки та застереження

1. Зовнішня URL-адреса в ADD не підтримується

На даний момент не підтримується використання зовнішньої URL-адреси в директиві ADD. Werf не буде ініціювати перескладання при зміні ресурсу за вказаною URL-адресою. Незабаром планується додавання цієї можливості.

2. Не можна додавати .git в образ

Взагалі кажучи, додавання директорії .git в образ - порочна погана практика і ось чому:

  1. Якщо .git залишається у фінальному образі, це порушує принципи 12 factor app: оскільки підсумковий образ має бути пов'язаний з одним коммітом, не повинно бути можливості зробити git checkout довільного комміту.
  2. .git збільшує розмір образу (репозиторій може бути більшим через те, що до нього колись додали великі файли, а потім видалили). Розмір же work-tree, пов'язаного лише з певним коммітом, не залежатиме від історії операцій у Git. При цьому додавання та подальше видалення .git із фінального образу не спрацює: образ все одно набуде зайвого шару — так працює Docker.
  3. Docker може ініціювати зайве перескладання, навіть якщо йде складання одного і того ж комміту, але з різних work-tree. Наприклад, GitLab створює окремі склоновані директорії в /home/gitlab-runner/builds/HASH/[0-N]/yourproject при включеному паралельному складанні. Зайве перескладання буде пов'язане з тим, що директорія .git відрізняється в різних склонованих версіях одного і того ж репозиторію, навіть якщо збирається той самий коміт.

Останній пункт має наслідок та при використанні werf. Werf вимагає, щоб зібраний кеш був присутній під час запуску деяких команд (наприклад, werf deploy). Під час роботи таких команд werf розраховує сигнатури стадій для образів, зазначених у werf.yaml, і вони мають бути в складальному кеші - інакше команда не зможе продовжити роботу. Якщо ж сигнатура стадій залежатиме від вмісту .git, то ми отримуємо нестійкий до змін у нерелевантних файлах кеш, і werf не зможе пробачити таку помилку (докладніше див. документації).

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

Підсумок

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

Однак у процесі написання власного збирача ми згаяли підтримку вже існуючих Dockerfile'ів. Зараз цей недолік виправлений, а надалі ми плануємо розвивати підтримку Dockerfile поряд з нашим кастомним збирачем Stapel для розподіленого збирання та для збирання з використанням Kubernetes (тобто збирання на runner'ах усередині Kubernetes, як це зроблено в kaniko).

Тож якщо у вас раптом завалялася пара Dockerfile'ів… спробуйте werf!

PS Список документації на тему

Читайте також у нашому блозі: «werf — наш інструмент для CI/CD у Kubernetes (огляд та відео доповіді)».

Джерело: habr.com

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