Перейти до вмісту

Модуль 3.2: Логування контейнерів

Hands-On Lab Available
K8s Cluster intermediate 30 min
Launch Lab ↗

Opens in Killercoda in a new tab

Складність: [ШВИДКИЙ] — базова щоденна навичка, прості команди

Час на проходження: 25-30 хвилин

Передумови: Модуль 1.1 (Поди), базове розуміння stdout/stderr


Результати навчання

Розділ «Результати навчання»

Після завершення цього модуля ви зможете:

  • Діагностувати збої контейнерів, обираючи правильні прапорці kubectl logs для поточного, попереднього, обраного-контейнера та відібраного-за-міткою потоків логів
  • Впровадити патерни stdout/stderr і структурованого логування, які Kubernetes і колектори рівня кластера можуть захопити без зчитування приватних файлів
  • Діагностувати доступ до логів багатоконтейнерних та init-контейнерів, визначаючи імена контейнерів і обираючи один потік або всі доступні потоки
  • Оцінити, коли kubectl logs, селектори міток, експортовані файли, сайдкари та централізоване логування є правильним інструментом для питання спостережуваності

Чому цей модуль важливий

Розділ «Чому цей модуль важливий»

Гіпотетичний сценарій: ви розгортаєте невеликий сервіс оформлення замовлення, протягом хвилини розгортання виглядає зеленим і здоровим, а потім одна репліка починає перезапускатися, тоді як інша репліка все ще справно відповідає на запити користувачів. Перелік ендпоінтів (endpoints) Сервісу не пояснює вам, чому саме процес завершився, а команда kubectl describe pod може показати лише загальну, нічого не пояснювальну причину завершення. Корисною підказкою зазвичай є останнє, що застосунок устиг записати перед збоєм, а це означає, що найшвидший шлях від замішання до точного діагнозу — це часто одна влучна команда kubectl logs.

Логи контейнерів — це ваш інструмент першої реакції на інцидент, бо вони розташовані найближче до тієї поведінки застосунку, яку ви намагаєтеся пояснити. Метрики можуть сказати вам, що рівень помилок зріс, трейси можуть показати, де саме запит уповільнився, а події можуть сказати вам, що Kubernetes перезапустив контейнер, але саме логи дуже часто містять справжню причину на рівні самого застосунку. На іспиті CKAD і в щоденній операційній роботі вам потрібно вміти швидко отримувати ці логи, не припускаючи наперед, що под має лише один контейнер, не затоплюючи свій термінал тисячами старих, уже неважливих рядків і не втрачаючи попередній вивід контейнера після того, як він перезапустився.

Kubernetes навмисно не перетворює kubectl logs на надійну, повноцінну платформу логування. Він лише надає вам потоки stdout і stderr, які kubelet здатен зчитати від середовища виконання контейнерів на тій ноді, де працював контейнер. Цей дизайн навмисно зроблено вузьким: він дає вам негайний доступ до даних під час діагностики, тоді як виробниче зберігання, пошук, індексування, оповіщення та довготривалий аналіз належать уже до окремих систем логування рівня кластера. Цей модуль навчає вас саме цій межі, щоб ви могли впевнено користуватися вбудованим інструментом, водночас чітко розуміючи, коли вам потрібно щось значно потужніше.

Аналогія з бортовим самописцем

Логи контейнерів схожі на чорну скриньку літака. Вони записують усе, що застосунок повідомляє через stdout і stderr. Коли щось іде не так, ви витягуєте запис, щоб зрозуміти, що сталося. Однак, на відміну від надійної чорної скриньки, Kubernetes зберігає лише нещодавні локальні дані логів. Якщо контейнер замінюють багато разів, под витісняють або нода втрачає свої локальні файли логів, давніші записи можуть зникнути, якщо окремий бекенд логування вже не зібрав їх.

Найважливіша звичка логування в Kubernetes водночас є й найпростішою з усіх: застосунки мають записувати свої операційні повідомлення саме в stdout і stderr. Середовище виконання контейнерів захоплює ці потоки, kubelet керує файлами логів на ноді, а Kubernetes API дозволяє команді kubectl logs запитати в kubelet відповідний потік. Застосунку при цьому зовсім не потрібно знати, де саме нода зберігає файл, а вам не потрібен прямий доступ до оболонки ноди для звичайної повсякденної діагностики.

Саме це розділення пояснює, чому логування лише у файл усередині контейнера є такою пасткою. Процес може цілком собі дописувати до /var/log/app.log у своїй файловій системі, тоді як kubectl logs не показуватиме нічого корисного, бо Kubernetes не зчитує довільні файли з контейнера. Образ контейнера може містити власну бібліотеку логування, але для нативної поведінки Kubernetes бібліотека має виводити в stdout для звичайних повідомлень і в stderr для попереджень або збоїв, які мають бути розрізнені інструментами нижчого рівня.

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

Зупиніться і спрогнозуйте: якщо застосунок записує кожне повідомлення в /var/log/app.log усередині контейнера і ніколи не пише в stdout чи stderr, що ви очікуєте побачити від kubectl logs? Правильний прогноз — “нічого важливого”, навіть якщо застосунок справний, а його приватний файл логів зростає. Цей один прогноз ловить багато помилок діагностики початківців, бо відокремлює поведінку логування застосунку від доступу до логів у Kubernetes.

Локальний шлях найлегше запам’ятати як короткий ланцюжок. Ваш процес записує рядок, середовище виконання фіксує його у форматі логів Kubernetes, kubelet керує файлами логів контейнерів у каталогах ноди, таких як /var/log/containers/ і /var/log/pods/, а kubectl logs запитує в API-сервера потік від kubelet. Зазвичай ви взаємодієте лише з останнім кроком, але знання попередніх кроків пояснює і силу, і обмеження команди.

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

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

Деталь зберігання, локального для ноди, має значення і під час обслуговування кластера. Якщо ноду спорожняють (drain), под витісняють або контролер робочого навантаження замінює поди під час розгортання, зручний локальний потік може бути не приєднаний до нового об’єкта, який ви перевіряєте. Саме тому найбезпечніша жива реакція — зчитати і зберегти відповідні рядки перед виконанням руйнівних дій, які можуть замінити под.

