Post

Pause 컨테이너란?

KANS 스터디 2주차 PAUSE 컨테이너 알아보기

💡 KANS 스터디
CloudNet에서 주관하는 KANS(Kubernetes Advanced Networking Study)으로 쿠버네티스 네트워킹 스터디입니다. 아래의 글은 스터디의 내용을 기반으로 작성했습니다.

스터디에 관심이 있으신 분은 CloudNet Blog를 참고해주세요.

파드 & PAUSE 컨테이너

여기서는 파드 생성시 Init으로 생성되는 PAUSE 컨테이너에 대해 알아봅니다.

PAUSE 컨테이너 역할

먼저 컨테이너의 역할을 먼저 확인하고 가면 아래와 같습니다. 추후 실습에서 한번 더 확인합니다.

  • 파드에서 Linux 네임스페이스 공유의 유지 역할을 합니다.
  • 각 파드에 대한 PID 1 역할을 하며 좀비 프로세스를 제거한다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*
... Apache 라이선스 내용
*/

#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;
}

💡 코드 설명(요약본)

  • 이 프로그램은 주로 컨테이너 환경에서 실행되며, 신호(SIGINT, SIGTERM, SIGCHLD)를 처리하는 데 사용됩니다.

  • 컨테이너에서 첫 번째 프로세스로 동작하며, 적절한 신호 처리(프로세스 종료, 자식 프로세스 종료 처리)를 수행합니다.

  • pause() 함수를 사용하여 신호가 발생할 때까지 무한 대기합니다.

[실습] 네트워크 네임스페이스 공유 확인

여기에서는 파드를 배포하여, 실제 pause container의 존재를 확인하고, 같은 파드내에서는 네트워크 스택을 공유함을 확인합니다.

파드 배포

간단하게 nginx 파드를 배포합니다.

1
kubectl run web-app1 --image=nginx

worker노드에 접속하여 정보를 확인합니다.

  1. 현재 3개의 파드가 동작 중(kindnet, kube-proxy, web-app1)
  2. 모든 파드에 pause 프로세스가 확인됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
