Зачем нужен контейнер pause в Kubernetes
Почему в поде k8s есть служебный контейнер pause? Когда вы проверяете контейнеры, запущенные в вашем кластере K8s, вы часто видите контейнеры pause
, как в следующем примере:
Вы когда-нибудь задумывались, почему появляются контейнеры pause
? Когда мы создаем поды, мы не помним, что когда-либо создавали эти контейнеры pause
, так откуда же они берутся? Вы можете подумать, что, так как мы не создавали эти контейнеры самостоятельно, возможно, кластер K8s автоматически создал их?
Так называемый контейнер pause
в K8s иногда называют контейнером infra
. Он «поставляется» вместе с пользовательским контейнером и запускается в том же поде.
Контейнер pause
— основа сетевой модели пода. Понимание контейнера pause
поможет вам лучше понять исходный замысел структуры пода K8s.
Контейнер pause
При создании пода процесс kubelet
сначала вызывает Runtime Service.RunPodSandbox
интерфейса CIR, чтобы создать окружение “песочницу” и настроить базовую среду выполнения, например, как сеть.
Как только создано изолированное окружение для пода, kubelet
может создавать в нем пользовательские контейнеры. Когда придет время удалять под, kubelet
сначала удалит “песочницу” пода, а затем остановит все контейнеры внутри.
Контейнер pause
— это контейнер, который существует в каждом поде, это похоже на шаблон или на родительский контейнер, от которых все новые контейнеры в поде наследуют namespaces. Контейнер pause
запускается, затем переходит в “спящий режим”.
Это контейнер-шаблон, который резервирует namespaces, которые являются общими для всех контейнеров внутри пода.
Вы можете это увидеть в исходном коде pause.c
ниже:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#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
выполняет следующие две вещи.
регистрирует различные функции обработки сигналов, которые в основном обрабатывают два типа информации: сигналы выхода и дочерние сигналы. Когда он получает
SIGINT
илиSIGTERM
, он завершает работу напрямую. Когда получен сигналSIGCHLD
, вызываетwaitpid
и завершающийся (дочерний) процесс подвергается сборке мусора.Основной процесс цикла вызывает функцию
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
.
? 14 февраля стартует шестой поток курса «Kubernetes: Мега».В честь Дня всех влюбленных мы запускаем акцию «1+1». Купите участие в потоке курса и получите второе место в подарок.
Узнать больше о внутрянке K8s
Если вы хотите заглянуть под капот Kubernetes и уверенно использовать в работе его продвинутые возможности, приходите на курс Kubernetes: Мега, который стартует уже 14 февраля!
Курс будет полезен всем, кто собирается запускать Kubernetes в продакшн и отвечать за его работу в дальнейшем. Применение углубленных знаний о k8s поможет компании сэкономить сотни тысяч рублей, а также повысить вашу ценность, как специалиста.
Что будет на курсе?
создадим отказоустойчивый кластер в ручном режиме
авторизация в кластере
настройка autoscaling
резервное копирование
Stateful приложения в кластере
интеграция Kubernets и Vault для хранения секретов
HorizontalPodAutoscaler
ротация сертификатов в кластере
Blue-Green Deploy и Canary Deploy
настройка Service mesh
Курс основан на практике и хорошо подойдет тем, кто ранее уже работал с k8s. Теория помогает закрепить и глубже понять все тонкости работы в Kubernetes.
13 онлайн-встреч со спикерами по 1,5 часа, более 6 часов практики на стендах, групповой чат с куратором и итоговая сертификация — всё это ждёт вас на курсе!