Init контейнеры в Pod

В этом посте представлен обзор init контейнеров: специализированных контейнеров, которые запускаются перед контейнерами приложений в Pod. Init контейнеры могут содержать утилиты или сценарии установки, отсутствующие в образе приложения.

Вы можете указать init контейнеры в спецификации Pod вместе с массивом Containers (который описывает контейнеры приложения).

Pod может иметь несколько контейнеров, в которых выполняются приложения, но он также может иметь один или несколько init контейнеров (контейнеров инициализации), которые запускаются до запуска контейнеров приложений.

Init контейнеры точно такие же, как обычные контейнеры, за исключением:

  • Init контейнеры всегда выполняются до конца.
  • Каждый init контейнер должен успешно завершиться до запуска следующего.

В случае сбоя контейнера инициализации Pod'а Kubernetes несколько раз перезапускает Pod, пока init контейнер не будет успешно выполнен. Однако, если у Pod есть RestartPolicy равная Never, Kubernetes не перезапускает Pod.

Чтобы указать init контейнер для Pod, добавьте поле initContainers в спецификацию Pod как массив объектов типа Container вместе с массивом контейнеров приложения. Статус контейнеров инициализации возвращается в поле .status.initContainerStatuses в виде массива статусов контейнера (аналогично полю .status.containerStatuses).

Отличия от обычных контейнеров

Init контейнеры поддерживают все поля и функции контейнеров приложений, включая ограничения ресурсов, объемы и параметры безопасности.

Кроме того, init контейнеры не поддерживают пробы готовности (readiness probes), потому что они должны быть выполнены до завершения, прежде чем Pod будет готов.

Если вы указываете несколько init контейнеров для Pod, Kubelet запускает каждый init контейнер последовательно. Каждый контейнер инициализации должен успешно завершиться, прежде чем будет запущен следующий. Когда все init контейнеры завершены, Kubelet инициализирует контейнеры приложений для Pod и запускает их как обычно.

Использование init контейнеров

Поскольку у init контейнеров есть отдельные от контейнеров приложений образы, у них есть некоторые преимущества для кода, связанного с запуском:

  • Init контейнеры могут содержать утилиты или пользовательский код для настройки, которых нет в образе приложения. Например, нет необходимости создавать образ из другого образа только для того, чтобы использовать такой инструмент, как sed, awk, python или dig во время установки.
  • Init контейнеры могут безопасно запускать утилиты, которые сделали бы образ контейнера приложения менее безопасным.
  • Роль конструктора приложений и роль развертывания могут работать независимо друг от друга без необходимости совместного создания одного образа приложения.
  • Init контейнеры могут работать с другим видом файловой системы, чем контейнеры приложений в одном Pod. Следовательно, им может быть предоставлен доступ к секретам, к которым контейнеры приложения не могут получить доступ.
  • Поскольку init контейнеры выполняются полностью до запуска любых контейнеров приложения, init контейнеры предлагают механизм, блокирующий или задерживающий запуск контейнера приложения, пока не будет выполнен набор предварительных условий. После выполнения предварительных условий все контейнеры приложений в Pod могут запускаться параллельно.

Примеры init контейнеров

