Суть сети Kubernetes Pod: подробное объяснение контейнера паузы

Kubernetes

Эта статья взята из новой книги Ду Джуна «Авторитетное руководство по сетям Kubernetes» с авторизацией.Публичный аккаунт WeChat.

При проверке узлов вашего кластера Kubernetes выполните на узлахdocker psвы можете заметить некоторые контейнеры под названием «пауза», например:

🐳  → docker ps
CONTAINER ID IMAGE COMMAND ...
3b45e983c859 gcr.io/google_containers/pause-amd64:3.1  “/pause”
dbfc35b00062 gcr.io/google_containers/pause-amd64:3.1  “/pause”
c4e998ec4d5d gcr.io/google_containers/pause-amd64:3.1  “/pause”
508102acf1e7 gcr.io/google_containers/pause-amd64:3.1  “/pause”

Вы бы удивились, если бы эти контейнеры не были созданы вами. Да, Kubernetes «раздает» эти контейнеры.

Так называемый контейнер паузы в Kubernetes иногда называютinfraКонтейнер, который «связан» с контейнером пользователя, работает в том же поде, и его самая большая роль заключается в поддержке стека сетевых протоколов пода (конечно, он также включает в себя другую работу, которая будет представлена ​​позже).

都说 Pod 是 Kubernetes 设计的精髓,而 pause 容器则是 Pod 网络模型的精髓,理解 pause 容器能够更好地帮助我们理解 Kubernetes Pod 的设计初衷。 Почему ты это сказал?还得从 Pod 沙箱(Pod Sandbox)说起。

Pod Sandbox и контейнер паузы

Учащиеся, знакомые с жизненным циклом пода, должны знать, что при создании пода Kubelet сначала вызывает интерфейс CRI.RuntimeService.RunPodSandboxЧтобы создать среду песочницы, настройте базовую операционную среду, такую ​​как сеть (например, назначьте IP-адрес) для Pod. Как только Pod Sandbox установлен, Kubelet может создавать в нем пользовательские контейнеры. Когда придет время удалить под, Kubelet сначала удалит песочницу пода, а затем остановит все контейнеры внутри.

Некоторые читатели могут задаться вопросом, что такое Pod Sandbox? На самом деле это просто разные названия одного и того же, увиденного с разных сторон. С точки зрения CRI базовой среды выполнения контейнеров Kubernetes, Pod, набор контейнеров с ограниченными ресурсами в единой изолированной среде, называется песочницей.

Советы. Изолированная среда выполнения приложения называется контейнером, а группа контейнеров, ограниченных подами, называется песочницей пода. Они живут и умирают вместе и разделяют основные ресурсы.

Читатели, знакомые с нижним уровнем KVM, должны знать, что виртуальные машины используют тот же нижний уровень, что и контейнеры.cgroupsВыполнение квот ресурсов и концептуальное извлечение изолированной среды выполнения, единственная разница заключается в реализации изоляции ресурсов. Поэтому буквально виртуальные машины и контейнеры все еще имеют возможность «настраивать» концепцию песочницы. На самом деле концепция песочницы Pod состоит в том, чтобы зарезервировать место для Kubernetes, чтобы оно было совместимо с различными средами выполнения (даже с виртуальными машинами!), и позволить среде выполнения создавать разные песочницы Pod в соответствии с их соответствующими реализациями. на основеhypervisorсреды выполнения (KVM, kata и т. д.), Песочница Pod — это виртуальная машина. Для контейнеров Linux песочницей Pod является пространство имен Linux (сетевое пространство имен и т. д.).

Pod Sandbox неразрывно связан с контейнером паузы «главный герой», о котором мы поговорим сегодня. В системе Linux CRI Pod Sandbox на самом деле является контейнером паузы. Изображение по умолчанию SandboxImage, на которое ссылается код Kubelet, на самом деле предоставлено официальнымgcr.io/google_containers/pause-amd64зеркало.

Мы знаем, что абстракция Pod в Kubernetes основана на Linux.namespaceиcgroups, который обеспечивает изолированную среду выполнения для группы контейнеров. С точки зрения сети разные контейнеры в одном поде работают на одном и том же выделенном хосте и могут обмениваться данными через локальный хост.

В принципе, любой может настроить Docker для управления уровнем совместного использования между группами контейнеров — вы просто создаете родительский контейнер, создаете новые контейнеры, которые совместно используют ресурсы с родительским контейнером, и управляете жизненным циклом этих контейнеров. В Kubernetes контейнер паузы считается «родительским контейнером» всех контейнеров в поде и предоставляет следующие возможности для каждого бизнес-контейнера:

  • В POD это основа для совместного использования пространства имен Linux (NetWork, UTS и т. д.);
  • Включите общий доступ к пространству имен PID, который предоставляет процесс номер 1 для каждого модуля и собирает зомби-процессы внутри модуля.

приостановить исходный код контейнера

Контейнер паузы Kubernetes не имеет сложной логики. Он запускает очень простой процесс. Он не выполняет никаких функций и фактически «спит» вечно. Исходный код находится в проекте kubernetes.build/pause/в каталоге. Поскольку это относительно просто, полный исходный код написан здесь следующим образом:

#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()системный вызов). Как видите, он точно знает не только сон. Он выполняет еще одну важную функцию — т. е. действует как PID 1, и когда дочерний процесс становится осиротевшим, вызываяwait()Сбор этих детских процессов зомби. Таким образом, мы не беспокоимся о нашем POD PID пространства имен будут заполнены в процессе зомби. Причина: (например, nginx) в качестве родительского контейнера, а затем разрешать пользователям присоединиться к судному судному судному сосуду, поэтому Kubernetes не легко найти контейнер.

Просмотр контейнера паузы из пространства имен

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

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

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

Во-первых, мы запускаем контейнер паузы с помощью Docker, чтобы мы могли добавить другие контейнеры в модуль, например:

🐳  → docker run -d --name pause gcr.io/google_containers/pause-amd64:3.0

Затем мы запускаем другие контейнеры в POD, агенты - приложение Nginx и Ghost Blog.

Серверная часть прокси-сервера Nginx настроена какhttp://127.0.0.1:2368, который является адресом, по которому процесс-призрак прослушивает:

# cat <<EOF >> nginx.conf
> error_log stderr;
> events { worker_connections  1024; }
> http {
>     access_log /dev/stdout combined;
>     server {
>         listen 80 default_server;
>         server_name example.com www.example.com;
>         location / {
>             proxy_pass http://127.0.0.1:2368;
>         }
>     }
> }
> EOF

# docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 --net=container:pause --ipc=container:pause --pid=container:pause nginx

Создайте еще один контейнер для приложения блога-призрака в качестве сервера приложений следующим образом:

🐳  → docker run -d --name ghost --net = container:pause --ipc = container:pause --pid = container:pause ghost

В нашем случае мы указали контейнер pause в качестве контейнера, к которому мы хотим добавить пространство имен. Если вы посетите http://localhost:8080/ , вы сможете увидеть призрак, работающий через прокси-сервер Nginx, потому что пространство имен Network совместно используется контейнерами pause, nginx и ghost, как показано на изображении ниже.

Благодаря модулям Kubernetes защищает вас от всех вышеперечисленных сложностей.

Просмотр контейнера паузы из PID

В системах UNIX PID для процесса 1 — это процесс инициализации, который является родительским для всех процессов. Процесс инициализации довольно специфичен, он поддерживает таблицу процессов и продолжает проверять состояние других процессов. Одна из функций процесса инициализации является дочерним процессом из-за ошибки, когда родительский процесс завершается и становится «осиротевшим», будет принят процесс инициализации и перерабатывать ресурсы при завершении процесса.

Процессы могут запускать другие процессы, используя два системных вызова fork и exec. Когда запускаются другие процессы, родительским процессом нового процесса является процесс, вызвавший системный вызов fork. fork используется для запуска другой копии работающего процесса, а exec используется для запуска другого процесса. Каждый процесс имеет запись в таблице процессов операционной системы. Это зарегистрирует статус и код выхода о процессе. Когда дочерний процесс завершит выполнение, его запись в таблице процессов останется до тех пор, пока родительский процесс не получит свой код выхода с помощью системного вызова ожидания и запись процесса не будет очищена. Это называется "сбором" процесса-зомби, и процесс-зомби не может пройтиkillкоманда очистить.

Процесс-зомби — это процесс, который прекратил выполнение, но запись в таблице процессов все еще существует, поскольку родительский процесс еще не был получен с помощью системного вызова ожидания. Технически каждый процесс, который завершается, является процессом-зомби, хотя и только на короткий период времени. Более длительные зомби-процессы возникают, когда пользовательские программы написаны плохо и просто пропускают системный вызов ожидания, или когда родительский процесс аварийно завершается раньше дочернего процесса, а новый родительский процесс не вызывает ожидание для извлечения дочернего процесса. Слишком много процессов-зомби в системе будут занимать много ресурсов таблицы процессов операционной системы.

Когда родительский процесс завершается до завершения дочернего процесса, ОС назначает дочерний процесс процессу инициализации. Процесс инициализации «принимает» дочерний процесс и становится его родителем. Это означает, что когда дочерний процесс завершается в этот момент, новый родительский процесс (процесс инициализации) должен вызвать ожидание, чтобы получить свой код выхода, в противном случае его запись в таблице процессов останется, и он также станет процессом-зомби. В то же время процесс инициализации должен иметь функцию «экранирования сигнала» и не может обрабатывать определенную логику сигналов, что предотвращает ошибочное уничтожение процесса инициализации. Таким образом, не любой процесс может быть процессом инициализации.

