Лабораторна робота №3 Сторення Docker-образів та робота з ними.

Мета: Ознайомитись з процесом створення Docker-образів та роботи з ними.

Теоретичні відомості

Створення Docker-образів

Розробники створюють власні образи разом зі своїм кодом та компонентами середовища виконання, щоб запускати свої додатки. Однак процес створення зазвичай починається з попереднього образу. Усі процеси створення образів починаються з інструкції FROM. Це вказує на те, що попередній образ (складений із шарів) буде використано для додавання нових компонентів, бінарних файлів, конфігурацій або дій для створення нового образу.

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

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

Існує три методи створення образів:

  1. Використання файлу з усіма інструкціями для створення цього образу (Dockerfile).
  2. Взаємодія з файлами на різних рівнях контейнера, виконання контейнера, зміна його вмісту та збереження внесених змін (commit).
  3. Використання порожнього рівня та додавання компонентів вручну, файл за файлом, також відоме як створення образу з нуля.

Створення образів за допомогою Dockerfile

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

У цьому розділі ми розглянемо основні примітиви, доступні для створення Dockerfile образів.

Приклад Dockerfile виглядає приблизно так:

FROM ubuntu:18.04
RUN apt-get update -qq && apt-get install -qq package1 package2
COPY . /myapp
RUN make /myapp
CMD python /myapp/app.py

Основні інструкції Dockerfile:

Команда Опис
FROM Ця інструкція встановлює базовий образ і ініціалізує нову збірку. Це єдина обов'язкова інструкція, з якої мають починатися всі Dockerfile. Ми можемо використовувати будь-який дійсний образ як базовий для збірки або зарезервоване слово scratch, щоб почати з порожньої файлової системи root. Ми можемо визначити ім'я для етапу збірки, ініціалізованого за допомогою AS name у тій самій інструкції FROM. Ми використаємо це в розділі про багатоступеневу збірку наприкінці цього розділу. Базовий образ можна визначити за його назвою (репозиторієм) і конкретною міткою (версією цього образу) або за його дайджестом; наприклад, FROM <image>[:tag] або FROM <image>[@digest].
ARG Інструкція ARG визначає змінну, якій буде присвоєно значення, надане під час збірки, передаючи його як аргумент за допомогою --build-arg <variable>=<value>. Щоб уникнути проблем при збірці через відсутні значення, ми можемо використовувати ARG для визначення значення за замовчуванням для змінної, яке буде перезаписане, якщо буде передано аргумент.
LABEL Інструкція LABEL додає метаінформацію до образу. Ця інформація повинна бути у форматі ключ-значення, і ми можемо включати кілька ключів та значень в одному рядку LABEL
ENV Інструкція ENV встановлює змінну середовища для наступного кроку та всіх наступних кроків. Ми можемо додати кілька змінних середовища в одному рядку, і значення будуть перезаписані, якщо ми вкажемо нові значення під час створення Docker-контейнера.
WORKDIR Встановлює робочу директорію для наступних команд і всіх наступних кроків. Ми можемо вказати як абсолютні, так і відносні шляхи.
RUN Є однією з найчастіше використовуваних інструкцій Dockerfile. Виконує всі команди в рядку в новому шарі та фіксує результати в новому. Цей новий шар буде використовуватися в наступній інструкції як базовий шар, з урахуванням змін, внесених інструкцією RUN. Це означає, що кожна інструкція RUN створює новий шар. Тому RUN безпосередньо впливає на загальну кількість шарів у нашому образі. Щоб уникнути створення надмірної кількості шарів, зазвичай додають більше ніж одну команду в кожен рядок RUN.
COPY Копіює нові файли та директорії з контексту збірки в зазначену директорію файлової системи контейнера. COPY приймає аргумент --chown=: для надання прав власності на файли в Linux-контейнерах. Якщо цей аргумент не використовується, власником буде root:root. Також приймає аргумент --from=, щоб копіювати файли або директорії з інших етапів збірки (це є ключовим при використанні багатоступеневої збірки).
ADD ADD схожий на COPY, але може також використовуватися з URL-адресами та файлами пакунків TAR. Він приймає ті ж аргументи для зміни прав власності на файли та директорії призначення. Приклад ADD http://example.com/bigpackagefile.tar.gz /myapp
USER Інструкція USER використовується для вказівки користувача разом з групою, які будуть використовуватися в наступних командах. Дуже важливо розуміти необхідні дозволи для нашого процесу і вказати користувача та його групу за допомогою USER. Якщо ця інструкція відсутня, команди будуть виконуватися як root:root, і процес всередині контейнера буде працювати як root. Виробниче середовище повинно обов'язково використовувати специфічного не-root користувача для процесів контейнера, і, якщо root необхідний, слід використовувати мапування користувачів. Приклад USER www-data:www-data
VOLUME Означення VOLUME створить точку монтування для обходу системи Copy-on-Write (CoW). Це означає, що вміст цієї директорії буде поза життєвим циклом контейнера. Оскільки вона знаходиться за межами контейнера, будь-які зміни в наступних командах, що стосуються цієї директорії, будуть ігноровані. Тому, якщо ми хочемо надати певні файли під час ініціалізації тома, інструкція VOLUME повинна бути після того, як ми підготували файли всередині директорії. Приклад VOLUME /mydata
EXPOSE використовується для інформування Docker-демона про порти, на яких контейнер, створений за допомогою цього образу, буде слухати. Це не означає, що визначені порти будуть слухати на рівні хоста Docker. Вони будуть слухати лише всередині мережі контейнера. Ми можемо визначити, який транспортний протокол використовувати – UDP або TCP (за замовчуванням). Приклад EXPOSE 80/tcp
CMD Інструкція CMD визначає процес або аргумент за замовчуванням при виконанні контейнера на основі цього образу. Ця поведінка буде застосовуватися незалежно від того, чи визначена інструкція ENTRYPOINT. За замовчуванням, і залежно від використаного формату, CMD надасть аргументи за замовчуванням для оболонки, яка є точкою входу за замовчуванням (основний виконавець процесів всередині контейнера). Приклад CMD ["/usr/bin/curl","--help"] або CMD /usr/bin/curl -I https://www.google.com
ENTRYPOINT Директива ENTRYPOINT визначає, яку команду контейнер буде виконувати як виконувану. Як ми дізналися раніше, CMD буде аргументом для цієї команди. Взаємодія між CMD і ENTRYPOINT визначає команду, яка буде виконана при запуску контейнера. Вони не є обов'язковими, але є хорошою практикою визначити принаймні CMD, щоб мати процес за замовчуванням для запуску під час виконання.
HEALTHCHECK Визначає команду для перевірки стану контейнера. Можна налаштувати інтервал перевірок, таймаут та кількість спроб до позначення контейнера як несправного. Повертає статуси: 0 – OK, 1 – помилка.

