1 - Виконання автоматизованих завдань за допомогою CronJob

Ця сторінка показує, як виконувати автоматизовані завдання за допомогою обʼєкта Kubernetes CronJob.

Перш ніж ви розпочнете

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

Створення CronJob

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

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

Запустіть приклад CronJob за допомогою цієї команди:

kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml

Вивід буде подібний до цього:

cronjob.batch/hello created

Після створення CronJob отримайте його статус за допомогою цієї команди:

kubectl get cronjob hello

Вивід буде подібний до цього:

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          10s

Як видно з результатів команди, CronJob ще не планував або не запускав жодних завдань. Спостерігайте за створенням завдання протягом хвилини:

kubectl get jobs --watch

Вивід буде подібний до цього:

NAME               COMPLETIONS   DURATION   AGE
hello-4111706356   0/1                      0s
hello-4111706356   0/1           0s         0s
hello-4111706356   1/1           5s         5s

Тепер ви бачите одне запущене завдання, заплановане cron job "hello". Ви можете припинити спостереження за завданням і переглянути cron job ще раз, щоб побачити, що він запланував завдання:

kubectl get cronjob hello

Вивід буде подібний до цього:

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        50s             75s

Ви повинні побачити, що cron job hello успішно запланував завдання в час, вказаний у LAST SCHEDULE. Наразі немає активних завдань, що означає, що завдання завершилось або зазнало невдачі.

Тепер знайдіть Podʼи, які створило останнє заплановане завдання, та перегляньте стандартний вивід одного з них.

# Замініть "hello-4111706356" на назву завдання у вашій системі
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items[*].metadata.name})

Покажіть лог Pod:

kubectl logs $pods

Вивід буде подібний до цього:

Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster

Видалення CronJob

Коли вам більше не потрібно cron job, видаліть його за допомогою kubectl delete cronjob <cronjob name>:

kubectl delete cronjob hello

Видалення cron job призводить до видалення всіх створених ним завдань і Podʼів і припинення створення додаткових завдань. Докладніше про видалення завдань читайте в збиранні сміття.

2 - Груба паралельна обробка за допомогою черги роботи

У цьому прикладі ви запустите Job Kubernetes з кількома паралельними робочими процесами.

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

Ось огляд кроків у цьому прикладі:

  1. Запустіть службу черги повідомлень. У цьому прикладі ви використовуєте RabbitMQ, але ви можете використовувати іншу. На практиці ви налаштовували б службу черги повідомлень один раз і використовували б її для багатьох робочих завдань.
  2. Створіть чергу та заповніть її повідомленнями. Кожне повідомлення являє собою одне завдання для виконання. У цьому прикладі повідомлення — це ціле число, на якому ми будемо виконувати тривалі обчислення.
  3. Запустіть Job, що працює над завданнями з черги. Job створює декілька Podʼів. Кожен Pod бере одне завдання з черги повідомлень, обробляє його та завершує роботу.

Перш ніж ви розпочнете

Ви вже маєте бути знайомі з основним, не-паралельним використанням Job.

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

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

Цей приклад завдання також передбачає, що у вас вже встановлений Docker локально.

Запуск служби черги повідомлень

У цьому прикладі використовується RabbitMQ, проте ви можете адаптувати приклад для використання іншої служби повідомлень типу AMQP.

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

Запустіть RabbitMQ таким чином:

# Створіть Service для використання StatefulSet
kubectl create -f https://kubernetes.io/examples/application/job/rabbitmq/rabbitmq-service.yaml
service "rabbitmq-service" created
kubectl create -f https://kubernetes.io/examples/application/job/rabbitmq/rabbitmq-statefulset.yaml
statefulset "rabbitmq" created

Тестування служби черги повідомлень

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

Спочатку створіть тимчасовий інтерактивний Pod.

# Створіть тимчасовий інтерактивний контейнер
kubectl run -i --tty temp --image ubuntu:22.04
Waiting for pod default/temp-loe07 to be running, status is Pending, pod ready: false
... [ previous line repeats several times .. hit return when it stops ] ...

Зверніть увагу, що ваше імʼя Pod та запрошення командного рядка будуть відрізнятися.

Далі встановіть amqp-tools, щоб працювати з чергами повідомлень. Наступні команди показують, що вам потрібно запустити в середовищі інтерактивної оболонки у цьому Pod:

apt-get update && apt-get install -y curl ca-certificates amqp-tools python3 dnsutils

Пізніше ви створите образ контейнера, який включає ці пакети.

Далі перевірте, що ви можете виявити Service для RabbitMQ:

# Виконайте ці команди всередині Pod
# Зауважте, що для служби rabbitmq-service існує DNS-ім\я, яке надається Kubernetes:
nslookup rabbitmq-service
Server:        10.0.0.10
Address:    10.0.0.10#53

Name:    rabbitmq-service.default.svc.cluster.local
Address: 10.0.147.152

(адреси IP будуть відрізнятися)

Якщо надбудова kube-dns не налаштована правильно, попередній крок може не працювати для вас. Ви також можете знайти IP-адресу для цього Service у змінній середовища:

# виконайте цю перевірку всередині Pod
env | grep RABBITMQ_SERVICE | grep HOST
RABBITMQ_SERVICE_SERVICE_HOST=10.0.147.152

(IP-адреса буде відрізнятися)

Далі перевірте, що ви можете створити чергу, і публікувати та отримувати повідомлення.

# Виконайте ці команди всередині Pod
# У наступному рядку rabbitmq-service - це імʼя хоста, за яким можна звертатися до rabbitmq-service.
# 5672 - це стандартний порт для rabbitmq.
export BROKER_URL=amqp://guest:guest@rabbitmq-service:5672
# Якщо ви не могли отримати доступ до "rabbitmq-service" на попередньому кроці,
# використовуйте цю команду натомість:
BROKER_URL=amqp://guest:guest@$RABBITMQ_SERVICE_SERVICE_HOST:5672

# Тепер створіть чергу:

/usr/bin/amqp-declare-queue --url=$BROKER_URL -q foo -d
foo

Опублікуйте одне повідомлення в черзі:

/usr/bin/amqp-publish --url=$BROKER_URL -r foo -p -b Hello

# І отримайте його назад.

/usr/bin/amqp-consume --url=$BROKER_URL -q foo -c 1 cat && echo 1>&2
Hello

У останній команді інструмент amqp-consume взяв одне повідомлення (-c 1) з черги, і передав це повідомлення на стандартний ввід довільної команди. У цьому випадку програма cat виводить символи, зчитані зі стандартного вводу, а echo додає символ нового рядка, щоб приклад був читабельним.

Заповнення черги завданнями

Тепер заповніть чергу деякими симульованими завданнями. У цьому прикладі завдання — це рядки, які потрібно надрукувати.

На практиці вміст повідомлень може бути таким:

  • назви файлів, які потрібно обробити
  • додаткові прапорці для програми
  • діапазони ключів у таблиці бази даних
  • параметри конфігурації для симуляції
  • номери кадрів сцени для рендерингу

Якщо є великі дані, які потрібно лише для читання всіма Podʼами Job, зазвичай це розміщують на спільній файловій системі, наприклад NFS, і монтується це на всі Podʼи тільки для читання, або напишіть програму в Pod так, щоб вона могла нативно читати дані з кластерної файлової системи (наприклад: HDFS).

У цьому прикладі ви створите чергу та заповните її, використовуючи інструменти командного рядка AMQP. На практиці ви можете написати програму для заповнення черги, використовуючи бібліотеку клієнтів AMQP.

# Виконайте це на вашому комп'ютері, а не в Pod
/usr/bin/amqp-declare-queue --url=$BROKER_URL -q job1  -d
job1

Додайте елементи до черги:

for f in apple banana cherry date fig grape lemon melon
do
  /usr/bin/amqp-publish --url=$BROKER_URL -r job1 -p -b $f
done

Ви додали 8 повідомлень у чергу.

Створення образу контейнера

Тепер ви готові створити образ, який ви будете запускати як Job.

Job буде використовувати утиліту amqp-consume для читання повідомлення з черги та виконання реальної роботи. Ось дуже простий приклад програми:

#!/usr/bin/env python

# Просто виводить стандартний вивід і очікує протягом 10 секунд.
import sys
import time
print("Processing " + sys.stdin.readlines()[0])
time.sleep(10)

Дайте скрипту дозвіл на виконання:

chmod +x worker.py

Тепер створіть образ. Створіть тимчасову теку, перейдіть в неї, завантажте Dockerfile, і worker.py. У будь-якому випадку, створіть образ за допомогою цієї команди:

docker build -t job-wq-1 .

Для Docker Hub позначте ваш образ застосунка вашим імʼям користувача і завантажте його на Hub за допомогою таких команд. Замініть <username> на ваше імʼя користувача Hub.

docker tag job-wq-1 <username>/job-wq-1
docker push <username>/job-wq-1

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

Визначення Job

Ось маніфест для Job. Вам потрібно зробити копію маніфеста Job (назвіть його ./job.yaml), і змініть імʼя образу контейнера, щоб відповідало імені, яке ви використовували.

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-1
spec:
  completions: 8
  parallelism: 2
  template:
    metadata:
      name: job-wq-1
    spec:
      containers:
      - name: c
        image: gcr.io/<project>/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

У цьому прикладі кожен Pod працює над одним елементом з черги, а потім виходить. Таким чином, кількість завершень завдання відповідає кількості виконаних робочих елементів. Тому у прикладі маніфесту .spec.completions встановлено на 8.

Запуск Job

Тепер запустіть завдання:

# це передбачає, що ви вже завантажили та відредагували маніфест
kubectl apply -f ./job.yaml

Ви можете зачекати, доки завдання успішно виконається, використовуючи тайм-аут:

# Перевірка для умови що імена нечутливі до регістру
kubectl wait --for=condition=complete --timeout=300s job/job-wq-1

Далі перевірте стан завдання:

kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2022 16:42:02 +0000
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                job-name=job-wq-1
  Containers:
   c:
    Image:      container-registry.example/causal-jigsaw-637/job-wq-1
    Port:
    Environment:
      BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672
      QUEUE:            job1
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message
  ─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam5
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr
  26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj
  15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

