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

Модуль 1.2: Job'и та CronJob'и

Складність: [СЕРЕДНЯ] — невіддільна навичка CKAD з конкретними виробничими компромісами

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

Передумови: Модуль 1.1 (Образи контейнерів), базове знання життєвого циклу Pod’а та робочий кластер Kubernetes 1.35+

Приклади в цьому модулі використовують стандартний для CKAD псевдонім alias k=kubectl. Створіть його у своїй оболонці перед практикою, щоб команди збігалися з екзаменаційним стилем роботи, а наведені нижче приклади діагностики працювали як належить.

Terminal window
alias k=kubectl

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

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

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

  • Спроєктувати Job’и та CronJob’и з правильними лічильниками завершень, паралелізмом, обмеженнями повторних спроб, контролем очищення та розкладами.
  • Налаштувати конкурентність, дедлайн, призупинення та поведінку історії CronJob’а для періодичних пакетних навантажень.
  • Діагностувати збійні пакетні навантаження, зіставляючи статус Job’а, події Pod’а, логи контейнера, політику перезапуску та поведінку повторних спроб контролера.
  • Порівняти Job’и, CronJob’и, Деплойменти та одноразові Pod’и, щоб обирати правильний контролер для одноразової, періодичної та довготривалої роботи.

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

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

Гіпотетичний сценарій: операція технічного обслуговування виробничої бази даних іде геть погано і залишає команду відновлювати дані під сильним тиском з боку клієнтів. Урок прямо стосується теми: операційна робота, яка здається “просто скриптом”, може перетворитися на бізнес-інцидент, коли повторні спроби, планування, відповідальність та очищення нечіткі. Завдання резервного копіювання, міграції, звіту чи очищення не стає безпечнішим лише тому, що воно короткочасне; воно безпечне лише тоді, коли платформа знає, коли воно має початися, коли має зупинитися, скільки разів може повторитися та які докази має залишити для діагностики.

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

Для роботи з CKAD Job’и та CronJob’и є цінними, бо вони поєднують знання API із судженням про діагностику. Вас можуть попросити створити Job імперативно, згенерувати YAML для CronJob’а, змінити parallelism, виправити недійсний restartPolicy або пояснити, чому запланована резервна копія не запустилася. Цей модуль навчає механіки, але він також навчає операційної форми: кожне пакетне навантаження має правило старту, правило завершення, правило повторної спроби, правило очищення та шлях розслідування збоїв.

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

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

Аналогія заводської зміни

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

Job’и: контролери завершення для одноразової роботи

Розділ «Job’и: контролери завершення для одноразової роботи»

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

Найпростіший Job запускає один Pod і потребує одного успішного завершення. Це патерн, який ви використовуєте для невеликих міграцій, оглядових звітів та екзаменаційних завдань, де команда виконує одну ділянку роботи і завершується. Імперативна команда швидка для завдань CKAD, а форма з пробним запуском (dry-run) корисна, коли потрібно додати поля, які k create job не надає напряму.

Terminal window
# Simple job
k create job backup --image=busybox -- echo "Backup complete"
# Job with a shell command
k create job report --image=busybox -- /bin/sh -c "date; echo 'Report generated'"
# Generate YAML
k create job backup --image=busybox --dry-run=client -o yaml -- echo "done" > job.yaml

Коли ви перетворюєте цю команду на YAML, важлива не сама лише рядок kind: Job. Важливим є вкладений шаблон Pod’а та елементи керування на рівні Job’а навколо нього. Шаблон визначає, яка робота має виконуватися; backoffLimit, activeDeadlineSeconds, completions, parallelism та ttlSecondsAfterFinished визначають, як контролер має поводитися, коли робота вдається, зазнає невдачі, триває надто довго або завершується і потребує очищення.

apiVersion: batch/v1
kind: Job
metadata:
name: backup-job
spec:
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo 'Backing up data' && sleep 10"]
restartPolicy: Never # or OnFailure
backoffLimit: 4 # Retry attempts
ttlSecondsAfterFinished: 100 # Auto-cleanup
ВластивістьПризначенняЗа замовчуванням
restartPolicyЩо робити при збоїМає бути Never або OnFailure
backoffLimitМаксимум повторних спроб6
activeDeadlineSecondsМаксимальний час виконання Job’аНемає (виконується безкінечно)
ttlSecondsAfterFinishedАвтовидалення після завершенняНемає (зберігати назавжди)
completionsПотрібна кількість успішних завершень1
parallelismМаксимум паралельних Pod’ів1

Правило restartPolicy — один з найлегших способів виявити підроблений маніфест Job’а. Job’и можуть використовувати Never або OnFailure; вони не можуть використовувати Always, бо Pod, що завжди перезапускається, ніколи не зможе природно виразити успішне завершення. З Never збійний контейнер залишає збійний Pod, і контролер Job’а створює інший Pod, якщо повторні спроби ще лишилися. З OnFailure kubelet перезапускає контейнер усередині того самого Pod’а, що може бути дешевшим, але іноді приховує історію, яку ви хотіли оглянути.

Компроміс між Never та OnFailure частково стосується доказів. Never зазвичай залишає чіткіший слід, бо кожен збійний Pod має власні події, статус та логи, що допомагає під час навчання або коли збій змінюється між спробами. OnFailure може бути кращим для коротких тимчасових збоїв, бо kubelet може перезапустити контейнер, не змушуючи контролер Job’а створювати Pod на заміну. Жодна політика не виправляє зламану команду; кожна лише змінює, де відбуваються повторні спроби та які докази залишаються.

Зробіть паузу і передбачте: Job вимагає, щоб restartPolicy був встановлений у Never або OnFailure. Чому не можна використати Always — типовий стиль, який ви можете асоціювати з довготривалими контролерами? Подумайте, що Job має довести, перш ніж читати пояснення в наступному абзаці.

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

# Never: Don't restart failed containers (create new pod)
restartPolicy: Never
# Pod fails -> New pod created (up to backoffLimit)
# OnFailure: Restart failed container in same pod
restartPolicy: OnFailure
# Container fails -> Same pod restarts container

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

