Модуль 1.2: Основи контейнерів
Складність: [ШВИДКИЙ] — основоположні концепції. Час на проходження: 35-45 хвилин. Передумови: Модуль 1.1 та базова впевненість у роботі з командним рядком Linux. У прикладах припускається Kubernetes 1.35 або новіша версія, а коли далі з’являтимуться команди Kubernetes, налаштуйте alias k=kubectl, щоб коротка форма k була зрозумілою.
Результати навчання
Розділ «Результати навчання»Після завершення цього модуля ви зможете:
- Порівнювати контейнери та віртуальні машини, оцінюючи спільне використання ядра, межі ізоляції, вартість запуску та операційні компроміси.
- Діагностувати поведінку просторів імен (namespaces) і контрольних груп (cgroups), коли контейнер бачить не той процес, мережу, файлову систему чи обмеження ресурсів.
- Оцінювати OCI-образи, реєстри, теги, шари та дайджести, щоб обирати відтворювані практики пакування для робочих навантажень Kubernetes.
- Описувати робочий процес інспекції середовища виконання контейнерів, який поєднує Docker, containerd, CRI-O, CRI та поведінку Pod’а в Kubernetes.
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Гіпотетичний сценарій: Платіжна платформа постачає невеликий сервіс звітності у вигляді контейнера, бо команда хоче швидкого розгортання та узгоджених залежностей у середовищах розробки, тестування та виробництва. Сервіс виглядає безпечним під час тестування, але під час сплеску трафіку наприкінці місяця з’являється витік пам’яті, і контейнер продовжує виділяти пам’ять, доки нода не починає виселяти не пов’язані з ним робочі навантаження. Витрати після інциденту охоплюють компенсації клієнтам, понаднормову роботу та затримку розрахункових операцій, проте першопричина — не якась екзотична відмова Kubernetes; це базове нерозуміння того, що ізоляція контейнерів забезпечує, а що ні.
Така відмова спершу здається несправедливою, бо контейнери часто описують як ізольовані пакети, а слово «ізольований» звучить сильніше, ніж є насправді. Контейнер може мати власний перелік процесів, ім’я хоста, власний вигляд монтувань і мережеві інтерфейси, водночас і далі спільно використовуючи ядро хоста та конкуруючи за той самий обмежений ресурс — процесор і пам’ять. Kubernetes уміє планувати Pod’и, перезапускати контейнери та завантажувати образи, але він не здатен скасувати хибні припущення; якщо інженери не знають, де пролягає межа ізоляції, під тиском вони налагоджуватимуть не той рівень.
Цей модуль будує ту ментальну модель, на яку розраховує KCNA і якою щодня користуються оператори. Ви порівняєте контейнери з віртуальними машинами, простежите функції Linux, що змушують контейнери працювати, дослідите стандарти образів і середовища виконання, які забезпечують переносимість, та пов’яжете ці ідеї з поведінкою середовища виконання Kubernetes 1.35. Мета — поки що не стати фахівцем з Dockerfile; мета — розпізнавати, чим саме оркеструє Kubernetes, коли каже, що запускає контейнер.
Що насправді пакує контейнер
Розділ «Що насправді пакує контейнер»Контейнер найкраще розуміти як процес із підготовленою файловою системою, контрольованим виглядом операційної системи та правилами щодо ресурсів, які забезпечує ядро хоста. Образ надає файли застосунку, бібліотеки, середовище виконання та мінімальний простір користувача, потрібні процесу, тоді як ядро залишається поза пакетом. Саме це розрізнення треба внести першою поправкою, коли хтось каже, що контейнер містить цілу операційну систему, бо пакет несе достатньо простору користувача для запуску програми, а не окреме ядро Linux.
Початкове скорочене визначення досі допомагає, якщо читати його уважно: контейнер — це легкий, автономний, виконуваний пакет, що містить усе потрібне для запуску частини програмного забезпечення, окрім ядра хоста. Пакет може містити інтерпретатор Python, середовище виконання Node.js, спільні бібліотеки, файли сертифікатів, інструменти оболонки та код застосунку. Він не містить планувальника, диспетчера пам’яті, реалізації стека TCP/IP чи механізмів безпеки ядра, які насправді працюють на ноді.
┌─────────────────────────────────────────────────────────────┐│ WHAT'S INSIDE A CONTAINER │├─────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────┐ ││ │ CONTAINER │ ││ │ ┌─────────────────────────────────────────────┐ │ ││ │ │ Application Code │ │ ││ │ │ (your app, scripts, binaries) │ │ ││ │ └─────────────────────────────────────────────┘ │ ││ │ ┌─────────────────────────────────────────────┐ │ ││ │ │ Dependencies │ │ ││ │ │ (libraries, packages, frameworks) │ │ ││ │ └─────────────────────────────────────────────┘ │ ││ │ ┌─────────────────────────────────────────────┐ │ ││ │ │ Runtime │ │ ││ │ │ (Python, Node.js, Java JVM, etc.) │ │ ││ │ └─────────────────────────────────────────────┘ │ ││ │ ┌─────────────────────────────────────────────┐ │ ││ │ │ System Tools & Libraries │ │ ││ │ │ (minimal OS userspace) │ │ ││ │ └─────────────────────────────────────────────┘ │ ││ │ └─────────────────────────────────────────────┘ │ ││ └─────────────────────────────────────────────────────┘ ││ ││ NOT included: Kernel (shared with host) ││ │└─────────────────────────────────────────────────────────────┘Уявіть образ радше як ретельно зібраний ланчбокс, а не як цілу кухню. У ланчбоксі можуть бути страва, прибори, серветки та приправа, але він усе одно залежить від будівлі, що надає столи, електрику, воду та правила безпеки. Так само образ контейнера може містити ваш застосунок і його залежності, але ядро, драйвери пристроїв і механізми примусу, що дають процесу працювати, надає нода.
Це має значення під час налагодження, бо багато проблем контейнерів узагалі не всередині застосунку. Процес може дати збій, бо в образі бракує спільної бібліотеки, бо середовище виконання запускає не ту точку входу, бо контекст безпеки Kubernetes блокує запис у файлову систему або бо налаштування cgroup на ноді завершують процес через використання пам’яті. Ставлення до контейнера як до крихітної машини приховує ці рівні, а ставлення до нього як до запакованого процесу робить кожен шлях відмови легшим для перевірки.
Розглянутий приклад робить межу конкретною. Якщо образ застосунку Node.js містить /usr/local/bin/node, /app/server.js та бібліотеки в /app/node_modules, цей образ може працювати на багатьох хостах Linux, бо приносить із собою потрібні йому файли простору користувача. Він усе одно не зможе працювати на ноді Linux із цілком несумісною архітектурою процесора і не зможе викликати функції ядра, яких ядро хоста не надає, бо образ переносимий лише між сумісними середовищами виконання.
Фраза «у мене на машині працює» — це культурна причина, чому контейнери стали такими популярними. До того, як робочі процеси з контейнерами стали поширеними, команди часто виявляли, що версія бібліотеки, дзеркало пакетів, набір сертифікатів чи змінна середовища різняться між ноутбуком і сервером. Образ контейнера не усуває кожну різницю в середовищі, але робить файлову систему застосунку та команду запуску достатньо явними, щоб ці відмінності стали видимими артефактами, а не фольклором, що передається між колегами.
Образ також дає командам експлуатації чистішу передачу справ, ніж інструкція, повна ручних кроків встановлення. Замість того, щоб просити сервер з часом набути правильної форми, команда збирає версіонований артефакт і просить середовище виконання його запустити. Це змінює аналіз відмов, бо невдале розгортання можна простежити до конкретного посилання на образ, процесу збирання чи налаштування середовища виконання замість невідомої послідовності команд оболонки, виконаних кілька місяців тому.
Зупиніться та зробіть прогноз: якщо образ контейнера каже, що він базується на Ubuntu, чи означає це, що запущений процес має ядро Ubuntu, чи лише простір користувача в стилі Ubuntu? Практична відповідь впливає на реагування на інциденти. Якщо застосунок спричиняє помилку ядра, оновлення пакетів усередині образу може не виправити ноду; якщо застосунку бракує ca-certificates, перезбирання образу може бути саме правильним виправленням.
Контейнери також заохочують незмінність, тобто запущений екземпляр слід вважати одноразовим. Усередині контейнера можна писати тимчасові файли, але ці записи належать до записуваного шару, прикріпленого до того одного запущеного контейнера, якщо тільки не змонтовано том. Kubernetes спирається на цю модель, замінюючи контейнери замість того, щоб виходжувати окремі екземпляри, тож конфігурація, логи та постійні дані потребують свідомо обраних місць поза одноразовим процесом.
Цю одноразову модель легко проголосити й важче впровадити на практиці. Інженери, звиклі заходити на сервери, можуть захотіти встановити пакет для налагодження, відредагувати файл конфігурації та залишити змінений сервер в експлуатації. У робочому процесі з контейнерами ці дії зазвичай мають перетворюватися на новий образ, змінений маніфест чи приєднаний діагностичний сеанс, який потім зникає. Винагорода в тому, що наступну репліку, ноду чи кластер можна створити з тих самих оголошених вхідних даних, а не з прихованої ручної історії.
Контейнери проти віртуальних машин
Розділ «Контейнери проти віртуальних машин»Віртуальні машини та контейнери обидва дозволяють командам запускати кілька робочих навантажень на спільному обладнанні, але вони проводять межу ізоляції в різних місцях. ВМ віртуалізує обладнання та завантажує гостьову операційну систему з власним ядром, тож кожне робоче навантаження можна відокремити межею гіпервізора. Контейнер ізолює процеси всередині однієї хостової операційної системи, тож середовище виконання легше та швидше, але спільне ядро стає залежністю з погляду безпеки та сумісності.
Різницю найлегше побачити як стек. У проєкті з ВМ кожен гість несе власне ядро та сервіси операційної системи, що дає сильне розділення, але споживає більше пам’яті й дискового простору. У проєкті з контейнерами кожен застосунок несе власні залежності простору користувача, тоді як ядро хоста залишається спільним для всіх контейнерів на тій ноді.
┌─────────────────────────────────────────────────────────────┐│ VIRTUAL MACHINES vs CONTAINERS │├─────────────────────────────────────────────────────────────┤│ ││ VIRTUAL MACHINES CONTAINERS ││ ───────────────────────────────────────────────────────── ││ ││ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ││ │App A│ │App B│ │App C│ │App A│ │App B│ │App C│ ││ ├─────┤ ├─────┤ ├─────┤ ├─────┤ ├─────┤ ├─────┤ ││ │Bins │ │Bins │ │Bins │ │Bins │ │Bins │ │Bins │ ││ │Libs │ │Libs │ │Libs │ │Libs │ │Libs │ │Libs │ ││ ├─────┤ ├─────┤ ├─────┤ └──┬──┘ └──┬──┘ └──┬──┘ ││ │Guest│ │Guest│ │Guest│ │ │ │ ││ │ OS │ │ OS │ │ OS │ │ │ │ ││ └──┬──┘ └──┬──┘ └──┬──┘ └───────┼───────┘ ││ └───────┼───────┘ │ ││ │ ┌───────┴───────┐ ││ ┌───────┴───────┐ │Container │ ││ │ Hypervisor │ │Runtime │ ││ └───────┬───────┘ └───────┬───────┘ ││ │ │ ││ ┌───────┴───────┐ ┌───────┴───────┐ ││ │ Host OS │ │ Host OS │ ││ └───────┬───────┘ └───────┬───────┘ ││ │ │ ││ ┌───────┴───────┐ ┌───────┴───────┐ ││ │ Hardware │ │ Hardware │ ││ └───────────────┘ └───────────────┘ ││ ││ Each VM has full OS Containers share ││ (heavy, slow to start) host kernel (light) ││ │└─────────────────────────────────────────────────────────────┘Таблиця нижче зберігає звичне для KCNA порівняння, але кожен рядок слід читати як компроміс, а не як універсальне ранжування. Контейнери зазвичай запускаються за секунди й споживають мегабайти, бо уникають завантаження гостьової ОС, але ця ефективність випливає зі спирання на ядро хоста. ВМ часто запускаються повільніше й використовують більше сховища, але вони лишаються корисними, коли команді потрібні різні ядра, міцніші межі між орендарями чи операційні системи, які не можуть спільно використовувати ядро Linux.
| Аспект | Віртуальна машина | Контейнер |
|---|---|---|
| Розмір | Гігабайти | Мегабайти |
| Запуск | Хвилини | Секунди |
| Ізоляція | Сильна (окреме ядро) | На рівні процесу (спільне ядро) |
| Накладні витрати | Високі | Низькі |
| Щільність | ~десятки на хост | ~сотні на хост |
| Переносимість | Середня | Висока |
Наслідки для безпеки — це та частина, яку початківці схильні недооцінювати. Втеча з ВМ зазвичай має перетнути межу гіпервізора, тоді як втеча з контейнера часто означає, що зловмисник знайшов спосіб зловжити спільним ядром, змонтованим шляхом хоста, привілейованою можливістю чи неправильно налаштованим середовищем виконання. Це не робить контейнери небезпечними за замовчуванням, але означає, що виробничі кластери потребують ешелонованого захисту: користувачів без прав root, мінімальних можливостей, файлових систем лише для читання там, де це можливо, довірених образів і політик ізоляції робочих навантажень.
Гіпотетичний сценарій: Команда платформи переносить внутрішні пакетні завдання з ВМ у контейнери та святкує перший звіт про потужність, бо ті самі хости можуть виконувати значно більше завдань. Через два тижні команда виявляє, що одне завдання очікувало писати в /var/log/app назавжди, і кожен перезапущений контейнер втрачає локальний файл, якщо не змонтовано том. Припущення епохи ВМ полягало в тому, що сервер — це довговічна домівка; припущення епохи контейнерів мало бути таким, що процес можна замінити будь-якої миті.
Перш ніж щось запускати в Kubernetes, запитайте, яка межа вам насправді потрібна. Якщо дві команди володіють довіреними сервісами для того самого продукту, контейнери на спільних нодах можуть бути доброю відповідністю. Якщо не пов’язані між собою клієнти запускають довільний код, проєкт може потребувати окремих нод, ізольованих у пісочниці середовищ виконання чи контейнерів усередині легких ВМ. Відповідь рівня KCNA — це не «контейнери кращі» чи «ВМ кращі»; відповідь у тому, що кожна технологія оптимізує іншу межу.
Вартість і швидкість часто схиляють команди до контейнерів, але й ризик повинен мати право голосу. Платформа розробки, що виконує внутрішні завдання збирання, може прийняти звичайну ізоляцію контейнерів, бо користувачі, репозиторії сирцевого коду та адміністратори кластера належать до тієї самої організації. Публічна платформа виконання коду має іншу модель загроз, бо клієнти можуть надсилати невідомі програми, тож ту саму перевагу щільності, можливо, доведеться зрівноважити сильнішою ізоляцією, нодами на кожного орендаря чи середовищами виконання в пісочниці.
Операційні звички також різняться. Команди ВМ часто керують довгоживучими хостами з вікнами оновлень, керуванням конфігурацією та моніторингом на рівні хоста. Команди контейнерів так само оновлюють ноди, але вони ще й перезбирають образи, просувають артефакти через реєстри та сприймають перезапуски контейнерів як звичайні події життєвого циклу. Коли організація переходить із ВМ на контейнери, найскладніша частина часто не синтаксис команд; це заміна звичок володіння серверами на звички роботи з артефактами та оркестрацією.
Ізоляція в Linux: простори імен, cgroups і шари
Розділ «Ізоляція в Linux: простори імен, cgroups і шари»Контейнери здаються маленькими системами, бо ядро Linux може дати процесу приватний вигляд вибраних ресурсів операційної системи. Простори імен (namespaces) відповідають на запитання «що цей процес може бачити?», а контрольні групи (cgroups) відповідають на запитання «скільки цей процес може використати?» Об’єднані файлові системи (union filesystems) відповідають на третє запитання, що важливе для пакування: «як багато контейнерів можуть спільно використовувати ті самі файли лише для читання, при цьому кожен контейнер отримує власні записувані зміни?»
Простори імен — це механізм видимості. Процес у просторі імен PID може бачити власне дерево процесів, процес у мережевому просторі імен може бачити власні інтерфейси та маршрути, а процес у просторі імен монтування може бачити розкладку файлової системи, що відрізняється від хоста. Це не вдавані вигляди, створені обгорткою оболонки; це функції ядра, що змінюють те, які значення повертають системні виклики ізольованому процесу.
┌─────────────────────────────────────────────────────────────┐│ LINUX NAMESPACES │├─────────────────────────────────────────────────────────────┤│ ││ Namespace What It Isolates ││ ───────────────────────────────────────────────────────── ││ PID Process IDs (container sees own PIDs) ││ Network Network interfaces, IPs, ports ││ Mount Filesystem mounts ││ UTS Hostname and domain name ││ IPC Inter-process communication ││ User User and group IDs ││ ││ Each container gets its own namespaces ││ → Appears to have its own isolated system ││ │└─────────────────────────────────────────────────────────────┘Коли оболонка контейнера повідомляє, що вона є PID 1, вона каже правду в межах свого простору імен PID. Той самий процес усе одно має інший PID з погляду хоста, і хост може керувати ним, як будь-яким іншим процесом. Ця подвійна ідентичність пояснює, чому процес може виглядати як перший процес усередині контейнера, водночас залишаючись керованим середовищем виконання, kubelet і операційною системою ноди поза простором імен.
Зупиніться та зробіть прогноз: якщо контейнери спільно використовують ядро хоста, що станеться, якщо контейнеризований процес експлуатує вразливість ядра? Межа ізоляції може обвалитися, бо ядро — це спільна інфраструктура, і саме тому оновлення нод, зменшення можливостей Linux та уникання привілейованих контейнерів — це не опціональні деталі посилення. Простори імен зменшують те, що процес може бачити, але вони не перетворюють одне ядро на багато ядер.
Cgroups забезпечують бік обліку та примусу в цій моделі. Без cgroups процес у просторі імен міг би й далі споживати пам’ять чи процесор хоста, доки не постраждають інші робочі навантаження. З cgroups ядро може відстежувати використання ресурсів і застосовувати обмеження, і саме тому запити та обмеження ресурсів Kubernetes зрештою важать для ноди, а не лише для планувальника.
┌─────────────────────────────────────────────────────────────┐│ CONTROL GROUPS (cgroups) │├─────────────────────────────────────────────────────────────┤│ ││ Cgroups limit and track resources: ││ ││ CPU: "Container A gets max 2 cores" ││ Memory: "Container B gets max 512MB" ││ Disk I/O: "Container C gets max 100MB/s" ││ Network: "Container D gets max 100Mbps" ││ ││ Without cgroups: ││ One container could consume ALL resources ││ │└─────────────────────────────────────────────────────────────┘Відмова у фінансовому сервісі зі вступного сценарію — це класичний урок про cgroup. Ізоляція просторів імен робила контейнер звітності окремим на вигляд, але відсутні обмеження ресурсів дозволяли витоку пам’яті безпосередньо конкурувати з хостом і сусідніми контейнерами. У Kubernetes обмеження пам’яті дає ядру конкретну межу; коли процес її перевищує, контейнер можна завершити, а не дозволяти ноді стати нестабільною.
Об’єднані файлові системи роблять образи достатньо ефективними для розповсюдження та повторного використання. Образ збирається з шарів лише для читання, а запущений контейнер отримує записуваний шар зверху для змін під час виконання. Якщо багато контейнерів використовують той самий базовий образ, нода може зберегти ці базові шари один раз і прикріпити окремі записувані шари до кожного запущеного контейнера.
┌─────────────────────────────────────────────────────────────┐│ CONTAINER IMAGE LAYERS │├─────────────────────────────────────────────────────────────┤│ ││ Container Image (read-only layers): ││ ││ ┌─────────────────────────────────────────────────────┐ ││ │ Layer 4: Application code (your app) │ ││ ├─────────────────────────────────────────────────────┤ ││ │ Layer 3: Dependencies (npm packages) │ ││ ├─────────────────────────────────────────────────────┤ ││ │ Layer 2: Runtime (Node.js) │ ││ ├─────────────────────────────────────────────────────┤ ││ │ Layer 1: Base OS (Ubuntu slim) │ ││ └─────────────────────────────────────────────────────┘ ││ ││ Running Container adds: ││ ┌─────────────────────────────────────────────────────┐ ││ │ Writable layer (runtime changes, logs, temp files)│ ││ └─────────────────────────────────────────────────────┘ ││ ││ Benefits: ││ • Layers are cached and reused ││ • Multiple containers share base layers ││ • Efficient storage and transfer ││ │└─────────────────────────────────────────────────────────────┘Порядок шарів має практичні наслідки. Якщо шар залежностей змінюється під час кожного збирання, середовищу виконання може знадобитися перезібрати чи завантажити все над ним, навіть коли єдина реальна зміна — це код застосунку. Якщо залежності стабільні, а код застосунку лежить у фінальному шарі, збирання та завантаження стають швидшими, бо базова ОС, середовище виконання та залежності можуть залишатися в кеші.
Перш ніж це запускати, який вивід ви очікуєте від k get pods після того, як контейнер перевищить обмеження пам’яті в Kubernetes? Pod може перезапуститися, а його попередній стан контейнера може показати причину OOMKilled, бо ядро застосувало обмеження cgroup, а kubelet помітив завершення. Це спостереження пов’язує примітив Linux із підказкою для усунення несправностей у Kubernetes — а це саме той місток, який KCNA хоче, щоб ви побудували.
Найпростіша діагностична звичка — відокремлювати відмови видимості від відмов ресурсів. Якщо процес не може бачити файл, ім’я хоста, мережевий інтерфейс чи сусідній процес, думайте насамперед про простори імен і монтування. Якщо процес сповільнюється, перезапускається під навантаженням чи завершується через тиск на пам’ять, думайте насамперед про cgroups, запити, обмеження та потужність ноди. Симптоми іноді перекриваються, але цей поділ тримає ваше перше дослідження на твердій основі.
Простори імен користувачів додають ще один важливий нюанс, бо вони можуть по-різному відображати ідентифікатори користувачів і груп усередині та поза контейнером. Процес може здаватися запущеним як root усередині свого простору імен, водночас відображаючись на менш привілейовану ідентичність на хості, залежно від підтримки та налаштувань середовища виконання. Це відображення може зменшити ризик, але воно не замінює ретельне проєктування контексту безпеки, бо змонтовані файли, можливості Linux і привілеї середовища виконання все одно впливають на те, що процес насправді може робити.
Спробуйте швидкий прогноз, перш ніж рушити далі: якщо Pod показує OOMKilled після сплеску трафіку, який примітив ізоляції дає вам першу підказку — простори імен чи cgroups? Відповідь має бути cgroups, бо симптом — це примус ресурсів, а не видимість.
Мережеві простори імен — це ще одне місце, де абстракція Kubernetes будується на примітиві контейнера. Окремий контейнер Docker може отримати власний мережевий простір імен і віртуальний інтерфейс, тоді як Pod у Kubernetes зазвичай дає всім контейнерам у Pod’і спільний мережевий простір імен. Саме тому два контейнери в одному Pod’і можуть спілкуватися через localhost, і саме тому проєкт Pod’а має групувати тісно пов’язані процеси, а не не пов’язані сервіси, що лише випадково мають спільний графік розгортання.
Простори імен монтування пояснюють багато несподіванок «файл не знайдено» та «доступ заборонено». Контейнер може мати шлях, що виглядає знайомим, як-от /etc, /tmp чи /var, але ці шляхи належать до вигляду файлової системи контейнера, якщо тільки том чи шлях хоста не змінює розкладку монтувань. Коли процес пише логи в шлях, не підкріплений томом чи збирачем логів, дані можуть зникнути разом із контейнером, навіть якщо зсередини оболонки шлях виглядав довговічним.
Cgroups також впливають на очікування щодо планування ще до того, як процес стартує. Kubernetes використовує запити ресурсів, щоб вирішити, куди може поміститися Pod, а середовище виконання ноди використовує обмеження для примусу меж після того, як контейнер запущено. Якщо команда копіює крихітні запити з прикладу маніфесту, планувальник може напхати забагато Pod’ів на ноду; якщо команда встановлює обмеження, не спостерігаючи за сплесками пам’яті, ядро може завершити процес, що виглядав справним під час звичайного трафіку.
Образи, реєстри, теги та переносимість OCI
Розділ «Образи, реєстри, теги та переносимість OCI»Образ — це шаблон лише для читання, який використовують для створення контейнерів, тоді як контейнер — це запущений екземпляр, створений із того шаблону. Аналогія з рецептом досі корисна, бо рецепт може дати багато тортів, а образ може дати багато контейнерів. Аналогія руйнується, якщо уявити, що рецепт змінюється після того, як торт спечено; запущений контейнер може додавати записувані зміни, але ці зміни не мутують оригінальні шари образу.
| Концепція | Аналогія |
|---|---|
| Образ | Рецепт / Креслення |
| Контейнер | Торт / Будівля |
Посилання на образи виглядають невеликими, але вони несуть важливе операційне значення. Посилання може містити розташування реєстру, ім’я репозиторію та тег, а посилання виробничого рівня може ще й містити дайджест, який ідентифікує точний вміст образу. Теги — це зручні мітки, але дайджести є сильнішим доказом, коли потрібно довести, що кожна нода виконує ідентичні байти.
registry/repository:tag
Examples:docker.io/library/nginx:1.25gcr.io/google-containers/pause:3.9mycompany.com/myapp:v2.1.0
Parts:• registry: Where image is stored (docker.io, gcr.io)• repository: Name of the image (nginx, myapp)• tag: Version identifier (1.25, latest, v2.1.0)Тег latest заслуговує на особливу підозру, бо звучить як обіцянка, а поводиться як змінний вказівник. Нода, що завантажила myapp:latest учора, та інша нода, що завантажує його сьогодні, можуть отримати різний вміст, якщо тег було перезаписано. Така поведінка зручна під час локальних експериментів, але у виробництві вона ламає відтворюваність, відкат інцидентів та криміналістичний аналіз.
Реєстри — це системи розповсюдження образів, і вони можуть бути публічними, приватними, розміщеними в хмарі чи самокерованими. Kubernetes байдуже, звідки прийшов образ — із Docker Hub, хмарного реєстру чи приватного корпоративного реєстру, доки нода може автентифікуватися, завантажити шари та передати образ сумісному середовищу виконання. Рішення щодо реєстру зазвичай залежить від засобів контролю ланцюга постачання, мережевої локальності, вимог аудиту та інтеграції із системою ідентифікації організації.
| Реєстр | Опис |
|---|---|
| Docker Hub | Публічний реєстр за замовчуванням |
| gcr.io | Google Container Registry |
| ECR | Amazon Elastic Container Registry |
| ACR | Azure Container Registry |
| Quay.io | Реєстр Red Hat |
Open Container Initiative, зазвичай скорочено OCI, — це те, що не дає екосистемі розпастися на один формат образу для кожного інструмента. Специфікації образу та середовища виконання OCI описують, як структуровані образи і як контейнери мають створюватися на низькому рівні. Завдяки цим стандартам образ, зібраний Docker, можна зберегти в реєстрі та запустити через containerd чи CRI-O на ноді Kubernetes.
Ця переносимість — це відповідь на поширений страх щодо припинення підтримки Docker. Kubernetes видалив dockershim у версії 1.24, але він не відкинув образи, зібрані за допомогою Docker. Образи Docker, що відповідають формату образу OCI, лишаються придатними до використання, бо Kubernetes спілкується із середовищем виконання через CRI, а середовище виконання розуміє формат образу; видаленою частиною був прошарок сумісності навколо Docker Engine як шляху середовища виконання.
Який підхід ви б тут обрали і чому: змінний тег на кшталт latest, тег семантичної версії на кшталт v1.2.4 чи закріплення за дайджестом для регульованого виробничого розгортання? Тег семантичної версії кращий за latest, але дайджест дає найсильнішу ідентичність вмісту, бо теги можна переміщувати. Багато команд використовують і читабельний тег, і дайджест у доказах релізу, щоб люди могли обговорювати версії, а автоматизація перевіряла точний вміст.
Реєстри також стають точками примусу політик у міру дозрівання організацій. Невелика команда може завантажувати безпосередньо з публічного реєстру під час експериментів, але виробнича платформа часто дзеркалить схвалені образи в приватний реєстр після сканування та підписання. Цей додатковий крок — не бюрократія заради бюрократії; він дає організації місце, де можна записати походження, заблокувати відомі вразливі артефакти та зберегти працездатність розгортань, навіть якщо зовнішній реєстр має збій чи обмеження частоти запитів.
Теги й дайджести відповідають на різні людські запитання. Тег на кшталт v1.2.4 відповідає на «який реліз ми мали намір розгорнути?», тоді як дайджест відповідає на «який саме вміст завантажило середовище виконання?» Під час відкату вам зазвичай потрібні обидві частини інформації, бо люди обговорюють інциденти в назвах релізів, але машини доводять узгодженість ідентифікаторами вмісту. Плутання цих завдань призводить до суперечок, де всі вживають те саме ім’я образу, але не ті самі байти.
Проєктування шарів образу також впливає на оновлення безпеки. Якщо базовий образ отримує важливе виправлення пакета, кожен образ застосунку, зібраний на цій базі, потрібно перезібрати та просунути, навіть коли код застосунку не змінився. Команди іноді припускають, що використання тега на кшталт ubuntu:22.04 автоматично оновлює запущені контейнери, але наявні образи та контейнери не змінюються магічним чином; конвеєр збирання та розгортання має створити й розгорнути нові артефакти.
Ось мінімальний маніфест Pod’а Kubernetes, який використовує явний тег образу та позицію безпеки без прав root. Запускати його для цього модуля не потрібно, але читання пов’язує рішення щодо пакування з об’єктами API, які ви використовуватимете далі. У прикладах команд вживається k, а псевдонім було введено на початку модуля.
apiVersion: v1kind: Podmetadata: name: container-fundamentals-demospec: containers: - name: web image: docker.io/library/nginx:1.25 securityContext: runAsNonRoot: true allowPrivilegeEscalation: false resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mialias k=kubectlk apply -f pod.yamlk get pods container-fundamentals-demok describe pod container-fundamentals-demoМаніфест навмисно скромний, бо мета поки що не у складному проєктуванні робочих навантажень. Тег явний, межі ресурсів відображаються на cgroups, а контекст безпеки зменшує наслідки, якщо застосунок буде скомпрометовано. У пізніших модулях Kubernetes ви дізнаєтеся, як Деплойменти, Сервіси та політики допуску будуються на тих самих припущеннях щодо образу та середовища виконання.
Середовища виконання контейнерів та CRI Kubernetes
Розділ «Середовища виконання контейнерів та CRI Kubernetes»Kubernetes не запускає контейнери самотужки. Kubelet на кожній ноді просить середовище виконання контейнерів завантажити образи, створити контейнери, запустити їх, зупинити та звітувати про стан. Ця робота середовища виконання надається через інтерфейс середовища виконання контейнерів (CRI), тож kubelet може використовувати стандартний API замість вбудовування приватних деталей реалізації кожного середовища виконання.
Ландшафт середовищ виконання може заплутувати, бо Docker — це і інструмент розробника, і історичний шлях середовища виконання. Docker Engine охоплює високорівневі функції збирання образів і робочого процесу розробника, тоді як containerd — це нижчорівневий компонент середовища виконання, зосереджений на завантаженні образів, керуванні знімками та нагляді за контейнерами. CRI-O — це ще одне зорієнтоване на Kubernetes середовище виконання, яке реалізує CRI безпосередньо і поширене в середовищах, орієнтованих на OpenShift.
┌─────────────────────────────────────────────────────────────┐│ CONTAINER RUNTIME INTERFACE (CRI) │├─────────────────────────────────────────────────────────────┤│ ││ Kubernetes ││ │ ││ │ CRI (standard interface) ││ │ ││ ┌───────────────┼───────────────┐ ││ │ │ │ ││ ▼ ▼ ▼ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │containerd│ │ CRI-O │ │ Docker │ ││ │ │ │ │ │(via shim)│ ││ └──────────┘ └──────────┘ └──────────┘ ││ ││ containerd: Default in most K8s distributions ││ CRI-O: Lightweight, Kubernetes-focused ││ Docker: Deprecated in K8s 1.24+ ││ │└─────────────────────────────────────────────────────────────┘Для KCNA ключова думка в тому, що Kubernetes використовує CRI для спілкування із середовищами виконання контейнерів, а containerd — найпоширеніше середовище виконання в багатьох дистрибутивах Kubernetes. Старіший шлях Docker потребував dockershim, бо Docker Engine не надавав CRI у тій формі, якої потребував kubelet. Видалення dockershim прибрало зайвий прошарок інтеграції, але не прибрало Docker як інструмент збирання з ноутбуків розробників.
Життєвий цикл контейнера починається ще до того, як процес існує. Середовище виконання завантажує образ, готує кореневу файлову систему з шарів образу, налаштовує простори імен і cgroups, створює контейнер, запускає точку входу, стежить за запущеним процесом і зрештою зупиняє та видаляє його. Kubernetes додає оркестрацію на рівні Pod’а навколо цього життєвого циклу, але створення процесу лишається роботою середовища виконання на ноді.
┌─────────────────────────────────────────────────────────────┐│ CONTAINER LIFECYCLE │├─────────────────────────────────────────────────────────────┤│ ││ 1. Pull Image ││ Download image from registry ││ │ ││ ▼ ││ 2. Create Container ││ Prepare filesystem, namespaces, cgroups ││ │ ││ ▼ ││ 3. Start Container ││ Execute the container's entry point ││ │ ││ ▼ ││ 4. Running ││ Container is executing ││ │ ││ ┌─────────┴─────────┐ ││ │ │ ││ ▼ ▼ ││ 5a. Stop 5b. Crash ││ Graceful Unexpected ││ shutdown termination ││ │ │ ││ └─────────┬─────────┘ ││ │ ││ ▼ ││ 6. Remove Container ││ Clean up resources ││ │└─────────────────────────────────────────────────────────────┘Мислення в категоріях життєвого циклу запобігає поширеній помилці налагодження. Якщо завантаження образу зазнало невдачі, код застосунку ніколи не стартував, тож читати логи застосунку — хибний перший крок. Якщо контейнер запускається й одразу виходить, точка входу чи поведінка процесу ймовірніша за реєстр. Якщо процес працює, а потім перезапускається під навантаженням, кращими підозрюваними стають ресурси, проби чи примус середовища виконання.
Та сама логіка застосовується під час інспекції Pod’ів у Kubernetes. Pod, застряглий у ImagePullBackOff, указує на доступ до реєстру, ім’я образу, тег, облікові дані чи мережевий шлях. Pod у CrashLoopBackOff означає, що образ було завантажено й процес контейнера стартував, але головний процес неодноразово виходив. Pod з OOMKilled у попередньому стані вказує прямо назад на обмеження пам’яті, забезпечені cgroup.
Ось компактна послідовність інспекції середовища виконання, яку можна використати на тестовому кластері, коли присутній Pod із попереднього розділу. Мета — не запам’ятати команди; вона в тому, щоб пов’язати стан Kubernetes, який ви бачите через k, з активністю середовища виконання під kubelet.
alias k=kubectlk get pod container-fundamentals-demo -o widek describe pod container-fundamentals-demok get pod container-fundamentals-demo -o jsonpath='{.status.containerStatuses[0].imageID}{"\n"}'Якщо вивід показує ідентифікатор образу з дайджестом, Kubernetes звітує про конкретний образ, який використало середовище виконання, а не просто про дружній тег із маніфесту. Це важливо під час відкатів, бо тег можна повторно використати, тоді як дайджест ідентифікує вміст. Коли виробничі команди досліджують, чи всі репліки виконують одне й те саме збирання, ідентифікатори образів і дайджести часто надійніші за самі лише імена.
Межа середовища виконання також пояснює, чому усунення несправностей ноди Kubernetes іноді використовує інструменти, яких розробники застосунків ніколи не торкаються. Розробник може збирати за допомогою Docker і пушити в реєстр, тоді як оператор на ноді може інспектувати стан containerd за допомогою crictl чи інструментів, специфічних для середовища виконання. Обидві особи працюють з тим самим життєвим циклом контейнера, але з різних точок управління: одна створює артефакти, а інша перевіряє, як kubelet і середовище виконання перетворили ці артефакти на процеси.
Kubelet — це перекладач між наміром Kubernetes і реальністю ноди. API-сервер зберігає бажаний Pod, планувальник обирає ноду, а kubelet просить середовище виконання зробити так, щоб стан контейнера відповідав специфікації Pod’а. Якщо середовище виконання не може завантажити образ, створити контейнер чи запустити процес, kubelet звітує про цю відмову назад через стан Pod’а та події. Тому читання цих подій — не опціональне ведення записів; це нода каже вам, яка частина життєвого циклу дала збій.
У виробництві вибір середовища виконання зазвичай робить дистрибутив Kubernetes, а не кожна команда застосунку. Керований кластер може стандартизуватися на containerd, тоді як середовище OpenShift може використовувати CRI-O, і командам застосунків зазвичай не варто перейматися цим, доки їхні образи переносимі, а маніфести уникають припущень, специфічних для середовища виконання. Виграш проєктування в тому, що команди платформи можуть обирати реалізацію середовища виконання, а команди робочих навантажень спираються на контракти CRI та OCI.
Патерни та антипатерни
Розділ «Патерни та антипатерни»Навіть вступний модуль про контейнери виграє від невеликого каталогу патернів, бо виробничі відмови часто походять із повторення тієї самої простої помилки під тиском. Патерни — це не правила для кожної ситуації; це значення за замовчуванням, що роблять звичайний шлях безпечнішим. Антипатерни — це привабливі скорочення, що здаються нешкідливими під час розробки, але створюють неоднозначність, слабку ізоляцію чи невідтворювану поведінку в кластерах.
| Тип | Практика | Коли її застосовувати | Чому вона працює | Міркування щодо масштабування |
|---|---|---|---|---|
| Патерн | Закріплюйте образи явними версіями чи дайджестами | Будь-яке спільне чи виробниче середовище | Робить розгортання та відкати відтворюваними | Автоматизуйте оновлення, щоб закріплення не блокувало виправлення |
| Патерн | Свідомо встановлюйте запити та обмеження CPU й пам’яті | Робочі навантаження з передбачуваними потребами в ресурсах | Відображає намір планування та примус cgroup на поведінку навантаження | Переглядайте обмеження за реальними метриками замість копіювання значень за замовчуванням |
| Патерн | Запускайте без прав root з мінімальними можливостями | Як для сервісів, відкритих в інтернет, так і для внутрішніх | Зменшує наслідки компрометації застосунку | Поєднуйте з контролем допуску, щоб винятки були видимими |
| Антипатерн | Ставлення до контейнерів як до легких ВМ | Команди, що прямо переносять серверні звички в Kubernetes | Призводить до змінного стану, прихованих залежностей і поганого керування життєвим циклом | Перенесіть стан у томи чи сервіси та перезбирайте образи для змін |
| Антипатерн | Використання latest у виробничих маніфестах | Команди, що намагаються отримати автоматичні оновлення без процесу релізу | Робить завантаження на нодах недетермінованими та ламає докази аудиту | Використовуйте теги релізів, дайджести та автоматичне сканування вразливостей |
| Антипатерн | Ігнорування полів стану середовища виконання | Команди, що стрибають прямо від відмови Pod’а до коду застосунку | Пропускає підказки про завантаження образу, cgroup і життєвий цикл, які виводить kubelet | Стандартизуйте команди першого реагування для стану та подій Pod’а |
Патерн, що стоїть за всіма трьома добрими практиками, — це явність. Явна версія образу каже середовищу виконання, який вміст отримати, явні ресурси кажуть ядру, що примушувати, а явний контекст безпеки каже середовищу виконання, яких привілеїв процес отримувати не повинен. Kubernetes потужний, бо перетворює ці рішення на декларативний стан, але він не може вивести політику релізів, бюджет ресурсів чи межу безпеки з добрих намірів.
Антипатерни мають спільний протилежний режим відмови: вони зберігають неоднозначність. Ставлення до контейнера як до ВМ змушує людей виправляти запущені екземпляри замість перезбирання образів. Використання latest змушує дві ноди не погоджуватися щодо того, що означає «застосунок». Ігнорування стану середовища виконання змушує кожен симптом виглядати як помилка застосунку, навіть коли kubelet уже каже вам, що образ так і не завантажився або ядро вбило процес.
Масштабування цих патернів вимагає автоматизації, а не героїзму. Якщо закріплення версій залежить від того, що кожен розробник пам’ятає правильний формат тега, воно дрейфуватиме під тиском релізу. Якщо налаштування ресурсів залежить від одноразового здогаду, воно зігниє з часом, коли трафік і код змінюватимуться. Хороші платформи роблять безпечний шлях звичайним, надаючи базові образи, шаблони релізів, сканування вразливостей, перевірки політик і дашборди, що показують, коли робоче навантаження виходить за межі очікуваного діапазону ресурсів.
Тут усе одно є місце для розсуду, бо не кожне середовище має той самий профіль ризику. Одноразовий навчальний кластер може терпіти latest, контейнери з root і крихітні здогади щодо ресурсів, поки студент досліджує концепції. Спільний виробничий кластер має робити ці самі вибори складними чи неможливими. Сенс вивчення основ — у тому, щоб знати, коли скорочення є навчальною зручністю, а коли воно стало операційною небезпекою.
Каркас прийняття рішень
Розділ «Каркас прийняття рішень»Коли ви обираєте, як запакувати чи запустити робоче навантаження, почніть із називання межі, яка вам потрібна. Якщо навантаження довірене, базується на Linux і виграє від швидкого масштабування, звичайне середовище виконання контейнерів зазвичай доречне. Якщо навантаження потребує іншого ядра, суворої ізоляції орендарів чи виконання довільного недовіреного коду, у проєкті слід розглянути ВМ, ізольовані в пісочниці середовища виконання чи розділення нод, перш ніж контейнер зі спільним ядром стане значенням за замовчуванням.
┌─────────────────────────────────────────────────────────────┐│ CONTAINER DECISION FRAMEWORK │├─────────────────────────────────────────────────────────────┤│ ││ Need a different kernel or OS? ── yes ──► Use a VM path ││ │ ││ no ││ ▼ ││ Running untrusted tenant code? ── yes ──► Add sandboxing ││ │ or node isolation ││ no ││ ▼ ││ Need fast rollout and density? ── yes ──► Use containers ││ │ ││ no ││ ▼ ││ Prefer the simpler operating model that matches the risk ││ │└─────────────────────────────────────────────────────────────┘Використовуйте контейнери, коли головна проблема — це пакування залежностей, масштабування багатьох подібних процесів Linux і швидка заміна екземплярів. Використовуйте ВМ, коли головна проблема — це ізоляція ядра, різноманіття операційних систем чи сильна адміністративна межа між орендарями. Використовуйте підходи з контейнерами в пісочниці, коли команда хоче робочих процесів з контейнерами, але потребує товстішої межі, ніж та, що надають звичайні простори імен і cgroups.
Той самий каркас рішень застосовується до посилань на образи. Використовуйте рухомий тег лише для локальних експериментів, де несподіванка прийнятна. Використовуйте явний тег версії для звичайних робочих процесів розробки та підготовки, де людям потрібні читабельні назви релізів. Використовуйте дайджести чи теги плюс докази дайджесту, коли виробнича відтворюваність і придатність до аудиту важать більше за зручність.
Для усунення несправностей оберіть рівень, що відповідає симптому. Проблеми з посиланням на образ і реєстром належать до періоду перед створенням контейнера. Проблеми з простором імен, файловою системою та точкою входу з’являються під час створення чи запуску. Проблеми з cgroup і працездатністю зазвичай з’являються, поки контейнер працює. Ця послідовність економить час, бо не дає вам інспектувати логи застосунку для контейнера, який ніколи не існував.
Цей каркас також корисний під час оглядів проєктів. Коли хтось пропонує покласти три не пов’язані сервіси в один Pod, бо вони всі «маленькі контейнери», запитайте, чи справді вони мають ділити життєвий цикл, мережевий простір імен і долю планування. Коли хтось пропонує привілейований контейнер заради зручності, запитайте, який саме доступ до ядра чи хоста насправді потрібен. Коли хтось пропонує рухомий тег образу, запитайте, як команда доведе, що працювало під час інциденту.
Для екзаменаційних запитань KCNA перекладайте формулювання в рівні. Якщо запитання згадує спільне використання ядра хоста, порівнюйте контейнери з ВМ. Якщо воно згадує видимість процесів чи мереж, думайте про простори імен. Якщо воно згадує голодування за ресурсами, думайте про cgroups. Якщо воно згадує, що образи Docker досі працюють після видалення dockershim, думайте про переносимість образів OCI та інтеграцію середовища виконання CRI. Ця звичка перекладу перетворює завчені факти на модель усунення несправностей.
Чи знали ви?
Розділ «Чи знали ви?»- Контейнери старші за Docker. Unix
chrootз’явився 1979 року, FreeBSD jails прибули 2000 року, а Docker популяризував зручний для розробників робочий процес із контейнерами 2013 року. - Образи Docker не зникли з Kubernetes. Kubernetes видалив dockershim у версії 1.24, але OCI-сумісні образи, зібрані за допомогою Docker, досі працюють через containerd і CRI-O.
- OCI має дві основні специфікації для цього модуля. Специфікація образу стандартизує запакований вміст, а специфікація середовища виконання стандартизує, як набір файлової системи стає запущеним контейнером.
- Спільне ядро пояснює обмеження ОС. Контейнер Linux очікує інтерфейс ядра Linux, тож запуск контейнерів Windows на Linux вимагає рівня віртуалізації, а не звичайної ізоляції зі спільним ядром.
Типові помилки
Розділ «Типові помилки»Типові помилки зазвичай починаються як розумні скорочення в невеликому середовищі. Виправлення — не в тому, щоб соромити скорочення; воно в тому, щоб розпізнати, де скорочення перестає масштабуватися. Використовуйте цю таблицю як контрольний список для огляду, коли читаєте маніфест, налагоджуєте невдалий Pod чи пояснюєте основи контейнерів колезі.
| Помилка | Чому вона трапляється | Як її виправити |
|---|---|---|
| «Контейнери — це легкі ВМ» | Інтерфейс відчувається як крихітна машина, тож команди не помічають архітектуру зі спільним ядром. | Поясніть, що контейнери — це ізольовані процеси, що спільно використовують ядро хоста, а потім обирайте ізоляцію ВМ чи пісочниці, коли межа цього вимагає. |
| «Кожен контейнер має власну ОС» | Образи містять файли простору користувача, що виглядають як дистрибутив операційної системи. | Скажіть, що образи несуть простір користувача, бібліотеки та інструменти, тоді як ядро надає нода хоста. |
| «Docker — це і є контейнери» | Docker — найзнайоміший інструмент розробника, тож люди плутають інструмент із моделлю середовища виконання. | Відокремте Docker як інструмент збирання та робочого процесу від середовищ виконання на кшталт containerd і CRI-O. |
| «Образи й контейнери — це те саме» | Обидва слова з’являються разом у командах і дашбордах. | Сприймайте образ як шаблон лише для читання, а контейнер — як запущений екземпляр із власним записуваним шаром. |
| Запуск контейнерів від імені root за замовчуванням | Локальні приклади часто працюють без налаштування користувача чи контексту безпеки. | Налаштуйте користувача без прав root в образі чи в контексті безпеки Kubernetes та приберіть зайві привілеї. |
Використання тега latest у виробництві | Команди хочуть автоматичних оновлень без проєктування процесу релізу. | Закріплюйте версії чи дайджести, а потім використовуйте автоматизацію, щоб свідомо перезбирати та просувати виправлені образи. |
| Ігнорування порядку шарів образу | Збирання все одно успішні, тож поведінку кешу легко не помітити. | Розміщуйте стабільні кроки бази, середовища виконання та залежностей перед файлами застосунку, що часто змінюються. |
| Невстановлення обмежень ресурсів cgroup | Невеликі тестові навантаження рідко вичерпують ноду. | Визначте запити та обмеження CPU й пам’яті, а потім налаштуйте їх за спостережуваними метриками навантаження. |
Тест
Розділ «Тест»Кожне запитання нижче засноване на сценарії, бо основи контейнерів важать найбільше, коли ви можете застосувати їх під тиском. Спершу прочитайте симптом, вирішіть, який рівень найімовірніше задіяний, а потім відкрийте відповідь, щоб порівняти своє міркування з поясненням.
Ваша команда каже, що контейнери — це просто легкі ВМ, бо обидва пакують застосунки. Як ви порівняєте контейнери та віртуальні машини, і чому це розрізнення важить під час реагування на інциденти?
Контейнери та ВМ пакують навантаження за різними межами. ВМ охоплює гостьову операційну систему та ядро поверх гіпервізора, тоді як контейнер — це ізольований процес, що спільно використовує ядро хоста й несе лише потрібний йому простір користувача. Під час реагування на інциденти це розрізнення каже вам, чи досліджувати поведінку завантаження гостьової ОС та ізоляцію гіпервізора, чи поведінку просторів імен, cgroup, образу та середовища виконання на ноді. Воно також формує рішення щодо ризику, бо контейнери зі спільним ядром потребують посилення та оновлень на рівні ноди.
Pod може бачити файли, яких не повинен бачити, після того як для усунення несправностей було змонтовано шлях хоста. Яка ідея простору імен чи файлової системи допомагає діагностувати проблему?
Почніть із простору імен монтування та вигляду файлової системи контейнера. Контейнери зазвичай бачать підготовлену кореневу файлову систему, зібрану з шарів образу та монтувань, але монтування шляху хоста навмисно відкриває частину файлової системи ноди в цей вигляд. Виправлення — прибрати зайвий шлях хоста, звузити монтування до мінімально потрібної директорії чи замінити його на безпечніший патерн тома. Це насамперед не проблема cgroup, бо симптом стосується видимості, а не споживання ресурсів.
Сервіс перезапускається лише під час сплесків трафіку, а `k describe pod` показує попередній стан контейнера як OOMKilled. Яка функція Linux задіяна, і що команда має змінити?
Доречна функція — це cgroups, бо ядро примушує межу пам’яті контейнера. Перезапуск означає, що процес перевищив налаштоване обмеження пам’яті чи працював на ноді, де тиск на пам’ять виявив нереалістичне налаштування. Команді слід дослідити реальне використання пам’яті, налаштувати запити й обмеження та виправити витоки чи надмірну буферизацію в застосунку. Підняття обмежень без розуміння використання може приховати симптом, лишивши навантаження неефективним.
Розробник збирає образ за допомогою Docker, але виробничі ноди Kubernetes 1.35 використовують containerd. Чи може цей образ працювати, і який стандарт робить відповідь надійною?
Так, образ може працювати, якщо він OCI-сумісний, а звичайні зібрані Docker образи й мають таким бути. Kubernetes спілкується з containerd через CRI, а containerd може завантажувати та запускати OCI-образи з реєстрів. Docker як інструмент збирання розробника — це не те саме, що Docker Engine як шлях середовища виконання Kubernetes. Важливе розрізнення в тому, що формат образу лишається переносимим, навіть попри те, що dockershim було видалено в Kubernetes 1.24.
Виробничий Деплоймент використовує `myapp:latest`, і після заміни ноди лише одна репліка починає давати збій. Як ви оцінюєте проблему з посиланням на образ?
Змінний тег — імовірне джерело неузгодженості, бо нода-заміна могла завантажити інший вміст, ніж старіші ноди. Теги — це зручні для людей мітки, але їх можна переміщувати, тож latest не гарантує ідентичних байтів у часі. Виправлення — розгортати явні теги версій чи дайджести та записувати ідентифікатор образу, що насправді працював. Це дає відкату, аудиту та робочим процесам налагодження стабільну ціль.
Образ застосунку має шари базової ОС, середовища виконання, залежностей і коду застосунку. Команда змінює лише код застосунку, але перезбирання повільні, бо встановлення залежностей виконується щоразу. Що вам слід дослідити?
Дослідіть Dockerfile чи рецепт збирання щодо порядку шарів та інвалідації кешу. Якщо метадані залежностей чи кроки встановлення розміщені після файлів застосунку, що часто змінюються, кожне редагування коду може інвалідувати шар залежностей і змусити до дорогих перезбирань. Стабільні шари мають іти першими, тоді як код застосунку, що часто змінюється, має лежати ближче до кінця. Така розкладка також допомагає нодам повторно використовувати наявні шари, коли багато контейнерів спільно використовують ту саму базу та середовище виконання.
Аудит безпеки виявляє, що головний процес працює від імені root усередині контейнера, а розробник доводить, що просторів імен достатньо. Як вам слід відповісти?
Простори імен зменшують те, що процес може бачити, але вони не роблять root нешкідливим. Процес root усередині контейнера може все одно мати більше привілеїв, ніж потребує навантаження, а неправильне налаштування середовища виконання, ядра, можливостей чи монтування може збільшити радіус ураження від компрометації. Кращий проєкт — запускати від імені користувача без прав root, відкинути зайві можливості, уникати привілейованого режиму та явно встановлювати поля контексту безпеки Kubernetes. Цей підхід приймає, що контейнери — це корисна ізоляція, а не ідеальна межа безпеки.
Вам потрібно впровадити перший робочий процес інспекції для невдалого Pod'а, перш ніж читати код застосунку. Які підказки життєвого циклу середовища виконання слід перевірити першими?
Почніть із перевірки, чи завантажився образ, чи був створений контейнер, чи стартувала точка входу та як завершився попередній контейнер. k describe pod виводить такі події, як невдачі завантаження образу, цикли збоїв і стани OOMKilled, тоді як поля стану контейнера можуть показати ідентифікатор образу та історію перезапусків. Ця послідовність простежує життєвий цикл від реєстру до середовища виконання й до запущеного процесу. Вона не дає вам налагоджувати логіку застосунку, коли середовище виконання так і не запустило застосунок успішно.
Практична вправа
Розділ «Практична вправа»Ця вправа зберігає початковий шлях локальної інспекції контейнерів і додає рівень тлумачення, зорієнтований на Kubernetes. Ви можете використати Docker, якщо його встановлено локально, або nerdctl із containerd, якщо це ваш інструмент середовища виконання. Мета — поспостерігати за ізоляцією просторів імен, шарами образу та станом життєвого циклу, а потім пов’язати ці спостереження з тим, що Kubernetes звітує через kubelet і CRI.
Налаштування
Розділ «Налаштування»Використовуйте один термінал для локальних команд середовища виконання і, якщо у вас є тестовий кластер, інший термінал для опціональних команд Kubernetes. Наведені нижче команди Docker навмисно прості, бо навчальна ціль — це модель, а не складний застосунок. Якщо Docker недоступний, перекладіть ті самі кроки на ваш локальний робочий процес containerd із nerdctl.
Завдання 1: Запустити простий контейнер
Розділ «Завдання 1: Запустити простий контейнер»Запустіть інтерактивний контейнер Alpine Linux і зверніть увагу, що оболонка стає головним процесом усередині простору імен контейнера.
docker run -it alpine shНотатки до рішення
Команда завантажує образ Alpine, якщо потрібно, створює контейнер, запускає sh і приєднує ваш термінал до нього. Тепер ви всередині вигляду простору імен контейнера, а не всередині нової ВМ. Тримайте оболонку відкритою для наступного завдання, щоб ви могли дослідити дерево процесів з погляду контейнера.
Завдання 2: Діагностувати ізоляцію процесів за допомогою просторів імен
Розділ «Завдання 2: Діагностувати ізоляцію процесів за допомогою просторів імен»Усередині контейнера перелічіть запущені процеси та порівняйте цей короткий перелік процесів із тим, що, як ви знаєте, працює на хості.
ps auxНотатки до рішення
Оболонка зазвичай з’являється як PID 1 усередині контейнера, бо простір імен PID дає процесу приватний вигляд. Хост усе одно має власний PID для того самого процесу, а середовище виконання може керувати ним ззовні. Це найпростіший практичний доказ того, що ізоляція просторів імен змінює видимість, а не створює окреме ядро.
Завдання 3: Вийти та поспостерігати за станом життєвого циклу контейнера
Розділ «Завдання 3: Вийти та поспостерігати за станом життєвого циклу контейнера»Вийдіть з оболонки, а потім інспектуйте всі контейнери, щоб побачити, що сталося, коли головний процес завершився.
docker ps -aНотатки до рішення
Контейнер у стані Exited, бо його головний процес завершився, коли ви вийшли з оболонки. Kubernetes використовує ту саму базову ідею, коли процес контейнера виходить усередині Pod’а, хоча kubelet додає навколо неї поведінку політики перезапуску та звітування про стан Pod’а. Зупинений контейнер — це не придатний до повторного використання сервер; це свідчення життєвого циклу процесу.
Завдання 4: Дослідити шари образу
Розділ «Завдання 4: Дослідити шари образу»Завантажте образ та інспектуйте перелік шарів, щоб побачити частини файлової системи лише для читання, що утворюють образ.
docker pull nginx:latestdocker image inspect nginx:latest | grep -A 10 "Layers"Нотатки до рішення
Записи SHA256 представляють шари з адресацією за вмістом, які середовище виконання може кешувати та повторно використовувати. У виробництві ви б уникали latest, але він лишається прийнятним для цієї локальної інспекції, бо вправа стосується спостереження за шарами. Важливий урок у тому, що запущений контейнер додає записуваний шар поверх цих частин лише для читання.
Завдання 5: Пов’язати модель середовища виконання з Kubernetes
Розділ «Завдання 5: Пов’язати модель середовища виконання з Kubernetes»Якщо у вас є одноразовий тестовий кластер Kubernetes 1.35, створіть маніфест Pod’а з попереднього прикладу YAML, застосуйте його та інспектуйте стан Pod’а за допомогою короткого псевдоніма.
alias k=kubectlk apply -f pod.yamlk get podsk describe pod container-fundamentals-demoНотатки до рішення
Вивід Kubernetes має показати, чи завантажився образ, чи стартував контейнер і які події записав kubelet. Якщо Pod дає збій, класифікуйте відмову за стадією життєвого циклу, перш ніж змінювати маніфест. Проблеми з посиланням на образ, проблеми з командою запуску та проблеми з ресурсами дають різні підказки, навіть якщо всі вони можуть проявитися як несправний Pod.
Критерії успіху
Розділ «Критерії успіху»- Ви успішно запустили інтерактивний контейнер і впровадили базовий робочий процес інспекції середовища виконання.
- Ви діагностували поведінку простору імен, перевіривши, що контейнер має власне ізольоване дерево процесів із PID 1.
- Ви інспектували образ, щоб побачити його складені шари, та оцінили, чому повторне використання шарів важить.
- Ви порівняли контейнери та віртуальні машини, використовуючи межу спільного ядра, а не скорочення «легка ВМ».
- Ви пов’язали Docker, containerd чи CRI-O, CRI та стан Pod’а Kubernetes в одну модель життєвого циклу середовища виконання.
- Ви визначили принаймні один крок виробничого посилення: виконання без прав root, явні теги чи дайджести або обмеження ресурсів, підкріплені cgroup.
Джерела
Розділ «Джерела»Наведені нижче джерела — це первинні проєктні, стандартизаційні чи вендорські посилання для моделі контейнерів, описаної в цьому модулі. Доступність джерел навмисно відокремлена від локального запуску верифікатора для цього переписування, але посилання обрано так, щоб ви могли перевірити твердження за авторитетною документацією.
- Kubernetes: Container Runtimes
- Kubernetes: Images
- Kubernetes: Pods
- Kubernetes: Configure a Security Context
- Kubernetes: Resource Management for Pods and Containers
- Kubernetes: Kubernetes v1.24 Release Notes
- OCI Image Specification
- OCI Runtime Specification
- containerd Documentation
- CRI-O Project
- Docker: Use Containers to Build, Share and Run Applications
- Linux man-pages: namespaces
- Linux man-pages: cgroups
Наступний модуль
Розділ «Наступний модуль»Модуль 1.3: Архітектура Kubernetes — Площина управління знайомить із компонентами площини управління, що вирішують, де працюють контейнери, як зберігається бажаний стан і як агенти нод перетворюють намір API на запущені робочі навантаження.