Усі Podʼи для цього завдання успішно виконалися! У вас вийшло.

Альтернативи

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

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

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

У цьому прикладі ви використовували утиліту amqp-consume для читання повідомлення з черги та запуску реальної програми. Це має перевагу у тому, що вам не потрібно модифікувати свою програму, щоб вона була обізнана про існування черги. У прикладі тонкої паралельної черги робочих елементів показано, як спілкуватися з чергою робочих елементів за допомогою клієнтської бібліотеки.

Обмеження

Якщо кількість завершень встановлена меншою, ніж кількість елементів у черзі, то не всі елементи будуть оброблені.

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

Існують малоймовірні перегони для цього патерну. Якщо контейнер був завершений між часом, коли повідомлення було підтверджено командою amqp-consume, і часом, коли контейнер вийшов з успішним завершенням, або якщо вузол зазнає збою, перш ніж kubelet зможе розмістити успіх Podʼа назад до сервера API, то завдання не буде здаватися завершеним, навіть якщо всі елементи у черзі були оброблені.

3 - Тонка паралельна обробка за допомогою черги роботи

In цьому прикладі ви запустите Job Kubernetes, яке виконує декілька паралельних завдань як робочі процеси, кожен з яких працює як окремий Pod.

У цьому прикладі, при створенні кожного Podʼу, він бере одиницю роботи з черги завдань, обробляє її та повторює цей процес до досягнення кінця черги.

Ось загальний огляд кроків у цьому прикладі:

  1. Запустіть службу зберігання, щоб зберігати чергу завдань. У цьому прикладі ви використаєте Redis для зберігання робочих елементів. У попередньому прикладі, ви використали RabbitMQ. У цьому прикладі ви будете використовувати Redis та власну бібліотеку клієнтів черг завдань; це тому, що AMQP не надає зручний спосіб клієнтам виявити, коли скінчиться черга робочих елементів з обмеженою довжиною. На практиці ви налаштуєте сховище, таке як Redis, один раз і повторно використовуватимете його для черг робочих завдань багатьох завдань та іншого.
  2. Створіть чергу та заповніть її повідомленнями. Кожне повідомлення представляє одне завдання, яке потрібно виконати. У цьому прикладі повідомленням є ціле число, над яким ми виконаємо тривалі обчислення.
  3. Запустіть завдання, яке працює над завданнями з черги. Завдання запускає декілька Podʼів. Кожний Pod бере одне завдання з черги повідомлень, обробляє його та повторює цей процес до досягнення кінця черги.

Перш ніж ви розпочнете

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

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

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

Ви маєти бути знайомі з базовим, не-паралельним використанням Job.

Запуск Redis

У цьому прикладі, для спрощення, ви запустите один екземпляр Redis. Дивіться Приклад Redis для прикладу розгортання Redis масштабовано та надійно.

Ви також можете завантажити наступні файли безпосередньо:

Для запуску одного екземпляра Redis вам потрібно створити Pod Redis та Service Redis:

kubectl apply -f https://k8s.io/examples/application/job/redis/redis-pod.yaml
kubectl apply -f https://k8s.io/examples/application/job/redis/redis-service.yaml

Заповнення черги завданнями

Тепер заповнімо чергу деякими "задачами". У цьому прикладі завданнями є рядки, які потрібно вивести.

Запустіть тимчасовий інтерактивний Pod для використання Redis CLI.

kubectl run -i --tty temp --image redis --command "/bin/sh"
Waiting for pod default/redis2-c7h78 to be running, status is Pending, pod ready: false
Hit enter for command prompt

Тепер натисніть Enter, запустіть Redis CLI та створіть список з деякими елементами роботи в ньому.

redis-cli -h redis
redis:6379> rpush job2 "apple"
(integer) 1
redis:6379> rpush job2 "banana"
(integer) 2
redis:6379> rpush job2 "cherry"
(integer) 3
redis:6379> rpush job2 "date"
(integer) 4
redis:6379> rpush job2 "fig"
(integer) 5
redis:6379> rpush job2 "grape"
(integer) 6
redis:6379> rpush job2 "lemon"
(integer) 7
redis:6379> rpush job2 "melon"
(integer) 8
redis:6379> rpush job2 "orange"
(integer) 9
redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

Отже, список з ключем job2 буде чергою роботи.

Примітка: якщо у вас неправильно налаштовано Kube DNS, вам може знадобитися змінити перший крок вищезазначеного блоку на redis-cli -h $REDIS_SERVICE_HOST.

Створення образу контейнера

Тепер ви готові створити образ, який буде обробляти завдання в цій черзі.

Ви будете використовувати робочу програму на Python з клієнтом Redis для читання повідомлень з черги повідомлень.

Надається проста бібліотека клієнтів черги роботи Redis, яка називається rediswq.py (Завантажити).

Програма "робітник" в кожному Pod Job використовує бібліотеку клієнтів черги роботи, щоб отримати роботу. Ось вона:

#!/usr/bin/env python