Саме тут багато тих, хто навчається, випадково переоцінюють Kubernetes. Контролер Job’а може рахувати успішні Pod’и, але він не ділить ваші вхідні дані на безпечні одиниці автоматично. Якщо десять Pod’ів усі запускають process-all-files.sh, ви можете обробити ті самі файли десять разів, якщо тільки скрипт, черга або проєкт індексованих завершень цьому не запобігають. Паралелізм — це регулятор пропускної здатності, а не гарантія правильності. Правильність усе ще походить від ідемпотентної поведінки застосунку, безпечного захоплення завдань та шляхів виведення, що витримують повторні спроби.

apiVersion: batch/v1
kind: Job
metadata:
name: single-job
spec:
template:
spec:
containers:
- name: worker
image: busybox
command: ["echo", "Single task done"]
restartPolicy: Never

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

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

apiVersion: batch/v1
kind: Job
metadata:
name: sequential-job
spec:
completions: 5 # Run 5 times
parallelism: 1 # One at a time
completionMode: Indexed # Exposes JOB_COMPLETION_INDEX
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "echo Task $JOB_COMPLETION_INDEX"]
restartPolicy: Never

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

Безпечний спосіб обрати паралелізм — починати з найвужчого вузького місця, а не з кількості нод. Кластер може мати достатньо CPU для багатьох Pod’ів, але зовнішній API, пул з’єднань бази даних, об’єктне сховище або сервер ліцензій можуть витримувати значно менше одночасних клієнтів. Job, що перевантажує залежність, може виглядати як збій Kubernetes, навіть коли контролер поводиться бездоганно. Запити ресурсів захищають кластер; обмеження на рівні застосунку захищають системи, які викликають ваші Pod’и.

apiVersion: batch/v1
kind: Job
metadata:
name: parallel-job
spec:
completions: 10 # 10 total completions
parallelism: 3 # 3 pods at a time
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "echo Processing batch && sleep 5"]
restartPolicy: Never

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

Job’и, керовані чергою, поширені у виробництві, бо вони відокремлюють пропускну здатність кластера від бізнес-попиту. Якщо в черзі багато елементів, ви можете підвищити parallelism і запустити більше воркерів; якщо черга порожня, воркери завершуються, і Job закінчується. Небезпека в тому, що воркер, який опитує безкінечно, завадить завершенню, тоді як воркер, що завершується надто рано, може залишити роботу позаду. Надійний воркер потребує чіткої семантики порожньої черги, поведінки за тайм-аутом та логування, яке пояснює, чи завершив він роботу, чи не знайшов нічого для виконання.

apiVersion: batch/v1
kind: Job
metadata:
name: queue-job
spec:
parallelism: 3 # 3 workers
# No completions: workers process until they exit 0
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "process-queue && exit 0"]
restartPolicy: Never

Корисний спосіб налагодити власний проєкт — сформулювати правило завершення простими словами, перш ніж писати YAML. Наприклад, “запустити одну резервну копію і зупинитися” зіставляється з типовими completions та parallelism. “Обробити десять незалежних шардів, маючи щонайбільше три активні Pod’и” зіставляється з completions: 10 та parallelism: 3. “Запускати воркери, доки Redis не скаже, що завдань не лишилося” зіставляється з проєктом, керованим чергою, де Kubernetes контролює кількість воркерів, а ваш застосунок контролює володіння завданнями.

Ще одна проєктна звичка окупається і на іспиті, і в реальних командах: вирішіть, чи є збійні спроби цінними доказами, чи просто шумом. Під час розробки ви можете навмисно тримати завершені Job’и поруч, щоб порівнювати Pod’и, логи та події. У стабільній запланованій роботі ви зазвичай тримаєте лише невелике вікно історії і покладаєтеся на сповіщення або метрики для довгострокової видимості. Збереження — це не естетична вподобаність; воно визначає, чи можуть оператори бачити достатньо доказів, не потопаючи в застарілих об’єктах.

CronJob’и: розклади, що створюють Job’и

Розділ «CronJob’и: розклади, що створюють Job’и»

CronJob — це фабрика Job’ів із годинником. Він не запускає ваш контейнер напряму; він створює Job’и зі spec.jobTemplate щоразу, коли спрацьовує розклад, а кожен створений Job потім керує власними Pod’ами. Саме через це нашарування діагностика CronJob’а часто має два рівні: спершу перевірте, чи CronJob створив Job, а потім огляньте Job та Pod’и, щоб дізнатися, чи запуск був успішним.

Розклад використовує знайомий формат cron із п’яти полів: хвилина, година, день місяця, місяць та день тижня. CronJob’и Kubernetes 1.35 також підтримують поле timeZone, яке безпечніше за покладання на локальний час менеджера контролерів або коментарі в маніфесті. Якщо ваш розклад відображає бізнес-локальний дедлайн, задокументуйте часовий пояс прямо у специфікації, щоб переходи на літній час та географія оператора не стали прихованими припущеннями.

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

Terminal window
# Every minute
k create cronjob minute-task --image=busybox --schedule="* * * * *" -- echo "Every minute"
# Every hour at minute 30
k create cronjob hourly-task --image=busybox --schedule="30 * * * *" -- date
# Daily at midnight
k create cronjob daily-cleanup --image=busybox --schedule="0 0 * * *" -- echo "Daily cleanup"
# Generate YAML
k create cronjob backup --image=busybox --schedule="0 2 * * *" --dry-run=client -o yaml -- /backup.sh > cronjob.yaml

Згенерований YAML вартий уважного прочитання, бо шаблон Job’а вкладений глибше, ніж у звичайному Job’і. Поля на кшталт schedule, concurrencyPolicy, successfulJobsHistoryLimit, failedJobsHistoryLimit, startingDeadlineSeconds та suspend належать CronJob’у. Поля на кшталт backoffLimit, ttlSecondsAfterFinished та шаблон Pod’а належать під jobTemplate.spec, бо вони налаштовують кожен Job, який створює CronJob.

apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # 2 AM daily
concurrencyPolicy: Forbid # Don't overlap
successfulJobsHistoryLimit: 3 # Keep last 3 successful
failedJobsHistoryLimit: 1 # Keep last 1 failed
startingDeadlineSeconds: 200 # Max delay to start
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo 'Backup at $(date)'"]
restartPolicy: OnFailure

Cron-вираз компактний, тож використовуйте діаграму як інструмент читання, а не запам’ятовуйте приклади наосліп. Найлівіше поле змінюється найчастіше, а найправіші поля звужують календар. У виробничих оглядах найбільша помилка — це не одрук; це розклад, який технічно дійсний, але виражає неправильний бізнес-намір, бо автор сплутав день місяця, день тижня або UTC.

Cron-вирази також приховують вартість частоти. Розклад * * * * * виглядає крихітним, але він створює до 1440 нагод на день для контролера створити Job’и. Це може бути нормально для легкого пульсу (heartbeat), але надмірно для звіту, що витягує великі набори даних. Коли ви обираєте розклад, оцініть, скільки Job’ів він створює на день і скільки логів, подій та об’єктної метушні це передбачає. Вартість — це не лише обчислення; це також операційний шум.

┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday = 0)
│ │ │ │ │
* * * * *
РозкладЗначення
* * * * *Щохвилини
*/5 * * * *Кожні 5 хвилин
0 * * * *Щогодини (на хвилині 0)
0 */2 * * *Кожні 2 години
0 0 * * *Щодня опівночі
0 0 * * 0Щотижня в неділю опівночі
0 0 1 * *Щомісяця 1-го числа опівночі
30 4 * * 1-54:30 ранку в будні

Політика конкурентності — це поле CronJob’а, яке перетворює дійсний розклад на безпечний розклад. Типове Allow означає, що контролер може створити новий Job, навіть якщо попередній ще виконується. Forbid пропускає новий запуск, коли попередній запуск не завершився. Replace припиняє старий запуск і стартує новий, що може бути правильним для роботи, орієнтованої на свіжість, але небезпечним для руйнівного обслуговування.

Вибір серед цих політик — це бізнес-рішення, замасковане під поле YAML. Allow каже, що кожен запланований час важливий незалежно, навіть якщо робота перекривається. Forbid каже, що завершення важливіше за суворий підрахунок розкладу, тож система може пропустити запуск, щоб захистити спільний стан. Replace каже, що останній запуск цінніший за завершення попереднього. Якщо ви не можете пояснити цей вибір одним реченням, CronJob, ймовірно, потребує проєктного огляду, перш ніж йому довіряти.

Зупиніться і подумайте: У вас є CronJob, що запускає резервне копіювання бази даних щогодини, але іноді копіювання триває 75 хвилин. Що відбувається, коли наступний запланований запуск спрацьовує, поки попередній ще виконується? Яку політику ви обрали б тут і чому: Allow, Forbid чи Replace?

spec:
concurrencyPolicy: Allow # Run concurrent (default)
# or
concurrencyPolicy: Forbid # Skip if previous still running
# or
concurrencyPolicy: Replace # Kill previous, start new
ПолітикаПоведінкаВипадок використання
AllowЗапускати конкурентні Job’иНезалежні завдання
ForbidПропустити, якщо попередній виконуєтьсяУникнути конкуренції за ресурси
ReplaceЗупинити попередній, стартувати новийВажливі найсвіжіші дані

startingDeadlineSeconds опрацьовує пропущені розклади, а не звичайний час виконання. Якщо контролер недоступний, кластер під тиском або CronJob інакше затримується, це поле каже Kubernetes, як довго після запланованого часу запуск ще вартий старту. Короткий дедлайн корисний для роботи, яка швидко втрачає цінність, як-от часті оновлення кешу; довший дедлайн кращий для роботи, яка зрештою має відбутися, як-от експорти для відповідності.

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

spec:
startingDeadlineSeconds: 100 # Must start within 100s of schedule

Призупинення — це тихіший, але важливий операційний елемент керування. Встановлення spec.suspend: true зупиняє майбутні розклади без видалення CronJob’а, що корисно під час реагування на інциденти, вікон обслуговування або ризикованих релізів. Воно зазвичай не видаляє вже створені Job’и, тож ви все одно оглядаєте та очищаєте активні Job’и окремо. Це розділення корисне, бо зупинка майбутньої роботи не повинна стирати докази з поточного збійного запуску.

Ручний запуск добре поєднується з призупиненням. Під час ризикованої зміни ви можете призупинити майбутні заплановані запуски, створити Job вручну з поточного шаблону, поспостерігати за результатом, а потім відновити розклад, коли впевнитеся. Цей робочий процес безпечніший за редагування розкладу на штучний час, бо зберігає виробничий розклад незмінним. Він також дає рецензентам чистий аудиторський слід: CronJob було призупинено, ручна перевірка запустилася, а розклад було відновлено.

Поведінка повторних спроб, очищення та діагностики

Розділ «Поведінка повторних спроб, очищення та діагностики»

Усунення несправностей пакетних навантажень починається з визначення, на який стан контролера ви дивитеся. CronJob може бути здоровим, поки створений ним Job зазнає невдачі, а Job може повторювати спроби, поки окремі Pod’и вже завершилися. Починайте широко з k get cronjobs, k get jobs та міток, потім звужуйте до k describe, логів, подій та статусу Pod’а. Цей порядок запобігає поширеній помилці: прочитати лог найновішого Pod’а і припустити, що він пояснює всю історію Job’а.

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

Terminal window
# List jobs
k get jobs
# List cronjobs
k get cronjobs
# Get job pods
k get pods -l job-name=my-job
# Check job status
k describe job my-job
# Watch job completion
k get job my-job -w

Логи найлегше отримати, коли Job має рівно один активний або завершений Pod, бо k logs job/my-job може знайти Pod за вас. Коли було кілька повторних спроб, використовуйте мітку job-name, щоб перелічити Pod’и і оглянути конкретний Pod, який зазнав невдачі у спосіб, що вас цікавить. З restartPolicy: OnFailure пам’ятайте, що той самий Pod може містити перезапущені спроби контейнера, тож за потреби дивіться на лічильники перезапусків та попередні логи.