использование контейнераPID namespaceИзолируйте pids, чтобы каждый контейнер мог иметь отдельный процесс инициализации. при отправке на хостSIGKILLилиSIGSTOP(I.e., Docker Kill или Docker STOP, насильственно прекращается время выполнения контейнера, фактически в процессе init в пределах завершения контейнера. После уничтожения процесса init процесс в том же пространстве имен PID также разрушен.

В контейнере должен быть процесс, который действует как процесс инициализации для каждого пространства имен PID.ENTRYPOINTПроцесс — это процесс инициализации. Если пространство имен PID совместно используется несколькими контейнерами, процесс с пространством имен PID должен взять на себя роль процесса инициализации, а другие контейнеры добавляются в пространство имен PID как дочерние процессы процесса инициализации.

Чтобы дать читателю интуитивное представление, ниже приведен пример, иллюстрирующий взаимосвязь PID между контейнером пользователя и контейнером паузы.

Сначала запустите контейнер паузы:

🐳  → docker run -idt --name pause gcr.io/google_containers/pause-amd64:3.0
7f6e459df5644a1db4bc9ad2206a0f99e40312de1892695f8a09d52faa9c1073

Запустите еще один контейнер busybox и добавьте его в пространство имен (network, PID, IPC) контейнера pause:

🐳  → docker run -idt --name busybox --net=container:pause --pid=container:pause --ipc=container:pause busybox
ad3029c55476e431101473a34a71516949d1b7de3afe3d505b51d10c436b4b0f

Вышеупомянутый способ добавления контейнера паузы также является принципом запуска Kubernetes Pod.

Затем давайте войдем в контейнер busybox, чтобы просмотреть процессы внутри, и обнаружим, что процесс с PID=1/pause:

🐳  → docker exec -it ad3029c55476 /bin/sh
/ # ps aux
PID   USER     TIME   COMMAND
    1 root       0:00 /pause
    5 root       0:00 sh
    9 root       0:00 /bin/sh
   13 root       0:00 ps aux

Мы можем полностью запустить Nginx в родительском контейнере и добавить призрак в пространство имен PID контейнера Nginx.

🐳  → docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 nginx
🐳  → docker run -d --name ghost --net=container:nginx --ipc=container:nginx --pid=container:nginx ghost

В этом случае Nginx возьмет на себя роль PID 1 и добавит призрак в качестве дочернего процесса Nginx. Хотя это может показаться хорошей идеей, технически Nginx теперь отвечает за все дочерние процессы процесса-призрака. Например, если привидение аварийно завершает работу до завершения своих дочерних процессов, эти дочерние процессы будут приняты Nginx. Однако Nginx не предназначен для запуска в качестве процесса инициализации и сбора процессов-зомби. Это означает, что таких зомби-процессов будет много, и это будет сохраняться на протяжении всего времени жизни контейнера.

Наконец, чтобы подвести итог, процесс инициализации пода, контейнер паузы, кто еще?

Совместное использование/изоляция пространства имен PID Kubernetes

Что касается совместного использования/изоляции пространства имен PID контейнера в поде, это вопрос мнения.Люди, поддерживающие совместное использование, находят это удобным для межпроцессного взаимодействия.Например, они могут отправить семафор процессу в другом контейнере в контейнер, и им не нужно беспокоиться о зомби.Проблема утилизации процессов.

До Kubernetes 1.8 совместное использование пространства имен PID было включено по умолчанию, если не использовался флаг kubelet.--docker-disable-shared-pid=trueНеполноценный. Однако после Kubernetes версии 1.8 ситуация как раз обратная, по умолчанию флаг kubelet--docker-disable-shared-pid=true, если вы хотите включить его, установите для него значение false. Давайте посмотрим на нисходящий API, предоставляемый Kubernetes, чтобы узнать, нужно ли совместно использовать пространство имен PID.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox
    securityContext:
      capabilities:
        add:
        - SYS_PTRACE
    stdin: true
    tty: true

Как показано выше,podSpec.shareProcessNamespaceУказывает, включено ли совместное использование пространства имен PID.

Из предыдущего обсуждения мы знаем, что очень важно совместно использовать пространство имен PID между контейнерами в поде, так зачем открывать этот переключатель, который запрещает совместное использование пространства имен PID? Это связано с тем, что когда приложение не генерирует другие процессы, а проблемы, вызванные зомби-процессом, можно игнорировать, совместное использование пространства имен PID не используется. А в некоторых сценариях пользователи хотят, чтобы контейнер в поде мог изолировать пространство имен PID от других контейнеров, например, в следующих двух сценариях:

(1) Когда пространство имен PID является общим, поскольку контейнер паузы становится PID = 1, другие пользовательские контейнеры не будут иметь PID 1. Но изображения, такие как systemd, требуют PID 1, иначе он не загрузится должным образом. некоторые контейнеры проходятkill -HUP 1Команда перезапускает процесс, однако в поде, где на паузе находится процесс инициализации, приведенная выше команда будет сигнализировать только о паузе.

(2) Совместное использование пространства имен PID делает процессы разных контейнеров в поде видимыми для других контейнеров, включая/procВся информация, видимая, например, в паролях, передаваемых в качестве параметров или переменных среды, что представляет определенную угрозу безопасности.

Публичный аккаунт WeChat

Отсканируйте QR-код ниже, чтобы подписаться на официальную учетную запись WeChat, и ответьте на официальную учетную запись ◉Добавить группу◉, чтобы присоединиться к нашей облачной коммуникационной группе и обсудить облачные технологии с Сунь Хунляном, директором Чжаном, Ян Мином и другими важными шишками.