import time
import rediswq

host="redis"
# Якщо у вас немає працюючого Kube-DNS, розкоментуйте наступні два рядки.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")

q = rediswq.RedisWQ(name="job2", host=host)
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
  item = q.lease(lease_secs=10, block=True, timeout=2) 
  if item is not None:
    itemstr = item.decode("utf-8")
    print("Working on " + itemstr)
    time.sleep(10) # Put your actual work here instead of sleep.
    q.complete(item)
  else:
    print("Waiting for work")
print("Queue empty, exiting")

Ви також можете завантажити файли worker.py, rediswq.py та Dockerfile, а потім побудувати контейнерний образ. Ось приклад використання Docker для побудови образу:

docker build -t job-wq-2 .

Збереження образу в реєстрі

Для Docker Hub, позначте свій образ програми імʼям користувача та завантажте його до Hub за допомогою наступних команд. Замість <username> вкажіть своє імʼя користувача Hub.

docker tag job-wq-2 <username>/job-wq-2
docker push <username>/job-wq-2

Вам потрібно завантажити в публічний репозиторій або налаштувати кластер для доступу до вашого приватного репозиторію.

Визначення завдання

Ось маніфест для створення Job:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-2
spec:
  parallelism: 2
  template:
    metadata:
      name: job-wq-2
    spec:
      containers:
      - name: c
        image: gcr.io/myproject/job-wq-2
      restartPolicy: OnFailure

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

Запуск завдання

Отже, зараз запустіть завдання:

# передбачається, що ви вже завантажили та відредагували маніфест
kubectl apply -f ./job.yaml

Тепер зачекайте трохи, а потім перевірте стан завдання:

kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2022 17:07:59 +0000
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                job-name=job-wq-2
  Containers:
   c:
    Image:              container-registry.example/exampleproject/job-wq-2
    Port:
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8

Ви можете зачекати, поки завдання завершиться успішно, з тайм-аутом:

# Перевірка умови назви нечутлива до регістру
kubectl wait --for=condition=complete --timeout=300s job/job-wq-2
kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

Як бачите, один з Podʼів для цього завдання працював над кількома робочими одиницями.

Альтернативи

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

Якщо у вас є постійний потік фонової обробки, яку потрібно виконувати, то розгляньте запуск ваших фонових робітників за допомогою ReplicaSet, і розгляньте використання бібліотеки фонової обробки, такої як https://github.com/resque/resque.

4 - Індексоване завдання (Job) для паралельної обробки з фіксованим призначенням роботи

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.24 [stable]

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

Індекс Podʼа доступний в анотації batch.kubernetes.io/job-completion-index у вигляді рядка, який представляє його десяткове значення. Щоб контейнеризований процес завдання отримав цей індекс, можна опублікувати значення анотації за допомогою механізму downward API. Для зручності панель управління автоматично встановлює downward API для експонування індексу в змінну середовища JOB_COMPLETION_INDEX.

Нижче наведено огляд кроків у цьому прикладі:

  1. Визначте маніфест завдання з використанням індексованого завершення. Downward API дозволяє передавати індекс Podʼа як змінну середовища або файл до контейнера.
  2. Запустіть Indexed завдання на основі цього маніфесту.

Перш ніж ви розпочнете

Ви маєти бути знайомі з базовим, не-паралельним використанням Job.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21. Для перевірки версії введіть kubectl version.

Оберіть підхід

Щоб отримати доступ до робочого елемента з програми робочого процесу, у вас є кілька варіантів:

  1. Прочитайте змінну середовища JOB_COMPLETION_INDEX. Job контролер автоматично повʼязує цю змінну з анотацією, що містить індекс завершення.
  2. Прочитайте файл, який містить індекс завершення.
  3. Припускаючи, що ви не можете змінити програму, ви можете обгорнути її сценарієм, який читає індекс за допомогою будь-якого з методів вище і перетворює його в щось, що програма може використовувати як вхід.

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

rev data.txt

Ви будете використовувати інструмент rev з контейнерного образу busybox.

Оскільки це лише приклад, кожен Pod робить лише невеликий шматок роботи (реверсування короткого рядка). У реальному навантаженні ви, наприклад, можете створити Job, що представляє задачу рендерингу 60 секунд відео на основі даних про сцену. Кожен робочий елемент в завданні відеорендерингу буде створювати певний кадр цього відеокліпу. Індексоване завершення означатиме, що кожен Pod у Job знає, який кадр рендерити та опублікувати, рахуючи кадри від початку кліпу.

Визначте Indexed Job

Ось приклад маніфесту Job, який використовує режим завершення Indexed:

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      initContainers:
      - name: 'input'
        image: 'docker.io/library/bash'
        command:
        - "bash"
        - "-c"
        - |
          items=(foo bar baz qux xyz)
          echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt          
        volumeMounts:
        - mountPath: /input
          name: input
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        emptyDir: {}