Події пояснюють те, чого не можуть логи. Помилка завантаження образу, невдале рішення планування, відсутній ConfigMap, відмовлене монтування тому або перевищений дедлайн можуть статися до того, як ваш застосунок запуститься, тож корисного логу застосунку може взагалі не бути. k describe поєднує статус та події в одному вигляді, тому це має бути частиною кожного розслідування Job’а. Якщо контейнер ніколи не запускався, виправлення зазвичай у шаблоні Pod’а, дозволах, посиланні на образ або пропускній здатності кластера, а не в скрипті застосунку.

Terminal window
# Get logs from job's pod
k logs job/my-job
# Get logs from specific pod
k logs my-job-abc12
# Follow logs
k logs -f job/my-job

Ручний запуск — найбезпечніший спосіб протестувати шаблон CronJob’а, не чекаючи на годинник. Команда нижче створює одноразовий Job із поточного шаблону CronJob’а, що дозволяє перевірити поведінку завантаження образу, синтаксис команди, монтування ConfigMap та RBAC, перш ніж покладатися на розклад. Це не доводить, що cron-вираз правильний, але доводить, що шаблон Job’а може виконатися просто зараз.

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

Terminal window
# Create job from cronjob immediately
k create job manual-backup --from=cronjob/daily-backup

Очищення слід проєктувати, а не ставитися до нього як до запізнілого додатку. Завершені Job’и та Pod’и є корисними доказами, але вони також створюють візуальний шум і споживають об’єкти API. Обмеження історії CronJob’а контролюють, скільки створених Job’ів залишається прикріпленими до CronJob’а, тоді як ttlSecondsAfterFinished дозволяє контролеру TTL видаляти завершені Job’и після затримки. Використовуйте обидва, коли хочете недавні докази, не зберігаючи кожен успішний запуск назавжди.

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

Terminal window
# Delete job
k delete job my-job
# Delete cronjob (also deletes jobs it created)
k delete cronjob my-cronjob
# Delete completed jobs older than TTL
# (Automatic if ttlSecondsAfterFinished is set)

Коли Job не завершується, читайте його як ланцюг контрактів. Образ має завантажитися, Pod має заплануватися, команда має виконатися, контейнер має вийти з наміреним кодом, а контролер Job’а має все ще мати повторні спроби та час, що лишилися. Не-нульовий код виходу не є за своєю суттю поганим; це спосіб застосунку сказати Kubernetes, що робота не відповідала контракту успіху і має бути повторена або позначена як збійна.

Контракт коду виходу — ось чому оболонкові обгортки заслуговують на обережність. Команда на кшталт sh -c "step1; step2; echo done" може надрукувати радісне повідомлення, навіть коли попередній крок зазнав невдачі, залежно від того, як написано оболонку. У виробничих скриптах команди часто використовують set -e або явні перевірки помилок, щоб контейнер виходив із не-нульовим кодом, коли робота насправді не вдалася. Kubernetes може діяти лише на статус виходу, який отримує, тож недбале написання скриптів може перетворити справжні збої на хибні завершення.

Terminal window
# Check status
k describe job my-job
# Common issues:
# - Container command exits non-zero
# - Image pull fails
# - Resource limits too low
# - restartPolicy not set correctly
# Check pod logs
k logs $(k get pods -l job-name=my-job -o jsonpath='{.items[0].metadata.name}')

Що сталося б, якби: Ви створюєте Job із backoffLimit: 6 (типове значення) та restartPolicy: Never. У скрипті контейнера є помилка, що завжди виходить із кодом 1. Перш ніж запускати це, який вивід ви очікуєте від k get pods -l job-name=my-job і чому?

Контролер продовжуватиме створювати Pod’и на заміну, доки політика збоїв не вичерпається. З restartPolicy: Never кожна збійна спроба видима як окремий збійний Pod, що шумно, але дуже корисно під час діагностики. З restartPolicy: OnFailure ви можете натомість бачити перезапуски всередині меншої кількості Pod’ів, тож ваша звичка діагностики має відповідати політиці перезапуску. В обох випадках корінне виправлення однакове: огляньте причину виходу та лог застосунку, потім виправте команду, образ, залежності або дозволи.

Terminal window
# Check backoffLimit
k get job my-job -o jsonpath='{.spec.backoffLimit}'
# If hitting limit, check why pods fail
k describe pods -l job-name=my-job

Збої CronJob’а вимагають одного додаткового питання: чи розклад взагалі створив Job? Якщо lastScheduleTime порожній або застарілий, огляньте розклад, прапорець призупинення, дедлайн та події контролера. Якщо Job’и існують, але зазнають невдачі, перенесіть свою увагу на Job та Pod’и. Це розділення вберігає вас від зміни полів повторних спроб, коли справжня проблема — призупинений CronJob або пропущений розклад.

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

Terminal window
# Check cronjob status
k describe cronjob my-cronjob
# Check last schedule time
k get cronjob my-cronjob -o jsonpath='{.status.lastScheduleTime}'
# Check if suspended
k get cronjob my-cronjob -o jsonpath='{.spec.suspend}'
# Resume if suspended
k patch cronjob my-cronjob -p '{"spec":{"suspend":false}}'

Гіпотетичний сценарій: платформена команда планує нічний генератор звітів як CronJob із типовою політикою Allow, бо YAML виглядає меншим, а перші кілька запусків завершуються швидко. На кінець місяця кожен запуск триває довше, наступний розклад створює ще один Job, і кілька Pod’ів конкурують за ту саму репліку читання бази даних, доки затримка дашборду не стрибне. Виправлення не екзотичне: встановити concurrencyPolicy: Forbid, додати дедлайн виконання, налаштувати запити ресурсів і створити сповіщення, коли запуск пропускається через те, що попередній ще активний.

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

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

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

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