┌─────────────────────────────────────────────────────────────┐
│ Container Logging │
├─────────────────────────────────────────────────────────────┤
│ │
│ Application │
│ │ │
│ ▼ │
│ stdout/stderr ─────────────▶ Container Runtime │
│ │ │
│ ▼ │
│ /var/log/containers/ │
│ /var/log/pods/ │
│ │ │
│ ▼ │
│ kubectl logs │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Pod: my-pod │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Container A │ │ Container B │ │ │
│ │ │ (stdout) │ │ (stdout) │ │ │
│ │ │ (stderr) │ │ (stderr) │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ kubectl logs -c a kubectl logs -c b │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

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

Читати один под, не потонувши

Розділ «Читати один под, не потонувши»

Базова команда kubectl logs отримує лише знімок поточного потоку логів контейнера на цей момент. Якщо под має рівно один звичайний контейнер, Kubernetes може самостійно вивести ім’я цього контейнера, тому простого kubectl logs pod-name цілком достатньо. Щойно ж под має більше одного звичайного контейнера, цей скорочений запис одразу стає неоднозначним, і Kubernetes просить вас явно вказати потрібний контейнер через -c, бо кожен контейнер володіє своїми власними окремими потоками stdout і stderr.

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

Terminal window
# Basic logs
kubectl logs pod-name
# Follow logs (stream)
kubectl logs -f pod-name
# Last N lines
kubectl logs --tail=100 pod-name
# Logs since timestamp
kubectl logs --since=1h pod-name
kubectl logs --since=30m pod-name
kubectl logs --since=10s pod-name
# Logs since specific time
kubectl logs --since-time=2024-01-15T10:00:00Z pod-name # replace with a recent RFC3339 time
# Show timestamps
kubectl logs --timestamps pod-name

Прапорець --tail — це ваш найбезпечніший перший крок саме тоді, коли ви ще не знаєте загального обсягу логів. Він зберігає ваш термінал чистим, уникає марнування часу на старе непотрібне балакання при запуску застосунку і дозволяє вам поступово розширити пошук, якщо в останніх рядках підказки так і не знайшлося. Прапорці --since і --since-time, своєю чергою, корисніші тоді, коли у вас уже є відоме вікно інциденту, наприклад “помилки почалися одразу після розгортання о 10:00 UTC” або “сповіщення спрацювало десь протягом останньої півгодини”.

Відносні вікна та абсолютні мітки часу відповідають на різні операційні питання. --since=30m зручний, коли ви діагностуєте одразу після сповіщення, бо він слідує за вашим поточним годинником. --since-time=2024-01-15T10:00:00Z кращий, коли ви зіставляєте з фіксованою подією, як-от початок розгортання, зміна конфігурації або мітка часу в тікеті, яку вже зафіксував інший інженер.

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

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

Стрімінг із прапорцем -f — це вже зовсім інший спосіб мислення про проблему. Замість того щоб питати “що вже сталося в минулому”, ви питаєте “що станеться далі, коли я відтворю запит або дочекаюся наступної повторної спроби системи”. Це дуже корисно під час активної діагностики, але такий режим може приховати від вас давніші рядки, які пояснюють саме початковий збій, тому завжди поєднуйте його з --tail або спочатку зробіть звичайний знімок. Якщо ж ви спостерігаєте за кількома подами одночасно, будьте особливо уважні до префіксів і селекторів, щоб точно знати, який рядок з якого джерела походить.

Багатоконтейнерні поди додають лише одне правило: назвіть потрібний вам потік. Імена контейнерів живуть у специфікації пода, тому ви можете опитати їх через JSONPath перед вибором потоку логів. --all-containers=true корисний, коли вам потрібен швидкий огляд, але він може змішувати непов’язані рядки, що робить дисципліну міток часу й префіксів важливішою у більших подах.

Terminal window
# Specify container (required for multi-container)
kubectl logs pod-name -c container-name
# All containers
kubectl logs pod-name --all-containers=true
# List containers in pod
kubectl get pod pod-name -o jsonpath='{.spec.containers[*].name}'

Зупиніться і спрогнозуйте: ви запускаєте kubectl logs my-pod і не отримуєте виводу, але застосунок точно працює й обробляє запити. Імовірні причини — не всі проблеми Kubernetes. Застосунок може логувати лише в приватний файл, корисні рядки можуть бути в контейнері-сайдкарі, або поточний контейнер міг перезапуститися після того, як цікавий вивід було записано.

Прапорець попереднього контейнера — найшвидший спосіб перевірити один нещодавній збій. Коли контейнер перезапускається, kubectl logs --previous запитує логи останнього завершеного екземпляра, а не поточного свіжого. Це саме те, що вам потрібно для CrashLoopBackOff, але це не машина часу для кожного минулого перезапуску, тому використовуйте його рано і захоплюйте докази, якщо збій усе ще розслідується.

Terminal window
# Logs from previous crashed/restarted container
kubectl logs pod-name --previous
kubectl logs pod-name -p
# Previous instance of specific container
kubectl logs pod-name -c container-name --previous

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

Селектори, Деплойменти та шум реплік

Розділ «Селектори, Деплойменти та шум реплік»

Логування одного пода є цілком керованим завданням, бо тут є лише одне ім’я для введення й один-єдиний потік для осмислення. Натомість реальні застосунки часто керуються Деплойментами, ReplicaSet, Job або іншими контролерами робочих навантажень, які створюють поди вже зі згенерованими, заздалегідь невідомими іменами. Щойно Деплоймент починає мати кілька реплік, діагностика з ручним копіюванням одного імені пода за раз стає повільною й упередженою, бо ви легко можете перевіряти справну репліку, тоді як саме несправна репліка видає той корисний рядок.

Саме селектори міток і розв’язують цю проблему групування. Якщо поди мають спільну стабільну мітку, як-от app=myapp, то команда kubectl logs -l app=myapp може зібрати логи з усіх відповідних подів, не змушуючи вас вручну перелічувати кожне окреме згенероване ім’я пода. Це особливо корисно під час екзаменаційних завдань CKAD, бо мітки вже є центральним поняттям для Сервісів, Деплойментів і відбору загалом, тож та сама концепція групування природно переноситься й у логування.

