Питання 1 · Розділ 14

Що таке контейнеризація і навіщо вона потрібна?

Ви пишете додаток на Java. На вашому комп'ютері він працює, але коли передаєте колезі або розгортаєте на сервері — виникають помилки: «у мене інша версія Java», «бракує бібліоте...

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Просте пояснення

Контейнеризація — це технологія пакування додатку разом з усіма його залежностями (бібліотеками, конфігураціями, системними утилітами) в єдиний ізольований блок — контейнер.

Контейнер – це не файл і не архів. Це звичайний процес Linux, якому ядро дозволяє бачити лише свої файли, свою мережу і свої процеси (через namespaces і cgroups).

Ви пишете додаток на Java. На вашому комп’ютері він працює, але коли передаєте колезі або розгортаєте на сервері — виникають помилки: «у мене інша версія Java», «бракує бібліотеки», «інші налаштування оточення». Контейнеризація вирішує цю проблему: ви пакуєте все необхідне в один образ, і він працює однаково скрізь.

Образ (image) – це шаблон/клас (як клас в ООП). Контейнер – запущений екземпляр образу (як об’єкт).

Аналогія

Контейнер — це як морський вантажний контейнер. Неважливо, що всередині (електроніка, одяг, їжа) — крани і кораблі працюють з ним однаково. Так і програмні контейнери: неважливо, який додаток всередині (Java, Python, Node.js) — Docker працює з ними одноманітно.

Приклад

# Простий Dockerfile для Java-додатку
FROM openjdk:17-jdk-slim
COPY myapp.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
# Збірка та запуск
docker build -t myapp .
docker run -p 8080:8080 myapp

Основні переваги

  1. «Працює на моїй машині» більше не проблема — один і той самий образ запускається у розробника, на тестовому стенді і в продакшені.
  2. Швидкий запуск — контейнер стартує за секунди, бо йому не потрібно завантажувати свою ОС — він використовує вже запущене ядро хоста. ВМ ж завантажує повноцінну ОС.
  3. Легкість — контейнери займають мегабайти, а не гігабайти.
  4. Ізоляція — додатки не конфліктують один з одним за бібліотеки і версії.

Що запам’ятати

  • Контейнер = додаток + усі його залежності в одному пакуванні
  • Контейнери працюють однаково на будь-якій машині з Docker
  • Контейнери легкі і швидкі
  • Для керування великою кількістю контейнерів використовується Kubernetes

Коли НЕ використовувати контейнери

  1. GUI-додатки – контейнери оптимізовані для headless-сервісів
  2. Realtime-системи з жорсткими вимогами до ядра
  3. Додатки, що вимагають повної ізоляції – краще ВМ

🟡 Middle Level

Як працює контейнеризація під капотом

Контейнеризація базується на двох ключових механізмах ядра Linux:

1. Namespaces (Простори імен) — забезпечують ізоляцію

Кожен контейнер отримує свій ізольований простір:

Namespace Що ізолює
PID Процеси (контейнер бачить лише свої процеси)
NET Мережа (свої мережеві інтерфейси і порти)
MNT Файлова система (своя точка монтування)
UTS Ім’я хоста
IPC Міжпроцесна взаємодія
USER Користувачі і групи

2. Control Groups (cgroups) — забезпечують обмеження ресурсів

Cgroups дозволяють задати жорсткі ліміти: скільки CPU і RAM може використовувати контейнер, обмеження на дисковий I/O, мережеві ліміти. Без cgroups один «ненажерливий» контейнер міг би забрати всі ресурси системи.

Контейнер vs Віртуальна машина

Характеристика Контейнери Віртуальні машини
Архітектура Спільне ядро ОС Кожна ВМ має свою Guest OS
Ізоляція На рівні процесів На рівні заліза
Швидкість запуску Секунди Хвилини
Вага образу Десятки/сотні МБ Гігабайти
Продуктивність Майже нативна Оверхед на гіпервізор

Типові помилки

Помилка Наслідок Як уникнути
Зберігання даних всередині контейнера Дані втрачаються при перезапуску Використовуйте Volumes
Запуск від root Ризик безпеки Використовуйте USER nonroot
Використання тегу latest Недетерміновані збірки Фіксуйте версії образів
Ігнорування .dockerignore Повільна збірка, зайві файли Створіть .dockerignore

Volume – зовнішнє сховище, яке переживає перезапуск контейнера.

Навіщо потрібна контейнеризація в реальних проєктах

  1. Immutable Infrastructure — ми не змінюємо код на сервері. Створюємо новий образ, тестуємо і деплоїмо. Rollback — миттєвий.
  2. Environments Parity — розробник, QA і Production використовують один і той самий артефакт.
  3. Мікросервісна архітектура — контейнери ідеально підходять для запуску сотень дрібних незалежних компонентів.
  4. CI/CD пайплайни — контейнери стали стандартом доставки ПЗ: збірка → тести → образ → деплой.

Що запам’ятати

  • Контейнеризація заснована на Namespaces (ізоляція) і Cgroups (ресурси)
  • Контейнери легші і швидші за ВМ, але мають менш строгу ізоляцію
  • Контейнеризація — фундамент мікросервісів і CI/CD
  • Головний ризик: спільне ядро і складність керування станом

🔴 Senior Level

Глибока внутрішня архітектура

Контейнер — це звичайний процес Linux, обмежений через системні виклики ядра. Немає емуляції, немає проміжних шарів. Послідовність при запуску контейнера:

  1. Clone syscall з прапорцями CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC — створює процес з новими namespaces.
  2. Chroot/pivot_root — змінює кореневу файлову систему контейнера.
  3. Cgroups setup — встановлює ліміти CPU, memory, I/O через cgroup v2 (unified hierarchy).
  4. Seccomp/AppArmor — застосовує профілі безпеки, обмежуючи доступні syscall.

