Модуль 2.4: Jobs та CronJobs
Складність:
[ШВИДКИЙ]— прості пакетні робочі навантаженняЧас на проходження: 30-40 хвилин
Передумови: Модуль 2.1 (Pods)
Що ви зможете зробити
Розділ «Що ви зможете зробити»Після цього модуля ви зможете:
- Реалізувати Jobs та CronJobs з відповідними налаштуваннями completions, parallelism, політики перезапуску та повторних спроб.
- Діагностувати невдалі Jobs, пов’язуючи між собою статус Job, події Pod’ів, поведінку завершення контейнера та логи.
- Налаштувати планування CronJob, політику конкурентності, збереження історії, призупинення та ручний запуск для експлуатаційного використання.
- Порівняти Jobs, CronJobs та Deployments, щоб обрати правильний контролер для пакетної чи довготривалої роботи.
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Гіпотетичний сценарій: ви розгортаєте оновлення застосунку, яке потребує міграції бази даних, перш ніж нові Pod’и зможуть безпечно обслуговувати трафік. Якщо цю міграцію запакувати як Deployment, Kubernetes намагатиметься виконувати її нескінченно, і за успішною міграцією можуть піти повторні дублювальні спроби, що небезпечно для бази даних. Якщо ж запускати її вручну з термінала, робота залежить від того, чи залишиться живою людська сесія і чи пам’ятає хтось, як саме аналізувати збій, коли той станеться. Job дає Kubernetes правильний контракт для такого випадку: виконуй це завдання, доки не настане потрібна кількість успішних завершень, повторюй спроби лише в межах контрольованого бюджету та зберігай достатньо статусу й логів для подальшого налагодження.
CronJobs розв’язують повторювану, періодичну версію тієї самої проблеми. Резервні копії, генерація звітів, оновлення кешу, процедури очищення, перевірки сертифікатів та пакетні імпорти зазвичай не потребують ані стабільної адреси Service, ані безперервних реплік, що завжди працюють. Натомість їм потрібні розклад, шаблон для завдання та свідомий вибір політик для запізнілих або накладених один на одного запусків. CronJob акуратно огортає ці рішення навколо шаблону Job, тож кожне заплановане виконання стає звичайним Job зі звичайними Pod’ами, логами, подіями, повторними спробами та поведінкою очищення, яку ви вже вмієте інспектувати.
Іспит CKA схильний перевіряти цю тему через практичну роботу, а не через теоретичні дрібниці чи запам’ятовування означень. Вас можуть попросити створити Job імперативно, отримати YAML за допомогою виводу dry-run, скоригувати parallelism, усунути несправність невдалого пакетного завдання або вручну запустити CronJob у відповідь на конкретну умову. Глибша навичка, яку перевіряють під усім цим, — це розпізнавання контракту контролера: Deployments підтримують бажану кількість довготривалих реплік, Jobs прагнуть успішного завершення, а CronJobs створюють Jobs за календарним розкладом. Щойно цей контракт стає зрозумілим, команди й окремі поля стають набагато легшими для осмислення в умовах браку часу, бо ви вже знаєте, якої поведінки очікувати від кожного об’єкта.
Jobs: завершення — це бажаний стан
Розділ «Jobs: завершення — це бажаний стан»Job — це контролер Kubernetes для скінченної роботи. Від Pod’а під управлінням Deployment очікують, що він продовжуватиме обслуговування, доки щось його не замінить, тож чисте завершення трактується як проблема, яку треба зцілити. Від Pod’а під управлінням Job очікують, що він завершиться, і важливе питання полягає в тому, чи достатньо Pod’ів завершилося успішно. Саме ця відмінність пояснює, чому Pod’и Job вимагають restartPolicy: Never або restartPolicy: OnFailure; Always суперечив би самій ідеї завершення, перезапускаючи контейнери навіть після того, як завдання вже виконане.
Уявіть Job радше як заявку в черзі на обслуговування, ніж як працівника, закріпленого за постійним робочим місцем. Контролер Job створює один або кілька Pod’ів, спостерігає за їхніми фазами та результатами завершення і позначає Job завершеним, коли потрібна робота виконана успішно. Якщо Pod зазнає невдачі, контролер вирішує, чи дозволена ще одна спроба. Якщо ж раніше вичерпається бюджет повторних спроб або дедлайн, Job стає невдалим і зберігає достатньо стану, щоб ви могли дослідити, що сталося.
Цей зв’язок із контролером також пояснює, чому для пакетної роботи зазвичай не створюють Pod безпосередньо, навіть коли це здається швидшим. Окремий Pod може виконати команду й завершитися, але в нього немає об’єкта вищого рівня, який вирішував би, чи потрібна ще одна спроба, чи достатньо спроб виявилися успішними, чи слід тепер вважати всю операцію невдалою. Job додає саме цей відсутній намір над голим Pod’ом. Він володіє Pod’ами через мітки та посилання-власники (owner references), фіксує статус на рівні контролера й дає вам один стабільний об’єкт, на який можна чекати, навіть якщо самі Pod’и під ним можуть з’являтися та зникати протягом виконання.
┌────────────────────────────────────────────────────────────────┐│ Job Lifecycle ││ ││ Job Created ││ │ ││ ▼ ││ Pod Created ─────────────────────────────────────────┐ ││ │ │ ││ ▼ │ ││ Pod Running │ ││ │ │ ││ ├───► Exit 0 (Success) ──► Job Complete │ ││ │ │ ││ └───► Exit ≠ 0 (Fail) ──► Retry? ──────────────►┘ ││ (based on backoffLimit) ││ │└────────────────────────────────────────────────────────────────┘Найменший корисний Job має шаблон Pod’а та політику перезапуску. Приклад нижче обчислює цифри числа пі за допомогою контейнера, який завершується після виконання команди. backoffLimit — це не налаштування продуктивності; це налаштування бюджету помилок для невдалих спроб. Низьке значення швидко виявляє системні проблеми, тоді як вище значення дає тимчасовим завантаженням образів, перериванням роботи вузлів чи нестабільним зовнішнім залежностям більше шансів, перш ніж Job буде визнано невдалим.
До шаблону Pod’а всередині Job слід ставитися з тією самою ретельністю, що й до будь-якого іншого шаблону робочого навантаження. Образи слід закріплювати згідно з практикою релізів вашої команди, команди мають чітко завершуватися помилкою, коли бракує вхідних даних, а залежності від змінних середовища чи томів мають бути явними. Job робить завдання повторюваним лише тоді, коли повторюваним є шаблон. Якщо людині доводиться пам’ятати про додаткову змінну оболонки чи вручну завантажувати файл перед кожним запуском, маніфест ще не є надійним експлуатаційним інструментом.
apiVersion: batch/v1kind: Jobmetadata: name: pi-calculationspec: template: spec: containers: - name: pi image: perl command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never # Required for Jobs backoffLimit: 4 # Retry up to 4 times on failureІмперативне створення корисне на іспиті та під час швидких експлуатаційних перевірок, але ви все одно маєте розуміти YAML, який воно породжує. kubectl create job створює одноразовий шаблон Job з образу та команди, що йдуть після роздільника --. Форма dry-run особливо цінна, бо дозволяє згенерувати валідний початковий маніфест, додати поля на кшталт completions чи ttlSecondsAfterFinished, а потім застосувати відредаговану версію.
# Create job imperativelykubectl create job pi --image=perl -- perl -Mbignum=bpi -wle "print bpi(100)"
# Generate YAMLkubectl create job pi --image=perl --dry-run=client -o yaml -- perl -Mbignum=bpi -wle "print bpi(100)"Повсякденні команди інспекції повторюють той самий патерн, який ви вже знаєте за Pod’ами та Deployments: перелічити контролер, описати його для отримання умов і подій, а потім прочитати логи з Pod’а або через ярлик Job. kubectl logs job/pi-calculation зручний, коли є один очевидний Pod для вибору, але під час збоїв вам часто потрібні й самі імена Pod’ів. Невдалі Pod’и можуть залишатися після того, як Job припинив повторні спроби, і саме ці старі Pod’и часто є найкориснішим доказом.
Тут є корисна дисципліна: інспектуйте ззовні всередину. Job каже вам, чи вважає контролер, що бажаного стану досягнуто. Pod’и кажуть вам, скільки спроб відбулося і яких фаз вони сягнули. Контейнери кажуть вам причину успіху чи невдачі на рівні застосунку. Якщо почати одразу з логів контейнера, можна пропустити збій планування, дедлайн чи ліміт повторних спроб, який пояснює, чому логи неповні.
# List jobskubectl get jobs
# Watch job progresskubectl get jobs -w
# Describe jobkubectl describe job pi-calculation
# Get job logskubectl logs job/pi-calculation
# Delete job (also deletes pods)kubectl delete job pi-calculationЗробіть паузу й передбачте: Job має restartPolicy: Never та backoffLimit: 4. Контейнер зазнає невдачі за кожної спроби. Перш ніж читати далі, що ви очікуєте побачити в kubectl get pods після того, як Job здасться, і чим це відрізнятиметься від restartPolicy: OnFailure?
Відповідь залежить від того, де відбувається повторна спроба. З Never Kubernetes зазвичай створює свіжий Pod для кожної невдалої спроби, тож початковий невдалий Pod залишається видимим, а додаткові невдалі Pod’и з’являються, доки Job не сягне свого ліміту. З OnFailure kubelet перезапускає контейнер усередині того самого Pod’а, тож докази зосереджені в лічильнику перезапусків Pod’а та попередніх логах. Обидві політики можуть бути правильними, але Never часто легше пояснювати та інспектувати, бо кожна спроба — це окремий об’єкт.
spec: template: spec: restartPolicy: Never # New pod per failure # restartPolicy: OnFailure # Restart same pod| Політика | Поведінка |
|---|---|
Never | Зазвичай створює новий Pod після невдачі |
OnFailure | Перезапускає контейнер у тому самому Pod’і при невдачі |
Найпоширеніша помилка початківця — трактувати backoffLimit як гарантію того, скільки саме Pod’ів існуватиме. Безпечніше трактувати його як політику контролера, а потім інспектувати статус і події, щоб побачити, що сталося насправді. Заміна Pod’ів, поведінка перезапуску контейнера, дедлайни та тайминг контролера — усе це може впливати на те, що ви спостерігаєте в конкретний момент. Для іспиту практичний урок простіший: задайте навмисний бюджет повторних спроб, знайте, яку політику перезапуску ви обрали, і використовуйте логи Pod’ів плюс умови Job для підтвердження результату.
Ще одна практична деталь — іменування. Імена Job стають частиною згенерованих імен Pod’ів та міток, тож короткі описові імена допомагають під час усунення несправностей. Ім’я на кшталт backup чи report-daily легше фільтрувати, ніж довге речення, закодоване з пунктуацією. Для ручних повторних запусків обирайте унікальні імена, бо імена об’єктів Kubernetes діють у межах простору імен. Якщо ви повторно використаєте ім’я, поки ще існує старий Job, API відхилить запит на створення, і ця затримка може коштувати дорогоцінних іспитових хвилин.
Completions, Parallelism та форма роботи
Розділ «Completions, Parallelism та форма роботи»Багато Jobs не зводяться до «виконати один Pod один раз». Пакетний імпорт може мати сто незалежних файлів, генератор звітів може потребувати п’яти сегментів, а відновлення даних може потребувати кількох виконавців, що тягнуть із черги. Kubernetes дає вам дві важливі ручки для цієї форми: completions, яка каже, скільки успішних завершень Pod’ів потрібно, та parallelism, яка каже, скільки Pod’ів можуть виконуватися одночасно. Контролер продовжує створювати замінні Pod’и, доки не буде досягнуто цільової кількості успіхів або політика невдач не зупинить Job.
Ці ручки розділені, бо вони відповідають на два окремі питання. completions відповідає на питання «скільки успішних одиниць роботи роблять цей Job завершеним?», тоді як parallelism відповідає на питання «скільки роботи може відбуватися одночасно?». Корисна аналогія тут — з рестораном: можливо, потрібно приготувати сто талонів на страви, але на кухні доступні лише шість конфорок. Друк більшої кількості талонів не додає конфорок, а вмикання більшої кількості конфорок не зменшує кількості страв, які все ще треба приготувати. Jobs використовують ту саму відмінність між обсягом і темпом для пакетних Pod’ів.
apiVersion: batch/v1kind: Jobmetadata: name: batch-jobspec: completions: 5 # Job succeeds when 5 pods complete successfully parallelism: 2 # Run 2 pods at a time template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo Processing item; sleep 5"] restartPolicy: NeverЗв’язок між completions та parallelism легко уявити як набір смуг руху. Якщо completions дорівнює п’яти, а parallelism дорівнює двом, Kubernetes може тримати два Pod’и активними, але йому все одно потрібно п’ять успішних завершень, перш ніж Job стане завершеним. Невдалий Pod не зараховується до успішних завершень, тож контролер створює ще одну спробу, якщо політика невдач це все ще дозволяє. Ця модель допомагає уникнути помилки задавати високий parallelism і вважати, що це означає швидше виконання завдання, незалежно від того, що насправді робить робота.
┌────────────────────────────────────────────────────────────────┐│ Completions=5, Parallelism=2 ││ ││ Time ─────────────────────────────────────────────────► ││ ││ Slot 1: [Pod 1 ✓] [Pod 3 ✓] [Pod 5 ✓] ││ Slot 2: [Pod 2 ✓] [Pod 4 ✓] ││ ││ 2 pods run concurrently, until 5 completions achieved ││ │└────────────────────────────────────────────────────────────────┘| Патерн | completions | parallelism | Поведінка |
|---|---|---|---|
| Один Pod | 1 (за замовч.) | 1 (за замовч.) | Один Pod виконується до завершення |
| Фіксовані completions | N | M | M Pod’ів виконуються паралельно, доки N не успішних |
| Черга роботи | не задано | N | N Pod’ів виконуються, доки один не успішний |
Parallelism допомагає лише тоді, коли завдання можна безпечно розділити. Якщо кожен Pod пише той самий вихідний файл, мігрує той самий рядок схеми або споживає API, яке не витримує сплесків, збільшення parallelism може перетворити пакетне завдання на стан гонитви. Якщо ж кожен Pod обробляє незалежний сегмент або тягне окрему роботу з черги, parallelism — це правильний важіль масштабування. Контролер Job не розуміє ваших бізнес-правил ідемпотентності, тож команда Pod’а та опорна система мають зробити дублювальні чи замінні спроби безпечними.
В експлуатації кластера parallelism також конкурує з квотою та плановою місткістю. Простір імен може мати достатньо CPU для двох виконавців, але не для десяти, а кластер може затримувати Pod’и, якщо кожен виконавець запитує велику кількість пам’яті. Коли Job виглядає повільнішим, ніж очікувалося, не вважайте, що контролер проігнорував parallelism. Перевірте події Pod’ів на предмет очікування планування, помилок квоти ресурсів, затримки завантаження образу та тиску на вузли. Job може запитувати конкурентність, але планувальник усе ще вирішує, де кожен Pod справді може виконуватися.
# Scale parallelism of an existing job (completions is immutable)kubectl create job batch --image=busybox -- sh -c "echo done; sleep 30"kubectl patch job batch -p '{"spec":{"parallelism":3}}'
# Or create with YAMLcat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: parallel-jobspec: completions: 10 parallelism: 3 template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo Task complete; sleep 2"] restartPolicy: NeverEOF
# Wait for completionkubectl wait --for=condition=complete job/parallel-job --timeout=90skubectl get jobs parallel-jobЗверніть увагу на коментар у тому блоці команд: completions фактично є частиною робочого контракту Job, тож зазвичай ви обираєте його до створення. parallelism — безпечніше коригування під час виконання, бо воно контролює, скільки виконавців дозволено одночасно. Під час інциденту зменшення parallelism може сповільнити галасливе пакетне завдання, не видаляючи Job, тоді як збільшення може допомогти осушити безпечне накопичення роботи. Навичка — це не запам’ятовування незмінності; це розпізнавання, чи ви змінюєте обсяг роботи, чи лише темп роботи.
Jobs у стилі черги роботи заслуговують на особливу обережність, бо саме черга, а не Job, вирішує, який елемент обробляє кожен Pod. Це може бути потужним рішенням, коли виконавці претендують на елементи атомарно й позначають їх завершеними в зовнішній системі. Це також може заплутати під час іспиту, бо сам маніфест не показує кількості елементів, що чекають у черзі. Для завдань у стилі CKA фіксовані completions зазвичай легше осмислити, якщо тільки умова явно не описує робоче навантаження на основі черги.
Для більш просунутих пакетних рішень Kubernetes також підтримує індексовані Jobs, де кожен Pod отримує індекс завершення. Цей патерн корисний, коли виконавець 0 має обробляти сегмент 0, виконавець 1 має обробляти сегмент 1 тощо. Для базових завдань CKA в цьому модулі індексовані Jobs вам не потрібні, але знання про їхнє існування допомагає пояснити, чому звичайні Jobs навмисно прості. Почніть зі звичайних completions та parallelism, а до індексів вдавайтеся лише тоді, коли робота вимагає стабільної ідентичності для кожного завершення.
Якщо ви проєктуєте справжню пакетну платформу, вирішіть, де фіксується прогрес, перш ніж обирати форму Job. Фіксовані completions фіксують прогрес у Kubernetes, рахуючи успішні Pod’и, тоді як виконавці черги зазвичай фіксують прогрес у черзі чи базі даних. Індексовані Jobs ділять різницю навпіл, даючи Kubernetes стабільний індекс для кожного завершення, але застосунок усе одно має відображати цей індекс у змістовну роботу. Найчистіше рішення — те, у якому невдалий Pod можна повторити без того, щоб оператор гадав, що той уже змінив.
Перш ніж це запустити, який вивід ви очікуєте від Job із completions: 10, parallelism: 3 та командою, яка спить дві секунди? Ви маєте очікувати не більше трьох активних Pod’ів одночасно, кілька завершених Pod’ів за час життя Job та фінальний статус Job 10/10, щойно всі успішні завершення будуть зараховані. Якщо ваш кластер створює менше активних Pod’ів, шукайте обмеження планування, затримку завантаження образу, квоту простору імен або нижче значення parallelism, ніж ви хотіли.
Обробка невдач, дедлайни та докази для налагодження
Розділ «Обробка невдач, дедлайни та докази для налагодження»Обробка невдач — це те, де Jobs стають по-справжньому експлуатаційно цікавими, бо реальні завдання зазнають невдач із багатьох незалежних причин. Пакетне завдання може зазнати невдачі, бо образ не вдається завантажити, команда завершується ненульовим кодом, відсутній ConfigMap, зникає вузол, перевищено дедлайн або зовнішня залежність відмовляє в трафіку. Контролер Job не розв’язує ці проблеми застосунку за вас, але він дає структуроване місце, куди дивитися, замість здогадів: умови Job кажуть вам, чи вважає контролер роботу завершеною чи невдалою, Pod’и кажуть, що сталося під час кожної окремої спроби, а події пояснюють проблеми планування чи рівня образу, яких немає в логах самого застосунку.
Хороша політика невдач починається з очікуваного режиму невдачі. Якщо команда хибна, більше повторних спроб лише повторюють ту саму помилку й заповнюють простір імен невдалими Pod’ами. Якщо віддалене API іноді повертає тимчасову помилку, кілька повторних спроб можуть бути саме тим, що вам потрібно. Якщо переривання роботи вузла вбиває Pod, заміна доречна, доки завдання можна безпечно відновити. Маніфест має виражати ці очікування, а не копіювати значення повторних спроб з іншого робочого навантаження.
apiVersion: batch/v1kind: Jobmetadata: name: failing-jobspec: backoffLimit: 3 # Retry 3 times, then fail template: spec: containers: - name: fail image: busybox command: ["sh", "-c", "exit 1"] # Always fails restartPolicy: NeverbackoffLimit — це перший запобіжник, бо він зупиняє зламане завдання від нескінченного створення спроб. Задавайте його низьким, коли невдача ймовірно детермінована, як-от хибна команда, відсутній файл чи невалідний аргумент. Задавайте його вищим, коли очікуються тимчасові невдачі, а завдання ідемпотентне. Це значення не є заміною оповіщень чи інспекції логів; це рішення на рівні контролера про те, коли Kubernetes має припинити витрачати місткість кластера на спроби, що не успішні.
Поведінка відкату також впливає на те, як швидко ви побачите фінальну умову невдачі. Kubernetes не обов’язково повторює спроби в щільному циклі; контролери використовують патерни відкату, щоб не бомбардувати кластер негайними замінами. Це означає, що Job з кількома дозволеними невдачами може зазнавати невдачі довше, ніж припускало б лише середовище виконання контейнерів. Під час усунення несправностей стежте і за Job, і за його Pod’ами, щоб відрізнити «усе ще повторює спроби» від «застряг, бо жоден Pod не вдається запланувати».
apiVersion: batch/v1kind: Jobmetadata: name: timeout-jobspec: activeDeadlineSeconds: 60 # Kill job after 60 seconds template: spec: containers: - name: long-task image: busybox command: ["sleep", "120"] # Tries to run 2 minutes restartPolicy: NeveractiveDeadlineSeconds — це інший вид захисту. Він накладає обмеження за настінним часом на Job, тож навіть не вичерпаний бюджет повторних спроб не може тримати Job живим після дедлайну. Це корисно, коли завдання цінне лише в межах часового вікна, як-от звіт, що має завершитися до робочих годин, чи процедура очищення, що не повинна заходити в наступне вікно обслуговування. Якщо існують і ліміт відкату, і дедлайн, то результат визначає та умова зупинки, яка стає істинною першою.
Дедлайни особливо важливі для CronJobs, бо запланована робота може концептуально накопичуватися, навіть коли об’єкти не накладаються. Щоденне завдання, що зазвичай триває десять хвилин, але раптом виконується годинами, усе ще може бути активним, коли починається наступне експлуатаційне вікно. Дедлайн змушує завдання оголосити невдачу замість того, щоб мовчки споживати час. Поєднуйте його з корисними логами та оповіщеннями, бо дедлайн без доказів лише каже вам, що час вичерпався, а не чому завдання не змогло завершитися.
Зробіть паузу й передбачте: Job із activeDeadlineSeconds: 60 та backoffLimit: 10 виконує контейнер, що триває п’ятнадцять секунд за спробу й завжди зазнає невдачі. Який запобіжник, на вашу думку, спрацює першим, і яка додаткова затримка може зробити точну кількість Pod’ів відмінною від простої арифметичної оцінки?
Правильне передбачення починається з дедлайну, а не з лічильника повторних спроб. Чотири п’ятнадцятисекундні спроби вже споживають цілу хвилину, перш ніж ви врахуєте час планування, запуск образу, затримку відкату та узгодження контролера. У справжньому кластері ви не повинні обіцяти точну кількість Pod’ів лише з маніфесту. Ви маєте інспектувати умову Job, перелічити Pod’и за job-name та прочитати події, щоб підтвердити, чи зупинився контролер тому, що сягнув дедлайну, ліміту відкату чи іншого режиму невдачі.
# Job statuskubectl get job myjob# NAME COMPLETIONS DURATION AGE# myjob 3/5 2m 5m
# Detailed statuskubectl describe job myjob | grep -A5 "Pods Statuses"
# Check failed podskubectl get pods -l job-name=myjob --field-selector=status.phase=Failed| Проблема | Симптом | Команда налагодження |
|---|---|---|
| Збій завантаження образу | Pod у ImagePullBackOff | kubectl describe pod <pod> |
| Збій команди | Job може не завершитися успішно | kubectl logs job/<job-name> |
| Тайм-аут | Job убито | Перевірте activeDeadlineSeconds |
| Забагато повторних спроб | Кілька невдалих Pod’ів | Перевірте backoffLimit |
Робочий процес налагодження має рухатися від контролера до Pod’а й до виводу контейнера. Почніть з kubectl get job, бо він каже вам кількість завершень та умову високого рівня. Використовуйте kubectl describe job, бо він збирає події контролера, підрахунки статусу Pod’ів та повідомлення про невдачі. Потім перелічіть Pod’и з міткою job-name, бо невдалий Job може мати кілька Pod’ів, і найновіший не завжди той, що має найчіткіший доказ.
# 1. Check job statuskubectl get job myjobkubectl describe job myjob
# 2. Find pods created by jobkubectl get pods -l job-name=myjob
# 3. Check pod logskubectl logs <pod-name>kubectl logs job/myjob # Auto-selects a pod
# 4. If still running, exec into podkubectl exec -it <pod-name> -- /bin/sh
# 5. Check eventskubectl get events --field-selector involvedObject.name=myjobЛоги необхідні, але недостатні. Збій завантаження образу може не мати логів застосунку, бо контейнер ніколи не запускався, тоді як проблема планування на вузлі може з’явитися лише в подіях. Команда, що швидко завершується, може залишити Pod у стані Error з корисними логами, але політика перезапуску OnFailure може вимагати kubectl logs --previous на Pod’і, щоб прочитати останню невдалу спробу контейнера. Тож хороше налагодження Job — це послідовність: статус контролера, Pod’и за міткою, опис Pod’а, логи та події.
Селектор міток — ваш друг, коли згенеровані імена Pod’ів важко запам’ятати. Jobs автоматично позначають свої Pod’и іменем Job, тож kubectl get pods -l job-name=myjob дає вам набір спроб без копіювання довгих імен Pod’ів з пам’яті. Саме тому видалення чи перепозначення Pod’ів вручну може ускладнити налагодження. Дайте контролеру володіти об’єктами й використовуйте мітки, щоб спостерігати зв’язок, який Kubernetes уже підтримує для вас.
Коли важливе очищення, віддавайте перевагу ttlSecondsAfterFinished над ручними звичками. Завершені Jobs та їхні Pod’и залишаються за замовчуванням, щоб ви могли їх інспектувати, але завантажений простір імен може накопичити багато завершених об’єктів. Контролер TTL може видаляти завершені Jobs після обраної кількості секунд, що також видаляє їхні залежні Pod’и. Використовуйте його після того, як ви впевнені, що логи відправляються кудись у надійне сховище або що доказів від завершеного Pod’а вже не потрібно для звичайного усунення несправностей.
Політика очищення — це компроміс спостережуваності, а не лише прибирання. Зберігання кожного завершеного Job назавжди робить kubectl get jobs галасливим і може сповільнити людську діагностику під час інциденту. Негайне видалення тримає простір імен охайним, але прибирає зручний доступ до логів та статусу Pod’ів. Збалансований підхід зберігає достатньо нещодавньої історії для звичайних питань і відправляє важливі логи в надійну систему. У іспитовому просторі імен ручне очищення цілком прийнятне; у промисловому просторі імен робіть очищення навмисним.
CronJobs: планування Jobs без приховування Job
Розділ «CronJobs: планування Jobs без приховування Job»CronJob — це розклад плюс шаблон Job, і не більше. У кожен запланований час контролер CronJob створює Job, а той Job, своєю чергою, створює Pod’и точно так само, як одноразові Jobs, які ви вже інспектували в попередніх розділах. Це нашарування важливе саме тому, що воно запобігає плутанині під час налагодження. Якщо розклад не спрацював узагалі, інспектуйте CronJob. Якщо ж запланований запуск спрацював, але робота зазнала невдачі, інспектуйте Job та Pod’и, створені для цього конкретного запуску. CronJob вирішує лише те, коли створювати роботу; Job вирішує, чи ця робота справді завершилася.
Згенеровані Jobs — це звичайні Jobs Kubernetes, тобто ваші наявні навички роботи з Job усе ще застосовні. Ви можете описувати їх, чекати на завершення, інспектувати їхні Pod’и та читати їхні логи. Основна відмінність — у володінні та іменуванні: CronJob володіє розкладом і створює Jobs зі згенерованими іменами. Коли ви налагоджуєте, тримайте ланцюжок «батько-дитина» чітким у своїх нотатках: розклад CronJob, згенерований Job, згенерований Pod, команда контейнера.
┌────────────────────────────────────────────────────────────────┐│ CronJob ││ ││ Schedule: "0 * * * *" (hourly) ││ ││ 1:00 ──► Creates Job ──► Creates Pod ──► Completes ││ 2:00 ──► Creates Job ──► Creates Pod ──► Completes ││ 3:00 ──► Creates Job ──► Creates Pod ──► Completes ││ ... ││ │└────────────────────────────────────────────────────────────────┘Розклади CronJob у Kubernetes використовують знайомий п’ятипольовий формат cron, той самий, що й системний cron на Linux. Найлівіше поле — це хвилина, потім година, день місяця, місяць та, нарешті, день тижня. Цей порядок полів — класичне джерело помилок, бо люди часто спершу промовляють «друга година ночі», тоді як cron записує хвилину перед годиною. Коли ви читаєте вираз 0 2 * * *, перекладайте його вголос як «хвилина нуль, година два, щодня». Ця проста звичка ловить багато помилок розкладу ще до того, як вони сягнуть кластера й створять не той Job не в той час.
Розклади також мають бути реалістичними щодо тривалості завдання. Розклад «щохвилини» чудовий для лабораторної, бо ви можете швидко побачити запуск, але він рідко доречний для дорогої роботи. Якщо завдання регулярно триває вісім хвилин, п’ятихвилинний розклад змушує вас приймати рішення про конкурентність кожного запуску. Іноді правильне виправлення — це не Forbid чи Replace; це зміна інтервалу розкладу так, щоб у завдання було достатньо часу на завершення за звичайних умов.
┌───────────── minute (0 - 59)│ ┌───────────── hour (0 - 23)│ │ ┌───────────── day of month (1 - 31)│ │ │ ┌───────────── month (1 - 12)│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday = 0)│ │ │ │ │* * * * *| Розклад | Опис |
|---|---|
* * * * * | Щохвилини |
0 * * * * | Щогодини |
0 0 * * * | Щодня опівночі |
0 0 * * 0 | Щонеділі опівночі |
*/5 * * * * | Кожні 5 хвилин |
0 9-17 * * 1-5 | Щогодини з 9 до 17, пн-пт |
У Kubernetes 1.35 вам також варто знати про часові пояси CronJob. Поле timeZone дозволяє вказати іменований часовий пояс для інтерпретації розкладу, тоді як старіші кластери часто залежали від локального часу controller-manager. Для портативної іспитової роботи найбезпечніше міркувати в стилі UTC, якщо тільки завдання явно не вимагає часового поясу. Для промислової роботи вказуйте часовий пояс, коли важливий місцевий діловий час, бо зміни переходу на літній час та конфігурацію контролера не варто залишати на здогад.
Ще одне пов’язане з розкладом поле, startingDeadlineSeconds, контролює, наскільки пізно запуск CronJob може стартувати, перш ніж Kubernetes порахує його пропущеним. Це має значення, коли контролер не працює, кластер перевантажений або планування затримується поза корисним вікном для завдання. Резервна копія все ще може бути цінною, якщо вона стартує на десять хвилин пізніше, тоді як звіт до відкриття ринку може бути марним після початку наради. Це поле перетворює таке експлуатаційне судження на політику контролера.
apiVersion: batch/v1kind: CronJobmetadata: name: backupspec: schedule: "0 2 * * *" # Daily at 2 AM jobTemplate: spec: template: spec: containers: - name: backup image: busybox command: ["sh", "-c", "echo Backup started; sleep 10; echo Backup done"] restartPolicy: OnFailure successfulJobsHistoryLimit: 3 # Keep 3 successful job records failedJobsHistoryLimit: 1 # Keep 1 failed job recordСекцію jobTemplate багато хто з тих, хто навчається, проскакує. Це не шаблон Pod’а безпосередньо під CronJob; це шаблон Job, який потім містить шаблон Pod’а. Саме через це вкладення ліміти історії, політика конкурентності та призупинення належать CronJob, тоді як політика перезапуску та команда контейнера належать шаблону Pod’а всередині Job. Якщо маніфест відхилено, перевірте відступи та розташування полів, перш ніж припускати, що об’єкт API недоступний.
Редагуючи YAML CronJob під тиском, читайте відступи як шлях. spec.schedule належить CronJob. spec.jobTemplate.spec.template.spec.containers належить Pod’у, який створить згенерований Job. Неправильно розташоване поле може бути або відхилене, або мовчки не зробити того, що ви хотіли, бо воно сидить під неправильним об’єктом. Це одна з причин, чому вивід dry-run корисний: він дає вам правильний каркас, перш ніж ви додасте поля політики.
# Create CronJob imperativelykubectl create cronjob backup --image=busybox --schedule="0 2 * * *" -- sh -c "echo Backup done"
# Generate YAMLkubectl create cronjob backup --image=busybox --schedule="*/5 * * * *" --dry-run=client -o yaml -- echo "hello"Базові команди віддзеркалюють інші контролери. kubectl get cronjobs показує розклад, призупинення, активні запуски та час останнього розкладу. kubectl describe cronjob дає події та згенерований шаблон Job. Ручний запуск за допомогою kubectl create job --from=cronjob/name — одна з найкорисніших експлуатаційних команд, бо вона дозволяє виконати точний запланований шаблон негайно, не чекаючи на наступний тік cron.
Ручний запуск також безпечніший за копіювання команди контейнера в спеціальний Pod. Якщо шаблон CronJob містить змінні середовища, налаштування сервісного акаунта, томи, контекст безпеки чи секрети завантаження образів, --from=cronjob/name зберігає ці деталі. Ручний Job усе ще потребує власного імені, і він не змінить наступний запланований запуск CronJob. Трактуйте його як одноразове виконання того самого шаблону, а не як зміну розкладу.
# List CronJobskubectl get cronjobskubectl get cj # Short form
# Describekubectl describe cronjob backup
# Manually trigger a job from CronJobkubectl create job --from=cronjob/backup backup-manual
# Suspend CronJobkubectl patch cronjob backup -p '{"spec":{"suspend":true}}'
# Resume CronJobkubectl patch cronjob backup -p '{"spec":{"suspend":false}}'
# Delete CronJob (also deletes Jobs it created)kubectl delete cronjob backupЗупиніться й подумайте: у вас є CronJob, який виконує резервне копіювання бази даних щогодини, але іноді резервне копіювання триває дев’яносто хвилин. Із політикою за замовчуванням concurrencyPolicy: Allow два завдання резервного копіювання можуть накластися. Що може піти не так із конкурентними резервними копіями, і яку політику конкурентності ви б обрали натомість?
Накладання не є автоматично хибним, але воно небезпечне, коли завдання торкається спільного стану. Дві резервні копії можуть конкурувати за пропускну здатність диска, утримувати блокування довше, ніж очікувалося, завантажувати об’єкт з тим самим іменем чи захоплювати неузгоджені знімки. Forbid зазвичай є консервативним вибором для резервних копій, бо він пропускає новий запуск, поки попередній усе ще активний. Replace краще підходить для завдань, де найновіший запуск цінніший за завершення застарілої роботи, як-от оновлення кешу, яке можна безпечно перезапустити.
spec: concurrencyPolicy: Allow # Default - allow concurrent jobs # concurrencyPolicy: Forbid # Skip if previous still running # concurrencyPolicy: Replace # Kill previous, start new| Політика | Поведінка |
|---|---|
Allow | Кілька Jobs можуть виконуватися одночасно |
Forbid | Пропустити новий Job, якщо попередній усе ще виконується |
Replace | Зупинити поточний Job, запустити новий |
Ліміти історії — це компаньйон очищення до політики конкурентності. successfulJobsHistoryLimit та failedJobsHistoryLimit вирішують, скільки завершених об’єктів Job зберігає CronJob. Зберігання кількох успішних записів допомагає підтвердити нормальну роботу, а зберігання невдалих записів дає вам докази, коли спрацьовує оповіщення. Зберігання надто багатьох записів створює безлад, тоді як незбереження жодного може ускладнити аналіз першопричини, якщо логи та події не захоплено десь в іншому місці.
Значення історії мають відповідати тому, як часто виконується CronJob і як швидко хтось помітить проблему. Зберігання трьох успішних запусків для щоденної резервної копії дає вам кілька днів видимого підтвердження. Зберігання трьох успішних запусків для завдання рівня хвилини показує лише кілька хвилин історії. Для невдалих запусків зберігання принаймні одного часто варте невеликої вартості об’єкта, бо воно зберігає точний Job, що зазнав невдачі, включно з мітками Pod’ів та подіями.
Призупинення — це ще один експлуатаційний контроль, що належить вашій ментальній моделі. Установлення spec.suspend: true ставить майбутні виконання розкладу на паузу, але не обов’язково видаляє Jobs, що вже існують. Це робить його корисним під час вікон обслуговування, коли ви хочете зупинити нову пакетну роботу без зміни шаблону Job. Коли ви відновлюєте CronJob, перевірте наступний запланований час і стежте за згенерованими Jobs, а не вважайте, що пропущена робота поводитиметься точно як черга.
Призупинення не є механізмом відкату. Якщо хибний шаблон уже породив хибний Job, призупинення CronJob запобігає новим запланованим Jobs, але не лагодить невдалий. Вам усе одно потрібно інспектувати чи видалити згенерований Job, виправити шаблон і вирішити, чи потрібен ручний запуск. Ця відмінність корисна в реагуванні на інциденти, бо вона відокремлює зупинку майбутньої шкоди від прибирання поточних доказів.
Патерни та антипатерни
Розділ «Патерни та антипатерни»Найсильніший патерн для Jobs — зробити завдання ідемпотентним, перш ніж робити його паралельним чи додавати повторні спроби. Ідемпотентність означає, що повторна чи дубльована спроба не псує систему й не залишає її в напівзміненому стані: імпорт може пропускати вже оброблені рядки, резервна копія може писати в унікальний шлях об’єкта, а міграція може фіксувати, яка версія схеми вже застосована. Kubernetes може повторювати контейнери та замінювати Pod’и за вас, але він не може знати, чи безпечно виконувати саме вашу команду двічі поспіль. Цю безпеку доводиться свідомо закладати в проєкт робочого навантаження, а не очікувати її від контролера.
Ще один тривкий патерн — тримати пакетну команду нудною. Складну оркестрацію, приховану всередині однорядкового скрипту оболонки, важко правильно процитувати, важко протестувати й важко прочитати в маніфесті. Покладіть суттєву логіку в образ, скрипт чи застосунок, що має власні тести, а потім дайте маніфесту Job описувати, як Kubernetes має його виконувати. Маніфест має прояснювати поведінку контролера, потреби в ресурсах та вибір політик, а не ставати крихкою мовою програмування.
| Патерн | Коли використовувати | Чому це працює | Міркування щодо масштабування |
|---|---|---|---|
| Одноразовий Job | Міграції, звіти, разові перевірки | Завершення явне та інспектоване | Тримайте backoffLimit низьким, коли невдача детермінована |
| Job з фіксованими completions | Відома кількість сегментів чи файлів | completions задає ціль успіху | Налаштовуйте parallelism під місткість та зовнішні ліміти |
CronJob з Forbid | Резервні копії чи очищення, що не мають накладатися | Пропускає небезпечні конкурентні запуски | Стежте за пропущеними розкладами й коригуйте тривалість чи інтервал |
| CronJob з ручним запуском | Аварійний повторний запуск чи валідація | Повторно використовує точний запланований шаблон | Використовуйте унікальні імена Job для кожного ручного запуску |
Найшкідливіший антипатерн — використання Deployment для скінченної роботи, бо він «уже виконує контейнери». Deployment продовжуватиме узгоджувати репліки, тож команду, що успішно завершується, може бути перезапущено так, ніби вона зазнала невдачі. Ще один поширений антипатерн — закладання складної логіки планування всередину довготривалого Pod’а, ігноруючи політику CronJob. Це приховує стан розкладу від Kubernetes і робить так, що збої виглядають як поведінка застосунку, а не контролера.
Команди також потрапляють у халепу, коли трактують CronJobs як невидимі фонові доручення. Заплановане завдання може бути таким самим критичним, як і сервіс, орієнтований на користувача, особливо коли воно обробляє резервні копії, експорт рахунків чи очищення, що захищає місткість кластера. Дайте важливим CronJobs чіткі імена, навмисну історію, оповіщення про збій чи надмірну тривалість та задокументовані кроки ручного запуску. Те, що завдання заплановане, не робить його самокерованим.
| Антипатерн | Що йде не так | Краща альтернатива |
|---|---|---|
| Deployment для міграції | Успішне завершення перезапускається або трактується як нездорове | Використайте Job із чітким бюджетом повторних спроб |
| Високий parallelism без ідемпотентності | Дубльовані записи, конкуренція за блокування чи неузгоджений вивід | Доведіть безпечність повторних спроб, потім масштабуйте конкурентність |
CronJob Allow для спільного стану | Заплановані запуски накладаються й конкурують | Використайте Forbid чи подовжіть інтервал розкладу |
| Нульова збережена історія без відправки логів | Докази для налагодження зникають надто швидко | Спершу зберігайте обмежену історію чи централізуйте логи |
Сценарій вправи: ви володієте нічним очищенням, що видаляє прострочені записи й іноді триває довше за звичайне. Хороший проєкт починається з операції над даними, а не з маніфесту. Якщо видалення того самого простроченого запису двічі нешкідливе, повторні спроби безпечні. Якщо два виконавці можуть сканувати той самий діапазон і битися за блокування, зменшіть parallelism чи розділіть роботу. Якщо очищення не повинно накладатися із завтрашнім запуском, задайте concurrencyPolicy: Forbid та оповіщайте, коли запуск триває довше за інтервал свого розкладу.
Для готовності до іспиту тренуйтеся перекладати речення в контролер та поля, перш ніж торкатися клавіатури. «Виконати раз і повторити двічі» вказує на Job із backoffLimit. «Виконати десять одиниць з трьома виконавцями» вказує на completions та parallelism. «Виконувати щодня, але ніколи не накладатися» вказує на розклад CronJob плюс concurrencyPolicy: Forbid. Цей крок перекладу зменшує синтаксичні помилки, бо маніфест тепер є вираженням рішення, яке ви вже прийняли.
Фреймворк прийняття рішень
Розділ «Фреймворк прийняття рішень»Обирайте контролер, питаючи себе насамперед, що саме означає «здоровий» для цього робочого навантаження. Якщо здоровий означає «процес продовжує обслуговувати запити», використовуйте Deployment чи інший контролер довготривалих робочих навантажень. Якщо здоровий означає «завдання сягнуло успішного завершення певну кількість разів і зупинилося», використовуйте Job. Якщо здоровий означає «цей шаблон Job має створюватися за розкладом», використовуйте CronJob. Це формулювання через визначення здоров’я надійніше за вибір на основі образу, довжини команди чи суб’єктивного відчуття того, наскільки важливе завдання.
Наступне питання — скільки доказів вам потрібно після завершення роботи. Одноразовий діагностичний Job у лабораторній можна видалити одразу після прочитання його логів. Промисловий експорт зарплати може потребувати збереженого статусу Job, централізованих логів та процедури ручного повторного запуску. Часте очищення через CronJob може потребувати агресивних лімітів історії, щоб тримати простір імен читабельним. Вибір контролера починає проєктування, але збереження доказів робить проєкт експлуатованим.
| Потреба | Використати | Ключові поля | Експлуатаційне питання |
|---|---|---|---|
| Виконати команду раз | Job | restartPolicy, backoffLimit | Скільки невдалих спроб прийнятно? |
| Виконати кілька незалежних одиниць | Job | completions, parallelism | Чи можна роботу безпечно повторити чи продублювати? |
| Виконати за календарем | CronJob | schedule, jobTemplate | Який часовий пояс та політику історії застосувати? |
| Уникнути накладання запланованої роботи | CronJob | concurrencyPolicy: Forbid | Чи пропуск безпечніший за накладання? |
| Віддати перевагу найновішій запланованій роботі | CronJob | concurrencyPolicy: Replace | Чи прийнятне завершення старої роботи? |
| Тримати сервіс запущеним | Deployment | replicas, проби, поля викочування | Чи варто перезапускати чисте завершення процесу? |
Який підхід ви б обрали тут і чому? Звіт має виконуватися щоранку в будні, але аналітикам також потрібно перезапускати його вручну після виправлення вхідних даних. Запланована частина належить CronJob, а повторний запуск має бути ручним Job, створеним із цього шаблону CronJob. Цей вибір тримає визначення звіту в одному місці, робить ручне виконання аудитованим як Job та уникає копіювання довгої команди в сесію оболонки під час напруженого ранку.
Коли рішення близьке, дивіться на очищення та спостережуваність. Jobs та CronJobs залишають об’єкти Kubernetes, які пояснюють, чи пакетна робота завершилася, зазнала невдачі, наклалася чи була призупинена. Deployments залишають стан викочування та реплік, що чудово для сервісів, але незручно для завершених завдань. Команда оболонки, запущена з вашого ноутбука, залишає в стані кластера майже нічого. На іспиті та в промисловому середовищі віддавайте перевагу контролеру, який робить бажаний стан видимим для Kubernetes.
Нарешті, перевірте, чи потребує робоче навантаження стабільної мережевої ідентичності. Jobs та CronJobs зазвичай не стоять за Services, бо клієнти не повинні викликати їх безперервно. Якщо іншим системам потрібно подавати роботу, вам може знадобитися довготривалий сервіс, що пише в чергу, та виконавець у стилі Job, що її осушує. Контролери Kubernetes — це будівельні блоки, а не ярлики важливості. Правильна архітектура часто поєднує Deployment для обробки запитів із Jobs чи CronJobs для скінченного фонового виконання.
Чи знали ви?
Розділ «Чи знали ви?»- CronJobs у Kubernetes перейшли до стабільного API
batch/v1у Kubernetes 1.21, замінивши старіший бета-шлях API. - Підтримка часових поясів CronJob є стабільною в сучасному Kubernetes, і Kubernetes 1.35 продовжує підтримувати
.spec.timeZoneдля іменованих часових поясів. - Завершені Jobs за замовчуванням не видаляються автоматично;
ttlSecondsAfterFinishedделегує очищення контролеру TTL-after-finished. - Індексовані Jobs можуть призначати кожному завершенню стабільний індекс, що корисно для орієнтованої на сегменти пакетної роботи, де ідентичність Pod’а має значення.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому вона трапляється | Як її виправити |
|---|---|---|
Використання restartPolicy: Always | Той, хто навчається, копіює шаблон Pod’а у стилі Deployment у Job | Використайте Never чи OnFailure, потім вирішіть, де мають бути видимі невдалі спроби |
Забування backoffLimit | Завдання виглядає простим, тож поведінку повторних спроб залишають неявною | Задайте бюджет повторних спроб, що відповідає тому, чи невдача тимчасова, чи детермінована |
Трактування parallelism як цілі успіху | Назва звучить як «кількість завдань» | Використовуйте completions для потрібних успіхів, а parallelism для конкурентних виконавців |
Використання CronJob Allow для резервних копій | Замовчування приймається без урахування тривалості виконання | Використовуйте Forbid, коли накладання може зіпсувати вивід чи перевантажити спільні системи |
Перевірка лише kubectl logs job/name | Ярлик приховує, який Pod надав логи | Перелічіть Pod’и за job-name, потім інспектуйте конкретний невдалий Pod та його події |
Розміщення полів CronJob усередині jobTemplate.spec.template | Вкладені шаблони Job та Pod легко сплутати | Тримайте розклад, призупинення, конкурентність та історію на spec CronJob |
| Видалення невдалих Jobs до прочитання доказів | Очищення відчувається як прогрес під час інциденту | Опишіть Job, перелічіть Pod’и, захопіть логи та перегляньте події перед видаленням |
Тест
Розділ «Тест»Питання 1: Розробник створює Job із `restartPolicy: Always`, і API відхиляє його. Він каже, що повтор має означати перезапуск назавжди. Як ви поясните відхилення, і яку політику перезапуску ви б обрали для легкої інспекції невдач?
restartPolicy: Always невалідна для Job, бо Job потребує завершення Pod’а, щоб представляти прогрес до завершення чи невдачі. Контейнер, який завжди перезапускається, ніколи не може чітко сказати «це завдання завершилося успішно». Для легкої інспекції невдач обирайте Never, бо кожна невдала спроба зазвичай залишає окремий невдалий Pod із власним статусом та логами. OnFailure теж може бути валідною, але вона перезапускає контейнер у тому самому Pod’і, тож вам можуть знадобитися лічильники перезапусків та попередні логи, щоб зрозуміти повторювані невдачі.
Питання 2: Ваш конвеєр даних має обробити сто незалежних об'єктів, і кожен об'єкт займає близько тридцяти секунд. Ви хочете завершити менш ніж за десять хвилин, не перевантажуючи опорне API. Які `completions` та `parallelism` ви б задали для початку, і що станеться, якщо один Pod зазнає невдачі?
Використайте completions: 100, бо ціль успіху — сто завершених одиниць роботи. Початковий parallelism шість чи вісім розумний, якщо опорне API витримує стільки конкурентних виконавців; він має завершитися менш ніж за десять хвилин, залишаючи місце для накладних витрат планування. Якщо Pod зазнає невдачі, ця невдала спроба не зараховується до ста завершень, і контролер Job створює ще одну спробу, якщо бюджет повторних спроб це дозволяє. Застосунок усе одно має зробити повторні спроби безпечними, бо Kubernetes не може запобігти дубльованим побічним ефектам усередині вашої бізнес-логіки.
Питання 3: Раннього ранку CronJob резервного копіювання з розкладом `0 2 * * *` показує `LAST SCHEDULE: `. Як ви досліджуєте проблему розкладу і як запустите резервне копіювання негайно, поки продовжуєте налагодження?
Спершу інспектуйте CronJob, а не рівень Pod’а, бо схоже, що жодного запланованого Job не було створено. Перевірте, чи spec.suspend дорівнює true, звірте вираз cron, підтвердьте очікуваний часовий пояс і прочитайте kubectl describe cronjob backup на предмет подій контролера. Щоб запустити резервне копіювання негайно, створіть Job із шаблону CronJob за допомогою kubectl create job --from=cronjob/backup backup-manual. Цей ручний Job дає вам звичайний статус та логи Job, зберігаючи запланований шаблон для виправлення першопричини.
Питання 4: CronJob агрегації метрик виконується кожні п'ять хвилин, але деякі запуски тривають сім хвилин. Із `Allow` ви бачите дубльовані дані. Ви перемикаєтесь на `Forbid`, і тепер деякі розклади пропускаються. Як ви оцінюєте `Forbid` проти `Replace`?
Forbid запобігає накладанню, пропускаючи новий запланований запуск, коли попередній Job усе ще активний, тож він захищає спільний стан, але може проґавити тіки розкладу. Replace завершує активний Job і запускає новий, тож він віддає перевагу найновішому запуску, але відкидає роботу, що вже триває. Для агрегації метрик Forbid зазвичай безпечніший, якщо кожен довгий запуск зрештою охоплює свій намічений інтервал. Краще довгострокове виправлення — скоротити завдання, збільшити інтервал чи зробити агрегацію достатньо ідемпотентною, щоб накладання більше не створювало дублікатів.
Питання 5: Job позначено невдалим, а `kubectl logs job/import` показує лише коротку помилку від одного Pod'а. Команда хоче видалити й перестворити його. Що ви маєте інспектувати перед видаленням і чому?
Перелічіть Pod’и за допомогою kubectl get pods -l job-name=import, щоб побачити кожну спробу, а не лише Pod, вибраний ярликом логів. Опишіть Job, щоб прочитати умови, лічильники повторних спроб та події контролера, потім опишіть невдалі Pod’и на предмет деталей образу, планування, тома та команди. Якщо контейнери перезапускалися на місці, перевірте також попередні логи на Pod’і. Видалення Job прибирає залежні Pod’и, тож захоплення доказів першими не дасть вам стерти інформацію, потрібну для виправлення маніфесту чи команди.
Питання 6: Завдання очищення наразі виконується як Deployment з однією реплікою. Контейнер видаляє старі записи й завершується з кодом нуль, потім Kubernetes запускає його знову. Порівняйте вибір контролерів і запропонуйте безпечніший проєкт.
Deployment хибний, бо його бажаний стан — це безперервно запущена репліка, тож чисте завершення спричиняє узгодження, а не завершення. Job — правильний контролер, якщо очищення має виконуватися раз на вимогу, бо успіх представлено завершеними завершеннями Pod’ів. CronJob — правильний контролер, якщо очищення має виконуватися за розкладом, бо він створює Jobs із шаблону й дозволяє вам налаштувати накладання та історію. Безпечніший проєкт — це CronJob з ідемпотентною командою очищення, навмисним бюджетом повторних спроб та Forbid, якщо накладені видалення можуть конкурувати за блокування.
Практична вправа
Розділ «Практична вправа»Сценарій вправи: ви готуєте простір імен для практики пакетних робочих навантажень. Ви створите простий Job, масштабуєте completions та parallelism, поспостерігаєте за контрольованою невдачею, створите запланований CronJob та вручну запустите запланований шаблон. Тримайте об’єкти малими, щоб вправа працювала на локальному чи тренувальному кластері, і видаляйте кожен об’єкт після інспекції, щоб подальші кроки було легше читати.
kubectl create namespace jobs-labkubectl config set-context --current --namespace=jobs-labЗавдання 1: Створення та інспекція простого Job
Розділ «Завдання 1: Створення та інспекція простого Job»Це перше завдання доводить базовий життєвий цикл. Створіть Job, що виводить повідомлення, дочекайтеся його завершення, прочитайте його логи й видаліть його. Важливе спостереження — те, що завершення є бажаним станом, тож Pod, що завершується з кодом нуль, є успіхом, а не циклом збоїв.
kubectl create job hello --image=busybox -- echo "Hello from job"kubectl wait --for=condition=complete job/hello --timeout=60skubectl get jobskubectl logs job/hellokubectl delete job helloНотатки до розв'язання
Job має швидко сягнути стану complete, а kubectl logs job/hello має вивести повідомлення з контейнера. Якщо команда очікування завершується тайм-аутом, опишіть Job та Pod, перш ніж щось видаляти. Найімовірніші причини на тренувальному кластері — затримка завантаження образу, квота простору імен чи помилка в команді.
Завдання 2: Створення Job із completions та parallelism
Розділ «Завдання 2: Створення Job із completions та parallelism»Тепер створіть Job, що вимагає п’яти успішних завершень, виконуючи два Pod’и одночасно. Поспостерігайте за Pod’ами недовго й підтвердьте, що успішні завершення накопичуються, доки Job не сягне цілі. Це найменший практичний приклад розділення «скільки роботи» від «скільки виконавців одночасно».
cat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: batch-processorspec: completions: 5 parallelism: 2 template: spec: containers: - name: processor image: busybox command: ["sh", "-c", "echo Processing $(hostname); sleep 3"] restartPolicy: NeverEOF
kubectl wait --for=condition=complete job/batch-processor --timeout=90skubectl get jobs batch-processorkubectl get pods -l job-name=batch-processorkubectl delete job batch-processorНотатки до розв'язання
Ви маєте побачити п’ять успішних завершень за час життя Job, але не більше двох активних Pod’ів одночасно. Якщо ви бачите лише один Pod за раз, перевірте parallelism, а потім інспектуйте події планування. Якщо Job завершується, але Pod’и залишаються, це нормально; Jobs зберігають Pod’и для інспекції, доки Job не буде видалено або політика очищення TTL їх не прибере.
Завдання 3: Створення та діагностика Job, що зазнає невдачі
Розділ «Завдання 3: Створення та діагностика Job, що зазнає невдачі»Це завдання навмисно створює команду, що завершується зі статусом один. Дайте Job зазнати невдачі, потім інспектуйте Job, Pod’и та логи перед очищенням. Мета не в тому, щоб змусити його пройти; мета — попрактикуватися в зборі доказів у правильному порядку.
cat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: failing-jobspec: backoffLimit: 2 template: spec: containers: - name: fail image: busybox command: ["sh", "-c", "echo 'About to fail'; exit 1"] restartPolicy: NeverEOF
kubectl wait --for=condition=failed job/failing-job --timeout=60skubectl get jobs failing-jobkubectl get pods -l job-name=failing-job # Multiple failed podskubectl logs job/failing-jobkubectl delete job failing-jobНотатки до розв'язання
Job має зазнати невдачі після вичерпання бюджету повторних спроб. Оскільки політика перезапуску — Never, ви маєте очікувати кілька невдалих Pod’ів, а не один Pod із багатьма перезапусками. Якщо kubectl logs job/failing-job вибирає лише один Pod, перелічіть Pod’и за міткою та інспектуйте логи окремих Pod’ів, щоб порівняти спроби.
Завдання 4: Створення CronJob та спостереження за запланованим запуском
Розділ «Завдання 4: Створення CronJob та спостереження за запланованим запуском»Створіть CronJob, що виконується щохвилини, дочекайтеся достатньо довго, щоб з’явився принаймні один Job, і прочитайте логи згенерованого Job. Розклад навмисно частий для практики; не використовуйте розклад «щохвилини» для дорогої промислової роботи, якщо тільки завдання не спроєктоване для цього.
kubectl create cronjob minute-job --image=busybox --schedule="*/1 * * * *" -- date
# Wait for it to runsleep 70kubectl get cronjobskubectl get jobsJOB_NAME=$(kubectl get jobs -o name | grep minute-job | head -n 1)kubectl logs $JOB_NAME
kubectl delete cronjob minute-jobНотатки до розв'язання
kubectl get cronjobs має показати розклад та час останнього розкладу після того, як контролер створить Job. Ім’я згенерованого Job містить ім’я CronJob плюс суфікс, тому команда захоплює його динамічно. Якщо жодного Job не з’являється, опишіть CronJob, звірте розклад і перевірте, чи нормально працює controller manager вашого кластера.
Завдання 5: Ручний запуск шаблону CronJob
Розділ «Завдання 5: Ручний запуск шаблону CronJob»Створіть щоденний CronJob, який не запуститься природно під час вправи, потім запустіть його шаблон Job вручну. Це той експлуатаційний хід, який ви використовуєте, коли запланованому завданню потрібен негайний повторний запуск після того, як ви виправили вхідні дані чи відновилися після пропущеного розкладу.
kubectl create cronjob backup --image=busybox --schedule="0 0 * * *" -- echo "backup"
# Trigger manuallykubectl create job --from=cronjob/backup backup-nowkubectl get jobskubectl wait --for=condition=complete job/backup-now --timeout=60skubectl logs job/backup-now
kubectl delete job backup-nowkubectl delete cronjob backupНотатки до розв'язання
Ручний Job має завершитися, навіть попри те, що природний розклад CronJob — це опівніч. Важливий момент — те, що --from=cronjob/backup копіює шаблон Job CronJob, тож ручний запуск задіює ту саму команду та образ. Використовуйте унікальне ім’я ручного Job щоразу, бо імена Job діють у межах простору імен.
Завдання 6: Виклик — повний робочий процес Job
Розділ «Завдання 6: Виклик — повний робочий процес Job»Створіть Job, що виконує чотири completions, виконує два Pod’и одночасно, виводить ім’я свого хоста, спить три секунди, має ліміт відкату два й автоматично видаляється через шістдесят секунд. Спробуйте написати маніфест із пам’яті, перш ніж відкривати розв’язання. Виклик поєднує кількість завершень, конкурентність, бюджет повторних спроб та очищення в одному малому об’єкті.
# Create the challenge Job manifest, apply it, and wait for completion.Розв'язання
cat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: challenge-jobspec: completions: 4 parallelism: 2 backoffLimit: 2 ttlSecondsAfterFinished: 60 template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo $HOSTNAME; sleep 3"] restartPolicy: NeverEOF
kubectl wait --for=condition=complete job/challenge-job --timeout=60skubectl get job challenge-jobДодаткові тренувальні вправи
Розділ «Додаткові тренувальні вправи»Ці вправи зберігають форми команд, які ви, найімовірніше, використовуватимете під час підготовки до іспиту. Виконуйте їх лише після того, як основні завдання стануть зрозумілими, бо швидкість без діагностики може приховати слабкі місця. Кожна вправа має закінчуватися очищенням, а кожну вправу, орієнтовану на невдачу, слід інспектувати перед видаленням.
# Create jobkubectl create job quick --image=busybox -- echo "done"
# Wait for completionkubectl wait --for=condition=complete job/quick --timeout=60s
# Check logskubectl logs job/quick
# Cleanupkubectl delete job quickcat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: parallelspec: completions: 6 parallelism: 3 template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo Pod: $HOSTNAME; sleep 5"] restartPolicy: NeverEOF
# Watchkubectl get pods -l job-name=parallel -w &kubectl get job parallel -w &sleep 30kill %1 %2 2>/dev/null
# Cleanupkubectl delete job parallelcat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: timeout-testspec: activeDeadlineSeconds: 10 template: spec: containers: - name: long-task image: busybox command: ["sleep", "60"] restartPolicy: NeverEOF
# Watch job timeoutkubectl get job timeout-test -w &sleep 15kill %1 2>/dev/null
# Check statuskubectl get job timeout-test -o jsonpath='{range .status.conditions[*]}{.type}{"="}{.status}{" reason="}{.reason}{"\n"}{end}'
# Cleanupkubectl delete job timeout-test# Create CronJobkubectl create cronjob every-minute --image=busybox --schedule="*/1 * * * *" -- date
# Verifykubectl get cronjob every-minute
# Wait for first runsleep 70
# Check jobs createdkubectl get jobs
# Cleanupkubectl delete cronjob every-minute# Create CronJob (won't run for a while)kubectl create cronjob daily --image=busybox --schedule="0 0 * * *" -- echo "daily task"
# Trigger manuallykubectl create job --from=cronjob/daily daily-manual-run
# Checkkubectl get jobskubectl wait --for=condition=complete job/daily-manual-run --timeout=60skubectl logs job/daily-manual-run
# Cleanupkubectl delete job daily-manual-runkubectl delete cronjob daily# Create intentionally broken jobcat << 'EOF' | kubectl apply -f -apiVersion: batch/v1kind: Jobmetadata: name: brokenspec: backoffLimit: 2 template: spec: containers: - name: app image: busybox command: ["sh", "-c", "cat /nonexistent/file"] restartPolicy: NeverEOF
# Diagnosekubectl get job brokenkubectl get pods -l job-name=brokenkubectl describe job brokenkubectl logs job/broken
# Answer: What's the error? How would you fix it?
# Cleanupkubectl delete job brokenКритерії успіху:
- Можу створювати Jobs імперативно та декларативно
- Можу пояснити й поспостерігати за completions та parallelism
- Можу діагностувати невдалі Jobs за допомогою статусу, Pod’ів, подій та логів
- Можу створювати CronJobs з розкладом та політикою історії
- Можу вручну запускати CronJobs через згенерований Job
- Можу обирати між Job, CronJob та Deployment для пакетного робочого навантаження
Джерела
Розділ «Джерела»- Kubernetes Jobs
- Kubernetes CronJobs
- Automatic Cleanup for Finished Jobs
- Kubernetes API Reference: Job v1
- Kubernetes API Reference: CronJob v1
- kubectl create job
- kubectl create cronjob
- kubectl wait
- kubectl logs
- Kubernetes Workload Resources
Наступний модуль
Розділ «Наступний модуль»Модуль 2.5: Керування ресурсами навчає, як requests, limits та класи QoS формують планування та поведінку під час виконання для Pod’ів, створених контролерами на кшталт Jobs, CronJobs та Deployments.