Селектор — це контракт із мітками на подах, а не з вашим наміром. Якщо розгортання внесло нове значення мітки, якщо канаркові поди використовують іншу мітку або якщо старий діагностичний под усе ще несе ту саму мітку, команда може включити неправильну сукупність. Швидкий kubectl get pods -l app=myapp --show-labels перед широкою командою логів може запобігти тому, щоб ви сприйняли змішані робочі навантаження як один сигнал.

Згенеровані імена подів — ще одна причина, чому селектори безпечніші за ручне копіювання. Деплоймент може замінювати поди, поки ви розслідуєте, а найновіше ім’я пода може бути не тим, що зазнало збою. Відбір за міткою дозволяє поставити питання на рівні робочого навантаження, тоді як читання конкретного пода з --previous дозволяє поставити питання рівня життєвого циклу про один екземпляр контейнера. Це взаємодоповнювальні масштаби, а не взаємозамінні скорочення.

Terminal window
# Logs from all pods with a label
kubectl logs -l app=myapp
# Follow logs from all matching pods
kubectl logs -l app=myapp -f
# Limit to specific number of pods
kubectl logs -l app=myapp --max-log-requests=5
# With tail
kubectl logs -l app=myapp --tail=50

Селектори також додають два операційні застереження. По-перше, селектор має відповідати тим подам, які ви маєте на увазі, а не просто імені Деплойменту, яке ви пам’ятаєте. По-друге, стеження за логами з багатьох подів використовує паралельні запити логів, а kubectl logs має обмеження, які вам, можливо, доведеться підняти через --max-log-requests для більших наборів реплік. Команда, що тихо спостерігає лише за частиною флоту, може ввести вас в оману під час масштабного інциденту.

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

Зупиніться і подумайте: под має два контейнери, app і sidecar. Ви запускаєте kubectl logs my-pod і отримуєте помилку, що потрібно вказати ім’я контейнера. Виправлення — не перестворювати под чи змінювати застосунок; виправлення — обрати -c app, обрати -c sidecar або використати --all-containers=true, коли ви навмисно хочете обидва потоки.

Terminal window
# Label + container + tail
kubectl logs -l app=myapp -c nginx --tail=100
# Label + since
kubectl logs -l app=myapp --since=30m

Селектори мають звужувати питання, перш ніж розширювати вивід. Якщо інцидент — це одна репліка зі збоєм, почніть із визначення цього пода і читання його попередніх логів. Якщо інцидент зачіпає кожну репліку після розгортання, селектор міток із --since і --timestamps дає вам швидший зріз. Команда найкорисніша, коли мітка, вікно часу й ім’я контейнера всі відповідають моделі збою, яку ви перевіряєте.

Фільтрування, експорт і структурований вивід

Розділ «Фільтрування, експорт і структурований вивід»

Команда kubectl logs навмисно зроблена невеликою, тому на практиці ви часто поєднуватимете її зі звичайними інструментами оболонки. grep може ізолювати потрібні помилки, перенаправлення виводу може зберегти докази у файл, а мітки часу можуть зробити збережений уривок корисним навіть після того, як сеанс терміналу завершиться. Це жодним чином не заміна централізованого бекенда логування, але це цілком практичні інструменти тоді, коли ви розв’язуєте екзаменаційне завдання або зберігаєте останні кілька рядків пода, що саме зараз зазнає збою.

Terminal window
# Stream with timestamps
kubectl logs -f --timestamps pod-name
# Stream only errors (grep)
kubectl logs -f pod-name | grep -i error
# Stream from multiple pods
kubectl logs -f -l app=myapp --all-containers=true

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

Фільтрування також створює проблему приватності та доказів, яку початківці часто пропускають. Команда на кшталт grep -i token може виявити, що секрет було виведено, але перенаправлення повного виводу у файл може продублювати конфіденційні дані на вашу робочу станцію. У продакшені поводьтеся з експортованими логами за тими самими правилами роботи з даними, що й з будь-яким іншим операційним артефактом, бо файл може містити тіла запитів, ідентифікатори користувачів або випадкові облікові дані.

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

Terminal window
# Save to file
kubectl logs pod-name > pod-logs.txt
# Save with timestamps
kubectl logs --timestamps pod-name > pod-logs-$(date +%s).txt
# All containers
kubectl logs pod-name --all-containers=true > all-logs.txt

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

Структуроване логування робить надійнішим і ручний, і централізований аналіз даних. Зрозумілий людині текстовий рядок може бути цілком ясним для того, хто його написав, але він водночас змушує інструменти нижчого рівня розбирати цей текст за допомогою крихких і ненадійних регулярних виразів. Натомість рядок логу у форматі JSON може послідовно й передбачувано розкрити такі поля, як мітка часу, рівень серйозності, сервіс, ідентифікатор запиту, сурогат ідентифікатора користувача або ім’я залежності, що значно допомагає колекторам на кшталт Fluent Bit, Fluentd або Loki коректно індексувати ці дані.

Неструктурований лог (важко розібрати):

2024-03-10 14:22:01 ERROR Connection failed to db-svc:5432 user=admin

Структурований лог (легко індексувати для Fluentd/Loki):

{"timestamp":"2024-03-10T14:22:01Z","level":"error","message":"Connection failed","service":"db-svc","port":5432,"user":"admin"}

Структурований приклад не є автоматично кращим лише тому, що це JSON. Він стає кращим, коли імена полів стабільні, значення безпечні для збирання, а застосунок послідовно записує одну подію на рядок у stdout або stderr. Якщо кожна команда вигадує різні ключі для того самого поняття, бекенду логування все одно доводиться нормалізувати дані, тож контракт застосунку важливий не менше за формат виводу.

Структуровані логи мають зберігати людський сенс так само, як і машинні поля. Поле message, яке каже “failed”, менш корисне за те, що каже, яка залежність зазнала збою і яку операцію було спробувано, навіть якщо обидва рядки є валідним JSON. Хороше логування дає людям достатньо контексту, щоб обрати наступну дію, і дає машинам достатньо стабільних полів, щоб групувати, фільтрувати й сповіщати без крихкого розбору тексту.

