[Kubernetes] Internals - Init Pod
이번 포스팅에서는 Pod가 초기화 되는 시점에 발생하는 내부 작업에 대해 알아보려고 한다.
Container
Container는 엄밀히 따지면 시스템 내부에서 동작하는 여타 Process와 다를게 없다. 그런데 Container로 따로 명명이 되는 이유는 다른 Process와 다르게 리눅스 커널에서 제공하는 namespace와 cgroups 기능을 사용하기 때문이다.
멀리 떠날 것 없이 주변에서 가장 많이 쓰이는 Container인 Docker만 봐도 Container를 구성하는데 namespace와 cgroups이 가장 핵심적인 기능이다.
(리눅스의 LXC 이전까지만 해도 전가상화(Full Virtualization) 또는 반가상화(Para Virtualization)와 같은 VM 기술이 주를 이루었다.)
그림 1. Docker Container
그림 1에서 볼 수 있는 lxc, libcontainer 등은 리눅스 기능을 표준으로 정의한 OCI(Open Container Initiative) Spec을 구현한 것으로 Docker는 해당 기술을 바탕으로 한다.
즉, namespace와 cgroups를 사용하여 Container가 생성된다. (물론 이전에는 LXC를 바탕으로 했지만 현재는 많이 바뀌었다.)
1) namespace
namespace는 Process 간에 독립적인 공간을 만들어 Process 상호간에 간섭이 발생하지 않도록 한다. 그런데 여기서 독립적인 공간이라는 것은 시스템 리소스를 격리시키는 것이 아닌 상호간에 볼 수만 없게 하는 것이다. 그렇기 때문에 A, B가 동일한 물리적인 공간에 있을 때, A가 시스템의 모든 리소스를 사용하면 B는 사용할 리소스가 부족해지게 된다.
namespace가 지원하는 리소스 목록 - Hostname - PID (Process ID) - IPC (Inter-Process Communication) - File System - Network Interface |
2) cgroups
cgroups는 Control Groups를 의미하며 namespace를 보완해주는 역할을 한다. 위에서 설명했듯이 namespace가 적용되어 있는 A, B 중 A가 모든 리소스를 사용 했을 때 B는 리소스가 부족해 작업을 수행 할 수 없다. 이를 방지하기 위해 cgroups는 Process 마다 사용 할 수 있는 리소스를 제한해준다. 따라서 A가 모든 리소스를 사용 할 수 없도록 한다.
(만약 제한된 것 이상을 사용하면 리소스가 부족하다는 메시지를 받을 것이다.)
cgroups가 제어할 수 있는 리소스 목록 - CPU - Memory - Block I/O - Network I/O |
이와 같이 2가지 주요 기능을 바탕으로 Container를 생성하면 리소스 영역이 아래 그림과 같다.
그림 2. Nginx Container
그림 2는 각 Container 마다 사용하는 리소스가 다른 Nginx Container을 보여준다. (물론 엄밀히 말해서 완전히 다른 리소스를 사용하는 것은 아니다. Volume 과 같이 공유하는 리소스도 있기 때문에...)
*추가로 각 리소스마다의 영역을 확인 할 수 있다. (IPC > Network > PID > Hostname > Mount)
각 생성된 Container가 서로 다른 리소스로 보여지는 이유는 Container 마다 namespace가 다르기 때문이다. 따라서 namespace에서 설명했듯이 물리적인 공간을 같을지라도 Container 마다 독립적으로 존재한다.
그럼 아래에서 독립적으로 구성되어 있는 Container를 하나의 공간에 묶는 예시는 아래와 같다.
$ 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
그림 2의 리소스 그림을 염두해두고 위와 같이 Container를 구성하면 아래와 같이 만들어진다.
그림 3. Integrated Container
그림 3을 보면 ghost에 nginx에 localhost로 묶여 있기 때문에 사용자는 nginx를 proxy로 사용하여 ghost에 접근 할 수 있다. 즉, ghost가 nginx의 namespace를 사용하기 때문에 같은 공간으로써 localhost로 묶인 것이다.
그런데 namespace는 동일하게 사용되는데 반해 cgroups의 구성은 어떻게 될까?
cgroups는 Container의 독립적인 역할을 그대로 수행한다. 즉, namespace는 동일하다 할지라도 내부 리소스는 각자 사용한다. 이해를 위해 그림 3을 다시 보면 nginx와 ghost는 서로 다른 Container로 표시된다. 이것은 같은 물리적인 공간이더라도 nginx와 ghost는 CPU, Memory, I/O 등은 각자에게 주어진 Limit 내에서 사용된다는 것이다.
Pod도 지금까지 설명한 것과 크게 다르지 않다. 특히 하나의 Pod에서 여러 개의 Container가 존재 할 수 있기 때문에 맥락은 같다.
그림 4. Pod
k8s에서 Pod를 생성할 때, namespace와 cgroups가 적절히 조합된다. 매니페스트 파일을 만들어서 Pod를 생성할 때 k8s에서 자동으로 설정해준다는 것이다.
그림 4를 보면 알 수 있듯이 2개의 Container인 File Puller와 Web Server를 생성 할 때 k8s에서 동일한 namespace에 Container 마다 cgroup을 할당한다. 그래서 Pod 내에 존재하는 Container 간에는 Localhost로 구성되고, IPC 안에 속하기 때문에 Process 간에 Signal(SIGHUP, SIGTERM과 같은)을 보낼 수 있다.
그러므로 k8s 입장에서 그림을 보면 아래와 같다.
그림 5. Init Pod
Reference
- https://www.ianlewis.org/en/what-are-kubernetes-pods-anyway