Skip to content

컨테이너 런타임 레이어

레이어 정리

우리가 일상에서 컨테이너를 띄우는 것은 아주 고수준의 레이어인 docker 커맨드(cli)를 사용해서 띄우게 된다

하지만 hostOS의 cgroup을 조절하고 namespace를 설정하는 건 마법처럼 뚝딱되는 것이 아니다

컨테이너가 실행되기까지 여러 레이러를 거치는데 각 레이어가 무슨역할을 하는지 정리해본다

bash
사용자 / 오케스트레이터 (docker, nerdctl)

high-level runtime (containerd, CRI-O)

shim (containerd-shim-runc-v2)

OCI runtime (runc, crun)

Linux kernel

high-level runtime: 이미지 pull, 스토리지, 네트워크 조율을 담당한다. 이미지를 rootfs + config.json으로 구성된 OCI bundle로 변환해서 shim을 통해 OCI runtime(runc)에 넘긴다.

shim은 containerd 같은 하이레벨 런타임이 죽어도 컨테이너가 유지되도록 분리해주는 역할을 한다.

  • 컨테이너 프로세스의 부모역할을 맡게 되는데, 만약 containerd 가 컨테이너들을 자식 프로세스로 지니고 있으면 containerd가 죽고나서 관리가 안된다
bash
# shim이 없을 경우
containerd 죽음
 컨테이너 프로세스는 살아있음
 근데 exit code를 수거할 놈이 없음
 stdio 연결이 끊김
 다시 살아난 containerd가 컨테이너 상태를 모름
# shim이 중간에 있으면?
containerd 죽음
 shim은 살아있음
 컨테이너 프로세스의 부모 = shim
 exit code, stdio shim이 들고 있음
 containerd 재시작 shim에 다시 연결
 컨테이너 상태 복구 가능

그 밑에 레이어가 OCI runtime 이다 (편하게 runc라고함)
runc는 OCI Runtime Spec에 정의된 5개의 operation을 구현한다

bash
create namespace, cgroup 설정. 프로세스는 블로킹 상태
start exec()으로 실제 프로세스 실행
state 현재 상태 JSON 출력
kill 시그널 전송
delete 리소스 정리
  • start 후 runc는 종료된다. runc는 컨테이너를 띄우는 역할까지만 하고, 이후 컨테이너 프로세스의 부모는 shim이 맡는다.
bash
runc create namespace, cgroup 설정. 프로세스 블로킹
runc start exec()으로 실제 프로세스 실행
runc exits runc 프로세스 자체는 종료
              이후 컨테이너 프로세스의 부모 = shim

runc는 리눅스 커널이 제공하는 기능들을 syscall로 호출하여 격리 환경을 구성한다.

bash
namespaces 프로세스 격리
cgroups 리소스 제한
seccomp syscall 필터링
capabilities root 권한 세분화
사용자 / 오케스트레이터docker CLI · kubectl · 직접 호출명령high-level runtimecontainerd · CRI-O — 이미지 pull, 스토리지, 네트워크 조율bundle 경로 + idshimcontainerd-shim-runc-v2 — 데몬 죽어도 컨테이너 유지, stdio·exit code 관리OCI 커맨드 호출OCI runtime (low-level runtime)runc · crun — create / start / state / kill / delete 구현체syscallLinux kernelnamespaces · cgroups · seccomp · capabilitiesOCI runtime이 실행하는 순서createbundle 읽기·forkCREATED블로킹·hook 실행startexec() 실행RUNNING프로세스 실행 중STOPPED종료·리소스 잔존delete리소스 정리소멸non-existentstate 조회 가능