Trade-offs

Аспект Перевага Недолік
Спільне ядро Мінімальний overhead (< 1% CPU) Уразливість ядра зачіпає всі контейнери
Ефемерність Легко замінювати, масштабувати Не можна зберігати стан всередині
UnionFS (Overlay2) Перевикористання шарів, економія disk/RAM overhead на disk I/O при великій кількості шарів
Network namespace Ізоляція мережі Складність: NAT, DNS, service discovery

Edge Cases

  • Container Escape: через уразливості ядра (Dirty COW, CVE-2019-5736 runc), неправильне налаштування capabilities, монтування /proc//sys. Мітигація: seccomp profiles, AppArmor/SELinux, read-only rootfs, drop all capabilities.
  • Zombie processes: процес з PID 1 в контейнері не отримує SIGTERM за замовчуванням і не reap’ить дочірні процеси. Рішення: exec form або tini.
  • Clock skew: контейнери можуть мати розсинхронізацію годинників з хостом, що критично для TLS і distributed consensus.
  • Inode exhaustion: контейнери з великою кількістю дрібних файлів можуть вичерпати inodes на хості, навіть якщо місце на диску є.

Продуктивність

Метрика Контейнери ВМ
CPU overhead < 1% 5-15%
Memory overhead Кілька МБ на контейнер ГБ на Guest OS
Network Практично native Віртуалізація NIC
Disk I/O Невеликий overhead через Overlay2 Віртуалізація storage controller
Щільність 100+ на сервер 10-30 на сервер
Утилізація CPU 60-80% 20-40%

Безпека в продакшені

# Production-ready підхід
FROM gcr.io/distroless/java17-debian12  # мінімальна поверхня атаки
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
USER appuser                              # non-root
COPY --chown=appuser:appgroup app.jar /app.jar
# Read-only файлову систему налаштовують на рівні docker run / K8s
# Seccomp profile, AppArmor profile — на рівні runtime

Ключові принципи:

  • Не використовуйте --privileged
  • Drop all capabilities, додавайте тільки потрібні
  • Read-only root filesystem де можливо
  • Image scanning в CI/CD (Trivy, Snyk)
  • Runtime protection (Falco)

Production Story

Компанія мігрувала моноліт з ВМ на контейнери. Результат: час деплою скоротився з 45 хвилин до 90 секунд, щільність пакування зросла у 4 рази, витрати на інфраструктуру знизились на 40%. Але знадобилося: переробити логування (stdout → ELK), налаштувати health checks, впровадити централізований моніторинг (Prometheus + Grafana), переписати роботу зі станом (external volumes), навчити команду Kubernetes. Контейнеризація — це не лише технологія, а й зміна процесів.

Моніторинг

  • Golden Signals: latency, traffic, errors, saturation
  • Інструменти: Prometheus (метрики), cAdvisor (контейнерні метрики), Jaeger (tracing), ELK/EFK (логи)
  • Ключові метрики: restart count, memory usage vs limit, CPU throttling, network I/O, container uptime
  • Alerting: OOMKilled, CrashLoopBackOff, HighRestartRate

Резюме

  • Контейнеризація — стандарт сучасної доставки ПЗ. Основа: Namespaces + Cgroups.
  • Головний плюс: відтворюваність і швидкість. Головний ризик: спільне ядро і складність керування станом.
  • Контейнер впливає на архітектуру: додаток має бути ефемерним, конфігурованим ззовні, самовідновлюваним.
  • На масштабі потрібен оркестратор (Kubernetes), що додає свою складність.
  • Для multi-tenancy і строгих compliance розгляньте MicroVMs (Firecracker, Kata Containers).

🎯 Шпаргалка для інтерв’ю

Обов’язково знати:

  • Контейнер = процес Linux, обмежений через namespaces + cgroups
  • Namespaces забезпечують ізоляцію (PID, NET, MNT), cgroups — ліміти ресурсів
  • Контейнери ділять ядро хоста, ВМ мають своє — звідси різниця у швидкості і вазі
  • Образ — шаблон (клас), контейнер — запущений екземпляр (об’єкт)
  • Контейнери ефемерні — стан зберігається у зовнішніх volumes
  • Для production: non-root користувач, read-only FS, image scanning
  • Контейнеризація — фундамент мікросервісів і CI/CD

Часті уточнюючі питання:

  • «Чому контейнери швидші за ВМ?» — Немає завантаження Guest OS, спільне ядро, старт за секунди
  • «Що таке namespace?» — Механізм ядра Linux, що ізолює ресурси процесу (PID, мережа, ФС)
  • «Чи можна запустити Linux-контейнер на Windows?» — Так, через WSL2 або VM з Linux-ядром
  • «Що таке Overlay2?» — Шарувата ФС, що перевикористовує базові шари між образами

Червоні прапорці (НЕ говорити):

  • «Контейнер — це легка ВМ» (ні, принципово інша архітектура)
  • «Контейнери повністю ізольовані» (спільне ядро = ризик container escape)
  • «Дані всередині контейнера зберігаються» (ефемерні, потрібні volumes)
  • «Контейнер = файл» (це процес Linux з namespaces/cgroups)

Пов’язані теми:

  • [[В чому відмінність контейнера від віртуальної машини]] — детальне порівняння
  • [[Що таке Dockerfile]] — як створити образ
  • [[Що таке Kubernetes і навіщо він потрібен]] — оркестрація контейнерів