root@myk8s-worker:/# crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
425023d6bf498       a9dfdba8b7190       13 seconds ago      Running             web-app1            0                   62300a8320ec7       web-app1
6c78d35736270       4740c1948d3fc       2 minutes ago       Running             kindnet-cni         0                   8cbc0152756f1       kindnet-ndvkp
07c51c7ffe9aa       d2d4e1917462f       2 minutes ago       Running             kube-proxy          0                   0c554532b5e25       kube-proxy-vtrnp
root@myk8s-worker:/# pstree -aln
systemd
  |-systemd-journal
  |-containerd
  |   `-14*[{containerd}]
  |-kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip=172.18.0.3 --node-labels= --pod-infra-container-image=registry.k8s.io/pause:3.9 --provider-id=kind://docker/myk8s/myk8s-worker --runtime-cgroups=/system.slice/containerd.service
  |   `-12*[{kubelet}]
  |-containerd-shim -namespace k8s.io -id 8cbc0152756f1be78361e598b20fa2ed9e0d0d70d7e3167061c72ffb44ba26b7 -address /run/containerd/containerd.sock
  |   |-11*[{containerd-shim}]
  |   |-pause
  |   `-kindnetd
  |       `-8*[{kindnetd}]
  |-containerd-shim -namespace k8s.io -id 0c554532b5e2582948170afc3646c9e77291bf13fb86efe5b49d019b1053a518 -address /run/containerd/containerd.sock
  |   |-11*[{containerd-shim}]
  |   |-pause
  |   `-kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=myk8s-worker
  |       `-7*[{kube-proxy}]
  `-containerd-shim -namespace k8s.io -id 62300a8320ec795961fedb61238854616bed234a28405a7079f6ed4ecfb2f9da -address /run/containerd/containerd.sock
      |-11*[{containerd-shim}]
      |-pause
      `-nginx
          |-nginx
          |-nginx
          |-nginx
          |-nginx
          `-nginx

pause 프로세스의 PID를 확인합니다. (가장 나중에 띄운 파드이므로, PID가 가장 큰 pause 프로세스를 찾으면 됩니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.2  0.1  21736 12360 ?        Ss   12:16   0:00 /sbin/init
...
65535        306  0.0  0.0    792     4 ?        Ss   12:16   0:00 /pause
65535        314  0.0  0.0    792     4 ?        Ss   12:16   0:00 /pause
root         364  0.0  0.6 1283236 50024 ?       Ssl  12:16   0:00 /usr/local/bin/kube-proxy --config=/var/lib/kub
root         447  0.0  0.3 740344 24628 ?        Ssl  12:16   0:00 /bin/kindnetd
root        1046  0.0  0.1 1238296 12476 ?       Sl   12:18   0:00 /usr/local/bin/containerd-shim-runc-v2 -namespa
65535       1065  0.0  0.0    792     4 ?        Ss   12:18   0:00 /pause
root        1078  0.0  0.0   4052  3280 pts/1    Ss   12:18   0:00 bash
root        1129  0.0  0.0  11136  6900 ?        Ss   12:18   0:00 nginx: master process nginx -g daemon off;
statd       1166  0.0  0.0  11596  2952 ?        S    12:18   0:00 nginx: worker process
statd       1167  0.0  0.0  11596  2952 ?        S    12:18   0:00 nginx: worker process
...

nginx와 위에서 찾은 pause 프로세스의 네임스페이스 정보를 확인합니다.

  • net, uts, ipc가 같은 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@myk8s-worker:/# lsns -p 1129
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       20     1 root  /sbin/init
4026531837 user       20     1 root  /sbin/init
4026533140 net         7  1065 65535 /pause
4026533255 uts         7  1065 65535 /pause
4026533256 ipc         7  1065 65535 /pause
4026533258 mnt         6  1129 root  nginx: master process nginx -g daemon off;
4026533259 pid         6  1129 root  nginx: master process nginx -g daemon off;
4026533260 cgroup      6  1129 root  nginx: master process nginx -g daemon off;
root@myk8s-worker:/# lsns -p 1065
        NS TYPE   NPROCS   PID USER  COMMAND
4026531834 time       20     1 root  /sbin/init
4026531837 user       20     1 root  /sbin/init
4026532731 cgroup     13     1 root  /sbin/init
4026533140 net         7  1065 65535 /pause
4026533254 mnt         1  1065 65535 /pause
4026533255 uts         7  1065 65535 /pause
4026533256 ipc         7  1065 65535 /pause
4026533257 pid         1  1065 65535 /pause

같은 파드내의 다른 컨테이너끼리 네임스페이스 공유 확인

이제 두 개의 컨테이너를 가진 파드를 배포하여, 네임스페이스 공유를 확인합니다.

netshoot 컨테이너는 네트워크 관련 디버깅 전용 컨테이너입니다. 자세한 내용은 GitHub에서 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: myweb2
spec:
  containers:
    - name: myweb2-nginx
      image: nginx
      ports:
        - containerPort: 80
          protocol: TCP

    - name: myweb2-netshoot
      image: nicolaka/netshoot
      command: ["/bin/bash"]
      args: ["-c", "while true; do sleep 5; curl localhost; done"]

  terminationGracePeriodSeconds: 0
EOF

배포가 완료되면, netshoot 컨테이너에 접속합니다.

1
kubectl exec myweb2 -c myweb2-netshoot -it -- zsh

현재 점유하고 있는 포트를 찾아보고, localhost로 curl를 날립니다.

결과를 확인하면, 마치 여기가 nginx 컨테이너 내부인 것처럼 설정되어있습니다.

image.png

위에서 봤듯이 네트워크 네임스페이스(스택)를 공유한다는 것을 다시 한번 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 12:16 ?        00:00:00 /sbin/init
...
65535        306     268  0 12:16 ?        00:00:00 /pause
65535        314     267  0 12:16 ?        00:00:00 /pause
root         364     268  0 12:16 ?        00:00:00 /usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.
root         447     267  0 12:16 ?        00:00:00 /bin/kindnetd
root        1460       1  0 12:25 ?        00:00:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 0
65535       1479    1460  0 12:25 ?        00:00:00 /pause
root        1509    1460  0 12:26 ?        00:00:00 nginx: master process nginx -g daemon off;
statd       1547    1509  0 12:26 ?        00:00:00 nginx: worker process
statd       1548    1509  0 12:26 ?        00:00:00 nginx: worker process
statd       1549    1509  0 12:26 ?        00:00:00 nginx: worker process
statd       1550    1509  0 12:26 ?        00:00:00 nginx: worker process
statd       1551    1509  0 12:26 ?        00:00:00 nginx: worker process
root        1631    1460  0 12:26 ?        00:00:00 /bin/bash -c while true; do sleep 5; curl localhost; done
root        2088       0  0 12:30 pts/1    00:00:00 bash
root        4097    1631  0 12:31 ?        00:00:00 sleep 5
root        4110    2088  0 12:31 pts/1    00:00:00 ps -ef

다시 한번 Pause 컨테이너의 정보를 확인합니다. 위에 결과에서는 nginx 프로세스 바로 위에 있는 1479번인 것을 확인했습니다.

crictl을 이용해서 네임스페이스 정보를 확인하면 Pause가 생성한 네트워크 네임스페이스를 갖는다는 것을 알 수 있습니다.

image.png

정리

  1. Pause 컨테이너가 net/ipc/uts 네임스페이스를 생성하고 공유한다.
    1. init 프로세스의 역할도 수행하여, 좀비 프로세스를 제거한다.
    2. network 네임스페이스는 CRI에서 생성한다고 한다. 악분님 블로그 참고.
  2. 같은 파드 내의 컨테이너는 네트워크 스택이 동일하다.
    1. 그렇기에 container port를 조심해서 세팅해야 한다. (중복되면 안된다.)
    2. 사이드카로 붙이면, 쉽게 네트워크 테스트가 가능하다.
This post is licensed under CC BY 4.0 by the author.