Підсумковий тест Частини 3: спостережуваність та обслуговування застосунків
Складність: огляд спостережуваності рівня CKAD — від середнього до просунутого.
Орієнтовний час: 70–90 хвилин на вивчення, практику, тест та прибирання.
Передумови: завершені модулі Частини 3 про проби, логи, діагностику, моніторинг ресурсів та версії API.
Припущення щодо кластера: Kubernetes 1.35 або новіша версія, зі встановленим Metrics Server для
kubectl top.Примітка щодо команд: у цьому модулі
kвикористовується як коротка заміна дляkubectl; налаштуйте її черезalias k=kubectlперед початком.
Результати навчання
Розділ «Результати навчання»Після завершення цього підсумкового модуля ви зможете діагностувати нездорові робочі навантаження застосунків, порівнюючи статус Пода, події (Events), конфігурацію проб, поточні логи, логи попереднього контейнера та ендпоінти Сервісу.
Ви зможете спроєктувати стратегії проб, які розділяють повільний запуск, справність процесу та готовність до трафіку, не спричиняючи зайвих перезапусків і не спрямовуючи трафік до непідготовленого контейнера.
Ви зможете оцінити докази спостережуваності з kubectl describe, k logs, k top та команд виявлення API, щоб обрати наступну найціннішу дію з діагностики під екзаменаційним тиском CKAD.
Ви зможете порівняти поширені режими відмов, такі як CrashLoopBackOff, Поди у стані NotReady, порожні ендпоінти (Endpoints), відсутні метрики та маніфести зі застарілим API, а потім обґрунтувати послідовність команд, яка підтверджує або відкидає кожну гіпотезу.
Ви зможете впровадити повний діагностичний робочий процес для реалістичного інциденту із застосунком і переконатися, що виправлення справді змінило стан кластера, а не лише вміст YAML-файлу.
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Команда розгортає платіжний API за кілька хвилин до початку гарячого розпродажу, і викочування виглядає зеленим, тому що Деплоймент повідомляє про бажану кількість реплік. За кілька хвилин запити клієнтів починають завершуватися за таймаутом, у Сервісу менше ендпоінтів, ніж очікувалося, а один контейнер перезапускається настільки швидко, що його поточні логи показують лише найновіший банер завантаження. Завчена напам’ять команда не допоможе нікому, якщо оператор не може вирішити, що робити далі: describe, logs --previous, top, перевірку ендпоінтів чи зміну проби.
Це момент, коли спостережуваність CKAD перестає бути списком команд і стає діагностичним ремеслом. Kubernetes дає вам багато сигналів, але ці сигнали нерівноцінні: події є короткочасними, логи можуть належати не тому екземпляру контейнера, готовність впливає на трафік без перезапуску контейнера, а liveness може приховати проблему повільного запуску, раз за разом убиваючи процес. Здібний оператор застосунків не виконує кожну команду навмання; він будує гіпотезу, збирає найдешевші докази, а потім змінює лише те поле, яке відповідає відмові.
Частина 3 є підсумковою, бо реальні інциденти теж кумулятивні. Несправний застосунок може поєднувати хибний шлях readiness, помилку в селекторі Сервісу, обмеження пам’яті, що змушує перезапуски, та маніфест, скопійований зі старішої версії API. Кожна окрема тема з попередніх модулів корисна, але і екзамен, і робоче місце винагороджують тих, хто вміє швидко їх поєднувати. Цей модуль навчає такого поєднання: прочитати симптом, звузити межу системи, перевірити правильні докази, зробити найменше безпечне виправлення та підтвердити його через стан Kubernetes.
Ставки є практичними, а не теоретичними. Проба readiness, що вказує на хибний порт, може вилучити кожен Под із Сервісу, тоді як контейнери продовжують працювати. Проба liveness, яка спрацьовує до завершення запуску, може перетворити повільний застосунок на нескінченний CrashLoopBackOff. Відсутня команда перегляду попередніх логів може стерти єдиний корисний стек виклику з того екземпляра контейнера, який насправді впав. Це дрібні помилки конфігурації, але у проді вони стають збоями, бо трафік, перезапуски та докази рухаються незалежно одне від одного.
Основний матеріал
Розділ «Основний матеріал»1. Спостережуваність починається з меж, а не з команд
Розділ «1. Спостережуваність починається з меж, а не з команд»Перша помилка в діагностиці Kubernetes — трактувати кожен симптом як проблему Пода. Збій, помітний користувачеві, перетинає кілька меж: запит клієнта, Сервіс, ендпоінти (Endpoints) або EndpointSlices, готовність Пода, процес контейнера, файлову систему, тиск на ресурси, а іноді й версію API, що використовувалася для створення об’єкта. Найшвидший діагностичний шлях — це не найдовший список команд; це найкоротший шлях, який визначає, яка саме межа зламана.
Використовуйте цю ментальну модель, перш ніж торкатися YAML. Якщо Сервіс не має ендпоінтів, негайне питання — не чи правильно встановлено nginx. Негайне питання — чи селектор Сервісу збігається з готовими (Ready) Подами, бо тільки готові Поди, що збігаються, мають отримувати трафік. Якщо Под у стані Running, але не Ready, негайне питання — чи readiness зазнає невдачі через те, що застосунок не приймає трафік, чи через те, що проба націлена не туди. Якщо Под перезапускається, поточні логи можуть вводити в оману, бо вони показують новий екземпляр контейнера, тоді як --previous показує той, що впав.
+-------------------+ +-------------------+ +-------------------+| External symptom | ---> | Kubernetes route | ---> | Container reality || timeout or 503 | | Service endpoints | | process and logs |+-------------------+ +-------------------+ +-------------------+ | | | v v v+-------------------+ +-------------------+ +-------------------+| Check Service | | Check readiness | | Check previous || selector and port | | probe and Events | | logs and restarts |+-------------------+ +-------------------+ +-------------------+Діаграма навмисно проста, бо перший прохід через інцидент має бути простим. Ви намагаєтеся знайти зламану межу, перш ніж заглиблюватися в деталі. Коли межа — між Сервісом і Подом, перевіряйте селектори та ендпоінти. Коли межа — між kubelet і контейнером, перевіряйте проби, лічильники перезапусків, події та логи. Коли межа — між планувальником і ресурсами вузла, перевіряйте запити (requests), обмеження (limits), тиск на вузол та метрики.
Корисна звичка для CKAD — запитати: «Що мало б бути правдою, щоб з’явився цей симптом?» Сервіс без ендпоінтів вимагає або відсутності Подів, що збігаються, або наявності таких Подів, що не є Ready, або селектора, який вказує на хибні мітки. CrashLoopBackOff вимагає контейнера, який завершується, який убиває проба, або який не може успішно стартувати. Відсутній результат kubectl top вимагає або відсутнього Metrics Server, або затримки збору, або об’єкта, який наразі не видає метрик. Кожен симптом має невелику множину ймовірних причин, і ця множина визначає наступну команду.
Підказка для активного навчання: перш ніж читати далі, уявіть Под, який показує Running, але не отримує трафіку від свого Сервісу. Запишіть дві різні причини, чому таке може траплятися. Одна причина має стосуватися самого Пода, а інша — об’єкта Сервіс, що маршрутизує до нього.
2. Стратегія проб: startup, liveness та readiness виконують різні завдання
Розділ «2. Стратегія проб: startup, liveness та readiness виконують різні завдання»Проби часто подають як три схожі YAML-блоки, але їхні значення достатньо різні, щоб їх плутання спричиняло збої. Проба startup захищає повільну ініціалізацію, відкладаючи перевірки liveness та readiness, доки застосунок не матиме достатньо часу на завантаження. Проба liveness відповідає на питання: «Чи має kubelet перезапустити цей контейнер, бо він зламаний?» Проба readiness відповідає на питання: «Чи має цей Под отримувати трафік прямо зараз?» Ці питання вказують на різні ризики, тож конфігурація проб має відображати різну поведінку при відмові.
Readiness зазвичай має бути консервативнішою за liveness. Якщо застосунок тимчасово втрачає з’єднання з базою даних, може бути правильним вилучити Под з ендпоінтів Сервісу, залишивши процес живим, щоб він міг повторно під’єднатися. Якщо liveness перевіряє ту саму залежність занадто агресивно, kubelet може перезапустити процес, який відновився б самостійно. Це створює додаткове навантаження під час збою і може погіршити інцидент.
Проби startup особливо важливі для застосунків, які виконують міграції, прогрівають кеш, завантажують моделі або ініціалізують великі графи залежностей. Без проби startup liveness починається після initialDelaySeconds, і затримка, що працює на ноутбуці розробника, може зазнати невдачі на навантаженому вузлі. Проба startup дає застосунку довше вікно завантаження, водночас і далі дозволяючи liveness захищати його після успішного запуску. Це не просто зручність; це змінює семантику відмови з «перезапуск під час повільного завантаження» на «перезапуск після того, як запуск зазнав невдачі понад прийнятний поріг».
apiVersion: apps/v1kind: Deploymentmetadata: name: observed-apispec: replicas: 2 selector: matchLabels: app: observed-api template: metadata: labels: app: observed-api spec: containers: - name: api image: nginx:1.27 ports: - containerPort: 80 startupProbe: httpGet: path: / port: 80 failureThreshold: 30 periodSeconds: 10 livenessProbe: httpGet: path: / port: 80 periodSeconds: 10 timeoutSeconds: 2 readinessProbe: httpGet: path: / port: 80 periodSeconds: 5 timeoutSeconds: 2Цей Деплоймент навмисно скромний, бо структура важливіша за образ. Проба startup дозволяє до п’яти хвилин спроб запуску, перш ніж kubelet вважатиме запуск невдалим. Після успішного запуску liveness перевіряє кожні десять секунд і може перезапустити контейнер, якщо HTTP-ендпоінт перестане відповідати. Readiness перевіряє частіше, бо маршрутизація Сервісу виграє від швидшого вилучення та відновлення трафіку.
Проба, яку ви обираєте, має відповідати тій відмові, яку ви хочете доручити Kubernetes. HTTP-проби природні, коли застосунок виставляє ендпоінт справності. TCP-проби корисні, коли відкритий сокет є добрим сигналом готовності, хоча вони не можуть перевірити коректність на рівні застосунку. Exec-проби корисні, коли справність залежить від локальних файлів або команд усередині контейнера, але вони можуть бути дорогими та крихкими, якщо виконують складну shell-логіку. Проба — це частина вашого контракту площини управління з kubelet, тож тримайте її вузькою, швидкою та змістовною.
| Тип проби | Головне питання | Типова дія при відмові | Поширений ризик на CKAD |
|---|---|---|---|
| startupProbe | Чи застосунок уже завершив ініціалізацію? | Відкладає інші проби, потім перезапускає, якщо запуск так і не вдався | Її відсутність для повільних застосунків спричиняє перезапуски liveness під час завантаження |
| livenessProbe | Чи контейнер достатньо зламаний, щоб перезапуститися? | Перезапускає контейнер після досягнення порога відмов | Перевірка зовнішніх залежностей може спричинити шторм перезапусків |
| readinessProbe | Чи має цей Под отримувати трафік Сервісу? | Вилучає або додає ендпоінти Пода без перезапуску | Хибний шлях або порт робить здорові Поди недосяжними |
| exec-проба | Чи команда всередині контейнера завершується успішно? | Залежить від поля проби, що її використовує | Припущення про shell зазнають невдачі в мінімальних образах |
| httpGet-проба | Чи HTTP-ендпоінт повертає успіх? | Залежить від поля проби, що її використовує | Невідповідність шляху, іменованого порту або схеми спричиняє хибну відмову |
Сильна стратегія проб трактує перезапуски як дорогі. Перезапуск корисний, коли процес заклинило, він у взаємоблокуванні або не може відновитися. Перезапуск шкідливий, коли застосунок просто чекає на залежність, обробляє зворотний тиск (backpressure) або повільно прогрівається. Readiness є безпечнішим сигналом для тимчасової нездатності обслуговувати трафік, бо вона змінює маршрутизацію, не руйнуючи стан процесу.
Підказка для активного навчання: припустимо, API не може обслуговувати запити, поки його база даних недоступна, але автоматично під’єднується знову, коли база повертається. Куди ви помістили б перевірку бази даних: у liveness, readiness, в обидві чи в жодну? Обґрунтуйте операційні наслідки свого вибору, перш ніж порівнювати його зі сценаріями тесту далі.
3. Розбір прикладу: діагностика Пода, що працює, але ніколи не отримує трафіку
Розділ «3. Розбір прикладу: діагностика Пода, що працює, але ніколи не отримує трафіку»Цей розбір прикладу показує діагностичну послідовність, перш ніж вас попросять самостійно розв’язати схожу проблему. Сценарій поширений: Деплоймент начебто має Поди, контейнери не падають, але запити через Сервіс зазнають невдачі. Мета — уникнути стрибка прямо в контейнер, коли рівень маршрутизації може вже пояснити проблему.
Спершу створіть невелике зламане навантаження з невідповідністю між мітками шаблону Пода та селектором Сервісу. Це можна запускати в практичному просторі імен, і воно використовує лише стандартні ресурси Kubernetes. Деплоймент створює Поди з міткою app: checkout-api, тоді як Сервіс обирає app: checkout, тож Сервіс не має жодних ендпоінтів, що збігаються, навіть попри наявність Подів.
k create namespace observability-labk -n observability-lab create deployment checkout-api --image=nginx:1.27 --replicas=2k -n observability-lab label deployment checkout-api tier=frontendk -n observability-lab expose deployment checkout-api --name=checkout-svc --port=80 --target-port=80k -n observability-lab patch svc checkout-svc -p '{"spec":{"selector":{"app":"checkout"}}}'Тепер перевіримо симптом з межі Сервісу. Перша команда підтверджує селектор Сервісу, а друга перевіряє, чи знайшов Kubernetes хоч якісь готові Поди, що йому відповідають. Залежно від версії вашого кластера та налаштувань відображення, k get endpoints може показувати <none> для Сервісу, тоді як EndpointSlices надають новіше подання тих самих даних маршрутизації.
k -n observability-lab describe svc checkout-svck -n observability-lab get endpoints checkout-svck -n observability-lab get endpointslice -l kubernetes.io/service-name=checkout-svcОчікувані докази вказують геть від логів контейнера. Контейнери, ймовірно, у порядку, бо Сервіс навіть не може їх обрати. Наступна команда порівнює мітки Подів із селектором Сервісу. Це цінніший крок, ніж запуск k logs, бо межа відмови вже пролягає між Сервісом і вибором Пода.
k -n observability-lab get pods --show-labelsk -n observability-lab get svc checkout-svc -o jsonpath='{.spec.selector}{"\n"}'Прицільне виправлення змінює або селектор Сервісу, або мітки Подів. У більшості реальних середовищ вам слід розуміти власника, перш ніж патчити мітки, бо Деплойменти відтворюють Поди зі своїх шаблонів. Для цієї лабораторної роботи запатчте селектор Сервісу, щоб він відповідав мітці Деплойменту, яка вже існує. Потім переконайтеся, що ендпоінти з’явилися, замість того щоб припускати, що патч спрацював.
k -n observability-lab patch svc checkout-svc -p '{"spec":{"selector":{"app":"checkout-api"}}}'k -n observability-lab get endpoints checkout-svck -n observability-lab get endpointslice -l kubernetes.io/service-name=checkout-svcУрок у тому, що спостережуваність — це не лише зазирання всередину контейнерів. Маршрутизація Kubernetes сама по собі спостережувана, і її стан часто пояснює збій раніше, ніж логи застосунку. Сервіс не надсилає трафік усім Подам у просторі імен; він надсилає трафік готовим Подам, чиї мітки відповідають селектору, а порти — цільовому порту. Якщо селектор хибний, застосунок може бути цілком здоровим і все одно недосяжним через Сервіс.
Схожий патерн з’являється з пробами readiness. Якщо мітки збігаються, але Поди не Ready, у Сервісу все одно може не бути придатних ендпоінтів. У цьому разі наступні докази — це k describe pod, особливо секція проби readiness та події ближче до низу. Правильним виправленням може бути зміна шляху проби, виставлення правильного порту контейнера або лагодження ендпоінта справності застосунку. Метод «спочатку межа» все одно діє: доведіть, що саме відповідальне — маршрутизація, readiness чи справність процесу, перш ніж редагувати YAML.
k -n observability-lab get podsk -n observability-lab describe pod -l app=checkout-apik -n observability-lab logs -l app=checkout-api --tail=40Цей розбір прикладу також показує, чому перевірка має спостерігати стан кластера, а не лише успіх команди. Успішна команда patch означає, що API прийняв зміну. Вона не означає, що трафік тепер має ендпоінти, проби тепер проходять чи застосунок тепер відповідає коректно. Завжди супроводжуйте виправлення тим станом, який воно мало змінити.
4. CrashLoopBackOff: збережіть докази, поки вони не зникли
Розділ «4. CrashLoopBackOff: збережіть докази, поки вони не зникли»CrashLoopBackOff — це не першопричина; це Kubernetes повідомляє вам, що контейнер раз за разом стартував, а потім зупинявся. Причиною може бути виняток у застосунку, відсутній файл, хибні аргументи команди, невдала проба, проблема з образом, що проявляється до початку циклу, або тиск на ресурси. Ваше завдання — відокремити «процес завершився» від «kubelet убив процес», а потім перевірити докази з того екземпляра контейнера, який упав.
Найважливіша звичка — рано перевіряти попередні логи. Поточні логи належать контейнеру, що зараз працює або нещодавно стартував. Якщо контейнер стартує, друкує повідомлення про завантаження, падає та перезапускається, поточні логи можуть не містити стека виклику з попереднього екземпляра. Прапорець --previous просить Kubernetes показати логи з останнього завершеного екземпляра контейнера, який часто є єдиним місцем, де з’являється першопричина.
k get pod crashing-podk describe pod crashing-podk logs crashing-pod --previous --tail=80k logs crashing-pod --tail=80Використовуйте describe, щоб пов’язати логи з деталями життєвого циклу. Секція Last State може показувати коди виходу, причини та позначки часу. Події можуть показувати невдачі проб, проблеми завантаження образу, OOM-убивства, невдалі монтування та проблеми планування. Якщо події згадують невдачі проби liveness перед перезапусками, застосунок може не завершуватися сам по собі; kubelet може вбивати його, бо налаштована перевірка справності зазнає невдачі.
k describe pod crashing-pod | sed -n '/Containers:/,/Conditions:/p'k describe pod crashing-pod | sed -n '/Events:/,$p'Коли Под має більш ніж один контейнер, завжди вказуйте ім’я контейнера. Sidecar- та init-контейнери дають різні докази, і k logs pod-name може обрати усталений варіант, що не є контейнером, який падає. У контексті CKAD це часте джерело витраченого часу, бо команда завершується успішно, відповідаючи на хибне питання.
k get pod multi-app -o jsonpath='{.spec.containers[*].name}{"\n"}'k logs multi-app -c backend --previous --tail=60k logs multi-app -c frontend --tail=60Тиск на ресурси додає ще один шар. Якщо контейнер було OOMKilled, логи можуть бути неповними, бо ядро завершило процес. kubectl top допомагає визначити поточне використання CPU та пам’яті, але не замінює стан життєвого циклу, бо top показує живі метрики, а не точну причину попереднього завершення. Використовуйте обидва сигнали разом: життєвий цикл каже, що сталося, а метрики кажуть, чи умова досі присутня.
k top pod crashing-podk top pod crashing-pod --containersk describe pod crashing-pod | grep -A8 "Last State"Наведений потік прийняття рішень — це практичний спосіб уникнути блукання серед команд під час CrashLoopBackOff. Він починається з доказів, які найімовірніше зникнуть або будуть неправильно витлумачені, а потім рухається до конфігурації. Вам не потрібно запам’ятовувати діаграму; виробляйте звичку запитувати, чи контейнер упав сам, чи його вбив kubelet, чи середовище завадило йому коректно працювати.
+----------------------+| Pod is CrashLooping |+----------+-----------+ | v+----------------------+| Read previous logs || k logs --previous |+----------+-----------+ | v+----------------------+| Describe lifecycle || exit code + Events |+----------+-----------+ | v+----------------------+ +----------------------+| Probe failure shown? | -----> | Inspect probe path, |+----------+-----------+ yes | port, delay, timing | | no +----------------------+ v+----------------------+ +----------------------+| OOMKilled or limits? | -----> | Compare top metrics |+----------+-----------+ yes | with requests/limits| | no +----------------------+ v+----------------------+| Inspect command, env,|| mounts, image, args |+----------------------+Зріла діагностична позиція — змінювати одну річ за раз і перевіряти конкретний очікуваний результат. Якщо liveness занадто агресивна, збільшення initialDelaySeconds має зменшити перезапуски, пов’язані з пробами, але не обов’язково виправить виняток у застосунку. Якщо обмеження пам’яті надто низькі, зміна шляху проби не виправить OOMKilled. Якщо шлях команди хибний, метрики ресурсів можуть виглядати нормально, бо процес завершується до виконання реальної роботи. Докази звужують виправлення.
5. Логи, метрики та виявлення API під екзаменаційним тиском
Розділ «5. Логи, метрики та виявлення API під екзаменаційним тиском»Завдання CKAD часто винагороджують швидкість, але швидкість має походити з відпрацьованого добору команд, а не з вгадування. Логи відповідають на питання «що видав процес?» Метрики відповідають на питання «які ресурси об’єкти споживають зараз?» Виявлення API відповідає на питання «яку схему приймає цей кластер?» Ці інструменти доповнюють одне одного, і їхнє плутання веде до поверхневої діагностики.
Логи найсильніші для поведінки застосунку та виводу контейнера. Використовуйте --tail, щоб тримати вивід сфокусованим, -f, щоб стежити за активними логами, --previous для перезапущених контейнерів та -c для конкретного контейнера. Використовуйте селектори міток, коли питання стосується всіх реплік Деплойменту, але пам’ятайте, що поєднані логи з кількох Подів можуть перемішувати рядки й приховувати послідовність. Для точної діагностики звужуйте докази за потреби.
k logs deploy/checkout-api --tail=100k logs deploy/checkout-api --all-containers --tail=100k logs pod/checkout-api-abc123 -c api --previous --tail=80k logs -l app=checkout-api --since=10m --tail=200Метрики найсильніші для поточного використання ресурсів та відносного порівняння. Вони залежать від Metrics Server, тож помилка від k top може бути проблемою системи спостережуваності, а не проблемою застосунку. Сортування допомагає під екзаменаційним тиском, коли вам потрібен найбільший споживач CPU або пам’яті. Використовуйте --containers, коли багатоконтейнерний Под приховує, який контейнер відповідальний.
k top pods -A --sort-by=memoryk top pods -n observability-lab --sort-by=cpuk top pod checkout-api-abc123 --containersk top nodesВиявлення API запобігає помилкам зі застарілими маніфестами. Kubernetes 1.35 приймає поточні стабільні API для поширених ресурсів CKAD, таких як Ingress networking.k8s.io/v1, CronJob batch/v1 та NetworkPolicy networking.k8s.io/v1. Замість того щоб покладатися на пам’ять під час екзамену, використовуйте k explain або k api-resources, щоб підтвердити групу та версію, які обслуговує кластер.
k explain ingress | grep VERSIONk explain cronjob | grep VERSIONk explain networkpolicy | grep VERSIONk api-resources | grep -E 'ingresses|cronjobs|networkpolicies'Тонкий нюанс у тому, що команди виявлення показують вам, що знає API-сервер, а не чи коректний семантично саме ваш маніфест. CronJob може використовувати batch/v1 і все одно зазнати невдачі, бо імена полів хибні. Ingress може використовувати правильну версію API і все одно маршрутизувати неправильно, бо pathType або імена бекенд-сервісів хибні. Виявлення — це перший шлюз, а не повна історія валідації.
Корисна послідовність, перевірена тиском, така: симптом, область, докази, виправлення, перевірка. Симптом — це те, про що повідомляє користувач або завдання. Область визначає, чи межа об’єкта — Сервіс, Под, контейнер, метрики чи API маніфеста. Докази використовують найменшу команду, що перевіряє цю межу. Виправлення змінює одну доречну річ. Перевірка спостерігає стан, який має змінитися в результаті. Ця послідовність повільніша за одне вгадування, але швидша за три вгадування.
+----------+ +-------+ +----------+ +------+ +-------------+| Symptom | --> | Scope | --> | Evidence | --> | Fix | --> | Verification|+----------+ +-------+ +----------+ +------+ +-------------+| 503 | | svc | | endpoints| | label| | endpoints || restart | | pod | | previous | | probe| | restarts || high mem | | node | | top | | limit| | top + state || bad YAML | | api | | explain | | field| | apply dryrun|+----------+ +-------+ +----------+ +------+ +-------------+Підказка для активного навчання: ваша команда каже: «Деплоймент зламаний, бо Сервіс повертає 503». Вирішіть, яку команду ви запустили б першою і чому. Потім вирішіть, яку команду ви запустили б другою, якщо перша команда показала порожні ендпоінти.
6. Від огляду тесту до операційної практики
Розділ «6. Від огляду тесту до операційної практики»Цей модуль усе ще містить підсумковий тест, але тест більше не є першою навчальною подією. Ви вже оглянули діагностичну модель, семантику проб, розбір прикладу із Сервісом, докази CrashLoopBackOff та відмінність між логами, метриками та виявленням API. Сценарії тесту нижче просять вас застосувати цю модель, а не пригадати окремі команди.
Перед проходженням тесту встановіть реалістичне обмеження. Дайте собі достатньо часу на роздуми, але не дозволяйте собі безсистемно гортати кожен попередній модуль. Екзамен CKAD очікує, що ви використовуватимете документацію, але він також очікує сильного робочого процесу в командному рядку. На практиці ваші найшвидші відповіді походитимуть з розпізнавання межі відмови, а не із запам’ятовування кожного прапорця.
Якщо ви помиляєтеся у питанні, класифікуйте помилку. Проблема була в тому, що ви обрали хибну межу, забули прапорець команди, змінили хибний об’єкт чи не змогли перевірити? Ця класифікація важлива, бо виправлення для кожного випадку різне. Хибна межа вимагає більше практики зі сценаріями. Забутий прапорець вимагає повторення команд. Пропущений крок перевірки вимагає суворішої звички після кожного виправлення.
Чи знали ви?
Розділ «Чи знали ви?»-
k logs --previousпрацює лише тоді, коли Kubernetes усе ще має завершений екземпляр контейнера для читання, тож повторні перезапуски та прибирання можуть змусити ці докази зникнути.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це шкодить | Краща практика |
|---|---|---|
| Перевірка логів застосунку до перевірки того, чи має Сервіс ендпоінти | Контейнер може бути здоровим, тоді як маршрутизація зламана, тож логи марнують час і відвертають увагу від доказів селектора чи readiness | Починайте з межі, яку передбачає симптом, і рухайтеся всередину лише тоді, коли стан маршрутизації правдоподібний |
| Використання того самого ендпоінта, навантаженого залежностями, для liveness і readiness | Тимчасовий збій залежності може спричинити перезапуски, замість того щоб лише вилучити трафік з Пода | Тримайте liveness сфокусованою на справності процесу, а readiness використовуйте для здатності обслуговувати трафік |
Забування --previous під час CrashLoopBackOff | Поточні логи можуть показати лише найновіший перезапуск і пропустити екземпляр контейнера, що впав | Захоплюйте попередні логи рано, потім пов’язуйте їх із кодами виходу та подіями |
Пропуск -c у багатоконтейнерних Подах | Kubernetes може показати логи не того контейнера або попросити обрати контейнер під час браку часу | Перелічіть імена контейнерів і перевірте той, що відповідає симптому |
Трактування kubectl top як інструмента визначення першопричини | Метрики показують поточне використання, а не повну історію чи причину завершення | Поєднуйте метрики з describe, подіями, лічильниками перезапусків та причинами завершення |
| Патчинг селектора Сервісу без перевірки ендпоінтів | API може прийняти патч, який усе одно не обирає жодного готового Пода | Перевіряйте через Endpoints або EndpointSlices після кожної зміни маршрутизації |
| Копіювання старих маніфестів без виявлення API | Застарілі або вилучені версії API зазнають невдачі ще до того, як можна перевірити поведінку навантаження | Підтверджуйте поточні версії через k explain або k api-resources у цільовому кластері |
| Збільшення затримок проб без розуміння відмови | Зміни таймінгу можуть приховати помилку застосунку або подовжити збій | Спершу читайте події та логи, потім свідомо коригуйте тип проби, шлях, порт, поріг або затримку |
Тест
Розділ «Тест»Питання 1: Сервіс маршрутизує до жодного Пода
Розділ «Питання 1: Сервіс маршрутизує до жодного Пода»Ваша команда виставляє Деплоймент на ім’я orders-api через Сервіс на ім’я orders-svc. Деплоймент має два Поди у стані Running, але запити через Сервіс зазнають невдачі з помилками з’єднання. k get endpoints orders-svc не показує жодних адрес. Що слід перевірити першим і яким є прицільне виправлення, якщо селектор Сервісу не збігається з мітками Подів?
Відповідь
Почніть з межі маршрутизації, бо Сервіс не має ендпоінтів. Перевірте селектор Сервісу та мітки Подів, потім запатчте або селектор Сервісу, або мітки навантаження, щоб вони збігалися згідно з бажаною моделлю власності.
k describe svc orders-svck get pods --show-labelsk get svc orders-svc -o jsonpath='{.spec.selector}{"\n"}'
# Приклад прицільного виправлення, коли Поди мають мітку app=orders-api:k patch svc orders-svc -p '{"spec":{"selector":{"app":"orders-api"}}}'
# Перевірте стан, який має змінитися:k get endpoints orders-svck get endpointslice -l kubernetes.io/service-name=orders-svcКлючова логіка в тому, що логи не є першими доказами, коли Сервіс уже доводить, що не має ендпоінтів. Застосунок може бути здоровим і все одно недосяжним, коли мітки або readiness заважають реєстрації ендпоінтів.
Питання 2: Под у стані Running не стає Ready
Розділ «Питання 2: Под у стані Running не стає Ready»Под на ім’я catalog-0 у стані Running, але ніколи не стає Ready. Проба readiness налаштована як HTTP GET на /ready порт 8080, але застосунок слухає порт 8000. Які команди ви використали б, щоб підтвердити невдачу проби, і як ви виправили б конфігурацію?
Відповідь
Підтвердіть невдачу readiness через describe, потім перевірте порт контейнера або поведінку застосунку, щоб підтвердити невідповідність. Виправте шаблон Пода в контролері, що володіє Подом, бо редагування живого Пода напряму зазвичай недовговічне.
k describe pod catalog-0 | sed -n '/Readiness:/,/Environment:/p'k describe pod catalog-0 | sed -n '/Events:/,$p'k get pod catalog-0 -o jsonpath='{.spec.containers[*].ports}{"\n"}'
# Якщо цей Под належить Деплойменту на ім'я catalog:k patch deployment catalog --type='json' \ -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/port","value":8000}]'
k rollout status deployment catalogk get pods -l app=catalogk get endpoints catalogВиправлення слід перевіряти через readiness та ендпоінти, а не лише через успіх патча. Готовий Под має з’явитися у відповідних ендпоінтах Сервісу, якщо мітки та порти також збігаються.
Питання 3: Повільний запуск спричиняє цикл перезапусків
Розділ «Питання 3: Повільний запуск спричиняє цикл перезапусків»Java-застосунок ініціалізується три хвилини на навантаженому вузлі. Деплоймент має пробу liveness з initialDelaySeconds: 20, periodSeconds: 10 та failureThreshold: 3. Поди потрапляють у CrashLoopBackOff, хоча застосунок працює локально. Як би ви переробили проби, щоб Kubernetes не вбивав застосунок під час нормального запуску?
Відповідь
Додайте пробу startup, що дає достатньо часу на ініціалізацію, потім збережіть liveness для справності процесу після запуску, а readiness — для обслуговування трафіку. Проба startup вимикає liveness і readiness, доки запуск не вдасться, що запобігає перетворенню нормального повільного завантаження на цикл перезапусків.
k patch deployment java-api --type='json' -p='[ { "op": "add", "path": "/spec/template/spec/containers/0/startupProbe", "value": { "httpGet": { "path": "/healthz", "port": 8080 }, "periodSeconds": 10, "failureThreshold": 30 } }, { "op": "replace", "path": "/spec/template/spec/containers/0/livenessProbe/httpGet/path", "value": "/healthz" }, { "op": "replace", "path": "/spec/template/spec/containers/0/readinessProbe/httpGet/path", "value": "/ready" }]'
k rollout status deployment java-apik describe pod -l app=java-api | sed -n '/Startup:/,/Environment:/p'Важливий проєктний вибір — не просто збільшення затримки liveness. Проба startup моделює окрему фазу запуску, тоді як readiness усе одно може тримати Под поза трафіком, доки він справді не зможе обслуговувати.
Питання 4: CrashLoopBackOff із відсутнім стеком виклику
Розділ «Питання 4: CrashLoopBackOff із відсутнім стеком виклику»Под на ім’я billing-worker у стані CrashLoopBackOff. Запуск k logs billing-worker показує лише найновіше повідомлення про запуск, а корисний виняток не видно. Які докази вам слід зібрати далі і як ви вирішуєте, чи процес завершився сам, чи його вбив kubelet?
Відповідь
Зберіть попередні логи та деталі життєвого циклу, перш ніж докази буде замінено наступним перезапуском. Використайте describe, щоб перевірити завершений стан, код виходу, причину та події, потім порівняйте це з логами.
k logs billing-worker --previous --tail=100k describe pod billing-worker | sed -n '/Last State:/,/Ready:/p'k describe pod billing-worker | sed -n '/Events:/,$p'Якщо попередні логи показують виняток застосунку, а Last State показує ненульовий код виходу, процес, імовірно, завершився сам. Якщо події показують повторні невдачі проби liveness, після яких ідуть перезапуски, kubelet, імовірно, убив контейнер, бо проба зазнала невдачі. Якщо причина — OOMKilled, тиск на ресурси є центральним, а поточні логи можуть бути неповними.
Питання 5: Багатоконтейнерний Под приховує контейнер, що падає
Розділ «Питання 5: Багатоконтейнерний Под приховує контейнер, що падає»Под на ім’я web-stack має контейнери з іменами frontend, backend та log-agent. Под раз за разом перезапускається, але логи frontend виглядають нормально. Як ви визначаєте, який контейнер падає, і отримуєте відповідні логи?
Відповідь
Перелічіть імена контейнерів і перевірте статуси контейнерів з describe або JSON-виводу, потім запросіть логи для контейнера з перезапусками або завершеним попереднім станом. Не покладайтеся на усталений вибір контейнера.
k get pod web-stack -o jsonpath='{.spec.containers[*].name}{"\n"}'k describe pod web-stack | sed -n '/Containers:/,/Conditions:/p'
k logs web-stack -c backend --previous --tail=100k logs web-stack -c backend --tail=100Якщо backend показує перезапуски, а попередні логи містять виняток, це і є докази для виправлення. Логи frontend можуть бути коректними і все одно не стосуватися справи, бо симптом на рівні Пода може спричинити будь-який контейнер у Поді.
Питання 6: Високе споживання пам’яті під час інциденту
Розділ «Питання 6: Високе споживання пам’яті під час інциденту»Деплоймент API відповідає повільно. Підозрюють, що один Под використовує набагато більше пам’яті, ніж інші, але він ще не перезапускався. Які команди допомагають порівняти використання в межах простору імен, а потім перевірити використання на рівні контейнера для підозрілого Пода?
Відповідь
Використайте kubectl top для порівняння поточних ресурсів, відсортованого за пам’яттю, потім перевірте підозрілий Под за контейнерами, якщо він має більш ніж один контейнер.
k top pods -n production --sort-by=memoryk top pod suspicious-pod -n production --containersk describe pod suspicious-pod -n production | sed -n '/Limits:/,/Requests:/p'Метрики допомагають визначити живий патерн ресурсів, але не доводять історичної причини попереднього перезапуску. Якщо Под пізніше перезапуститься, поєднайте це з describe та попередніми логами, щоб перевірити наявність OOMKilled або відмови на рівні застосунку.
Питання 7: Маніфест використовує застарілий API
Розділ «Питання 7: Маніфест використовує застарілий API»Колега дає вам маніфест Ingress, що починається з apiVersion: extensions/v1beta1, і k apply зазнає невдачі на кластері Kubernetes 1.35. Як ви підтверджуєте підтримувану версію API і уникаєте вгадування форми заміни?
Відповідь
Використайте виявлення API проти цільового кластера, потім перевірте схему ресурсу. Для Ingress у сучасному Kubernetes стабільний API — це networking.k8s.io/v1, і маніфест має використовувати поточну структуру полів.
k explain ingress | grep VERSIONk explain ingress.speck api-resources | grep ingressКоректне виправлення змінює більше, ніж рядок версії, якщо старий маніфест використовував застарілі поля. Наприклад, pathType є обов’язковим у шляхах сучасного Ingress, а посилання на бекенд-сервіс використовують поточну структуру service.name та service.port.
Питання 8: Виправлення проби без перевірки
Розділ «Питання 8: Виправлення проби без перевірки»Ви патчите Деплоймент, щоб змінити його шлях readiness з /health на /ready. Команда завершується успішно, тож колега каже, що інцидент виправлено. Яку послідовність перевірки ви запустили б, перш ніж погодитися?
Відповідь
Перевірте викочування, готовність Подів, події та ендпоінти Сервісу, бо саме ці стани має змінити виправлення readiness. Успіх патча лише доводить, що API прийняв зміну.
k rollout status deployment checkout-apik get pods -l app=checkout-apik describe pod -l app=checkout-api | sed -n '/Readiness:/,/Environment:/p'k describe pod -l app=checkout-api | sed -n '/Events:/,$p'k get endpoints checkout-svcІнцидент виправлено лише тоді, коли нові Поди стають Ready, а Сервіс має очікувані ендпоінти. Якщо readiness усе ще зазнає невдачі, зміна шляху не усунула справжню межу або залишається інша проблема.
Практична вправа
Розділ «Практична вправа»У цій вправі ви побудуєте та полагодите невеликий інцидент спостережуваності у практичному просторі імен. Мета — не запам’ятати точний YAML; мета — відпрацювати рух від симптому до межі, від межі до доказів і від доказів до найменшого корисного виправлення. Виконуйте це в одноразовому кластері чи просторі імен, бо вправа навмисно створює зламану маршрутизацію та поведінку проб.
Налаштування вправи
Розділ «Налаштування вправи»Створіть простір імен і розгорніть невеликий застосунок із двома навмисними проблемами. Селектор Сервісу не збігатиметься з мітками Подів, а проба readiness вказуватиме на хибний порт. Ці дві відмови дозволяють відпрацювати розділення маршрутизації Сервісу та готовності Пода.
k create namespace part3-observabilitycat << 'EOF' | k apply -n part3-observability -f -apiVersion: apps/v1kind: Deploymentmetadata: name: inventory-apispec: replicas: 2 selector: matchLabels: app: inventory-api template: metadata: labels: app: inventory-api tier: backend spec: containers: - name: nginx image: nginx:1.27 ports: - containerPort: 80 readinessProbe: httpGet: path: / port: 8080 periodSeconds: 5 timeoutSeconds: 2 livenessProbe: httpGet: path: / port: 80 periodSeconds: 10 timeoutSeconds: 2EOFcat << 'EOF' | k apply -n part3-observability -f -apiVersion: v1kind: Servicemetadata: name: inventory-svcspec: selector: app: inventory ports: - port: 80 targetPort: 80EOFКрок 1: Спостерігайте межу маршрутизації
Розділ «Крок 1: Спостерігайте межу маршрутизації»Почніть із Сервісу, бо симптом, помітний користувачеві, полягає в тому, що трафік через Сервіс не працює. Перевірте селектор Сервісу, мітки Подів, ендпоінти (Endpoints) та EndpointSlices. Не змінюйте нічого, доки не зможете пояснити, чи має Сервіс Поди, що збігаються.
k -n part3-observability describe svc inventory-svck -n part3-observability get pods --show-labelsk -n part3-observability get endpoints inventory-svck -n part3-observability get endpointslice -l kubernetes.io/service-name=inventory-svcОчікувана логіка: селектор Сервісу використовує app: inventory, тоді як Поди використовують app: inventory-api. Навіть якби Поди були Ready, цей Сервіс не обрав би їх. Це означає, що перше виправлення має стосуватися міток або логіки селектора, перш ніж ви витратите час на логи.
Крок 2: Виправте селектор Сервісу та перевірте новий симптом
Розділ «Крок 2: Виправте селектор Сервісу та перевірте новий симптом»Запатчте селектор Сервісу, щоб він збігався з Подами. Потім знову перевірте ендпоінти. Ви все ще можете не побачити готових адрес, якщо readiness зазнає невдачі, що корисно, бо це розкриває наступну межу.
k -n part3-observability patch svc inventory-svc -p '{"spec":{"selector":{"app":"inventory-api"}}}'k -n part3-observability get endpoints inventory-svck -n part3-observability get podsЯкщо ендпоінти все ще порожні або непридатні, не скасовуйте виправлення селектора. Проблема селектора була справжньою, але вона була не єдиною проблемою. Багатопричинні інциденти поширені, і дисципліна полягає в тому, щоб зберігати підтверджені виправлення, продовжуючи перевіряти наступну межу, що зазнає невдачі.
Крок 3: Перевірте докази readiness
Розділ «Крок 3: Перевірте докази readiness»Тепер перевірте один із Подів і прочитайте секцію проби readiness та події. Проба readiness вказує на порт 8080, але nginx слухає порт 80 у цьому маніфесті. Це саме та помилка, що залишає контейнери працювати, тримаючи їх поза трафіком Сервісу.
POD_NAME="$(k -n part3-observability get pod -l app=inventory-api -o jsonpath='{.items[0].metadata.name}')"k -n part3-observability describe pod "$POD_NAME" | sed -n '/Readiness:/,/Environment:/p'k -n part3-observability describe pod "$POD_NAME" | sed -n '/Events:/,$p'Очікувана логіка: readiness — це шлюз трафіку, тож хибний порт readiness пояснює, чому Поди не стають Ready. Liveness використовує порт 80, тож контейнери можуть уникати перезапусків, усе одно не проходячи readiness. Ця відмінність — навчальний момент: liveness і readiness не мають однакового операційного значення.
Крок 4: Виправте пробу readiness у шаблоні Деплойменту
Розділ «Крок 4: Виправте пробу readiness у шаблоні Деплойменту»Запатчте шаблон Деплойменту, щоб нові Поди використовували правильний порт readiness. Оскільки Поди, керовані Деплойментами, відтворюються з шаблону, зміна лише живого Пода була б недовговічною. Після патча дочекайтеся викочування та перевірте готовність Подів.
k -n part3-observability patch deployment inventory-api --type='json' \ -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/port","value":80}]'
k -n part3-observability rollout status deployment inventory-apik -n part3-observability get pods -l app=inventory-apik -n part3-observability get endpoints inventory-svcОчікувана логіка: виправлення має дати готові Поди та ендпоінти Сервісу. Якщо викочування завершується, але ендпоінти залишаються порожніми, поверніться до селектора, міток, подій readiness та конфігурації порту. Стан перевірки має відповідати очікуваному ефекту виправлення.
Крок 5: Додайте тренування зі збору доказів CrashLoopBackOff
Розділ «Крок 5: Додайте тренування зі збору доказів CrashLoopBackOff»Створіть Под, який негайно завершується, щоб ви могли відпрацювати попередні логи та перевірку життєвого циклу. Це контрольована відмова, а не застосунок, який ви намагаєтеся утримати робочим. Важлива поведінка в тому, що поточний стан змінюється, тоді як логи попереднього контейнера зберігають повідомлення про відмову.
cat << 'EOF' | k apply -n part3-observability -f -apiVersion: v1kind: Podmetadata: name: failing-workerspec: restartPolicy: Always containers: - name: worker image: busybox:1.36 command: - sh - -c - echo "starting worker"; echo "fatal: missing required queue name"; exit 2EOFЗачекайте трохи, потім перевірте статус Пода, попередні логи та стан життєвого циклу. Якщо перша спроба --previous зарання, зачекайте кілька секунд і запустіть її знову. Мета — упіймати завершений екземпляр, а не лише найновіший запуск.
k -n part3-observability get pod failing-workerk -n part3-observability logs failing-worker --previous --tail=20k -n part3-observability describe pod failing-worker | sed -n '/Last State:/,/Ready:/p'k -n part3-observability describe pod failing-worker | sed -n '/Events:/,$p'Очікувана логіка: попередні логи показують відмову на рівні застосунку, а секція життєвого циклу показує ненульовий код виходу. Це відрізняється від убивства пробою liveness або OOM-убивства, тож наступне виправлення цілилося б в аргументи команди, середовище або конфігурацію застосунку, а не в таймінг проб.
Крок 6: Перевірте метрики та виявлення API
Розділ «Крок 6: Перевірте метрики та виявлення API»Якщо ваш кластер має Metrics Server, порівняйте використання ресурсів у просторі імен. Якщо ні, занотуйте відмову як обмеження системи спостережуваності, а не діагноз застосунку. Потім підтвердіть версії API для поширених ресурсів за допомогою команд виявлення.
k top pods -n part3-observability --sort-by=memoryk top pod -n part3-observability --containersk explain ingress | grep VERSIONk explain cronjob | grep VERSIONk explain networkpolicy | grep VERSIONОчікувана логіка: метрики корисні лише тоді, коли конвеєр метрик доступний і актуальний. Виявлення API корисне для підтвердження того, що приймає цільовий кластер. Жодне з них не замінює describe, події, логи, ендпоінти чи перевірку викочування; вони відповідають на різні питання.
Критерії успіху
Розділ «Критерії успіху»- Ви можете пояснити, чому початковий Сервіс не мав ендпоінтів, перш ніж перевіряти логи застосунку.
- Ви запатчили селектор Сервісу та перевірили стан ендпоінтів після патча.
- Ви визначили невідповідність порту проби readiness із виводу
describeта подій. - Ви запатчили шаблон Деплойменту, а не трактували керований Под як довговічне джерело істини.
- Ви перевірили, що Поди стали Ready і що Сервіс отримав ендпоінти після виправлення readiness.
- Ви захопили попередні логи з навмисно несправного Пода та пов’язали їх із кодом виходу.
- Ви належно використали
kubectl topабо розпізнали, що Metrics Server недоступний. - Ви підтвердили принаймні одну поточну версію API за допомогою
k explainабоk api-resources. - Ви можете описати різницю між виправленням маршрутизації, виправленням readiness та виправленням процесу, що падає.
Прибирання
Розділ «Прибирання»Видаліть практичний простір імен після завершення вправи. Видалення простору імен прибирає Деплоймент, Сервіс, Поди та навмисно несправний worker, створений цією лабораторною роботою.
k delete namespace part3-observabilityНаступний модуль
Розділ «Наступний модуль»Частина 4: середовище застосунку, конфігурація та безпека — перейдіть від спостереження за робочими застосунками до конфігурування даних середовища, Secret’ів та налаштувань безпеки, що формують поведінку застосунків у кластері.
Джерела
Розділ «Джерела»- training.linuxfoundation.org: certified kubernetes application developer ckad — Офіційна сторінка CKAD називає Kubernetes v1.35 і перелічує ці компетенції зі спостережуваності та обслуговування.
- kubernetes.io: kubectl top — Довідник kubectl прямо вказує, що Metrics Server має бути встановлений і запущений, щоб
kubectl topпрацював. - kubernetes.io: glossary — Стаття глосарія Kubernetes про Event явно описує обмежене утримання та найкращі-зусилля, допоміжну семантику.
- kubernetes.io: liveness readiness startup probes — Документація про проби вказує, що невдала readiness не перезапускає контейнер і встановлює умову готовності Пода у false.
- kubernetes.io: endpoint slices — Документація про EndpointSlice пояснює, що Сервіси із селекторами відстежують Поди, що збігаються, і що готовність ендпоінта відповідає готовності Пода.
- kubernetes.io: pod lifecycle — Документація про життєвий цикл Пода визначає CrashLoopBackOff як стан затримки для контейнера, який раз за разом падає та перезапускається.
- kubernetes.io: kubectl logs — Довідник kubectl logs явно визначає
--previousяк друк логів попереднього екземпляра контейнера, якщо він існує. - v1-35.docs.kubernetes.io: v1.35 — Довідник API Kubernetes v1.35 перелічує ці ресурси під відповідними обслуговуваними парами група/версія API.
- v1-35.docs.kubernetes.io: deprecation guide — Посібник з виведення з обігу v1.35 документує вилучення старих API Ingress та обов’язкові зміни форми полів для
networking.k8s.io/v1. - kubernetes.io: pod condition — Документація про умови Пода вказує, що Поди, які не є Ready, вилучаються з ендпоінтів Сервісу, а документація про проби визначає поведінку при невдачі readiness.
- kubernetes.io: index.html — Документація про Сервіс прямо підтримує первинність EndpointSlice та обмеження застарілих Endpoints; настанова про швидку перевірку є практичною порадою, а не точним твердженням документації.
- Debug Services — Вона проходить через діагностику селектора, порту та EndpointSlice у тому самому стилі «спочатку межа», якого навчає цей модуль.