Рівні серйозності заслуговують на таку саму увагу. Якщо кожну рутинну повторну спробу логувати як error, центральні дашборди стають шумними, а grep -i error стає менш значущим під час живого інциденту. Якщо реальні збої логувати як info, сповіщення й швидкі пошуки їх пропускають. Транспорт stdout/stderr доставляє дані в Kubernetes, але застосунок усе ще володіє якістю сигналу, який він видає.

Для CKAD практичний висновок прямий. Налаштуйте застосунки й приклади так, щоб логи доходили до stdout і stderr, використовуйте kubectl logs для їх перегляду й розумійте, чому сайдкар може знадобитися для застарілого програмного забезпечення, яке не можна змінити. Для виробничої інженерії наступний крок — спроєктувати конвеєр збирання, політику зберігання, модель опитування та засоби контролю приватності, які живуть за межами kubectl logs.

Потоки багатоконтейнерних, init- і сайдкар-контейнерів

Розділ «Потоки багатоконтейнерних, init- і сайдкар-контейнерів»

Сучасні поди часто містять значно більше, ніж лише основний застосунок. Сайдкар може проксіювати трафік, публікувати метрики, синхронізувати файли або адаптувати застарілий файл логів до формату stdout. Init-контейнер може виконувати потрібну підготовчу роботу ще перед самим запуском застосунку. Кожен із цих контейнерів може мати власний окремий потік логів, а це означає, що ваше перше діагностичне рішення — це вже не лише питання “який саме под”, а й питання “який саме контейнер усередині цього пода”.

Патерн сайдкара робить межу потоку видимою. У наступному поді app і sidecar — це окремі контейнери з окремими іменами й окремими логами. Основний контейнер nginx може мати мало виводу, доки не отримає запити, тоді як сайдкар BusyBox виводить повідомлення кожні кілька секунд. Якщо ви читаєте лише один потік, ви можете пропустити поведінку, яка пояснює под.

apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
containers:
- name: app
image: nginx
- name: sidecar
image: busybox
command: ['sh', '-c', 'while true; do echo sidecar running; sleep 10; done']
Terminal window
# View main app logs
kubectl logs sidecar-demo -c app
# View sidecar logs
kubectl logs sidecar-demo -c sidecar
# All containers
kubectl logs sidecar-demo --all-containers=true

Використовуйте --all-containers=true, коли ви оглядаєте под, а потім перемикайтеся на -c, коли вам потрібен чистий потік. Режим огляду відповідає на питання “який компонент говорить”, тоді як режим обраного контейнера відповідає на питання “що саме сказав цей компонент”. Ця відмінність важлива, коли сайдкар виводить повідомлення про справність, через які под виглядає активним, тоді як основний контейнер уже зазнав збою.

Init-контейнери заслуговують на окрему, особливу увагу, бо вони працюють ще до запуску звичайних контейнерів застосунку. Якщо init-контейнер зазнає збою, то застосунок може взагалі ніколи не запуститися, тож читання логів контейнера застосунку легко може завести вас у повний глухий кут. Вам потрібне саме ім’я init-контейнера, і вам потрібно запитати цей потік напряму через прапорець -c, точно так само, як ви зробили б це для сайдкара чи будь-якого іншого названого контейнера в поді.

apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: init-setup
image: busybox
command: ['sh', '-c', 'echo Init complete']
containers:
- name: app
image: nginx
Terminal window
# View init container logs
kubectl logs init-demo -c init-setup

Є тонка різниця між “попереднім екземпляром контейнера” та “init-контейнером”. --previous обирає попередній завершений екземпляр названого контейнера після перезапуску, тоді як -c init-setup обирає конкретний потік контейнера за іменем. Ви можете поєднати вибір контейнера й попередні логи, коли названий контейнер перезапустився, але вам не варто використовувати --previous як заміну визначенню правильного контейнера.

Імена контейнерів є частиною операційного інтерфейсу пода. Імена на кшталт app, proxy, collector і init-db повідомляють намір під час діагностики, тоді як імена на кшталт container-1 роблять кожен інцидент повільнішим. Ви можете не контролювати імена під час екзаменаційного питання, але в реальних маніфестах чіткі імена зменшують шанс того, що хтось прочитає неправильний потік під тиском.

Те саме правило застосовується до власності. Проксі-сайдкар може належати команді платформи, контейнер застосунку — команді сервісу, а сайдкар-колектор логів — команді спостережуваності. Коли ви зберігаєте логи з прикріпленим іменем контейнера, наступна передача набагато чіткіша, бо докази вже вказують на компонент, який їх створив.

Сайдкари також можуть з’єднати застаріле логування лише у файл із моделлю Kubernetes. Кращим довгостроковим виправленням є змусити застосунок писати в stdout і stderr напряму, але інколи вам дістається програмне забезпечення, яке не можна швидко змінити. У такому разі сайдкар може зчитувати файл зі спільного тому й записувати кожен рядок у свій власний stdout, роблячи дані видимими для kubelet і агентів збирання, поки ви плануєте зміну застосунку.

Розбір розслідування логів

Розділ «Розбір розслідування логів»

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

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

Далі прочитайте поточні логи з коротким tail і мітками часу. Якщо контейнер нещодавно перезапустився, поточні логи можуть показати лише нову спробу запуску, що все одно корисно, бо підтверджує, що процес почався знову. Не зупиняйтеся на цьому, якщо лічильник перезапусків ненульовий. Перейдіть до --previous для того самого пода й контейнера, щоб прочитати вивід, записаний перед останнім завершенням.

Якщо под має кілька контейнерів, перелічіть імена контейнерів перед запитом логів. У поді з app, proxy і metrics проксі може повідомляти про збої вищого рівня, тоді як застосунок повідомляє про помилки розбору конфігурації. Читання неправильного потоку не просто неповне; воно може направити вас до неправильного власника й неправильного виправлення.

Якщо збій з’являється у всіх репліках, перемкніться з одного пода на селектор міток. Додайте --since, щоб відповідати вікну інциденту, --tail, щоб обмежити вивід, і --timestamps, коли вам потрібно порівняти час. Підніміть --max-log-requests, коли селектор відповідає більшій кількості подів, ніж стандартна межа паралельного стеження, особливо коли ви стрімите живу проблему.

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

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

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

