Як швидко створити тонкі зображення Docker

Саймон Хау

20 листопада 2019 · 7 хв читання

Ви пам’ятаєте ті дні, коли ви писали чудове програмне забезпечення, але не могли встановити його на чужій машині, або воно там розбилося? Хоча це ніколи не є приємним досвідом, ми завжди могли сказати

створити

На сьогодні це вже не виправдання через контейнеризацію.

Дуже коротко, за допомогою контейнеризації, ви додаєте свою програму та всі необхідні залежності в зображення. Після виконання ви запускаєте це зображення як контейнер. З цим вам не потрібно возитися з системою іншої людини, щоб ваше програмне забезпечення працювало. Контейнер, а отже, і ваше програмне забезпечення має працювати просто де завгодно, якщо воно працює на вашому комп'ютері. Це також корисно для науковців даних при розгортанні моделей, які залежать від різних пакетів та їх версій. Для мене вчені з даних повинні знати, як створювати зображення та контейнери.

Як ви всі знаєте, Docker є головним гравцем у цій царині, і зображення Docker всюдисущі. Це приголомшливо, оскільки ви можете запускати, наприклад, бази даних різних версій поруч, без зайвих клопотів. Злом зображень для ваших додатків також дуже простий. Це пов’язано з великою кількістю базових зображень та простою мовою визначення. Однак, коли ви зломьте зображення разом, не знаючи, що робите, у вас є дві проблеми.

  1. Ви витрачаєте місце на диску, оскільки ваші зображення стають надмірно жирними.
  2. Ви витрачаєте час, чекаючи збірок, які тривають занадто довго.

У цій статті я хочу показати вам, як можна пом'якшити ці дві проблеми. На щастя, для цього потрібно лише знати декілька прийомів та технік, запропонованих Docker. Щоб зробити підручник веселим та корисним, я покажу вам, як упакувати програму Python у образ Docker. Ви можете знайти весь код, на який посилаються нижче, у моєму сховищі Github.

Ви готові? Давайте його на.

Припустимо, що весь наш код живе в одному файлі Python main.py. Оскільки ми круті діти, ми використовуємо останню і найкращу версію Python, яка на момент написання цієї статті становила 3,8. Наш додаток - це просто простий веб-сервер, який залежить від панд, фастапі та uvicorn. Ми зберігаємо залежності у файлі requirements.txt. Місцево ми розробляємо додаток у віртуальному середовищі. Це середовище знаходиться в папці .venv у тій же папці, що і код (це стає важливим незабаром). Тепер ми вирішили упакувати все це в образ Docker. Для цього нам залишається лише

  1. Використовуйте базове зображення з доступним Python 3.8.
  2. Скопіюйте код та файл вимог.
  3. Встановіть вимоги та залежності на зображенні.
  4. Виставити команду, яка запускає наш додаток

Виглядає перша версія нашого образу Docker

Окрім нашого коду та вимог, нам потрібно встановити GCC, оскільки FastApi вимагає цього при встановленні. Ми будуємо свій імідж шляхом

Розмір цього зображення становить близько 683 МБ і займає близько хвилини для його створення (за винятком завантаження базового зображення). Давайте подивимося, як ми можемо це зменшити.

Основне зображення

Щодо базового зображення, я вже зробив свідомий вибір, використовуючи Python slim. Чому я саме це обрав?

Я міг взяти, наприклад, повний образ Ubuntu або CentOS, що призвело б до розміру зображення> 1 Гб. Але, оскільки мені потрібен лише Python, немає причин встановлювати все це.

На нижньому кінці розміру зображення можна взяти python: 3.8.0 - альпійський. Але мій код спирається на панд, що дуже важко встановити на альпійських. У Alpine також є проблеми щодо стабільності та безпеки. Крім того, тонкий - це лише

80 Мб більше, ніж альпійський, що все ще добре. Для отримання додаткової інформації про те, як вибрати оптимальний образ Python, я звертаюся до зацікавленого читача до цієї статті.

Контекст побудови

Коли ви створюєте зображення, перший рядок, надрукований на вашій консолі, говорить: Надсилання контексту збірки в демон Docker. На моєму комп'ютері це зайняло близько 5 секунд, і було надіслано 154 МБ. Що тут відбувається? Docker копіює всі файли та папки, що знаходяться в контексті побудови, до демона. Тут контекст збірки є каталогом, в якому зберігається файл Docker. Оскільки нам потрібні лише два текстові файли, 154 МБ звучить досить багато, чи не так? Причиною цього є те, що Docker копіює все, наприклад, папку .venv, яка містить віртуальне середовище, або папку .git.

