[Перевод] Зачем нужен контейнер pause в Kubernetes

05104b70acbed9f0cb6a54139a3485b0.jpg

Почему в поде k8s есть служебный контейнер pause? Когда вы проверяете контейнеры, запущенные в вашем кластере K8s, вы часто видите контейнеры pause, как в следующем примере:

b3f43b7e27c444e5046ac48b3adea30e.PNG

Вы когда-нибудь задумывались, почему появляются контейнеры pause? Когда мы создаем поды, мы не помним, что когда-либо создавали эти контейнеры pause, так откуда же они берутся? Вы можете подумать, что, так как мы не создавали эти контейнеры самостоятельно, возможно, кластер K8s автоматически создал их?

Так называемый контейнер pause в K8s иногда называют контейнером infra. Он «поставляется» вместе с пользовательским контейнером и запускается в том же поде.

Контейнер pause — основа сетевой модели пода. Понимание контейнера pause поможет вам лучше понять исходный замысел структуры пода K8s.

Контейнер pause

При создании пода процесс kubelet сначала вызывает Runtime Service.RunPodSandbox интерфейса CIR, чтобы создать окружение «песочницу» и настроить базовую среду выполнения, например, как сеть.

Как только создано изолированное окружение для пода, kubelet может создавать в нем пользовательские контейнеры. Когда придет время удалять под, kubelet сначала удалит «песочницу» пода, а затем остановит все контейнеры внутри.

Контейнер pause — это контейнер, который существует в каждом поде, это похоже на шаблон или на родительский контейнер, от которых все новые контейнеры в поде наследуют namespaces. Контейнер pause запускается, затем переходит в «спящий режим».

Это контейнер-шаблон, который резервирует namespaces, которые являются общими для всех контейнеров внутри пода.

fbc377beb2d3eca26ecac7e9052b99d9.PNG

Вы можете это увидеть в исходном коде pause.c ниже:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define STRINGIFY(x) #x
#define VERSION_STRING(x) STRINGIFY(x)
#ifndef VERSION
#define VERSION HEAD
#endif
static void sigdown(int signo) {
  psignal(signo, "Shutting down, got signal");
  exit(0);
}
static void sigreap(int signo) {
  while (waitpid(-1, NULL, WNOHANG) > 0)
    ;
}
int main(int argc, char **argv) {
  int i;
  for (i = 1; i < argc; ++i) {
    if (!strcasecmp(argv[i], "-v")) {
      printf("pause.c %s\n", VERSION_STRING(VERSION));
      return 0;
    }
  }
if (getpid() != 1)
    /* Not an error because pause sees use outside of infra containers. */
    fprintf(stderr, "Warning: pause should be the first process\n");
if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
    return 1;
  if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
    return 2;
  if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
                                             .sa_flags = SA_NOCLDSTOP},
                NULL) < 0)
    return 3;
for (;;)
    pause();
  fprintf(stderr, "Error: infinite loop terminated\n");
  return 42;
}

Из кода видно, что контейнер pause выполняет следующие две вещи.

  1. регистрирует различные функции обработки сигналов, которые в основном обрабатывают два типа информации: сигналы выхода и дочерние сигналы. Когда он получает SIGINT или SIGTERM, он завершает работу напрямую. Когда получен сигнал SIGCHLD, вызывает waitpid и завершающийся (дочерний) процесс подвергается сборке мусора.

  2. Основной процесс цикла вызывает функцию pause(), которая переводит процесс в спящий режим до тех пор, пока он не будет завершен или не получит сигнал.

Таким образом, даже если последний контейнер в поде выйдет из строя, общий namespace все равно останутся, потому что контейнер pause «держит» namespace.

Демо контейнера pause

Когда вы запускаете новый процесс в Linux, процесс наследует namespace от родительского процесса. Способ запуска процесса в отдельном namespace заключается в создании нового namespace путем отделения от общего namespace с родительским процессом. Ниже приведен пример запуска оболочки в новом namespace PID, UTS, IPC и mount с помощью инструмента unshare.

$ unshare --pid --uts --ipc --mount -f chroot rootfs /bin/sh

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

Используя docker в качестве примера, давайте посмотрим, как можно создать под с нуля, используя контейнеры pause и общий namespace.

Создание контейнера pause

$ docker run -d --name pause gcr.io/google_containers/pause-amd64:3.0
Unable to find image 'gcr.io/google_containers/pause-amd64:3.0' locally
3.0: Pulling from google_containers/pause-amd64
a3ed95caeb02: Pull complete
f11233434377: Pull complete
Digest: sha256:163ac025575b775d1c0f9bf0bdd0f086883171eb475b5068e7defa4ca9e76516
Status: Downloaded newer image for gcr.io/google_containers/pause-amd64:3.0
aa603afaba05b18f8e53844f216d965fb2487feddd32937f56611499af24c0a1

Запуск контейнера с nginx

Затем мы запускаем контейнер nginx:

$ docker run -d --name nginx --net=container:pause  --pid=container:pause nginx

Проверка сетевого namespace

Сначала давайте получим PID для обоих контейнеров pause и nginx:

$ ps -ef | grep pause
root      9377  9353  0 15:47 ?        00:00:00 /pause
$ ps -ef | grep nginx
root      9932  9910  0 15:53 ?        00:00:00 nginx: master process nginx -g daemon off;

Теперь проверим идентификатор сетевого namespace:

$ lsns | grep pause | grep net
4026532388 net         4  9377 root   /pause

Как можем увидеть, идентификатор сетевого namespace для контейнера pauseравен 4026532388.

Теперь давайте узнаем идентификатор сетевого namespace для nginx:

$ readlink /proc/9932/task/9932/ns/net
net:[4026532388]

Таким образом, мы можем подтвердить, что контейнер nginx использует один и тот же сетевой namespace с контейнером pause.

60dd5f84a7e7b6973f53e46c3ef33f8d.jpgУзнать больше о внутрянке K8s

Если вы хотите заглянуть под капот Kubernetes и уверенно использовать в работе его продвинутые возможности, приходите на курс Kubernetes: Мега, который стартует уже 14 февраля!

Курс будет полезен всем, кто собирается запускать Kubernetes в продакшн и отвечать за его работу в дальнейшем. Применение углубленных знаний о k8s поможет компании сэкономить сотни тысяч рублей, а также повысить вашу ценность, как специалиста. 

Что будет на курсе?  

  • создадим отказоустойчивый кластер в ручном режиме 

  • авторизация в кластере

  • настройка autoscaling

  • резервное копирование 

  • Stateful приложения в кластере

  • интеграция Kubernets и Vault для хранения секретов

  • HorizontalPodAutoscaler

  • ротация сертификатов в кластере

  • Blue-Green Deploy и Canary Deploy

  • настройка Service mesh

Курс основан на практике и хорошо подойдет тем, кто ранее уже работал с k8s. Теория помогает закрепить и глубже понять все тонкости работы в Kubernetes.

13 онлайн-встреч со спикерами по 1,5 часа, более 6 часов практики на стендах, групповой чат с куратором и итоговая сертификация — всё это ждёт вас на курсе!

Узнать подробности и записаться

© Habrahabr.ru