Terminal window
# Essential commands
kubectl logs POD # Basic logs
kubectl logs POD -f # Follow/stream
kubectl logs POD --tail=100 # Last 100 lines
kubectl logs POD --since=1h # Last hour
kubectl logs POD -c CONTAINER # Specific container
kubectl logs POD --previous # Previous instance
kubectl logs POD --all-containers=true # All containers
kubectl logs -l app=myapp # By label
kubectl logs POD --timestamps # With timestamps

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

Патерни та антипатерни

Розділ «Патерни та антипатерни»

Патерни логування контейнерів — це невеликі рішення, які повторюються в багатьох різних сервісах. Правильний патерн зберігає корисний сигнал застосунку, тримає kubectl logs справді корисним під час негайної діагностики й залишає достатньо простору для централізованого бекенда логування, щоб той виконав уже свою надійну, довготривалу роботу. Неправильний патерн зазвичай починається як проста зручність, на кшталт “запишемо локальний файл, бо фреймворк і так уже це робить”, а пізніше перетворюється на серйозну проблему під час збою, коли ніхто не може швидко отримати потрібні докази.

ПатернКоли його використовуватиЧому він працюєМіркування щодо масштабування
Застосунок записує одну подію на рядок у stdout/stderrНові або змінювані сервісиKubelet, kubectl logs і колектори ноди бачать той самий потікСтандартизуйте імена полів, перш ніж від запитів залежатиме багато команд
--tail, --since і --timestamps для першої реакціїБудь-який шумний под або набір реплікКоманда відповідає на обмежене питання замість затоплення терміналуПоєднуйте мітки часу з мітками при порівнянні реплік
-c для названих контейнерів і --all-containers=true для режиму оглядуПоди із сайдкаром, проксі, метриками й init-контейнеромВи обираєте між чистим потоком і швидким сканом усього подаПрефікс або мітки часу стають важливими, коли потоки змішані
Сайдкар стрімить застарілий файл у stdoutПрограмне забезпечення не можна змінити негайноKubernetes може зібрати потік сайдкара без доступу до оболонки нодиСтавтеся до цього як до мосту міграції, а не стандартного дизайну

Головний антипатерн — використання kubectl logs так, ніби це надійне сховище. Він чудовий для негайного перегляду, але обмежений життєвим циклом локальних логів ноди й перезапусками контейнерів. Бекенд рівня кластера існує, бо операційні питання часто надходять після того, як под перемістився, нода ротувала файли або зникло коротке вікно попереднього контейнера.

АнтипатернЩо йде не такКраща альтернатива
Застосунок записує лише в /var/log/app.log усередині контейнераkubectl logs і колектори ноди пропускають сигнал застосункуПишіть у stdout/stderr або додайте тимчасовий стрімінговий сайдкар
Починати кожне розслідування з необмежених логівВеликий вивід приховує потрібний рядок і сповільнює оператораПочніть із --tail, --since або обох
Читати багатоконтейнерний под, не називаючи контейнерKubernetes відхиляє команду або ви перевіряєте неправильний потікПерелічіть імена контейнерів і використовуйте -c, а --all-containers=true — лише навмисно
Видаляти под, що зазнає збою, перед читанням попередніх логівЛокальні докази можуть зникнути зі старим життєвим циклом контейнераЗахопіть kubectl logs --previous перед перестворенням чи змінами розгортання

Рамка прийняття рішень

Розділ «Рамка прийняття рішень»

Обирайте свій підхід до логування, ретельно зіставляючи поставлене питання з масштабом потрібних доказів. Одному поточному поду потрібен прямий знімок, нещодавньому збою — попередній екземпляр, масштабованому Деплойменту — селектор міток, а питанню аудиту чи довгострокового тренду — уже централізоване логування. Команда є по-справжньому “правильною” лише тоді, коли вона точно відповідає життєвому циклу й масштабу тих доказів, які вам насправді потрібні.

ПитанняПерший інструментДодайте це за потребиЗупиніться й ескалюйте, коли
Що цей один контейнер робить зараз?kubectl logs pod-name-c, --tail, --timestampsПод має кілька контейнерів або вивід порожній
Що контейнер вивів перед збоєм?kubectl logs pod-name --previous-c container-nameПод перезапустився багато разів і давніших доказів немає
Чи всі репліки показують ту саму помилку?kubectl logs -l app=name --since=30m--tail, --timestamps, --max-log-requestsСелектор відповідає надто великому шуму або пропускає відомі поди
Чи потрібен мені надійний запис інциденту?Централізований бекенд логуванняОпитування за сервісом, рівнем, ідентифікатором запиту й діапазоном часуБекенду бракує полів, потрібних для надійного пошуку
Чи може Kubernetes бачити файлові логи застарілого застосунку?Зміна застосунку або стрімінговий сайдкарСпільний том плюс stdout-потік сайдкараСайдкар стає постійним операційним боргом

Для CKAD надавайте перевагу найпростішій команді, що доводить суть. Іспит винагороджує правильний вибір ресурсу й швидку діагностику, а не складні конвеєри. Якщо под має один контейнер, читайте його напряму. Якщо він має два контейнери, назвіть контейнер. Якщо він зазнав збою, використовуйте --previous. Якщо робоче навантаження має репліки, використовуйте селектор міток лише після того, як зрозумієте, яка мітка відбирає потрібні поди.

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

  • Документація з архітектури логування Kubernetes стверджує, що логування рівня кластера потребує окремого бекенда, бо Kubernetes не надає нативного рішення для зберігання даних логів.
  • Налаштування ротації логів kubelet включають containerLogMaxSize, який за замовчуванням дорівнює 10Mi, і containerLogMaxFiles, який за замовчуванням дорівнює 5 у документованій поведінці конфігурації kubelet.
  • Згенерований довідник kubectl logs перелічує --max-log-requests зі стандартним значенням 5 паралельних запитів при стеженні за логами, відібраними за міткою.
  • Нативні сайдкар-контейнери стали активними за замовчуванням у Kubernetes v1.29, а кластери Kubernetes v1.35 все ще можуть використовувати звичайні багатоконтейнерні патерни сайдкарів для процесів логування.