ПатернКоли використовуватиЧому працюєМіркування щодо масштабування
Job з одним завершеннямОдна міграція, один тест резервної копії, один експорт звітуКонтролеру потрібен рівно один успішний Pod, і потім він зупиняєтьсяТримайте backoffLimit достатньо низьким, щоб погана команда зазнавала помітної невдачі
Фіксовані паралельні завершенняБагато незалежних шардів або файлівcompletions визначає весь обсяг роботи, а parallelism обмежує активні Pod’иУзгоджуйте паралелізм із пропускною здатністю кластера та обмеженнями частоти залежностей
Воркери, керовані чергоюЕлементи роботи живуть у Redis, таблиці бази даних або іншій черзіKubernetes контролює кількість воркерів, поки черга контролює володіння елементамиВоркери мають захоплювати елементи атомарно і чисто завершуватися, коли роботи не лишається
CronJob із забороненим перекриттямРезервні копії, очищення, ущільнення або звіти, що торкаються спільного стануНовий запуск пропускається замість того, щоб накладатися на незавершену роботуСповіщайте про пропущені запуски, щоб пропущені розклади були видимими

Антипатерни зазвичай походять від ставлення до Job’ів як до малих Деплойментів або до CronJob’ів як до звичайних рядків crontab. Kubernetes додає поведінку контролера, статус, події та збирання сміття, але він також очікує, що ваш маніфест чітко визначить життєвий цикл. Найбезпечніше питання для огляду: “Що станеться, якщо ця команда зазнає невдачі на півдорозі, а Kubernetes спробує знову?”

Ще одне корисне питання для огляду: “Хто володіє результатом після того, як Pod завершиться?” Job може сказати вам, що контейнер завершився успішно, але він не може довести, що резервну копію можна відновити, що звіт правильний або що очищення видалило лише призначені дані. Зрілі пакетні системи поєднують статус контролера Kubernetes із перевіркою на рівні застосунку, як-от валідація контрольної суми, підрахунок рядків, оглядові запити або тест відновлення. Контролер доводить виконання; застосунок має довести бізнес-правильність.

АнтипатернЩо йде не такКраща альтернатива
Використання Деплойменту для скінченної міграційної роботиPod перезапускається після успіху, і міграція може виконуватися повторноВикористайте Job із чіткою командою, обмеженням повторних спроб та політикою очищення
Залишення конкурентності CronJob’а на Allow для роботи зі спільним станомДовгі запуски перекриваються і конкурують за ті самі файли, блокування або бази данихВикористайте Forbid або спроєктуйте завдання безпечно конкурентним
Встановлення високих повторних спроб без спостережуваностіЗламана команда випалює час і створює багато збійних Pod’ів, перш ніж хтось помітитьВикористайте обачний backoffLimit, оглядайте події та сповіщайте про збійні Job’и
Зберігання кожного завершеного запуску назавждиПростори імен захаращуються, і люди перестають помічати справжні збоїПоєднайте обмеження історії з ttlSecondsAfterFinished

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

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

Рамка ухвалення рішень

Розділ «Рамка ухвалення рішень»

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

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

Потрібно, щоб навантаження продовжувало працювати?
|
+-- так --> Використайте Деплоймент, StatefulSet або інший довготривалий контролер.
|
+-- ні --> Чи робота планується повторно?
|
+-- ні --> Використайте Job із правилами completions, parallelism, повторних спроб та TTL.
|
+-- так --> Використайте CronJob із правилами розкладу, конкурентності, дедлайну та історії.
РішенняВіддавайте перевагу цьомуУникайте цього
Одна міграція має виконатися один разJob із parallelism: 1 та обачними повторними спробамиДеплоймент, що перезапускає контейнер міграції назавжди
Щогодинна резервна копія іноді перевищує годинуCronJob із concurrencyPolicy: ForbidТипове перекриття, що запускає конкурентні Pod’и резервного копіювання
Оновлення кешу, де найсвіжіший запуск важить найбільшеCronJob із Replace після перевірки безпеки припиненняВбивання неідемпотентної роботи без гарантій очищення
Сотні незалежних файлів потребують обробкиJob із фіксованими completions та обмеженим parallelismОдин величезний Pod, що серіалізує все і приховує часткові збої
Воркери черги споживають, доки не спорожнієJob із parallelism та логікою воркера, обізнаною про чергуПрипущення, що Kubernetes знає, скільки зовнішніх елементів черги лишається

Для швидкості CKAD побудуйте ментальний шаблон, який можна адаптувати під тиском. Створіть або згенеруйте об’єкт, огляньте вкладену специфікацію, встановіть елементи керування життєвим циклом, запустіть його, потім діагностуйте від контролера до Pod’а. Іспит рідко винагороджує екзотичні можливості; він винагороджує чіткий вибір ресурсу, дійсний YAML та практичну діагностику. У реальних кластерах ті самі звички вберігають пакетні завдання від перетворення на тихі фонові загрози.

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

  • Індексовані Job’и розкривають індекс завершення. У режимі індексованого завершення кожен Pod може знати свій індекс через JOB_COMPLETION_INDEX, що корисно, коли ви шардуєте дані і хочете, щоб кожен Pod обробляв окремий розділ.
  • CronJob’и підтримують часові пояси в сучасному Kubernetes. Поле spec.timeZone дозволяє сказати, що бізнес-розклад має виконуватися в названому часовому поясі замість покладання на поведінку локального годинника контролера.
  • Поле activeDeadlineSeconds обмежує час виконання всього Job’а. Якщо дедлайн спливає, Kubernetes припиняє активні Pod’и цього Job’а, навіть якщо деякі окремі спроби ще просувалися.
  • Обмеження історії CronJob’а та TTL Job’а вирішують різні проблеми очищення. Обмеження історії вирішують, скільки Job’ів зберігає CronJob, тоді як ttlSecondsAfterFinished дозволяє контролеру TTL видаляти завершені Job’и після налаштованої затримки.
