Модуль 1.2: Job'и та CronJob'и
Складність:
[СЕРЕДНЯ]— невіддільна навичка CKAD з конкретними виробничими компромісамиЧас на проходження: 45-50 хвилин
Передумови: Модуль 1.1 (Образи контейнерів), базове знання життєвого циклу Pod’а та робочий кластер Kubernetes 1.35+
Приклади в цьому модулі використовують стандартний для CKAD псевдонім alias k=kubectl. Створіть його у своїй оболонці перед практикою, щоб команди збігалися з екзаменаційним стилем роботи, а наведені нижче приклади діагностики працювали як належить.
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 не надає напряму.
# Simple jobk create job backup --image=busybox -- echo "Backup complete"
# Job with a shell commandk create job report --image=busybox -- /bin/sh -c "date; echo 'Report generated'"
# Generate YAMLk 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/v1kind: Jobmetadata: name: backup-jobspec: 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 podrestartPolicy: OnFailure# Container fails -> Same pod restarts containerНаступний проєктний вибір — скільки успішних завершень вам потрібно і скільки конкурентності кластер має дозволити. Одне завершення є типовим і пасує одноразовій роботі. Кілька послідовних завершень пасують повторюваній незалежній роботі, коли важливий порядок або тиск на ресурси. Паралельні завершення пасують навантаженню, де багато шардів можуть виконуватися одночасно, за умови, що застосунок може визначити, який шард обробляти, і витримує одночасне виконання кількох Pod’ів.
Саме тут багато тих, хто навчається, випадково переоцінюють Kubernetes. Контролер Job’а може рахувати успішні Pod’и, але він не ділить ваші вхідні дані на безпечні одиниці автоматично. Якщо десять Pod’ів усі запускають process-all-files.sh, ви можете обробити ті самі файли десять разів, якщо тільки скрипт, черга або проєкт індексованих завершень цьому не запобігають. Паралелізм — це регулятор пропускної здатності, а не гарантія правильності. Правильність усе ще походить від ідемпотентної поведінки застосунку, безпечного захоплення завдань та шляхів виведення, що витримують повторні спроби.
apiVersion: batch/v1kind: Jobmetadata: name: single-jobspec: 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/v1kind: Jobmetadata: name: sequential-jobspec: 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/v1kind: Jobmetadata: name: parallel-jobspec: 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: NeverJob з робочою чергою дещо інший, бо саме черга, а не Kubernetes, визначає, коли роботи більше немає. У цій моделі ви часто встановлюєте parallelism і опускаєте completions, а потім дозволяєте воркерам витягувати елементи, доки кожен воркер не завершиться успішно. Це потужно, але переносить правильність у протокол черги: воркери мають безпечно захоплювати роботу, обробляти повторні спроби і завершуватися, коли черга порожня, а не сидіти без діла безкінечно.
Job’и, керовані чергою, поширені у виробництві, бо вони відокремлюють пропускну здатність кластера від бізнес-попиту. Якщо в черзі багато елементів, ви можете підвищити parallelism і запустити більше воркерів; якщо черга порожня, воркери завершуються, і Job закінчується. Небезпека в тому, що воркер, який опитує безкінечно, завадить завершенню, тоді як воркер, що завершується надто рано, може залишити роботу позаду. Надійний воркер потребує чіткої семантики порожньої черги, поведінки за тайм-аутом та логування, яке пояснює, чи завершив він роботу, чи не знайшов нічого для виконання.
apiVersion: batch/v1kind: Jobmetadata: name: queue-jobspec: 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, а приклади часто уникають локальних бізнес-правил. Реальні розклади рідко настільки нейтральні. Експорт зарплат, звіт на кінець дня чи вікно обслуговування можуть бути прив’язані до регіону, юрисдикції або клієнтського контракту. Коли розклад бізнес-локальний, маніфест має нести цей факт. Інакше майбутня міграція платформи або перехід на літній час можуть змінити поведінку без жодного релізу застосунку.
# Every minutek create cronjob minute-task --image=busybox --schedule="* * * * *" -- echo "Every minute"
# Every hour at minute 30k create cronjob hourly-task --image=busybox --schedule="30 * * * *" -- date
# Daily at midnightk create cronjob daily-cleanup --image=busybox --schedule="0 0 * * *" -- echo "Daily cleanup"
# Generate YAMLk 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/v1kind: CronJobmetadata: name: daily-backupspec: 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: OnFailureCron-вираз компактний, тож використовуйте діаграму як інструмент читання, а не запам’ятовуйте приклади наосліп. Найлівіше поле змінюється найчастіше, а найправіші поля звужують календар. У виробничих оглядах найбільша помилка — це не одрук; це розклад, який технічно дійсний, але виражає неправильний бізнес-намір, бо автор сплутав день місяця, день тижня або 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-5 | 4: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 або писати вивід.
# List jobsk get jobs
# List cronjobsk get cronjobs
# Get job podsk get pods -l job-name=my-job
# Check job statusk describe job my-job
# Watch job completionk 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’а, дозволах, посиланні на образ або пропускній здатності кластера, а не в скрипті застосунку.
# Get logs from job's podk logs job/my-job
# Get logs from specific podk logs my-job-abc12
# Follow logsk logs -f job/my-jobРучний запуск — найбезпечніший спосіб протестувати шаблон CronJob’а, не чекаючи на годинник. Команда нижче створює одноразовий Job із поточного шаблону CronJob’а, що дозволяє перевірити поведінку завантаження образу, синтаксис команди, монтування ConfigMap та RBAC, перш ніж покладатися на розклад. Це не доводить, що cron-вираз правильний, але доводить, що шаблон Job’а може виконатися просто зараз.
Ця відмінність корисна під час реагування на інциденти, бо швидко звужує пошук. Якщо ручний запуск зазнає невдачі так само, як запланований, зосередьтеся на шаблоні Job’а та залежностях. Якщо ручний запуск вдається, але запланований не з’явився, зосередьтеся на розкладі CronJob’а, стані призупинення, дедлайні, часовому поясі та подіях контролера. Розділення цих двох класів збоїв вберігає вас від переписування робочих команд контейнера, поки справжня проблема — це правило планування.
# Create job from cronjob immediatelyk create job manual-backup --from=cronjob/daily-backupОчищення слід проєктувати, а не ставитися до нього як до запізнілого додатку. Завершені Job’и та Pod’и є корисними доказами, але вони також створюють візуальний шум і споживають об’єкти API. Обмеження історії CronJob’а контролюють, скільки створених Job’ів залишається прикріпленими до CronJob’а, тоді як ttlSecondsAfterFinished дозволяє контролеру TTL видаляти завершені Job’и після затримки. Використовуйте обидва, коли хочете недавні докази, не зберігаючи кожен успішний запуск назавжди.
Правильне вікно збереження залежить від того, наскільки швидко люди та системи моніторингу помічають збої. Для частого завдання очищення зберігання трьох успішних запусків та одного збійного може бути достатньо, бо наступний збій станеться скоро, а сповіщення мають спрацьовувати оперативно. Для щомісячного експорту для відповідності ви могли б зберігати більше історії або експортувати докази деінде, перш ніж TTL видалить Job. Налаштування збереження Kubernetes мають доповнювати, а не заміщати логи, метрики та зовнішнє аудиторське сховище.
# Delete jobk 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 може діяти лише на статус виходу, який отримує, тож недбале написання скриптів може перетворити справжні збої на хибні завершення.
# Check statusk describe job my-job
# Common issues:# - Container command exits non-zero# - Image pull fails# - Resource limits too low# - restartPolicy not set correctly
# Check pod logsk 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’ів, тож ваша звичка діагностики має відповідати політиці перезапуску. В обох випадках корінне виправлення однакове: огляньте причину виходу та лог застосунку, потім виправте команду, образ, залежності або дозволи.
# Check backoffLimitk get job my-job -o jsonpath='{.spec.backoffLimit}'
# If hitting limit, check why pods failk describe pods -l job-name=my-jobЗбої CronJob’а вимагають одного додаткового питання: чи розклад взагалі створив Job? Якщо lastScheduleTime порожній або застарілий, огляньте розклад, прапорець призупинення, дедлайн та події контролера. Якщо Job’и існують, але зазнають невдачі, перенесіть свою увагу на Job та Pod’и. Це розділення вберігає вас від зміни полів повторних спроб, коли справжня проблема — призупинений CronJob або пропущений розклад.
Імена також можуть скеровувати розслідування. Job’и, створені CronJob’ом, зазвичай містять ім’я CronJob’а плюс згенеровані суфікси, а їхні Pod’и несуть мітки, що пов’язують їх назад із Job’ом. Використовуйте посилання власника та мітки замість здогадок за позначками часу, коли кілька пакетних навантажень працюють у тому самому просторі імен. Це стає важливим під час збоїв, коли кілька CronJob’ів можуть створити запізнілу або збійну роботу приблизно в один час.
# Check cronjob statusk describe cronjob my-cronjob
# Check last schedule timek get cronjob my-cronjob -o jsonpath='{.status.lastScheduleTime}'
# Check if suspendedk get cronjob my-cronjob -o jsonpath='{.spec.suspend}'
# Resume if suspendedk 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 і зафіксуйте те, що лишилося.
# Create a job that simulates a database backupk create job db-backup --image=busybox -- sh -c "echo 'Backing up database' && sleep 5 && echo 'Backup complete'"
# Wait for completionk wait --for=condition=complete job/db-backup --timeout=60sk get job db-backup
# Check logsk logs job/db-backupНотатки до розв'язку для Завдання 1
Job має досягти умови Complete, а логи мають показати обидва повідомлення резервного копіювання. Якщо очікування завершиться за тайм-аутом, опишіть Job і перелічіть Pod’и з міткою job-name=db-backup, перш ніж щось змінювати. Це тримає ваше розслідування узгодженим з ієрархією контролерів.
Завдання 2: Створіть CronJob і запустіть його вручну
Розділ «Завдання 2: Створіть CronJob і запустіть його вручну»Тепер створіть запланований контролер, але не чекайте, поки годинник доведе, що шаблон працює. Job, створений вручну з CronJob’а, дає швидкий зворотний зв’язок щодо образу, команди та шаблону Pod’а, тримаючи діагностику розкладу окремо.
Це робочий процес, який багато команд використовують перед увімкненням нового розкладу. Вони створюють або оновлюють CronJob, запускають один Job вручну, оглядають логи та статус, і лише потім довіряють розкладу. Це уникає незручного патерну чекання до наступної години, виявлення збою образу чи команди, редагування під тиском часу і потім чекання знову. Ручний запуск перетворює заплановане навантаження на тестований шаблон.
# Create cronjob for hourly cleanupk create cronjob hourly-cleanup \ --image=busybox \ --schedule="0 * * * *" \ -- sh -c 'echo "Cleanup at $(date)"'
# Manually trigger for testingk create job manual-cleanup --from=cronjob/hourly-cleanup
# Check resultsk get jobsk wait --for=condition=complete job/manual-cleanup --timeout=60sk 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’и, які не вдалися.
# Create parallel-job.yamlcat << 'EOF' > parallel-job.yamlapiVersion: batch/v1kind: Jobmetadata: name: parallel-processspec: 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: NeverEOF
k apply -f parallel-job.yamlk get pods -l job-name=parallel-processk wait --for=condition=complete job/parallel-process --timeout=60sk get job parallel-processНотатки до розв'язку для Завдання 3
Job націлений на шість успішних завершень, дозволяючи два активні Pod’и водночас. Залежно від того, коли ви перелічуєте Pod’и, ви можете побачити запущені Pod’и, завершені Pod’и або їх суміш. Важливе спостереження: Kubernetes запускає більше Pod’ів, коли попередні завершення закінчуються, доки не буде досягнуто цільової кількості завершень.
Завдання 4: Відпрацюйте сфокусовані тренування CKAD
Розділ «Завдання 4: Відпрацюйте сфокусовані тренування CKAD»Ці тренування навмисно короткі, але не ставтеся до них лише як до запам’ятовування. Після кожної команди назвіть поведінку контролера, яку ви очікуєте, перш ніж перевіряти вивід. Ця звичка робить екзаменаційну діагностику швидшою, бо ви помічаєте, коли об’єкт поводиться інакше, ніж життєвий цикл, який ви мали на думці.
Тренування з повторними спробами особливо корисне, бо створює контрольований збій. Збійний Job тут не випадковість; це лабораторний інструмент. Поспостерігайте, як збійні Pod’и накопичуються з restartPolicy: Never, як backoffLimit змінюється, коли контролер припиняє спроби, і як k describe job підсумовує стан. Коли ви побачили навмисний збій, випадковий збій стає набагато менш загадковим.
# 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 completionk wait --for=condition=complete job/hello-job --timeout=60sk get job hello-job
# Check logsk logs job/hello-job
# Cleanupk delete job hello-job# 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 checksleep 65k get jobsEVERY_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 jobk wait --for=condition=complete "job/${EVERY_MINUTE_JOB}" --timeout=60sk logs "job/${EVERY_MINUTE_JOB}"
# Cleanupk delete cronjob every-minute# Create a job that fails and retriescat << 'EOF' | k apply -f -apiVersion: batch/v1kind: Jobmetadata: name: retry-jobspec: backoffLimit: 3 template: spec: containers: - name: fail image: busybox command: ["sh", "-c", "echo 'Trying...' && exit 1"] restartPolicy: NeverEOF
# Wait for the retry policy to finishk wait --for=condition=failed job/retry-job --timeout=120sk get pods -l job-name=retry-job
# Check job statusk describe job retry-job | grep -A5 Conditions
# Cleanupk delete job retry-job# Create a parallel jobcat << 'EOF' | k apply -f -apiVersion: batch/v1kind: Jobmetadata: name: parallelspec: completions: 5 parallelism: 2 template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo Worker done && sleep 2"] restartPolicy: NeverEOF
# Observe active Pods, then wait for all completionsk get pods -l job-name=parallelk wait --for=condition=complete job/parallel --timeout=60sk get job parallel
# Cleanupk delete job parallel# Create cronjob that forbids overlapcat << 'EOF' | k apply -f -apiVersion: batch/v1kind: CronJobmetadata: name: no-overlapspec: schedule: "* * * * *" concurrencyPolicy: Forbid jobTemplate: spec: template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo 'Start' && sleep 90 && echo 'Done'"] restartPolicy: NeverEOF
# Check policyk get cronjob no-overlap -o jsonpath='{.spec.concurrencyPolicy}'
# Wait 2 minutes and verify at most one CronJob-owned Job is activesleep 120k 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
# Cleanupk 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 — це планувальник та оболонка виконання; сам по собі він не є повним продуктом резервного копіювання.
# 1. Create configmap with backup scriptk create configmap backup-script --from-literal=script.sh='#!/bin/shecho "Starting backup at $(date)"echo "Compressing data..."sleep 3echo "Uploading to storage..."sleep 2echo "Backup complete at $(date)"'
# 2. Create CronJob using the scriptcat << 'EOF' | k apply -f -apiVersion: batch/v1kind: CronJobmetadata: name: backup-systemspec: 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-scriptEOF
# 3. Test with manual triggerk create job test-backup --from=cronjob/backup-system
# 4. Check logsk wait --for=condition=complete job/test-backup --timeout=60sk logs job/test-backup
# 5. Verify history limitsk get cronjob backup-system -o jsonpath='{.spec.successfulJobsHistoryLimit}'
# Cleanupk delete job test-backupk delete cronjob backup-systemk 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 виконувався в типовому неіндексованому режимі?
Джерела
Розділ «Джерела»- Kubernetes Documentation: Jobs
- Kubernetes Documentation: CronJob
- Kubernetes Documentation: TTL Mechanism for Finished Jobs
- Kubernetes Documentation: Pod Lifecycle
- Kubernetes Documentation: Managing Resources for Containers
- Kubernetes Documentation: Configure a Pod to Use a ConfigMap
- Kubernetes Documentation: Labels and Selectors
- Kubernetes Documentation: kubectl create job
- Kubernetes Documentation: kubectl create cronjob
- Kubernetes API Reference: batch/v1
Наступний модуль
Розділ «Наступний модуль»Модуль 1.3: Багатоконтейнерні Pod’и — патерни sidecar, init та ambassador допомагають проєктувати Pod’и, де кілька контейнерів співпрацюють, замість того щоб втискати кожен обов’язок в один образ.