ПомилкаЧому це трапляєтьсяЯк це виправити
Забути -c для багатоконтейнерних подівІм’я пода здається достатньо конкретним, але Kubernetes зберігає один потік логів на контейнерЗапустіть kubectl get pod POD -o jsonpath='{.spec.containers[*].name}', потім використайте kubectl logs POD -c CONTAINER
Шукати логи з видалених подівЖиттєвий цикл локальних логів пода й контейнера коротший за терміни виробничого розслідуванняПеревірте --previous перед видаленням і покладайтеся на логування рівня кластера для надійного зберігання
Застосунок логує у файли, а не в stdoutБагато фреймворків за замовчуванням використовують файлові аппендери (file appenders), успадковані від розгортань у стилі віртуальних машинНалаштуйте логування в stdout/stderr або використайте тимчасовий сайдкар, що зчитує файл у stdout
Не використовувати --tail для великих логівПершу команду скопійовано з простого прикладу, і вона повертає забагато історіїПочніть із --tail=100, потім розширюйте вікно лише за відсутності підказки
Ігнорувати логи init-контейнераКонтейнер застосунку ніколи не запускається, тож логи застосунку порожні або нерелевантніПеревірте імена init-контейнерів і запустіть kubectl logs POD -c INIT_CONTAINER
Стежити за логами селектора без підняття меж запитівМасштабований Деплоймент може перевищити стандартну поведінку паралельного стеженняВикористовуйте --max-log-requests навмисно й переконайтеся, що селектор відповідає кожному наміченому поду
Зарано застосовувати grepФільтрування приховує контекст, що пояснює, чому з’явився рядок помилкиСпочатку прочитайте обмежений знімок із мітками часу, потім робіть grep за відомим патерном
Ставитися до kubectl logs як до довготривалого сховищаВін зручний під час інциденту й здається системою логуванняВикористовуйте централізований бекенд для зберігання, пошуку, сповіщень і аналізу після інциденту
Питання 1: Ваш под `checkout` перезапустився двічі. `kubectl logs checkout` показує лише чисте повідомлення про запуск з поточного екземпляра, але вам потрібна помилка, що спричинила останній вихід. Яку команду ви запускаєте і чому?

Запустіть команду kubectl logs checkout --previous, додавши до неї -c CONTAINER, якщо под має більше одного контейнера. Звичайна команда читає лише поточний екземпляр контейнера, який цілком міг запуститися вже після самого збою. Натомість --previous запитує безпосередньо попередній завершений екземпляр, тож це і є правильна перша реакція саме на нещодавній збій. Вона при цьому не відновить необмежену історію всіх перезапусків, і саме тому виробничі системи завжди потребують централізованого зберігання логів.

Питання 2: Розробник каже, що `kubectl logs my-pod` повертає помилку, що потрібно вказати ім'я контейнера. Под у стані Running і не має перезапусків. Якою є ймовірна форма пода і як розробнику діяти далі?

Под майже напевно має кілька звичайних контейнерів, як-от контейнер застосунку плюс сайдкар. Kubernetes не може вивести, який потік stdout чи stderr хоче розробник, тож розробник має перелічити імена контейнерів через JSONPath і повторно запустити команду з -c container-name. Якщо мета — лише швидкий огляд, --all-containers=true прийнятний, але він змішує потоки і не повинен замінювати точний відбір під час діагностики.

Питання 3: Ваша команда розгорнула вісім реплік `checkout` із міткою `app=checkout`, і помилки почалися протягом останньої півгодини. Вам потрібен обмежений огляд між репліками без затоплення терміналу. Яку форму команди вам обрати?

Використовуйте селектор міток з обмеженням часу й обмеженням рядків, наприклад kubectl logs -l app=checkout --since=30m --tail=100 --timestamps. Якщо ви стежите за логами наживо, врахуйте --max-log-requests, бо стеження на основі селектора має стандартну межу паралелізму. Селектор відповідає на питання масштабу реплік, тоді як фільтри часу й tail тримають вивід достатньо малим для осмислення. Мітки часу допомагають корелювати рядки між різними подами.

Питання 4: Застосунок записує в `/var/log/app.log` усередині контейнера, а `kubectl logs` порожній, хоча файл зростає. Що не так із патерном логування і якими є два практичні виправлення?

kubectl logs читає потоки stdout і stderr, розкриті через шлях kubelet; він не перевіряє довільні приватні файли всередині файлової системи контейнера. Бажане виправлення — налаштувати застосунок на запис структурованих подій у stdout і stderr. Якщо застосунок не можна змінити негайно, сайдкар може зчитувати спільний файл логів і записувати рядки у свій власний stdout. Сайдкар-міст слід розглядати як допоміжний засіб міграції, бо він додає ще одну рухому частину.

Питання 5: Под використовує init-контейнер на ім'я `init-setup` для підготовки конфігурації, але контейнер застосунку ніколи не запускається. Читання `kubectl logs init-demo -c app` не дає підказки. Що вам перевірити далі?

Перевірте потік init-контейнера через kubectl logs init-demo -c init-setup. Init-контейнери працюють перед звичайними контейнерами, тож збій підготовки може заблокувати застосунок від видачі будь-яких логів. Це не проблема попереднього екземпляра, якщо тільки конкретний контейнер не перезапустився; це проблема вибору контейнера. Якщо init-контейнер неодноразово зазнає збою, його вивід — перше місце для пошуку відсутньої конфігурації, збоїв DNS або помилок дозволів.

Питання 6: Під час інциденту хтось пропонує видалити под, що зазнає збою, і дочекатися, поки Деплоймент створить чисту заміну, перш ніж дивитися логи. Як ви оцінюєте цю пропозицію?

Ця пропозиція ризикує знищити найкорисніші локальні докази. Заміна Деплойментом може відновити сервіс, але попередні логи контейнера слід спочатку захопити через kubectl logs POD --previous, включно з -c, коли под має кілька контейнерів. Якщо відновлення сервісу термінове, інший інженер може захопити логи, поки триває дія розгортання. У довгостроковій перспективі команда має переконатися, що централізований бекенд уже зібрав відповідне вікно часу.