ПомилкаЧому вона стаєтьсяЯк виправити
restartPolicy: Always у шаблоні Job’аАвтор копіює шаблон Pod’а або Деплойменту і забуває, що Job’и мають завершуватисяВикористайте Never, коли окремі збійні Pod’и допомагають діагностиці, або OnFailure, коли перезапуски всередині Pod’а прийнятні
Залишення backoffLimit неявним для ризикованих командТипове значення здається нешкідливим під час створення, але може приховати повторювані збої застосункуВстановіть явне обмеження повторних спроб, що відповідає вартості та безпеці операції
Використання неправильного поля cron для бізнес-часуCron-вирази компактні, а припущення про UTC чи часовий пояс легко проґавитиПеревірте вираз, встановіть spec.timeZone за потреби і протестуйте ручним запуском Job’а
Пропуск елементів керування очищенням для частих CronJob’івЗавершені Job’и спершу корисні, тож команди відкладають рішення про збереженняВстановіть successfulJobsHistoryLimit, failedJobsHistoryLimit та TTL Job’а, де доречно
Дозвіл перекриття для завдань зі спільним станомAllow є типовим, а короткі тестові запуски не виявляють часу виконання на кінець місяцяВикористайте Forbid для резервних копій, ущільнення та очищення, якщо конкурентні запуски явно не безпечні
Діагностика лише найновішого логу Pod’аПовторні спроби створюють кілька Pod’ів або перезапусків, а найновіша спроба може не показувати першу помилкуОгляньте статус Job’а, події, усі Pod’и з міткою job-name та попередні логи при використанні OnFailure
Ставлення до startingDeadlineSeconds як до обмеження часу виконанняНазва поля звучить як загальний тайм-аут, але воно контролює пропущені стартиВикористайте activeDeadlineSeconds для обмежень часу виконання та startingDeadlineSeconds для запізнілих розкладів
Ваша команда розгортає міграцію бази даних як Job, але маніфест використовує `restartPolicy: Always`, бо його скопіювали з Деплойменту. Що станеться і що слід змінити?

API-сервер відхиляє Job, бо шаблон Pod’а для Job’а має використовувати Never або OnFailure. Job’у потрібні Pod’и, які можуть завершуватися, щоб контролер міг порахувати завершення, а Always описує довготривалу поведінку. Використайте Never, якщо хочете, щоб кожна збійна спроба зберігалася як окремий Pod для легшого посмертного огляду. Використайте OnFailure, якщо перезапуск усередині того самого Pod’а прийнятний і ви хочете менше Pod’ів на заміну.

Вашій операційній команді потрібно, щоб скрипт очищення логів запускався о 4:30 ранку лише в будні, а очищення іноді триває понад добу. Який розклад та політику конкурентності ви обрали б?

Розклад — це 30 4 * * 1-5, що означає хвилину 30, годину 4, будь-який день місяця, будь-який місяць, з понеділка по п’ятницю. Для політики конкурентності оберіть Forbid, якщо тільки очищення не доведено безпечним для конкурентного запуску. Allow міг би накласти кілька очищень на ту саму файлову систему або базу даних, а Replace міг би припинити очищення на півдорозі. Пропуск перекривного запуску зазвичай безпечніший за множення руйнівного обслуговування.

Вам потрібно обробити 100 зображень через генератор мініатюр, кожне зображення триває близько 10 секунд, а кластер може впоратися з п'ятьма додатковими Pod'ами. Як налаштувати Job і що має опрацьовувати застосунок?

Встановіть completions: 100 та parallelism: 5, щоб Kubernetes запускав щонайбільше п’ять Pod’ів, працюючи в напрямку 100 успішних завершень. Застосунку все одно потрібен надійний спосіб зіставити кожне завершення з конкретним зображенням, як-от стратегія індексованого завершення або зовнішня черга. Без цього зіставлення п’ять Pod’ів можуть усі обробити те саме зображення або пропустити роботу. Контролер керує лічильниками завершень; ваш застосунок має керувати володінням бізнес-елементами.

CronJob запускається кожні п'ять хвилин, але завершені Job'и та Pod'и накопичуються в просторі імен. Які елементи керування слід додати і чому їх два види?

Додайте successfulJobsHistoryLimit та failedJobsHistoryLimit до CronJob’а, щоб він зберігав лише обмежену кількість недавніх Job’ів. Додайте ttlSecondsAfterFinished під шаблоном Job’а, коли ви також хочете, щоб завершені Job’и видалялися після часової затримки. Обмеження історії — це елементи керування збереженням CronJob’а, тоді як TTL опрацьовується для завершених Job’ів. Використання обох дає операторам недавні докази, не дозволяючи частим запускам захаращувати простір імен нескінченно.

Заплановане резервне копіювання не запустилося під час короткого збою площини управління, і тепер CronJob не показує нового Job'а для цього розкладу. Які поля та статус ви оглянули б, перш ніж змінювати образ або команду?

Спершу огляньте CronJob через k describe cronjob і перевірте status.lastScheduleTime, spec.suspend, spec.schedule, spec.timeZone та spec.startingDeadlineSeconds. Якщо пропущений розклад перевищив стартовий дедлайн, Kubernetes може правильно пропустити його, а не стартувати запізно. Якщо CronJob призупинено, майбутні розклади не створюються, доки його не відновлять. Лише після підтвердження, що Job справді було створено, слід переходити до діагностики образу, команди, Pod’а та логів.

Job із `restartPolicy: Never` та збійною командою має кілька збійних Pod'ів. Колега хоче видалити Job і одразу його перестворити. Що слід оглянути першим?

Огляньте k describe job, перелічіть Pod’и через k get pods -l job-name=<name> і прочитайте логи зі збійних Pod’ів, перш ніж видаляти докази. З restartPolicy: Never кожна збійна спроба може зберігати іншу послідовність подій або логів, особливо якщо збої планування, завантаження образу та застосунку сталися в різний час. Вам також слід перевірити backoffLimit та будь-які поля дедлайну, щоб зрозуміти, чи Kubernetes припинив повторні спроби, як налаштовано. Надто швидке перестворення Job’а може стерти слід, що пояснює корінну причину.

Ваша команда обирає між Деплойментом, Job'ом та CronJob'ом для нічного генератора звітів, що завершується після публікації файлу. Який контролер пасує і коли відповідь змінилася б?