У вищенаведеному прикладі ви використовуєте вбудовану змінну середовища JOB_COMPLETION_INDEX, яку встановлює контролер завдань для всіх контейнерів. Контейнер ініціалізації відображає індекс на статичне значення та записує його в файл, який спільно використовується з контейнером, що виконує робочий процес через том emptyDir. Додатково ви можете визначити свою власну змінну середовища через downward API для публікації індексу в контейнерах. Ви також можете вибрати завантаження списку значень з ConfigMap як змінної середовища або файлу.

У іншому варіанті ви можете безпосередньо використовувати downward API для передачі значення анотації як файлу тому, як показано у наступному прикладі:

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        downwardAPI:
          items:
          - path: "data.txt"
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']

Запуск Job

Тепер запустіть Job:

# Це використовує перший підхід (покладаючись на $JOB_COMPLETION_INDEX)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml

При створенні цього Завдання панель управління створює серію Podʼів, по одному для кожного вказаного індексу. Значення .spec.parallelism визначає, скільки може працювати одночасно, в той час, як .spec.completions визначає, скільки Podʼів створює Job в цілому.

Оскільки .spec.parallelism менше, ніж .spec.completions, панель управління чекає, поки деякі з перших Podʼів завершаться, перш ніж запустити ще.

Ви можете зачекати, поки Завдання завершиться успішно, з тайм-аутом:

# Перевірка умови назви нечутлива до регістру
kubectl wait --for=condition=complete --timeout=300s job/indexed-job

Тепер опишіть Завдання та переконайтеся, що воно виконалося успішно.

kubectl describe jobs/indexed-job

Вивід схожий на:

Name:              indexed-job
Namespace:         default
Selector:          controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels:            controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
                   job-name=indexed-job
Annotations:       <none>
Parallelism:       3
Completions:       5
Start Time:        Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses:     2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
  Labels:  controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
           job-name=indexed-job
  Init Containers:
   input:
    Image:      docker.io/library/bash
    Port:       <none>
    Host Port:  <none>
    Command:
      bash
      -c
      items=(foo bar baz qux xyz)
      echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt

    Environment:  <none>
    Mounts:
      /input from input (rw)
  Containers:
   worker:
    Image:      docker.io/library/busybox
    Port:       <none>
    Host Port:  <none>
    Command:
      rev
      /input/data.txt
    Environment:  <none>
    Mounts:
      /input from input (rw)
  Volumes:
   input:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-njkjj
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-9kd4h
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-qjwsz
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-fdhq5
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-ncslj

У цьому прикладі ви запускаєте Job з власними значеннями для кожного індексу. Ви можете оглянути вивід одного з Podʼів:

kubectl logs indexed-job-fdhq5 # Змініть це, щоб відповідати імені Podʼа з цього Завдання ```

Вивід схожий на:

xuq

5 - Завдання (Job) з комунікацією Pod-Pod

У цьому прикладі ви запустите Job в Indexed completion mode, налаштований таким чином, щоб Podʼи, створені Job, могли комунікувати один з одним, використовуючи назви хостів Podʼів, а не IP-адреси Podʼів.

Podʼи в межах Job можуть потребувати комунікації між собою. Робоче навантаження користувача, що виконується в кожному Podʼі, може звертатися до сервера API Kubernetes для отримання IP-адрес інших Podʼів, але набагато простіше покладатися на вбудований DNS-резолвер Kubernetes.

Job в Indexed completion mode автоматично встановлюють назви хостів Podʼів у форматі ${jobName}-${completionIndex}. Ви можете використовувати цей формат для детермінованого створення назв хостів Podʼів та забезпечення комунікації між Podʼами без необхідності створювати клієнтське з’єднання з панеллю управління Kubernetes для отримання назв хостів/IP-адрес Podʼів через API-запити.

Ця конфігурація корисна для випадків, коли необхідна мережева взаємодія Podʼів, але ви не хочете залежати від мережевого з’єднання з сервером API Kubernetes.

Перш ніж ви розпочнете

Ви повинні вже бути знайомі з основами використання Job.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21. Для перевірки версії введіть kubectl version.

Запуск роботи з комунікацією між Podʼами

Щоб увімкнути комунікацію між Podʼами з використанням назв хостів Podʼів у Job, ви повинні зробити наступне:

  1. Налаштуйте headless Service з дійсним селектором міток для Podʼів, створених вашим Job. Headless Service має бути в тому ж просторі імен, що й Job. Один із простих способів зробити це — використати селектор job-name: <your-job-name>, оскільки мітка job-name буде додана Kubernetes автоматично. Ця конфігурація активує систему DNS для створення записів назв хостів Podʼів, що виконують ваш обʼєкт Job.

  2. Налаштуйте headless Service як піддомен для Podʼів Job, включивши наступне значення у ваш шаблон специфікації Job:

    subdomain: <headless-svc-name>
    

Приклад

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


apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None # clusterIP має бути None для створення headless service
  selector:
    job-name: example-job # має відповідати імені Job
---
apiVersion: batch/v1
kind: Job
metadata:
  name: example-job
