Запланировать запуск кода в вашей контейнерной среде
Добро пожаловать в очередной выпуск серии блогов Kubernetes in a Nutshell. До сих пор мы рассматривали ресурсы (объекты) Kubernetes, такие как Deployment
s, Service
s, Volume
s и т. Д.
В этом блоге мы рассмотрим Job
и CronJob
. С помощью примеров вы узнаете о:
- Как использовать эти компоненты.
- Укажите ограничения, такие как ограничение по времени, параллелизм.
- Обработка сбоев и т. Д.
Код (много YAML) доступен на GitHub.
Работа Kubernetes
Вы можете использовать Kubernetes Job
для запуска пакетных процессов, заданий ETL, специальных операций и т. Д. Он запускает Pod
и позволяет ему работать до конца. Это сильно отличается от других Pod
контроллеров, таких как Deployment
или ReplicaSet
.
Как всегда, мы будем учиться на практике. Итак, приступим!
Привет, работа!
Вот как выглядит типичный Job
манифест:
apiVersion: batch/v1
kind: Job
metadata:
name: job1
spec:
template:
spec:
containers:
- name: job
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 90s; echo exiting...; date
restartPolicy: Never
Этот Job
просто запустит busybox
контейнер, который просто выполняет кучу команд оболочки. Давайте создадим это Job
и разберемся, что происходит
Чтобы упростить задачу, на файл YAML ссылаются непосредственно из репозитория GitHub, но вы также можете загрузить файл на свой локальный компьютер и использовать его таким же образом.
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/jobs/job1.yaml
Проверьте Job
и связанный с ним Pod
.
kubectl get job/job1
NAME COMPLETIONS DURATION AGE job1 0/1 8s 8s
Вы должны увидеть Pod
в состоянии Running
, например:
kubectl get pod -l=job-name=job1
job1-bptmd 1/1 Running
Если вы проверите Pod
журналы, вы должны увидеть что-то похожее на это:
kubectl logs <pod_name>
Thu Jan 9 10:10:35 UTC 2020 sleeping....
Проверьте задание еще раз через ~ 90 с.
kubectl get job/job1
NAME COMPLETIONS DURATION AGE job1 1/1 95s 102s
Job
проработал немногим более 90 секунд, и COMPLETIONS
отражает успешное завершение Pod
. Это также отразится в Pod
журналах.
Thu Jan 9 10:10:05 UTC 2020
sleeping....
exiting...
Thu Jan 9 10:11:35 UTC 2020
Кроме того, статус Pod
должен измениться на Completed
.
kubectl get pod -l=job-name=job1
job1-bptmd 0/1 Completed
Если все, что Job
было сделано, - это создать Pod
для запуска контейнера, почему мы не можем использовать старый добрый Pod
?
Это потому, что Job
может быть перезапущен Kubernetes в случае сбоя контейнера, чего не может произойти с изолированным Pod
. В дополнение к этому, есть много других возможностей, которые предоставляет контроллер заданий, которые мы рассмотрим в будущем.
Чтобы удалить это Job
, просто запустите kubectl delete job/job1
.
Установление ограничения по времени
Например, вы выполняете пакетное задание, и его выполнение по какой-то причине занимает слишком много времени. Это может быть нежелательно. Вы можете ограничить время, в течение которого Job
может продолжать работать, установив атрибут activeDeadlineSeconds
в спецификации.
Вот пример:
apiVersion: batch/v1
kind: Job
metadata:
name: job2
spec:
activeDeadlineSeconds: 5
template:
spec:
containers:
- name: job
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 10s; echo exiting...; date
restartPolicy: Never
Обратите внимание, что activeDeadlineSeconds
был установлен на 5 секунд, в то время как процесс контейнера был назначен для запуска в течение 10 секунд.
Создайте Job
, подождите несколько секунд (~ 10 секунд) и проверьте Job
.
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/jobs/job2.yaml
kubect get job/job2 -o yaml
Прокрутите вниз, чтобы проверить поле status
, и вы увидите, что Job
находится в состоянии Failed
из-за DeadlineExceeded
.
status:
conditions:
- lastProbeTime: "2020-01-09T10:57:13Z"
lastTransitionTime: "2020-01-09T10:57:13Z"
message: Job was active longer than specified deadline
reason: DeadlineExceeded
status: "True"
type: Failed
Чтобы удалить задание, просто запустите kubectl delete job/job2
.
Обработка сбоев
Что делать, если возникают проблемы из-за сбоя контейнера (процесс завершен) или Pod
сбоя? Давайте попробуем это сделать, смоделировав сбой.
В этом Job
контейнер печатает date
, sleep
s в течение 5 секунд и выходит со статусом 1 для имитации сбоя.
apiVersion: batch/v1
kind: Job
metadata:
name: job3
spec:
backoffLimit: 2
template:
spec:
containers:
- name: job
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 5s; exit 1;
restartPolicy: OnFailure
Обратите внимание, что restartPolicy: OnFailure
отличается от предыдущего примера, где он был установлен на Never
. Мы вернемся к этому чуть позже.
Создайте Job
и следите за конкретным Pod
для этой работы.
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/jobs/job3.yaml
kubectl get pod -l=job-name=job3 -w
Вы должны увидеть что-то похожее на изображение ниже:
NAME READY STATUS RESTARTS AGE
job3-qgv4b 0/1 ContainerCreating 0 4s
job3-qgv4b 1/1 Running 0 6s
job3-qgv4b 0/1 Error 0 12s
job3-qgv4b 1/1 Running 1 17s
job3-qgv4b 0/1 Error 1 22s
job3-qgv4b 0/1 CrashLoopBackOff 1 34s
job3-qgv4b 1/1 Running 2 40s
job3-qgv4b 1/1 Terminating 2 40s
job3-qgv4b 0/1 Terminating 2 45s
job3-qgv4b 0/1 Terminating 2 51s
Обратите внимание, как меняется Pod
статус.
- Он начинается с вытягивания и запуска контейнера.
- Он переходит в состояние
Error
, так как выходит со статусом 1 (после сна в течение 5 секунд). - Он снова возвращается к состоянию
Running
(обратите внимание, что счетчикRESTARTS
теперь равен 1). - Как и ожидалось, он снова переходит в состояние
Error
и перезапускается еще раз -RESTARTS
count теперь 2. - Наконец, это
terminated
.
Kubernetes (точнее, контроллер заданий) перезапустил контейнер за нас, потому что мы указали restartPolicy: OnFailure
.
Но может возникнуть ситуация, когда это может продолжаться бесконечно, поэтому мы ограничиваем это с помощью backoffLimit: 2
, что гарантирует, что Kubernetes повторяет попытки только дважды, прежде чем пометить этот Job
как Failed
.
Обратите внимание, что это был пример перезапуска контейнера. Контроллер заданий также может создать новый Pod
в случае сбоя Pod
.
Если вы проверите состояние Job
...
kubectl get job/job3 -o yaml
… Вы увидите, что у него Failed
из-за BackoffLimitExceeded
.
status:
conditions:
- lastProbeTime: "2020-01-09T11:16:24Z"
lastTransitionTime: "2020-01-09T11:16:24Z"
message: Job has reached the specified backoff limit
reason: BackoffLimitExceeded
status: "True"
type: Failed
restartPolicy
из Never
означает, что сбой не приведет к перезапуску контейнера или созданию нового Pod
, когда что-то пойдет не так. Кроме того, ограничение по умолчанию для backoffLimit
составляет 6
.
Чтобы удалить это задание, просто запустите kubectl delete job/job3
.
Больше лучше
Существуют требования, при которых вы можете захотеть, чтобы Job
набирал более одного Pod
, чтобы добиться результата.
Например, рассмотрим сценарий, в котором вы запускаете пакетное задание для обработки записей из базы данных - наличие нескольких Pod
, разделяющих нагрузку, определенно может помочь.
Один из способов сделать это может заключаться в том, чтобы каждый Pod
запускался последовательно, записывал количество строк, обработанных во внешнем источнике (например, в другой таблице БД), а другой Pod
мог забираться оттуда.
Это можно сделать, добавив свойство completions
в спецификацию Job
.
apiVersion: batch/v1
kind: Job
metadata:
name: job4
spec:
completions: 2
template:
spec:
containers:
- name: job
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 10s; echo exiting...; date
restartPolicy: Never
Создайте Job
и следите за его развитием.
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/jobs/job4.yaml
kubectl get job/job4 -w
Вы должны увидеть что-то похожее на это:
NAME COMPLETIONS DURATION AGE
job4 0/2 3s 3s
job4 1/2 20s 20s
job4 2/2 37s 37s
Поскольку мы установили completions
равным двум:
- Два
Pod
были созданы один за другим (последовательно). Job
был отмеченCompleted
(успешным) только после того, как обаPod
s были выполнены до конца. В противном случае были бы применены условия отказа (как описано выше).
Давайте также проверим Pod
журналы.
kubectl get pods -l=job-name=job4
kubect logs <pod_name>
Если вы видите журналы для обоих Pod
, вы сможете подтвердить, что они запускались один за другим в последовательности (и каждый работал в течение ~ 10 секунд).
Журналы для Pod
1.
Thu Jan 9 11:31:57 UTC 2020
sleeping....
exiting...
Thu Jan 9 11:32:07 UTC 2020
Журналы для Pod
2.
Thu Jan 9 11:32:15 UTC 2020
sleeping....
exiting...
Thu Jan 9 11:32:25 UTC 2020
Как насчет запуска пакетной обработки в параллельном режиме, когда все Pod
создаются одновременно (а не последовательно)?
Чтобы справиться с этим случаем, наша логика обработки должна быть соответствующим образом настроена, поскольку между параллельными Pod
s требуется координация с точки зрения того, какой набор рабочих элементов выбрать и как обновить их статус завершения.
Мы не будем углубляться в это, но я надеюсь, что вы уловили идею с точки зрения требований.
Теперь этого можно достичь, используя parallelism
вместе с completions
. Вот пример:
apiVersion: batch/v1
kind: Job
metadata:
name: job5
spec:
completions: 3
parallelism: 3
template:
spec:
containers:
- name: job
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 10s; echo exiting...; date
restartPolicy: Never
Используя атрибут parallelism
, мы смогли ограничить максимальное количество Pod
, которые могут выполняться одновременно. В этом случае, поскольку parallelism
установлено в три, это означает, что:
- Все три
Pod
будут созданы одновременно. Job
будет помечен какCompleted
(успешно), только если все три будут выполнены до конца. В противном случае применяются условия отказа (как описано выше).
Как только вы закончите
Вы можете использовать ttlSecondsAfterFinished
, чтобы указать количество секунд, по истечении которых Job
может быть автоматически удален после его завершения (либо Completed
, либо Failed
). Это также удаляет зависимые объекты, такие как Pod
s, порожденные Job
.
CronJob
Объект CronJob
позволяет вам планировать Job
выполнение, а не запускать их вручную.
Он использует формат Cron для выполнения задания по расписанию. По сути, CronJob
- это абстракция более высокого уровня, которая включает в себя шаблон Job
(как показано выше) вместе с расписанием (формат cron) и другими атрибутами.
Давайте создадим простой CronJob
, который повторяется каждую минуту.
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob1
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: cronjob
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 5s; echo exiting...;
restartPolicy: Never
Раздел jobTemplate
такой же, как у Job
. Он просто встроен в эту CronJob
спецификацию. Это тот же контейнер, который мы использовали в примере Job
.
Создайте CronJob
и проверьте его:
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/jobs/cronjob1.yaml
kubectl get cronjob/cronjob1
Выход:
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob1 */1 * * * * False 0 <none> 4s
Следите за Job
, порождаемым этим CronJob
.
kubectl get job -w
NAME COMPLETIONS DURATION AGE cronjob1-1578572340 0/1 2s 2s cronjob1-1578572340 1/1 11s 11s cronjob1-1578572400 0/1 0s cronjob1-1578572400 0/1 0s 0s cronjob1-1578572400 1/1 10s 10s cronjob1-1578572460 0/1 0s cronjob1-1578572460 0/1 0s 0s cronjob1-1578572460 1/1 11s 11s
Каждую минуту создается новый Job
, который, как и ожидалось, работал ~ 10 секунд. Вы также можете проверить журналы индивидуального Pod
, созданного Job
(точно так же, как вы это делали с предыдущими примерами).
kubectl get pod -l=job-name=<job_name>
kubectl logs <pod_name>
Есть и другие (необязательные) свойства CronJob
в дополнение к атрибуту schedule
. Давайте посмотрим на один из них.
concurrencyPolicy
У него есть три возможных значения - Forbid
, Allow
и Replace
.
Выберите Forbid
, если вы не хотите одновременного выполнения вашего Job
. Когда пора активировать Job
в соответствии с расписанием, а экземпляр Job
уже запущен, текущая итерация пропускается.
Если вы выберете Replace
в качестве политики параллелизма, текущий запущенный Job
будет остановлен и будет создан новый Job
.
Указание Allow
позволит нескольким Job
экземплярам работать одновременно.
Вот пример:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob2
spec:
schedule: "*/1 * * * *"
concurrencyPolicy: Allow
jobTemplate:
spec:
template:
spec:
containers:
- name: cronjob
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 90s; echo exiting...;
restartPolicy: Never
Вы можете создать это CronJob
, а затем отслеживать отдельных Job
, чтобы наблюдать за поведением.
kubectl apply -f https://raw.githubusercontent.com/abhirockzz/kubernetes-in-a-nutshell/master/jobs/cronjob2.yaml
kubectl get job -w
Поскольку расписание составляет каждую минуту, а контейнер работает в течение 90 секунд, вы увидите, что одновременно выполняется несколько Job
. Такое совпадение возможно, поскольку мы применили concurrencyPolicy: Allow
.
Вы можете увидеть что-то вроде этого:
cronjob2-1578573480 0/1 0s
cronjob2-1578573480 0/1 0s 0s
cronjob2-1578573540 0/1 0s
cronjob2-1578573540 0/1 0s 0s
cronjob2-1578573480 1/1 95s 95s
Обратите внимание, что задание cronjob2-1578573540
было запущено до завершения cronjob2-1578573480
.
Другие свойства CronJob
:
- История заданий:
successfulJobsHistoryLimit
иfailedJobsHistoryLimit
можно использовать, чтобы указать, сколько истории вы хотите сохранить для неудачных и завершенныхJob
s. - Срок начала указан
startingDeadlineSeconds
. - Приостановить указано
suspend
.
Заключение
Это все, что касается этой части серии «Kubernetes in a Nutshell». Следите за новостями.
Я очень надеюсь, что вам понравилась эта статья и вы ее чему-то научились. Рад получить ваш отзыв в виде комментария.