CronJob пасує, бо робота скінченна і повторювана за розкладом. Звичайний Job пасував би для одноразового звіту, але він не створював би майбутні запуски самостійно. Деплоймент був би неправильним типовим вибором, бо бажаний стан — це не безперервно запущений Pod; команда звіту має завершитися після успіху. Відповідь змінюється лише тоді, коли процес звіту насправді є довготривалим сервісом, що стежить за запитами або повідомленнями черги безкінечно.

Ця вправа будує невеликий робочий процес резервного копіювання та очищення шарами. Ви створите одноразовий Job, запустите CronJob вручну, виконаєте паралельні завершення, поспостерігаєте за поведінкою повторних спроб і завершите повнішим CronJob’ом резервного копіювання, що використовує елементи керування історією та TTL. Працюйте в одноразовому просторі імен, якщо він у вас є, і приберіть наприкінці, щоб ваша подальша практика CKAD не була забруднена старими Job’ами.

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

Завдання 1: Запустіть та огляньте одноразовий Job

Розділ «Завдання 1: Запустіть та огляньте одноразовий Job»

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

Перш ніж запускати команду, передбачте, які об’єкти мають існувати після завершення. У вас має бути об’єкт Job і принаймні один Pod, що належить цьому Job’у. Job має повідомити про умову завершення, а Pod має бути у фазі succeeded. Якщо ваш кластер тримає завершені Pod’и для огляду, лог має все ще бути доступним через посилання на Job; якщо збереження чи очищення швидко видаляє докази, спершу опишіть Job і зафіксуйте те, що лишилося.

Terminal window
# Create a job that simulates a database backup
k create job db-backup --image=busybox -- sh -c "echo 'Backing up database' && sleep 5 && echo 'Backup complete'"
# Wait for completion
k wait --for=condition=complete job/db-backup --timeout=60s
k get job db-backup
# Check logs
k logs job/db-backup
Нотатки до розв'язку для Завдання 1

Job має досягти умови Complete, а логи мають показати обидва повідомлення резервного копіювання. Якщо очікування завершиться за тайм-аутом, опишіть Job і перелічіть Pod’и з міткою job-name=db-backup, перш ніж щось змінювати. Це тримає ваше розслідування узгодженим з ієрархією контролерів.

Завдання 2: Створіть CronJob і запустіть його вручну

Розділ «Завдання 2: Створіть CronJob і запустіть його вручну»

Тепер створіть запланований контролер, але не чекайте, поки годинник доведе, що шаблон працює. Job, створений вручну з CronJob’а, дає швидкий зворотний зв’язок щодо образу, команди та шаблону Pod’а, тримаючи діагностику розкладу окремо.

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

Terminal window
# Create cronjob for hourly cleanup
k create cronjob hourly-cleanup \
--image=busybox \
--schedule="0 * * * *" \
-- sh -c 'echo "Cleanup at $(date)"'
# Manually trigger for testing
k create job manual-cleanup --from=cronjob/hourly-cleanup
# Check results
k get jobs
k wait --for=condition=complete job/manual-cleanup --timeout=60s
k logs job/manual-cleanup
Нотатки до розв'язку для Завдання 2

Ви маєте побачити, як Job manual-cleanup завершується і друкує позначку часу очищення. Це доводить, що шаблон Job’а може виконатися, але не доводить, що щогодинний розклад спрацював. Використайте k describe cronjob hourly-cleanup, коли хочете оглянути статус розкладу, час останнього розкладу та події.

Завдання 3: Запустіть паралельний Job

Розділ «Завдання 3: Запустіть паралельний Job»

Наступний маніфест зберігає оригінальний приклад паралельної обробки і дає конкретний спосіб спостерігати за завершеннями та активними Pod’ами. Застосуйте його, поспостерігайте за Pod’ами і порівняйте те, чого хоче Job, з тим, що кластер наразі запускає.

Не очікуйте, що кожен виклик k get pods показуватиме рівно два запущені Pod’и, бо час має значення. Деякі Pod’и можуть завершитися між операціями переліку, а контролер може створити заміну достатньо швидко, щоб знімок змінювався із секунди в секунду. Важливий доказ — це тенденція: Job просувається до шести завершень, тримаючи за нормальних умов не більше за налаштований паралелізм. Якщо він застрягає, опишіть Job і потім огляньте Pod’и, які не вдалися.

Terminal window
# Create parallel-job.yaml
cat << 'EOF' > parallel-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: parallel-process
spec:
completions: 6
parallelism: 2
completionMode: Indexed
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "echo Processing item $JOB_COMPLETION_INDEX && sleep 3"]
restartPolicy: Never
EOF
k apply -f parallel-job.yaml
k get pods -l job-name=parallel-process
k wait --for=condition=complete job/parallel-process --timeout=60s
k get job parallel-process
Нотатки до розв'язку для Завдання 3

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

Завдання 4: Відпрацюйте сфокусовані тренування CKAD

Розділ «Завдання 4: Відпрацюйте сфокусовані тренування CKAD»

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

Тренування з повторними спробами особливо корисне, бо створює контрольований збій. Збійний Job тут не випадковість; це лабораторний інструмент. Поспостерігайте, як збійні Pod’и накопичуються з restartPolicy: Never, як backoffLimit змінюється, коли контролер припиняє спроби, і як k describe job підсумовує стан. Коли ви побачили навмисний збій, випадковий збій стає набагато менш загадковим.

