Розуміння шарів Докера


27

У нас є такий блок Dockerfile:

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y groupinstall "Development Tools"
RUN yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

Мені сказали, що ми повинні об'єднати ці RUNкоманди для скорочення створених шарів docker:

RUN yum -y update \
    && yum -y install epel-release \
    && yum -y groupinstall "Development Tools" \
    && yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

Я дуже новачок у docker і не впевнений, що повністю розумію відмінності між цими двома версіями вказування декількох команд RUN. Коли можна об'єднати RUNкоманди в одну і коли має сенс мати кілька RUNкоманд?


Відповіді:


35

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

Кількість шарів, використаних у зображенні, є важливою

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

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

Порада № 1 Переконайтесь, що етапи збирання, в якому бере участь ваш вихідний код, надходять якнайпізніше в Dockerfile і не прив’язуються до попередніх команд за допомогою a &&або a ;.

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

Моя друга порада менш важлива, але я вважаю це дуже корисним з точки зору обслуговування:

Порада № 2 Не пишіть складні команди в Dockerfile, а скоріше використовуйте сценарії, які потрібно скопіювати та виконати.

Dockerfile слідуючи ця рада буде виглядати

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh

і так далі. Порада зв'язати кілька команд з &&лише обмеженою сферою застосування. Набагато простіше писати зі сценаріями, де можна використовувати функції тощо, щоб уникнути надмірності або для цілей документації.

Люди, які цікавляться попередніми процесорами та готові уникати невеликих накладних витрат, спричинених COPYкроками, і насправді створюють Dockerfile під час польоту, де

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh

послідовності замінюються на

RUN base64 --decode … | sh -x

де - кодована версія base64 apt_setup.sh.

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

Порада №3 Використовуйте with-idiom, щоб уникнути файлів, що знаходяться в посередницьких шарах, але не в отриманій файловій системі.

Файл, доданий якоюсь докерською інструкцією та видалений пізнішою інструкцією, відсутній у отриманій файловій системі, але він згадується два рази в шарах докера, що становить зображення докера в процесі побудови. Один раз, із назвою та повним вмістом у шарі, що є результатом додавання інструкції, та один раз як повідомлення про видалення в шарі, що виникає внаслідок видалення інструкції.

Наприклад, припустимо, що нам тимчасово потрібен компілятор C та деяке зображення та розглянемо

# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc

(Більш реалістичним прикладом може бути побудова деякого програмного забезпечення разом із компілятором, а не просто підтвердження його присутності --versionпрапором.)

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

with-Idiom є поширеною формою в функціональному програмуванні , щоб ізолювати власність ресурсів і ресурсів вивільнення з логіки його використання. Легко перенести цю ідіому в сценарій оболонки, і ми можемо перефразувати попередні команди як наступний сценарій, який буде використовуватися COPY & RUNяк у Раді №2.

# with_c_compiler SIMPLE-COMMAND
#  Execute SIMPLE-COMMAND in a sub-shell with gcc being available.

with_c_compiler()
(
    set -e
    apt-get install -y gcc
    "$@"
    trap 'apt-get --purge autoremove -y gcc' EXIT
)

with_c_compiler\
    gcc --version

Складні команди можна перетворити на функцію, щоб їх можна було подати with_c_compiler. Можливо також ланцюжок дзвінків кількох with_whateverфункцій, але, можливо, не дуже бажано. (Використовуючи більш езотеричні особливості оболонки, звичайно, можна зробити with_c_compilerприйняття складних команд, але в усіх аспектах бажано вбудувати ці складні команди у функції.)

Якщо ми хочемо ігнорувати пораду №2, то отриманий фрагмент Dockerfile буде

RUN apt-get install -y gcc\
 && gcc --version\
 && apt-get --purge autoremove -y gcc

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


1
Чи можете ви включити результат розміру поля після створення сценарію та використання декількох команд в одному операторі RUN?
030

1
Мені здається поганою ідеєю змішувати конфігурацію бази зображень (тобто матеріалів для ОС) і навіть файлів з налаштуванням джерела, яке ви написали. Ви кажете "Переконайтеся, що кроки збирання, в яких бере участь ваш вихідний код, приходять якомога пізніше". Чи є якесь питання, щоб зробити цю частину абсолютно незалежним артефактом?
JimmyJames

1
@ 030 Що ви маєте на увазі під розміром "коробка"? Я поняття не маю, до якого вікна ви звертаєтесь.
Michael Le Barbier Grünewald

1
Я мав на увазі розмір зображення докера
030

1
@JimmyJames Це дуже залежить від сценарію розгортання. Якщо ми припустимо складену програму, «правильною справою» було б упакувати її та встановити залежність цього пакета та сам пакет як два чіткі, що мають бути найближчі до кінцевого кроку. Це забезпечує максимальну корисність кеш-пам'яти докера та уникнення завантаження шарів із одним і тим же файлом. Мені легше ділитися рецептами побудови для створення зображень докерів, ніж побудова довгих ланцюжків зображень залежностей, тому що останнє ускладнює перебудову.
Michael Le Barbier Grünewald

13

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

Скажімо, у вас є такий Dockerfile:

FROM centos:6

RUN yum -y update 
RUN yum -y install epel-release

Отриманий розмір зображення буде

bigimage     latest        3c5cbfbb4116        2 minutes ago    407MB

Навпаки, із "подібним" Dockerfile:

FROM centos:6

RUN yum -y update  && yum -y install epel-release

Отриманий розмір зображення буде

smallimage     latest        7edeafc01ffe        3 minutes ago    384MB

Якщо ви очистите кеш yum в одному операторі RUN, ви отримаєте ще менший розмір.

Таким чином, ви хочете зберегти баланс між читабельністю / легкістю обслуговування та кількістю шарів / розміром зображення.


4

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.