Подібно до багатьох компаній, ми створюємо docker-зображення для всіх компонентів, що використовуються в нашому продукті. З часом кілька з цих зображень стали все більшими і більшими, і наші CI-збірки займали все більше часу. Моя мета полягає в тому, щоб CI-збірки не займали більше 5 хвилин. Ця ідея виникла з того факту, що це ідеальна тривалість для кавової перерви. Коли збірки займають більше часу, це уповільнює продуктивність розробників.
Причини втрат продуктивності полягають у наступному:
- розробники повинні чекати завершення збірки і, отже, витрачають час
- розробники переходять до чогось нового і повертаються до цього пізніше. Це вимагає більшого переключення контексту, що часто також призводить до неефективності.
У цьому блозі я хочу проілюструвати 2 невеликі зміни, які ми застосували, і які призвели до радикального покращення часу наших збірок. Перед тим, як сконцентруватися на цих покращеннях, переконайтеся, що ви вже дотримуєтесь найкращих практик щодо написання Dockerfile таких як:
- мінімізування кількості шарів
- використання багаторівневих збірок
- використання мінімального базового образу
- ...
Buildkit проти Buildx
Давайте почнемо з пояснення Buildkit і Buildx, оскільки обидва терміни часто використовуються взаємозамінно, але вони не одне й те саме. Перед написанням цього посту, я також не повністю розумів різницю між ними.
Buildkit
Buildkit - це вдосконалений базовий компонент для заміни застарілого будівельника Docker. Він поставляється з Docker з 2018 року і став більш стандартним засобом збірки з виходом двигуна Docker 23.0.
Він надає багато цікавих функцій:
- поліпшені можливості кешування
- паралельна збірка різних шарів
- ліниве завантаження базового образу (≥ Buildkit 0.9)
Коли ви використовуєте Buildkit, ви відразу помічаєте, що вивід команди docker build виглядає більш чисто і структуровано.
Типовий спосіб використання Buildkit з версією Docker старіше 23.0 полягає в наступному встановленні аргументу Buildkit:
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 . -t someImage:someVersion
DOCKER_BUILDKIT=1 docker push someImage:someVersion
Buildx
Buildx - це плагін для Docker, який дозволяє використовувати повний потенціал Buildkit в Docker. Він був створений через те, що Buildkit підтримує багато нових параметрів конфігурації, які не можуть бути інтегровані в команду docker build звичайним способом зі спиною сумісності.
Окрім побудови образів, Buildx підтримує керування декількома будівельниками. Це може бути корисним у CI для визначення обмежених середовищ з різними конфігураціями, які не модифікують загальний демон Docker.
Ви можете почати роботу з Buildx таким чином:
docker buildx create --bootstrap --name builder
docker buildx use builder
Використання віддаленої кешування
Перший спосіб прискорити ваші збірки - це кешування образу у віддаленому реєстрі. Цим чином ви можете скористатися кешем збірки навіть тоді, коли вашу збірку виконують на іншому комп'ютері, як це зазвичай буває в CI. Як обхідний шлях, багато людей завантажували останню версію образу перед побудовою нової версії образу. Перевага полягає в тому, що ви можете кешувати шари, що не змінилися, за рахунок повного завантаження образу спочатку. Завантаження повного образу може зайняти деякий час, але також немає гарантії, що шари можна буде повторно використовувати. Для ілюстрації, ми використовували наступні команди:
docker pull someImage:latest || true
docker build --platform linux/amd64 . \
-t someImage:someVersion \
-f Dockerfile \
--cache-from someImage:latest
За допомогою Buildx, ви можете зберігати інформацію про кеш у віддаленому місці (наприклад, реєстр контейнерів, сховище блоків, ...). Будівельник перевіряє, чи вже існує даний шар, і у разі успіху перевикористовує його замість повторного створення.
Це навіть можна зробити без завантаження шару локально. Щоб скористатися цим механізмом, ми переробили попередні команди на:
docker buildx build --platform linux/amd64 . \
-t someImage:someVersion - push \
--cache-to type=registry,ref=someCachedImage:someVersion,mode=max
--cache-from type=registry,ref=someCachedImage:someVersion
Режим "max" означає, що ми будемо зберігати інформацію про побудову для кожного шару, навіть для шарів, які не використовуються в кінцевому зображенні (наприклад, при використанні багатостадійних побудов). За замовчуванням використовується режим "min", який зберігає інформацію про побудову лише для шарів, що існують у кінцевому зображенні.
Особливий випадок кешування - це зберігання даних кешу "inline", що означає, що вони будуть збережені разом з зображенням. Ця опція підтримується при використанні Buildkit без Buildx. Це найпростіший спосіб початку, але складніше використовувати при багатостадійних побудовах, і вона не забезпечує чіткого розподілу між виводом артефактів і кешем. Команди для збереження даних кешу "inline" виглядають так:
docker buildx build - platform linux/amd64 . \
-t someImage:someVersion --push \
--cache-to type=inline,mode=max \
--cache-from someImage:somePreviousVersion
Новий спосіб додавання файлів до образу Docker
Docker ввів нову версію синтаксису для написання dockerfiles, а саме: #syntax=docker/dockerfile:1.4. Його підтримує додаткову опцію посилання для команд COPY і ADD.
Раніше, коли ви використовували команду COPY або ADD, будувальник створював новий знімок, який об'єднує нові файли з вже існуючою файловою системою. У результаті потрібно, щоб всі батьківські шари існували перед виконанням цієї операції, оскільки, в іншому випадку, каталог призначення може ще не існувати. У кінці ваш образ (результат команди побудови) складатиметься з архівів на кожний шар, які містять відмінності між відповідними знімками.
ВИКОРИСТОВУЮЧИ ОПЦІЮ ЛІНКУВАННЯ, НОВІ ФАЙЛИ РОЗМІЩУЮТЬСЯ У СВОЄМУ ВЛАСНОМУ СНАПШОТІ, НЕ ПОЛАГАЮЧИСЬ НА ПОПЕРЕДНІ ШАРИ. ПІДКЛЮЧЕНІ ФАЙЛИ ЗБЕРІГАЮТЬСЯ У СВОМУ ВЛАСНОМУ AL TARBALL, А РІЗНІ TARBALLS ПРИВ'ЯЗАЮТЬСЯ РАЗОМ, НЕ ПОЛАГАЮЧИСЬ НА ІСНУЮЧУ ФАЙЛОВУ СИСТЕМУ, ЯК ЦЕ ПРОІЛЮСТРОВАНО НА СЛІДНІЙ КАРТИНЦІ.
https://www.docker.com/blog/image-rebase-and-improved-remote-cache-support-in-new-buildkit/
# syntax=docker/dockerfile:1.4
FROM baseImage:version
COPY [--chown=\<user\>:\<group\>] [--chmod=\<perms\>] --link binary /opt/
Головна перевага полягає в тому, що файли вже не залежать від попередніх шарів. Шар може бути повторно використаний, доки файли не змінилися, навіть якщо змінилися батьківські шари.
Крім того, це також може покращити швидкість вашого збірки, оскільки декілька шарів копіювання даних тепер можуть бути виконані паралельно.
Висновок
У цьому блозі описані деякі нові візії, які ми отримали після оптимізації наших CI-пайплайнів. Я обговорюю 2 невеликих зміни, які призвели до 40-відсоткового зменшення загального часу збірки за допомогою Docker:
- Збереження інформації кешу збірки віддалено
- Використання опції лінкування при додаванні, копіюванні файлів у ваш образ Docker
Перекладено з: How we reduced our docker build times by 40%