Крихітні образи Docker, які вірили у себе*

[Відсилання до американської дитячої казки "Маленький паровозик, який вірив у себе" ("The Little Engine That Could") - прим. пров.]*

Крихітні образи Docker, які вірили у себе*

Як автомагічно створювати крихітні docker-образи для своїх потреб

Незвичайна одержимість

Останні кілька місяців я був одержимий нав'язливою ідеєю: наскільки можна зменшити образ Docker, так щоб при цьому додаток працював?

Розумію, ідея дивна.

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

Чому розмір має значення

Зменшуючи вміст образу Docker, ми скорочуємо список вразливостей. Додатково ми робимо образи чистішими, адже вони містять лише те, що потрібно для запуску додатків.

Є ще одна невелика перевага — образи скачуються трохи швидше, але, як на мене, це не так важливо.

Якщо вас турбує розмір, образи Alpine самі по собі малі і напевно підійдуть вам.

Distroless-образи

Проект Distroless пропонує добірку базових «distroless»-образів, вони не містять менеджерів пакетів, оболонок та інших утиліт, які ви звикли бачити в командному рядку. В результаті використовувати менеджери пакетів начебто pip и apt не вийде:

FROM gcr.io/distroless/python3
RUN  pip3 install numpy

Dockerfile, що використовує distroless-образ Python 3

Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM gcr.io/distroless/python3
 ---> 556d570d5c53
Step 2/2 : RUN  pip3 install numpy
 ---> Running in dbfe5623f125
/bin/sh: 1: pip3: not found

Pip у образі немає

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

FROM python:3 as builder
RUN  pip3 install numpy

FROM gcr.io/distroless/python3
COPY --from=builder /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.5/

Багатоетапне складання

В результаті виходить образ розміром 130 МВ. Не так уже й погано! Для порівняння: образ Python за умовчанням важить 929МВ, а «схудлий» (3,7-slim) - 179МВ, образ alpine (3,7-alpine) - 98,6MB, тоді як базовий distroless-образ, використаний у прикладі, - 50,9МВ.

Можна справедливо зазначити, що у попередньому прикладі ми копіюємо цілий каталог /usr/local/lib/python3.7/site-packages, В якому можуть лежати непотрібні нам залежності. Хоча ясно, що різниця в розмірах всіх базових образів Python коливається.

На момент написання цих рядків Google distroless підтримує не так багато образів: Java і Python ще на стадії експерименту, а Python існує тільки для 2,7 і 3,5.

Крихітні образи

Повернемося до мого божевілля на створенні невеликих образів.

Загалом я хотів подивитися, як влаштовані distroless-образи. Проект distroless використовує Google'івський інструмент збирання bazel. Однак, щоб встановити Bazel і написати власні образи, довелося попітніти (а якщо бути до кінця чесним, то знову винаходити колесо — це весело і пізнавально). Хотілося спростити створення зменшених образів: акт створення образу має бути гранично простим, банальним. Щоб ніяких файлів конфігурації, тільки один рядок в консолі: просто собрать образ для <приложение>.

Отже, якщо хочете створювати власні образи, знайте: є такий унікальний образ docker, scratch. Scratch - це "порожній" образ, в ньому немає файлів, хоча він і важить за умовчанням - ого! - 77 байт.

FROM scratch

Образ scratch

Ідея образу scratch в тому, що можна скопіювати в нього будь-які залежності з машини-хоста і або використовувати їх всередині Dockerfile (це як скопіювати їх в apt і встановити з нуля) або пізніше, коли образ Docker матеріалізований. Це дозволяє повністю контролювати вміст контейнера Docker, і, таким чином, повністю контролювати розмір образу.

А тепер нам треба якось зібрати ці залежності. Існуючі інструменти начебто apt дозволяють завантажувати пакети, але вони прив'язані до поточної машини і не підтримують Windows або MacOS.

І ось я взявся зібрати власний інструмент, який автоматично збирав базовий образ найменшого можливого розміру і щоб той ще запускав будь-який додаток. Використовував пакети Ubuntu/Debian, робив вибірку (отримуючи пакети прямо з репозиторіїв) і рекурсивно знаходив їх залежності. Програма повинна була автоматично завантажувати найновішу стійку версію пакета, максимально знижуючи ризики безпеки.

Інструмент я назвав fetchy, тому що він ... знаходить і приносить ... що потрібно [від англ. "fetch", "приносити" - прим. пров.]. Інструмент працює через інтерфейс командного рядка, але водночас пропонує і API.

Для того, щоб зібрати образ за допомогою fetchy (Візьмемо цього разу образ Python), вам треба лише використовувати CLI ось так: fetchy dockerize python. У вас можуть запитати цільову операційну систему та кодове ім'я, оскільки fetchy поки використовує лише пакети на базі Debian та Ubuntu.

Тепер можна вибирати, які залежності зовсім не потрібні (у нашому контексті) та виключити їх. Наприклад, Python залежить від perl, хоча чудово працює без встановленого Perl.

Результати

Python, створений за допомогою команди fetchy dockerize python3.5 важить лише 35МВ (я більш ніж впевнений, що в майбутньому його можна буде полегшити ще більше). Виходить, з distroless-образу вдалося "зголити" ще 15МВ.

Усі зібрані на даний момент образи подивитися можна тут.

Проект - тут.

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

Джерело: habr.com

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