Питання 7: Вам потрібно обрати між `kubectl logs`, експортом локального файлу та опитуванням бекенда логування для проблеми, що зачіпає клієнтів, про яку повідомили вчора. Який інструмент має бути головним і чому?

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

Сценарій вправи: ви створите кілька невеликих подів, які генерують корисні, нудні й відверто несправні логи, а потім добре потренуєтеся обирати правильну команду для кожної окремої ситуації. Мета тут — зовсім не запам’ятати цілу стіну прапорців. Справжня мета — виробити стійку звичку ставити собі три питання перед кожною командою: який саме под або селектор, який саме потік контейнера й яка саме межа часу або розміру.

Використовуйте одноразовий простір імен, якщо ваше навчальне середовище це дозволяє, або запускайте ці ресурси в тимчасовому навчальному кластері. Маніфести використовують BusyBox і nginx, бо вони швидко запускаються й тримають фокус на поведінці логів, а не на коді застосунку. Прочитайте кожне завдання перед запуском команд, спрогнозуйте вивід, який ви очікуєте, а потім порівняйте фактичний вивід із цим прогнозом.

Налаштування:

Terminal window
# Create a pod that generates logs
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: log-demo
labels:
app: log-demo
spec:
containers:
- name: logger
image: busybox
command: ['sh', '-c', 'i=0; while true; do echo "$(date) - Log entry $i"; i=$((i+1)); sleep 2; done']
EOF
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod/log-demo --timeout=30s

Завдання 1 — це базова лінія. Прочитайте знімок, ненадовго постежте за потоком, зупиніть стеження через Ctrl+C, а потім запитайте лише останні кілька рядків із мітками часу. Ви маєте помітити, що мітка часу застосунку, виведена BusyBox, і мітка часу Kubernetes, додана --timestamps, пов’язані, але не є ідентичними частинами інформації.

Частина 1: Базові логи

Terminal window
# View logs
kubectl logs log-demo
# Follow logs (Ctrl+C to stop)
kubectl logs log-demo -f
# Last 5 lines
kubectl logs log-demo --tail=5
# With timestamps
kubectl logs log-demo --timestamps --tail=5
Нотатки до розв'язку Завдання 1

Перша команда має показати накопичений вивід з єдиного контейнера logger. Команда стеження має продовжувати виводити нові рядки, доки ви її не перервете. Команда --tail=5 має зменшити вивід до п’яти нещодавніх рядків, а команда з мітками часу має додати мітки часу Kubernetes перед кожним рядком. Якщо якась команда порожня, перевірте, чи под готовий і чи команда BusyBox усе ще працює.

Завдання 2 додає другий потік. Контейнер nginx може мало що виводити під час цього простого навчального завдання, тоді як сайдкар виводить регулярні повідомлення. Ця відмінність корисна, бо змушує вас відокремити “под тихий” від “контейнер, який я обрав, тихий”. Використайте команду JSONPath, щоб перелічити імена перед читанням кожного потоку.

Частина 2: Багатоконтейнерний

Terminal window
# Create multi-container pod
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: multi-log
spec:
containers:
- name: app
image: nginx
- name: sidecar
image: busybox
command: ['sh', '-c', 'while true; do echo Sidecar log; sleep 5; done']
EOF
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod/multi-log --timeout=30s
# List containers
kubectl get pod multi-log -o jsonpath='{.spec.containers[*].name}'
# View each container
kubectl logs multi-log -c app
kubectl logs multi-log -c sidecar
# All containers
kubectl logs multi-log --all-containers=true
Нотатки до розв'язку Завдання 2

Вивід JSONPath має включати app і sidecar. Читання контейнера sidecar має показати повторювані рядки “Sidecar log”, тоді як контейнер застосунку nginx може мати мало або жодного виводу, доки не обробить запити. --all-containers=true корисний для швидкого огляду, але зосереджене розслідування має повернутися до -c, щоб один потік не затуляв інший.

Завдання 3 демонструє, чому поточні логи й попередні логи відповідають на різні питання. Под завершується зі збоєм і перезапускається Kubernetes відповідно до своєї політики перезапуску пода. Ви дочекаєтеся хоча б одного перезапуску, підтвердите стан пода, а потім перевірите попередній екземпляр на рядки, виведені перед виходом.

Частина 3: Попередній екземпляр

Terminal window
# Create pod that crashes
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: crasher
spec:
containers:
- name: app
image: busybox
command: ['sh', '-c', 'echo "Starting..."; echo "About to crash!"; exit 1']
EOF
# Wait until the container has restarted at least once, then check previous logs
until [ "$(kubectl get pod crasher -o jsonpath='{.status.containerStatuses[0].restartCount}')" -ge 1 ]; do sleep 2; done
kubectl get pod crasher
kubectl logs crasher --previous
Нотатки до розв'язку Завдання 3

Після перезапуску под має показати свідчення збою або повторних запусків залежно від часу. Попередні логи мають включати “Starting…” і “About to crash!” із завершеного екземпляра. Якщо ви запустите --previous до того, як restartCount досягне 1, Kubernetes 1.35 повертає порожню відповідь або помилку; опитуйте лічильник перезапусків замість того, щоб покладатися на фіксований sleep. Це та сама звичка, що потрібна вам для сортування CrashLoopBackOff.

Завдання 4 — це очищення для ресурсів налаштування. Видалення подів наприкінці тримає подальші вправи передбачуваними й не дає старим міткам відповідати командам селектора. У реальному розслідуванні ви захопили б потрібні докази перед видаленням; у цьому навчальному завданні докази навмисно одноразові.

Очищення:

Terminal window
kubectl delete pod log-demo multi-log crasher
Нотатки до розв'язку очищення

Команда видалення має прибрати три поди, створені завданнями налаштування. Якщо один под уже видалено або він усе ще завершується, Kubernetes може повідомити про цей стан, що прийнятно в навчальному середовищі. Підтвердіть через kubectl get pods, якщо хочете чистий робочий простір перед початком вправ.

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

Вправа 1: Базові логи (Ціль: 1 хвилина)

Розділ «Вправа 1: Базові логи (Ціль: 1 хвилина)»