spec:
  completions: 3
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      subdomain: headless-svc # має відповідати імені Service
      restartPolicy: Never
      containers:
      - name: example-workload
        image: bash:latest
        command:
        - bash
        - -c
        - |
          for i in 0 1 2
          do
            gotStatus="-1"
            wantStatus="0"             
            while [ $gotStatus -ne $wantStatus ]
            do                                       
              ping -c 1 example-job-${i}.headless-svc > /dev/null 2>&1
              gotStatus=$?                
              if [ $gotStatus -ne $wantStatus ]; then
                echo "Failed to ping pod example-job-${i}.headless-svc, retrying in 1 second..."
                sleep 1
              fi
            done                                                         
            echo "Successfully pinged pod: example-job-${i}.headless-svc"
          done          

Після застосування наведеного вище прикладу, Podʼи зможуть звертатись один до одного в мережі, використовуючи: <pod-hostname>.<headless-service-name>. Ви повинні побачити вихідні дані, подібні до наступних:

kubectl logs example-job-0-qws42
Failed to ping pod example-job-0.headless-svc, retrying in 1 second...
Successfully pinged pod: example-job-0.headless-svc
Successfully pinged pod: example-job-1.headless-svc
Successfully pinged pod: example-job-2.headless-svc

6 - Паралельна обробка з розширенням

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

У цьому прикладі є лише три елементи: apple, banana та cherry. Приклади Job обробляють кожен елемент, виводячи рядок, а потім очікуючи дії користувача.

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

Перш ніж ви розпочнете

Вам слід бути знайомим з базовим, не-паралельним використанням Job.

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

Для базового шаблонування вам потрібна утиліта командного рядка sed.

Для виконання розширеного прикладу шаблонування вам потрібен встановлений Python та бібліотека шаблонів Jinja2 для Python.

Після налаштування Python ви можете встановити Jinja2, виконавши:

pip install --user jinja2

Створення Job на основі шаблону

Спочатку завантажте наступний шаблон Job у файл з назвою job-tmpl.yaml. Ось що ви завантажите:

apiVersion: batch/v1
kind: Job
metadata:
  name: process-item-$ITEM
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox:1.28
        command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
      restartPolicy: Never
# Використайте curl для завантаження job-tmpl.yaml
curl -L -s -O https://k8s.io/examples/application/job/job-tmpl.yaml

Завантажений вами файл ще не є валідним маніфестом Kubernetes. Замість цього цей шаблон є YAML-представленням об’єкта Job з деякими заповнювачами, які потрібно замінити перед використанням. Синтаксис $ITEM не має значення для Kubernetes.

Створення маніфестів з шаблону

Наступний код використовує sed для заміни $ITEM на значення змінної з циклу, зберігаючи результат в тимчасову теку jobs:

# Розмноження шаблону на кілька файлів, по одному для коженого процесу
mkdir -p ./jobs
for item in apple banana cherry
do
  cat job-tmpl.yaml | sed "s/\$ITEM/$i/g" > ./jobs/job-$i.yaml
done

На виході ви маєте отримати три файли:

job-apple.yaml
job-banana.yaml
job-cherry.yaml

Створення Завдань (Job) з маніфестів

Далі, створіть всі три Job, використовуючи файли, які ви створили:

kubectl apply -f ./jobs

Вивід буде подібний до цього:

job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created

Тепер ви можете перевірити стан Job:

kubectl get jobs -l jobgroup=jobexample

Вивід буде подібний до цього:

NAME                  COMPLETIONS   DURATION   AGE
process-item-apple    1/1           14s        22s
process-item-banana   1/1           12s        21s
process-item-cherry   1/1           12s        20s

Використання опції -l для kubectl вибирає лише Job, які є частиною цієї групи Job (в системі можуть бути інші не Завдання (Job)).

Ви також можете перевірити стан Podʼів, використовуючи той самий селектор міток:

kubectl get pods -l jobgroup=jobexample

Вивід буде подібний до цього:

NAME                        READY     STATUS      RESTARTS   AGE
process-item-apple-kixwv    0/1       Completed   0          4m
process-item-banana-wrsf7   0/1       Completed   0          4m
process-item-cherry-dnfu9   0/1       Completed   0          4m

Ви можете використати цю одну команду для перевірки виводу всіх Job одночасно:

kubectl logs -f -l jobgroup=jobexample

Вивід має бути таким:

Processing item apple
Processing item banana
Processing item cherry

Очищення

# Видаліть створені Job
# Ваш кластер автомтично видаляє пов’язані з Job Pod
kubectl delete jobs -l jobgroup=jobexample

Використання розширених параметрів шаблонів

В першому прикладі, кожен екземпляр шаблону мав один параметр, цей параметр тако використовувався в назві Job. Однак, назви обмежені набором символів, які можна використовувати.

Ось трохи складніший шаблон Jinja, для створення маніфестів та обʼєктів на їх основі, з кількома параметрами для кожного Завдання (Job).

В першій частині завдання скористайтесь однорядковим скриптом Python для перетворення шаблонів в набір маніфестів.

Спочатку скопіюйте та вставте наступний шаблон обʼєкта Job у файл з назвою job.yaml.jinja2:

