Контейнери ініціалізації
Ця сторінка надає загальний огляд контейнерів ініціалізації: спеціалізованих контейнерів, які запускаються перед запуском контейнерів застосунків в Podʼі. Контейнери ініціалізації можуть містити утиліти або сценарії налаштування, які відсутні в образі застосунку.
Ви можете вказати контейнери ініціалізації в специфікації Podʼа разом із масивом containers
(який описує контейнери застосунку).
У Kubernetes контейнер sidecar — це контейнер, який запускається перед основним контейнером застосунку і продовжує працювати. Цей документ стосується контейнерів ініціалізації — контейнерів, які завершують свою роботу після ініціалізації Podʼа.
Контейнери ініціалізації
У Pod може бути кілька контейнерів, які виконують застосунки всередині нього, але також може бути один чи кілька контейнерів ініціалізації, які виконуються до того, як стартують контейнери застосунків.
Контейнери ініціалізації абсолютно такі ж, як і звичайні контейнери, окрім того:
- Контейнери ініціалізації завжди завершуються після виконання завдань ініціалізації.
- Кожен контейнер ініціалізації повинен успішно завершити свою роботу, перш ніж почнуть свою роботу наступні.
Якщо контейнер init Pod виходить з ладу, kubelet неодноразово перезапускає цей контейнер, поки він не досягне успіху. Однак якщо у Pod встановлено restartPolicy
рівне Never, і контейнер ініціалізації виходить з ладу під час запуску Pod, Kubernetes розглядає весь Pod як неуспішний.
Для зазначення контейнера ініціалізації для Pod додайте поле initContainers
у специфікацію Podʼа, у вигляді масиву обʼєктів container
(аналогічно полю containers
контейнерів застосунку і їх змісту). Дивіться Container в
довідці API для отримання докладнішої інформації.
Стан контейнерів внвціалізації повертається у полі .status.initContainerStatuses
у вигляді масиву станів контейнерів (аналогічно полю .status.containerStatuses
).
Відмінності від звичайних контейнерів
Контейнери ініціалізації підтримують всі поля та можливості контейнерів застосунків, включаючи обмеження ресурсів, томи та налаштування безпеки. Однак запити та обмеження ресурсів для контейнера ініціалізації обробляються по-іншому, як описано в розділі Спільне використання ресурсів в межах контейнерів.
Звичайні контейнери ініціалізації (іншими словами, виключаючи контейнери sidecar) не підтримують поля lifecycle
, livenessProbe
, readinessProbe
чи startupProbe
. Контейнери ініціалізації повинні успішно завершити свою роботу перед тим, як Pod може бути готовий; контейнери sidecar продовжують працювати протягом життєвого циклу Podʼа і підтримують деякі проби. Дивіться контейнер sidecar для отримання додаткової інформації про nfrs контейнери.
Якщо ви вказали кілька контейнерів ініціалізації для Podʼа, kubelet виконує кожен такий контейнер послідовно. Кожен контейнер ініціалізації повинен успішно завершити свою роботу, перш ніж може бути запущено наступний. Коли всі контейнери ініціалізації завершать свою роботу, kubelet ініціалізує контейнери застосунків для Podʼа та запускає їх як зазвичай.
Відмінності від контейнерів sidecar
Контейнери ініціалізації запускаються і завершують свої завдання до того, як розпочнеться робота основного контейнера застосунку. На відміну від контейнерів sidecar, контейнери ініціалізації не продовжують працювати паралельно з основними контейнерами.
Контейнери ініціалізації запускаються і завершують свою роботу послідовно, і основний контейнер не починає свою роботу, доки всі контейнери ініціалізації успішно не завершать свою роботу.
Контейнери ініціалізації не підтримують lifecycle
, livenessProbe
, readinessProbe
чи startupProbe
, у той час, як контейнери sidecar підтримують всі ці проби, щоб керувати своїм життєвим циклом.
Контейнери ініціалізації використовують ті ж ресурси (CPU, памʼять, мережу) що й основні контейнери застосунків, але не взаємодіють з ними напряму. Однак вони можуть використовувати спільні томи для обміну даними.
Використання контейнерів ініціалізації
Оскільки контейнери ініціалізації мають окремі образи від контейнерів застосунків, вони мають кілька переваг для коду, повʼязаного із запуском:
- Контейнери ініціалізації можуть містити утиліти або власний код для налаштування, які відсутні в образі застосунку. Наприклад, немає потреби створювати образ
FROM
іншого образу лише для використання інструменту, такого якsed
,awk
,python
чиdig
під час налаштування. - Ролі створення та розгортання образу застосунку можуть працювати незалежно одна від одної без необхідності спільного створення єдиного образу застосунку.
- Контейнери ініціалізації можуть працювати з різними видами файлових систем порівняно з контейнерами застосунків у тому ж Podʼі. Вони, отже, можуть мати доступ до Secret, до яких контейнери застосунків не можуть отримати доступ.
- Оскільки контейнери ініціалізації завершують свою роботу, перш ніж будь-які контейнери застосунків розпочнуть свою роботу, вони пропонують механізм блокування або затримки запуску контейнера застосунку до виконання певних умов. Після того, як умови виконані, всі контейнери застосунків у Pod можуть стартувати паралельно.
- Контейнери ініціалізації можуть безпечно виконувати утиліти або власний код, який інакше зробив би образ контейнера застосунку менш безпечним. Відокремлюючи непотрібні інструменти, ви можете обмежити область атак для вашого образу контейнера застосунку.
Приклади
Ось кілька ідей, як використовувати контейнери ініціалізації:
Очікування на створення Service, використовуючи команду оболонки у вигляді одного рядка, наприклад:
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
Реєстрація Podʼа у віддаленому сервері зі значеннями, отриманими з Downward API за допомогою команди, подібної цій:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
Очікування певного часу перед запуском контейнера застосунку за допомогою команди, подібної цій:
sleep 60
Клонування репозиторію Git в Том
Додавання значень у файл конфігурації та виклик інструменту шаблонування для динамічного створення файлу конфігурації для основного контейнера застосунку. Наприклад, додайте значення
POD_IP
у конфігурації та створюйте основний файл конфігурації застосунку, що використовує Jinja.
Використання контейнерів ініціалізації
У цьому прикладі визначається простий Pod, який має два контейнери ініціалізації. Перший чекає на myservice
, а другий — на mydb
. Як тільки обидва контейнери ініціалізації завершаться, Pod запускає контейнер застосунку зі свого розділу spec
.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
Ви можете запустити цей Pod, використовуючи:
kubectl apply -f myapp.yaml
Вивід подібний до цього:
pod/myapp-pod created
І перевірте його статус за допомогою:
kubectl get -f myapp.yaml
Вивід подібний до цього:
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
або для отримання більше деталей:
kubectl describe -f myapp.yaml
Вивід подібний до цього:
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container init-myservice
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container init-myservice
Щоб переглянути логи контейнерів ініціалізації у цьому Podʼі, виконайте:
kubectl logs myapp-pod -c init-myservice # Огляд першого контейнера ініціалізації
kubectl logs myapp-pod -c init-mydb # Огляд другого контейнера ініціалізації
На цей момент ці контейнери ініціалізації будуть чекати виявлення Сервісів з іменами mydb
та myservice
.
Ось конфігурація, яку ви можете використовувати для того, щоб ці Services зʼявилися:
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
Щоб створити Services mydb
та myservice
:
kubectl apply -f services.yaml
Вивід подібний до цього:
service/myservice created
service/mydb created
Потім ви побачите, що ці контейнери ініціалізації завершаться, і Pod myapp-pod
переходить у стан Running:
kubectl get -f myapp.yaml
Вивід подібний до цього:
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
Цей простий приклад повинен дати вам натхнення для створення ваших власних контейнерів ініціалізації. У розділі Що далі є посилання на більш детальний приклад.
Докладний опис поведінки
Під час запуску Pod kubelet затримує виконання контейнерів ініціалізації (init containers), доки мережеве зʼєднання та сховище не будуть готові. Після цього kubelet виконує контейнери ініціалізації Podʼа в порядку, в якому вони зазначені в специфікації Pod.
Кожен контейнер ініціалізації має успішно завершитися перед тим, як буде запущений наступний контейнер. Якщо контейнер не вдалося запустити через помилку середовища виконання або він завершується з помилкою, його перезапускають згідно з політикою перезапуску Podʼів (restartPolicy
). Однак, якщо політика перезапуску Podʼа (restartPolicy
) встановлена на Always
, контейнери ініціалізації використовують політику перезапуску OnFailure
.
Pod не може бути Ready
, поки всі контейнери ініціалізації не завершаться успішно. Порти контейнерів ініціалізації не агрегуються в Service. Pod, що ініціалізується, перебуває в стані Pending
, але повинен мати умову Initialized
, встановлену на false.
Якщо Pod перезапускається або був перезапущений, всі контейнери ініціалізації мають виконатися знову.
Зміни в специфікації контейнерів ініціалізації обмежуються полем образу контейнера. Безпосередня зміна поля image
контейнера init не призводить до перезапуску Podʼа або його перестворення. Якщо Pod ще не було запущено, ці зміни можуть вплинути на те, як він завантажиться.
У шаблоні pod template ви можете змінити будь-яке поле початкового контейнера; вплив цих змін залежить від того, де використовується шаблон podʼа.
Оскільки контейнери ініціалізації можуть бути перезапущені, повторно виконані або виконані заново, код контейнерів ініціалізації повинен бути ідемпотентним. Зокрема, код, що записує дані у том emptyDirs
, має бути підготовлений до того, що файл виводу вже може існувати.
Контейнери ініціалізації мають усі поля контейнера застосунку. Проте Kubernetes забороняє використання readinessProbe
, оскільки контейнери ініціалізації не можуть визначати готовність окремо від завершення. Це забезпечується під час валідації.
Використовуйте activeDeadlineSeconds
в Podʼі, щоб запобігти нескінченним збоям контейнерів ініціалізації. Загальний дедлайн включає контейнери ініціалізації. Однак рекомендується використовувати activeDeadlineSeconds
лише якщо команди розгортають свій застосунок як Job, оскільки activeDeadlineSeconds
впливає навіть після завершення роботи контейнера ініціалізації. Pod, який вже працює правильно, буде зупинений через activeDeadlineSeconds
, якщо ви це налаштуєте.
Імʼя кожного контейнера застосунку та контейнера ініціалізації в Pod має бути унікальним; якщо контейнер має імʼя, яке збігається з іншим, буде згенеровано помилку валідації.
Спільне використання ресурсів між контейнерами
З урахуванням порядку виконання контейнерів ініціалізації, обслуговування та застосунків застосовуються наступні правила використання ресурсів:
- Найвищий запит чи обмеження будь-якого конкретного ресурсу, визначеного у всіх контейнерах ініціалізації, вважається ефективним запитом/обмеженням ініціалізації. Якщо для будь-якого ресурсу не вказано обмеження, це вважається найвищим обмеженням.
- Ефективний запит/обмеження Podʼа для ресурсу — більше з:
- сума всіх запитів/обмежень контейнерів застосунків для ресурсу
- ефективний запит/обмеження для ініціалізації для ресурсу
- Планування виконується на основі ефективних запитів/обмежень, що означає, що контейнери ініціалізації можуть резервувати ресурси для ініціалізації, які не використовуються протягом життя Podʼа.
- Рівень якості обслуговування (QoS), рівень QoS Podʼа — є рівнем QoS як для контейнерів ініціалізації, так і для контейнерів застосунків.
Обмеження та ліміти застосовуються на основі ефективного запиту та ліміту Podʼа.
Контейнери ініціалізація та cgroups Linux
У Linux, розподіл ресурсів для контрольних груп рівня Podʼів (cgroups) ґрунтується на ефективному запиті та ліміті рівня Podʼа, так само як і для планувальника.
Причини перезапуску Pod
Pod може перезапускатися, що призводить до повторного виконання контейнерів ініціалізації, з наступних причин:
- Перезапускається контейнер інфраструктури Podʼа. Це рідкісне явище і його має виконати тільки той, хто має root-доступ до вузлів.
- Всі контейнери в Podʼі завершуються, коли
restartPolicy
встановлено вAlways
, що примушує до перезапуску, а запис про завершення контейнера ініціалізації був втрачено через збирання сміття.
Pod не буде перезапущено, коли змінюється образ контейнера ініціалізації, або запис про завершення контейнера ініціалізації був втрачений через збирання сміття. Це стосується Kubernetes v1.20 і пізніших версій. Якщо ви використовуєте попередню версію Kubernetes, ознайомтеся з документацією версії, яку ви використовуєте.
Що далі
Дізнайтеся більше про наступне:
- Створення Podʼа з контейнером ініціалізації.
- Налагодження контейнерів ініціалізації.
- Огляд kubelet та kubectl.
- Види проб: liveness, readiness, startup probe.
- Контейнери sidecar.