Вот несколько идей о том, как использовать init контейнеры:

  • Дождаться создания службы (Service), используя однострочную команду оболочки, например:

    for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; done; exit 1
    

  • Зарегистрировать Pod на удаленном сервере из нисходящего API (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.

Init контейнеры в действии

Этот пример определяет простой Pod, который имеет два init контейнера. Первый ждет myservice, а второй ждет mydb. Как только оба init контейнера завершены, Pod запускает контейнер приложения из своего раздела spec.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: 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; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

Следующий YAML файл описывает службы mydb и myservice:

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

Вы можете запустить этот 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=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 with docker id 5ced34a04634; Security:[seccomp=unconfined]
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container with docker id 5ced34a04634

Чтобы просмотреть журналы для init контейнеров в этом Pod, выполните:

kubectl logs myapp-pod -c init-myservice # Проверить первый init контейнер
kubectl logs myapp-pod -c init-mydb      # Проверить второй init контейнер

На этом этапе эти init контейнеры будут ожидать обнаружения служб с именами mydb и myservice.

Вот конфигурация, которую вы можете использовать, чтобы эти службы появились:

---
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

Чтобы создать сервисы mydb и myservice:

kubectl apply -f services.yaml

service/myservice created
service/mydb created

Затем вы увидите, что эти init контейнеры завершены, и что myapp-pod Pod переходит в состояние Running:

kubectl get -f myapp.yaml

NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

Подробности поведения Pod

Во время запуска Pod каждый init контейнер запускается по порядку, после инициализации сети и томов. Каждый контейнер должен успешно завершиться до запуска следующего контейнера. Если контейнер не запускается из-за времени выполнения или завершается с ошибкой, он повторяется в соответствии с Pod restartPolicy. Однако, если Pod restartPolicy установлен в Always, init контейнеры используют restartPolicy OnFailure.

Pod не может быть готов, пока все init контейнеры не будут выполнены успешно. Порты в init контейнере не агрегированы под Службой (Service). Pod, который инициализируется, находится в состоянии Pending, но для условия Initializing должно быть установлено значение true.

Если Pod перезапускается сам или его перезапускают, все init контейнеры должны выполняться снова.

Изменения в спецификации init контейнера ограничены полем container image. Изменение init поля container image эквивалентно перезапуску Pod.

Поскольку init контейнеры можно перезапустить, повторить или повторно выполнить, код init контейнера должен быть идемпотентным. В частности, код, который записывает файлы в EmptyDirs, должен быть подготовлен к тому, что выходной файл уже существует.

Init контейнеры содержат все поля контейнера приложения. Однако Kubernetes запрещает использование readinessProbe, поскольку init контейнеры не могут определять готовность, отличную от завершения. Это применяется во время валидации.

Используйте activeDeadlineSeconds на Pod и livenessProbe на контейнере, чтобы предотвратить бесконечный сбой init контейнеров. Активный срок включает в себя init контейнеры.

Имя каждого приложения и init контейнера в Pod должно быть уникальным; выдается ошибка проверки для любого контейнера, разделяющего имя с другим.

Ресурсы

Учитывая порядок и выполнение для init контейнеров, применяются следующие правила использования ресурсов:

  • Наибольшим из любого конкретного запроса ресурса или лимита, определенного для всех init контейнеров, является эффективный запрос/лимит инициализации (effective init request/limit).
  • Эффективный запрос/лимит Pod на ресурс выше:
    • сумма всех запросов/лимитов контейнеров приложения для ресурса
    • эффективный запрос инициализации/лимит для ресурса
  • Планирование выполняется на основе эффективных запросов/ограничений, что означает, что init контейнеры могут резервировать ресурсы для инициализации, которые не используются в течение срока службы Pod'а.
  • Уровень QoS (качество обслуживания) эффективного уровня QoS Pod - это уровень QoS для контейнеров init и контейнеров приложений.

Квота и лимиты применяются на основании действующего запроса и лимита Pod.

Группы управления уровнями Pod (cgroups) основаны на действующем запросе и ограничении Pod, так же как и планировщик.

Причины перезапуска Pod

Pod может перезапускаться, вызывая повторное выполнение init контейнеров по следующим причинам:

  • Пользователь обновляет спецификацию Pod, вызывая изменение образа init контейнера. Любые изменения в образе init контейнера перезапускают Pod. Изменения образа контейнера приложения только перезапускают контейнер приложения.
  • Контейнер инфраструктуры Pod перезапущен. Это необычно и должно быть сделано кем-то с корневым доступом к узлам.

Все контейнеры в Pod завершаются, пока для restartPolicy установлено значение Always, что приводит к принудительному перезапуску, а запись о завершении init контейнера была потеряна из-за сборки мусора.


Читайте также:


Комментарии

Популярные сообщения из этого блога

Контроллеры в Kubernetes: DaemonSet

Контроллеры в Kubernetes: ReplicaSet

Контроллеры в Kubernetes: StatefulSet