Образи Docker складаються з кількох рівнів (layers). Кожен рівень є захищеною від запису файловою системою. Для кожної інструкції в Dockerfile створюється свій рівень, який розміщується поверх попередніх рівнів. Під час перетворення образу в контейнер (командами docker run або docker create) механізм Docker вибирає потрібний образ і додає на самому верхньому рівні файлову систему з можливістю запису (одночасно з цим ініціалізуються різноманітні параметри налаштування, такі як IP-адреса, ім'я, ідентифікатор і обмеження ресурсів).

Оскільки непотрібні рівні значно збільшують розміри образів (э строгий ліміт, рівний 127 рівням), у багатьох файлах Dockerfile можна знайти спроби зменшити кількість рівнів шляхом об'єднання кількох команд Unix в одній інструкції RUN.

В деяких інструкціях (RUN, CMD і ENTRYPOINT) допускається використання як формату командної оболонки, так і формату exec. Використання формату exec рекомендується, оскільки він дозволяє уникнути додаткових обгорток, які можуть вплинути на обробку сигналів. Основна різниця між shell та exec форматами в Dockerfile полягає в тому, як вони обробляють команди та як ці команди виконуються всередині контейнера.

Shell Формат Shell формат використовує стандартну оболонку для виконання команди.

RUN <command> <arguments>
CMD <command> <arguments>
ENTRYPOINT <command> <arguments>
  • Команди виконуються через оболонку (/bin/sh -c на Linux або cmd /S /C на Windows).
  • Під час виконання команди оболонка обробляє аргументи, що дозволяє використовувати оболонкові оператори, такі як &&, ||, |, тощо.
  • Може використовуватися в командах, де потрібна обробка оболонки.
  • Сигнали ОС обробляються оболонкою, а не безпосередньо процесом, що може ускладнити обробку сигналів.

Exec Формат Exec формат запускає команду безпосередньо без оболонки.

RUN ["executable", "param1", "param2"]
CMD ["executable", "param1", "param2"]
ENTRYPOINT ["executable", "param1", "param2"]
  • Команди виконуються безпосередньо як процес, без обробки оболонкою.
  • Не підтримує оболонкові оператори, такі як &&, ||, |, тощо.
  • Процес отримує сигнали безпосередньо, що дозволяє краще обробляти їх, наприклад, для коректного завершення процесу.

Отже, Shell формат зручний для складних команд, які використовують оболонкові оператори, але має обмеження у обробці сигналів. Exec формат забезпечує точнішу і передбачувану обробку сигналів і краще підходить для основних процесів, але не підтримує оболонкові оператори. Таким чином, для ENTRYPOINT рекомендується використовувати exec формат, оскільки він дозволяє процесу отримувати сигнали безпосередньо а для RUN і CMD вибір формату може залежати від специфічних потреб. Використовуйте exec формат, коли потрібно точніше контролювати виконання процесів або коли відсутня оболонка (/bin/sh).

Завдання

  1. Обрати варіант завдання з таблиці варіантів завдань.
  2. Ознайомитися з офіційною документацією обраного варіанту Веб-серверу та диистрибутиву.
  3. На базі обраного дистрибутиву створити Docker-образ веб-сервера за допомогою Dockerfile, що повинен містити інформацію про версію, опис та автора образу, відкрити порти, змінні оточення, тощо.
  4. Виконати build образу та перевірити його наявність в локальному репозиторії за шаблоном.
     docker build -t <vendor-name>/<linux-image-name>-<web-server-image>:1.0
     docker images
    
    де <vendor-name> - ваш логін на Docker Hub, <linux-image-name> - назва образу дистрибутива linux, <web-server-image> - назва образу веб-сервера.
  5. Увійдіть у свій обліковий запис на Docker Hub (створіть його за необхідності):
    docker login
    
  6. Завантажте ваш образ на Docker Hub
     docker push <vendor-name>/<linux-image-name>-<web-server-image>:1.0
    
  7. Перевірте наявність вашого образу на Docker Hub.
  8. Підготуйте простий HTML-файл, що містить інформацію про курс, вас та вашу групу.
  9. Створіть новмй образ на базі вашого образу веб-сервера, що містить ваш HTML-файл, використовуючи Dockerfile.
  10. Виконайти білд та запуск нового образу.
  11. Перевірте роботу веб-сервера за допомогою браузера або утиліти curl.
  12. Зупиніть та видаліть контейнери та образи.
  13. Створіть та завантажте звіт з виконаними командами, результатами виконання завдання та посиланням на dockerhub репозиторій.

Варіанти завдань

# Linux-дистрибутив Веб-сервер
1 Ubuntu Nginx
2 Ubuntu Apache
3 Ubuntu Node.JS
4 Ubuntu Caddy
5 Alpine Nginx
6 Alpine Apache
7 Alpine Node.JS
8 Alpine Caddy
9 Debian Nginx
10 Debian Apache
11 Debian Node.JS
12 Debian Caddy
13 Rocky Linux Nginx
14 Rocky Linux Apache
15 Rocky Linux Node.JS
16 Rocky Linux Caddy
17 AlmaLinux Nginx
18 AlmaLinux Apache
19 AlmaLinux Node.JS
20 AlmaLinux Caddy
21 Fedora Nginx
22 Fedora Apache
23 Fedora Node.JS
24 Fedora Caddy
25 openSUSE Nginx
26 openSUSE Apache
27 openSUSE Node.JS
28 openSUSE Caddy

Контрольні питання

  1. Що таке Dockerfile і яку роль він відіграє у створенні Docker образів?

  2. Яка команда використовується для створення Docker образу на основі Dockerfile?

  3. Яка команда використовується для входу у ваш обліковий запис Docker Hub?

  4. Які інструкції в Dockerfile використовуються для копіювання файлів з хост-системи до контейнера?

  5. Як ви можете перевірити, що ваш Docker образ успішно завантажено на Docker Hub?

  6. Які основні етапи включає в себе процес завантаження Docker образу на Docker Hub?

  7. Що робить інструкція CMD у Dockerfile, і як вона відрізняється від ENTRYPOINT?

  8. Що таке базовий образ у Docker, і чому важливо вибрати правильний базовий образ для вашого проекту?

  9. Яка роль інструкції RUN у Dockerfile, і які типи команд ви можете використовувати з цією інструкцією?

  10. Які параметри потрібно вказати при запуску команди docker build, щоб побудувати образ?

  11. Які інструкції Dockerfile використовуються для визначення робочого каталогу всередині контейнера?