Terminal window
# Create a job that:
# - Named: hello-job
# - Runs busybox
# - Echoes "Hello from job"
k create job hello-job --image=busybox -- echo "Hello from job"
# Verify completion
k wait --for=condition=complete job/hello-job --timeout=60s
k get job hello-job
# Check logs
k logs job/hello-job
# Cleanup
k delete job hello-job
Terminal window
# Create a cronjob that:
# - Named: every-minute
# - Runs every minute
# - Prints current date
k create cronjob every-minute --image=busybox --schedule="* * * * *" -- date
# Wait 1 minute and check
sleep 65
k get jobs
EVERY_MINUTE_JOB=$(k get jobs --sort-by=.metadata.creationTimestamp -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep '^every-minute-' | tail -n 1)
test -n "$EVERY_MINUTE_JOB"
# Check logs of triggered job
k wait --for=condition=complete "job/${EVERY_MINUTE_JOB}" --timeout=60s
k logs "job/${EVERY_MINUTE_JOB}"
# Cleanup
k delete cronjob every-minute
Terminal window
# Create a job that fails and retries
cat << 'EOF' | k apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: retry-job
spec:
backoffLimit: 3
template:
spec:
containers:
- name: fail
image: busybox
command: ["sh", "-c", "echo 'Trying...' && exit 1"]
restartPolicy: Never
EOF
# Wait for the retry policy to finish
k wait --for=condition=failed job/retry-job --timeout=120s
k get pods -l job-name=retry-job
# Check job status
k describe job retry-job | grep -A5 Conditions
# Cleanup
k delete job retry-job
Terminal window
# Create a parallel job
cat << 'EOF' | k apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: parallel
spec:
completions: 5
parallelism: 2
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "echo Worker done && sleep 2"]
restartPolicy: Never
EOF
# Observe active Pods, then wait for all completions
k get pods -l job-name=parallel
k wait --for=condition=complete job/parallel --timeout=60s
k get job parallel
# Cleanup
k delete job parallel
Terminal window
# Create cronjob that forbids overlap
cat << 'EOF' | k apply -f -
apiVersion: batch/v1
kind: CronJob
metadata:
name: no-overlap
spec:
schedule: "* * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "echo 'Start' && sleep 90 && echo 'Done'"]
restartPolicy: Never
EOF
# Check policy
k get cronjob no-overlap -o jsonpath='{.spec.concurrencyPolicy}'
# Wait 2 minutes and verify at most one CronJob-owned Job is active
sleep 120
k get jobs --sort-by=.metadata.creationTimestamp -o jsonpath='{range .items[?(@.metadata.ownerReferences[0].name=="no-overlap")]}{.metadata.name}{" active="}{.status.active}{"\n"}{end}'
ACTIVE_COUNT=$(k get jobs -o jsonpath='{range .items[?(@.metadata.ownerReferences[0].name=="no-overlap")]}{.status.active}{"\n"}{end}' | awk '$1 == 1 {count++} END {print count+0}')
test "$ACTIVE_COUNT" -le 1
# Cleanup
k delete cronjob no-overlap
Нотатки до розв'язку для Завдання 4

Базовий Job має завершитися один раз, CronJob every-minute має створити принаймні один Job після спрацювання розкладу, Job повторних спроб має показати збійні спроби, доки не буде досягнуто політики відкату, паралельний Job має запускати до двох Pod’ів водночас, а CronJob no-overlap має уникнути старту другого активного Job’а, поки перший ще виконується. Якщо ваш вивід відрізняється, використайте k describe, перш ніж щось видаляти.

Завдання 5: Побудуйте повний CronJob резервного копіювання

Розділ «Завдання 5: Побудуйте повний CronJob резервного копіювання»

Фінальне завдання поєднує наданий через ConfigMap скрипт, CronJob, заборонену конкурентність, обмеження історії та очищення Job’а за TTL. Це ближче до виробничої форми, бо скрипт відокремлений від об’єкта CronJob, а поведінка збереження видима у специфікації. У реальному кластері ви також додали б дозволи сервісного акаунту, запити ресурсів, облікові дані сховища з безпечного робочого процесу Secret та моніторинг.

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

Terminal window
# 1. Create configmap with backup script
k create configmap backup-script --from-literal=script.sh='#!/bin/sh
echo "Starting backup at $(date)"
echo "Compressing data..."
sleep 3
echo "Uploading to storage..."
sleep 2
echo "Backup complete at $(date)"
'
# 2. Create CronJob using the script
cat << 'EOF' | k apply -f -
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-system
spec:
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
ttlSecondsAfterFinished: 300
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "/scripts/script.sh"]
volumeMounts:
- name: scripts
mountPath: /scripts
restartPolicy: OnFailure
volumes:
- name: scripts
configMap:
name: backup-script
EOF
# 3. Test with manual trigger
k create job test-backup --from=cronjob/backup-system
# 4. Check logs
k wait --for=condition=complete job/test-backup --timeout=60s
k logs job/test-backup
# 5. Verify history limits
k get cronjob backup-system -o jsonpath='{.spec.successfulJobsHistoryLimit}'
# Cleanup
k delete job test-backup
k delete cronjob backup-system
k delete configmap backup-script
Нотатки до розв'язку для Завдання 5

Ручний запуск має створити test-backup, виконати скрипт із ConfigMap і надрукувати кроки резервного копіювання. CronJob має показати конкурентність Forbid та налаштовані обмеження історії. Значення TTL застосовується до Job’ів, створених із шаблону, після їхнього завершення, тож не очікуйте, що воно видалить сам CronJob.

  • Ви можете створити одноразовий Job, дочекатися завершення і прочитати його логи через посилання на Job.
  • Ви можете створити CronJob і вручну запустити Job із його шаблону для швидкої перевірки.
  • Ви можете пояснити, як completions та parallelism взаємодіють у пакеті фіксованого розміру.
  • Ви можете діагностувати збійний Job, перевіряючи статус Job’а, помічені Pod’и, події, логи та обмеження повторних спроб.
  • Ви можете налаштувати CronJob із захистом від перекриття, обмеженнями історії та очищенням завершених Job’ів.
  • Ви можете прибрати кожен Job, CronJob та ConfigMap, створений під час вправи.

Перевірка засвоєння

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

Індексовані Job’и розкривають індекс завершення.

Коли ви використовуєте JOB_COMPLETION_INDEX, яке поле специфікації Job’а має бути присутнім і який симптом ви очікували б, якби Job виконувався в типовому неіндексованому режимі?

Модуль 1.3: Багатоконтейнерні Pod’и — патерни sidecar, init та ambassador допомагають проєктувати Pod’и, де кілька контейнерів співпрацюють, замість того щоб втискати кожен обов’язок в один образ.