{% set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
                  { "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
                  { "name": "cherry", "url": "http://dbpedia.org/resource/Cherry" }]
%}
{% for p in params %}
{% set name = p["name"] %}
{% set url = p["url"] %}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: jobexample-{{ name }}
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox:1.28
        command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
      restartPolicy: Never
{% endfor %}

Наведений шаблон визначає два параметри для кожного обʼєкта Job за допомогою списку словників Python (рядки 1-4). Цикл for генерує один маніфест Job для кожного набору параметрів (інші рядки).

Цей приклад використовує можливості YAML. Один файл YAML може містити кілька документів (у цьому випадку маніфестів Kubernetes), розділених рядком ---.

Ви можете передати вивід безпосередньо до kubectl, щоб створити Jobs.

Далі використовуйте цю однорядковий скрипт Python для розширення шаблону:

alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'

Використовуйте render_template для конвертації параметрів та шаблону в один файл YAML, що містить маніфести Kubernetes:

# Це вимагає визначеного раніше аліасу
cat job.yaml.jinja2 | render_template > jobs.yaml

Ви можете переглянути jobs.yaml, щоб переконатися, що скрипт render_template працює правильно.

Коли ви переконаєтесь, що render_template працює так, як ви задумали, ви можете передати його вивід до kubectl:

cat job.yaml.jinja2 | render_template | kubectl apply -f -

Kubernetes прийме та запустить створені вами Jobs.

Очищення

# Видалення створених вами Jobs
# Ваш кластер автоматично очищає їхні Pods
kubectl delete job -l jobgroup=jobexample

Використання Jobs у реальних робочих навантаженнях

У реальному випадку використання кожен Job виконує деяку суттєву обчислювальну задачу, наприклад, рендеринг кадру фільму або обробку діапазону рядків у базі даних. Якщо ви рендерите відео, ви будете встановлювати $ITEM на номер кадру. Якщо ви обробляєте рядки з таблиці бази даних, ви будете встановлювати $ITEM для представлення діапазону рядків бази даних, які потрібно обробити.

У завданні ви виконували команду для збирання виводу з Podʼів, отримуючи їхні логи. У реальному випадку використання кожен Pod для Job записує свій вивід у надійне сховище перед завершенням. Ви можете використовувати PersistentVolume для кожного Job або зовнішню службу зберігання даних. Наприклад, якщо ви рендерите кадри для відео, використовуйте HTTP PUT щоб включити дані обробленого кадру до URL, використовуючи різні URL для кожного кадру.

Мітки на Job та Podʼах

Після створення Job, Kubernetes автоматично додає додаткові мітки, які вирізняють Podʼи одного Job від Podʼів іншого Job.

У цьому прикладі кожен Job та його шаблон Pod мають мітку: jobgroup=jobexample.

Сам Kubernetes не звертає уваги на мітки з іменем jobgroup. Встановлення мітки для всіх Job, які ви створюєте за шаблоном, робить зручним керування всіма цими Jobs одночасно. У першому прикладі ви використовували шаблон для створення кількох Job. Шаблон гарантує, що кожен Pod також отримує ту саму мітку, тому ви можете перевірити всі Podʼи для цих шаблонних Job за допомогою однієї команди.

Альтернативи

Якщо ви плануєте створити велику кількість обʼєктів Job, ви можете виявити, що:

  • Навіть використовуючи мітки, керування такою кількістю Job є громіздким.
  • Якщо ви створюєте багато Job одночасно, ви можете створити високе навантаження на панель управління Kubernetes. Крім того, сервер API Kubernetes може обмежити швидкість запитів, тимчасово відхиляючи ваші запити зі статусом 429.
  • Ви обмежені квотою ресурсів на Job: сервер API постійно відхиляє деякі ваші запити, коли ви створюєте велику кількість роботи за один раз.

Існують інші шаблони роботи з Job, які ви можете використовувати для обробки значного обсягу роботи без створення великої кількості обʼєктів Job.

Ви також можете розглянути можливість написання власного контролера, щоб автоматично керувати обʼєктами Job.

7 - Обробка повторюваних і неповторюваних помилок Pod за допомогою політики збоїв Pod

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [beta]

Цей документ показує, як використовувати політику збоїв Pod, у поєднанні з типовою політикою відмови Podʼа, для покращення контролю над обробкою збоїв на рівні контейнера або Pod у Job.

Визначення політики збоїв Pod може допомогти вам:

Перш ніж ви розпочнете

Ви повинні вже бути знайомі з основним використанням Job.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.25. Для перевірки версії введіть kubectl version.

Переконайтеся, що feature gates PodDisruptionConditions і JobPodFailurePolicy увімкнені у вашому кластері.

Використання політики збоїв Pod для уникнення непотрібних повторних запусків Pod

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

Спочатку створіть Job на основі конфігурації:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-failjob
spec:
  completions: 8
  parallelism: 2
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]
        args:
        - -c
        - echo "Hello world! I'm going to exit with 42 to simulate a software bug." && sleep 30 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main
        operator: In
        values: [42]

виконавши команду:

kubectl create -f job-pod-failure-policy-failjob.yaml

Через приблизно 30 секунд весь Job повинен завершитися. Перевірте статус Job, виконавши команду:

kubectl get jobs -l job-name=job-pod-failure-policy-failjob -o yaml

У статусі Job побачите умову Failed із полем reason рівним PodFailurePolicy. Крім того, поле message містить детальнішу інформацію про завершення Job, наприклад: Container main for pod default/job-pod-failure-policy-failjob-8ckj8 failed with exit code 42 matching FailJob rule at index 0.

Для порівняння, якщо політика збоїв Pod була вимкнена, це б зайняло 6 спроб повторного запуску Pod, на що треба щонайменше 2 хвилини.

Прибирання

Видаліть створений Job:

kubectl delete jobs/job-pod-failure-policy-failjob

Кластер автоматично очищає Pod.

Використання політики збоїв Pod для ігнорування розладів Pod

На наступному прикладі ви можете навчитися використовувати політику збоїв Pod, щоб ігнорувати розлади Pod, які збільшують лічильник повторних спроб Pod до межі .spec.backoffLimit.

  1. Створіть Job на основі конфігурації:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-ignore
    spec:
      completions: 4
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: docker.io/library/bash:5
            command: ["bash"]
            args:
            - -c
            - echo "Hello world! I'm going to exit with 0 (success)." && sleep 90 && exit 0
      backoffLimit: 0
      podFailurePolicy:
        rules:
        - action: Ignore
          onPodConditions:
          - type: DisruptionTarget
    

    виконавши команду:

    kubectl create -f job-pod-failure-policy-ignore.yaml
    
  2. Виконайте цю команду, щоб перевірити nodeName, на якому розміщено Pod:

    nodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')
    
  3. Запустить очищення вузла, щоб виселити Pod до завершення його роботи (протягом 90 секунд):

    kubectl drain nodes/$nodeName --ignore-daemonsets --grace-period=0
    
  4. Перевірте .status.failed, щоб переконатися, що лічильник для Job не збільшено:

    kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
    
  5. Зніміть блокування з вузла:

    kubectl uncordon nodes/$nodeName
    

Job відновиться і завершиться успішно.

Для порівняння, якщо політика збоїв Pod була вимкнена, розлад Pod призведе до завершення всього Job (оскільки .spec.backoffLimit встановлено на 0).

Прибирання

Видаліть створений Job:

kubectl delete jobs/job-pod-failure-policy-ignore

Кластер автоматично очищає Pod.

Використання політики збоїв Pod для уникнення непотрібних повторних запусків Pod на основі власних умов Pod

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

  1. Спочатку створіть Job на основі конфігурації:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-config-issue
    spec:
      completions: 8
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: "non-existing-repo/non-existing-image:example"
      backoffLimit: 6
      podFailurePolicy:
        rules:
        - action: FailJob
          onPodConditions:
          - type: ConfigIssue
    

    виконавши команду:

    kubectl create -f job-pod-failure-policy-config-issue.yaml
    

    Зверніть увагу, що образ налаштоване неправильно, оскільки його не існує.

  2. Перевірте статус Pod Job, виконавши команду:

    kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    Ви побачите результат, подібний до цього:

    containerStatuses:
    - image: non-existing-repo/non-existing-image:example
       ...
       state:
       waiting:
          message: Back-off pulling image "non-existing-repo/non-existing-image:example"
          reason: ImagePullBackOff
          ...
    phase: Pending
    

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

  3. Додайте власну умову. Спочатку підготуйте патч, виконавши команду:

    cat <<EOF > patch.yaml
    status:
      conditions:
      - type: ConfigIssue
        status: "True"
        reason: "NonExistingImage"
        lastTransitionTime: "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
    EOF
    

    По-друге, виберіть один із Pod, створених Job, виконавши команду:

    podName=$(kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o jsonpath='{.items[0].metadata.name}')
    

    Потім застосуйте патч до одного з Pod, виконавши наступну команду:

    kubectl patch pod $podName --subresource=status --patch-file=patch.yaml
    

    Якщо патч успішно застосовано, ви отримаєте повідомлення такого типу:

    pod/job-pod-failure-policy-config-issue-k6pvp patched
    
  4. Видаліть Pod для переходу його до фази Failed, виконавши команду:

    kubectl delete pods/$podName
    
  5. Перевірте статус Job, виконавши:

    kubectl get jobs -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    У статусі Job перегляньте умову Failed з полем reason, рівним PodFailurePolicy. Додатково, поле message містить більш детальну інформацію про завершення завдання, таку як: Pod default/job-pod-failure-policy-config-issue-k6pvp має умову ConfigIssue, яка відповідає правилу FailJob за індексом 0.

Очищення

Видаліть створене вами завдання:

kubectl delete jobs/job-pod-failure-policy-config-issue

Кластер автоматично очищує поди.

Альтернативи

Ви можете покладатись виключно на політику відмови Pod backoff, вказавши поле .spec.backoffLimit завдання. Однак у багатьох ситуаціях важко знайти баланс між встановленням низького значення для .spec.backoffLimit для уникнення непотрібних повторних спроб виконання Podʼів, але достатньо великого, щоб забезпечити, що Job не буде припинено через втручання у роботу Podʼів.