Ця вправа перевіряє найпростіший шлях: створити одноконтейнерний под, дочекатися готовності, прочитати логи й очистити. Важливе спостереження — тихий контейнер не є автоматично зламаною командою. nginx може не видавати багато рядків, доки не обслуговує трафік, тож ваше завдання — перевірити под і зрозуміти поведінку логування застосунку.

Terminal window
# Create pod
kubectl run drill1 --image=nginx
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod/drill1 --timeout=30s
# View logs
kubectl logs drill1
# Cleanup
kubectl delete pod drill1

Вправа 2: Стеження за логами (Ціль: 2 хвилини)

Розділ «Вправа 2: Стеження за логами (Ціль: 2 хвилини)»

Ця вправа створює контейнер, що виводить рівномірне серцебиття. Використовуйте режим стеження достатньо довго, щоб довести, що потік живий, потім перервіть його й видаліть под. Якщо ви забудете перервати потік, термінал не завис; він робить саме те, що запросив -f.

Terminal window
# Create logging pod
kubectl run drill2 --image=busybox -- sh -c 'while true; do echo tick; sleep 1; done'
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod/drill2 --timeout=30s
# Follow (Ctrl+C after a few ticks)
kubectl logs drill2 -f
# Cleanup
kubectl delete pod drill2

Вправа 3: Багатоконтейнерний (Ціль: 3 хвилини)

Розділ «Вправа 3: Багатоконтейнерний (Ціль: 3 хвилини)»

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

Terminal window
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: drill3
spec:
containers:
- name: web
image: nginx
- name: monitor
image: busybox
command: ['sh', '-c', 'while true; do echo monitoring; sleep 5; done']
EOF
# Wait for pod to be ready
kubectl wait --for=condition=Ready pod/drill3 --timeout=30s
# Get logs from each
kubectl logs drill3 -c web
kubectl logs drill3 -c monitor
# Cleanup
kubectl delete pod drill3

Вправа 4: Вибір за міткою (Ціль: 2 хвилини)

Розділ «Вправа 4: Вибір за міткою (Ціль: 2 хвилини)»

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

Terminal window
# Create multiple pods
kubectl run drill4a --image=nginx -l app=drill4
kubectl run drill4b --image=nginx -l app=drill4
# Wait for pods to be ready
kubectl wait --for=condition=Ready pod -l app=drill4 --timeout=30s
# Logs from all with label
kubectl logs -l app=drill4
# Cleanup
kubectl delete pod -l app=drill4

Вправа 5: Попередній екземпляр (Ціль: 3 хвилини)

Розділ «Вправа 5: Попередній екземпляр (Ціль: 3 хвилини)»

Ця вправа закріплює м’язову пам’ять попереднього екземпляра. Контейнер виводить рядок, ненадовго засинає й виходить зі збоєм. Опитуйте, доки restartCount не стане хоча б 1, перевірте под, а потім прочитайте попередні логи, а не поточну свіжу спробу. Якщо --previous недоступний при дуже швидкому збої, поверніться до kubectl logs drill5 (поточний), перш ніж наступний перезапуск зітре докази.

Terminal window
# Create crashing pod
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: drill5
spec:
containers:
- name: app
image: busybox
command: ['sh', '-c', 'echo "Run at $(date)"; sleep 5; exit 1']
EOF
# Wait until the container has restarted at least once
until [ "$(kubectl get pod drill5 -o jsonpath='{.status.containerStatuses[0].restartCount}')" -ge 1 ]; do sleep 2; done
kubectl get pod drill5
# After restart, get previous logs
kubectl logs drill5 --previous
# Cleanup
kubectl delete pod drill5

Вправа 6: Повний сценарій логування (Ціль: 4 хвилини)

Розділ «Вправа 6: Повний сценарій логування (Ціль: 4 хвилини)»

Ця вправа поєднує поди Деплойменту, вибір за міткою й попередні логи. Деплоймент створює дві несправні репліки, тож селектор може показати нещодавній патерн помилок між робочими навантаженнями, тоді як конкретне ім’я пода дозволяє прочитати попередній екземпляр. Використовуйте обидва, бо вони відповідають на різні питання.

Terminal window
# Create "broken" deployment
cat << 'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: drill6
spec:
replicas: 2
selector:
matchLabels:
app: drill6
template:
metadata:
labels:
app: drill6
spec:
containers:
- name: app
image: busybox
command: ['sh', '-c', 'echo "Starting app"; echo "ERROR: Database connection failed"; exit 1']
EOF
# Wait for pods to be created
sleep 5
# Find pods
kubectl get pods -l app=drill6
# Check logs from one pod
kubectl logs -l app=drill6 --tail=10
# Get previous instance logs
POD=$(kubectl get pods -l app=drill6 -o jsonpath='{.items[0].metadata.name}')
until [ "$(kubectl get pod "$POD" -o jsonpath='{.status.containerStatuses[0].restartCount}')" -ge 1 ]; do sleep 2; done
kubectl logs "$POD" --previous
# Cleanup
kubectl delete deploy drill6

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

  • Я можу читати поточні логи з одноконтейнерного пода через kubectl logs.
  • Я можу обмежити вивід логів за кількістю рядків, вікном часу й мітками часу.
  • Я можу перелічити імена контейнерів і прочитати один обраний потік із багатоконтейнерного пода.
  • Я можу отримати попередні логи з перезапущеного контейнера перед видаленням пода.
  • Я можу використати селектор міток для перегляду логів між пов’язаними подами.
  • Я можу пояснити, коли потрібен централізований бекенд логування замість покладання на локальний kubectl logs.

Перевірка для учня

Розділ «Перевірка для учня»

Якщо ви запустите --previous до того, як restartCount досягне 1, Kubernetes 1.35 повертає порожню відповідь або помилку; опитуйте лічильник перезапусків замість того, щоб покладатися на фіксований sleep.

Перш ніж рухатися далі, поясніть власними словами, чому опитування лічильника перезапусків є надійнішим за простий sleep 15, коли под перебуває у стані CrashLoopBackOff, а також у яких саме випадках ви читали б логи поточного екземпляра замість того, щоб чекати на доступність --previous.

Модуль 3.3: Діагностика в Kubernetes — усунення несправностей подів, контейнерів і проблем кластера.