Щоб це виправити, потрібно лише додати файл .dockerignore поруч із вашим Dockerfile. У цьому файлі ви перелічуєте рядок за рядком те, що Docker не повинен копіювати. Це подібно до того, що git робить із файлом .gitignore. Як невеликий приклад, скажімо, у нашій папці є кілька файлів Excel та PNG, які ми не хочемо копіювати. Файл .dockerignore виглядає так

У нашому прикладі, після того, як я додав цей файл, “надсилання контексту збірки до докера” займає лише пару мілісекунд і надсилається лише 7,2 кб. Я зменшив розмір зображення з 683 Мб до 529 Мб, що приблизно відповідає розміру попереднього контексту побудови. Приємно! Додавання файлу .dockerignore сприяє пришвидшенню роботи будує і зменшення розмір зображення.

Кешування шарів

Як вже було сказано, для створення цього зображення на моїй машині потрібно близько 60 секунд. Більшість часу, я вважаю

99,98%, використовується для встановлення вимог та залежностей. Можна припустити, що зараз тут не так багато можливостей для вдосконалення. Але буває, коли вам доводиться часто будувати образ! Чому? Docker може використовувати Layer Caching.

Кожен рядок у файлі Docker представляє шар. Додаючи/видаляючи щось із рядка або змінюючи файл або папку, на яку він посилається, ви змінюєте шар. Коли це трапляється, цей шар та всі шари нижче переробляються. В іншому випадку Docker використовує кешовану версію цього шару. Щоб використати це, ви повинні структурувати свої Dockerfiles так, щоб

  1. Шари, які не часто змінюються, повинні з'являтися біля початку файлу Docker.Тут хорошим прикладом є встановлення компілятора.
  2. Шари, які часто змінюються, повинні з'являтися ближче до кінця файлу Docker.Копіювання вихідного коду - ідеальний приклад тут.

Класно. Досить теорії, повернімось до нашого прикладу.

Припустимо, ви не змінюєте вимоги, а лише оновлюєте свій код. Це досить часто при розробці програмного забезпечення. Тепер кожного разу, коли ви створюєте свій образ, ці неприємні залежності переустановлюються. Побудова образу завжди займає однакову кількість часу. Набридає! Ми ще не використовуємо кешування.

Ось чарівний новий файл Docker, який вирішує вашу проблему

Це виглядає не дуже чарівно і інакше, так? Єдине, що ми зробили, це спочатку встановити GCC та окремо скопіювати вимоги та скопіювати вихідний код.

GCC та залежності змінюються дуже рідко. Ось чому цей шар зараз з’являється дуже рано. Вимоги змінюються також повільно, але частіше, ніж GCC. Ось чому цей рівень приходить після рівня GCC. Наш вихідний код дуже часто змінюється. Отже, його копіювання відбувається пізно. Тепер, коли ми вносимо зміни у свій вихідний код і відновлюємо образ, залежності не переустановлюються, оскільки Docker використовує кешовані шари. Відновлення зараз майже не займає часу. Це чудово, оскільки ми можемо витратити більше часу на тестування та виконання нашого додатка!

Багатоетапні збірки

У нашому прикладі зображення ми повинні встановити GCC, щоб встановити FastApi та uvicorn. Але, для запуску програми нам не потрібен компілятор. А тепер уявіть, що вам потрібен не тільки GCC, але й інші програми, такі як Git, CMake, NPM або .... Ваш виробничий імідж стає все товстішим і товстішим.

Багатоступеневі побудови для нашого порятунку!

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

Для нашого прикладу це виглядає так

Створюючи це, ми отримуємо остаточний розмір робочого зображення 353 МБ. Це приблизно вдвічі менше, ніж наша перша версія. Вітаю, непогано. Пам’ятайте, чим меншим стає ваш виробничий імідж, тим кращим він є!

Як зауваження, багатоступеневі збірки також підвищують безпеку. Хуей, чому це? Припустимо, вам потрібен секрет, наприклад ключ SSH, для доступу до певних ресурсів під час збірки. Навіть якщо ви видалите цей секрет у пізнішому шарі, він все ще є у попередніх шарах. Це означає, що хтось, хто має доступ до вашого зображення, може отримати цю таємницю. У багатоступеневих збірках ви копіюєте лише необхідні артефакти виконання. Отже, виробничий образ ніколи не бачить секрету, і ви вирішили цю проблему.

Існує набагато більше деталей про багатоступеневі збірки, і я звертаюся до читача до цієї статті та цієї статті.

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

  • Завжди додайте файл .dockerignore.
  • Подумайте про порядок ваших шарів і впорядкуйте їх від повільних до швидкозмінних дій.
  • Спробуйте використовувати та використовувати багатоступеневі збірки.

Сподіваюсь, це заощадить вам трохи місця та часу в майбутньому.

Дякуємо, що стежили за цим дописом. Як завжди, не соромтеся звертатися до мене за запитаннями, коментарями чи пропозиціями.