컨테이너의 동작을 이해하기 위해 컨테이너 런타임을 만들어본다.

 

세상에 컨테이너 런타임이라고 알려진 소스들은 너무 많다.

containerd, cri-o, kata container, runc, crun, podman 등등..

 

그러나 이들은 같은 역할을 하는 도구가 아니다. 이 중 containerd, cri-o와 같은 런타임은

runc, crun 등을 사용하기도 한다. 같은 레벨의 소스가 아니라는 거다.

 

세간에서 불리는 컨테이너 런타임은 두 갈래로 나뉜다.

첫째. 실제로 컨테이너를 다루는 런타임.

둘째. 첫째의 런타임을 이용하여 컨테이너의 수명주기를 관리하는 런타임.

 

순서대로 OCI Runtime, CRI라는 표준을 따른다.

간략히 설명하자면 아래와 같다

 

OCI(Open Container Initiative) Runtime

싱글 바이너리로 산출되는 CLI프로그램으로, 실제로 컨테이너를 생성/삭제할 수 있다.

 

CRI(Container Runtime Interface)

Kubelet이 컨테이너 런타임과 소통하기 위해 정립한 인터페이스이나, 사실상 표준이다. (de fecto)

 

이 포스트 시리즈에서 만들 런타임은 둘이 섞여있는 형태일 것이다.

먼저 lizrice가 DockerCon 2017에서 발표한 containers-from-scratch 소스로부터 출발한다.

기본 코드블럭의 가독성이 좀 구린 것 같아서 carbon을 사용했다.
해당하는 커밋은 최하단에 Github 링크로 달아두었다.

 

대다수 컨테이너 런타임의 구조를 잘 담고 있는 소스다.

container create -> host 설정 -> /proc/self/exe 호출하며 프로세스 분리 -> 명령어 실행

runc, crun등 보통 컨테이너 런타임은 이런 구조를 차용한다.

 

하위호환을 잘 지키는 Go라지만 이는 6년 전 코드인지라 자잘하게 바꿔줘야 하는 부분이 있다.

크게 변경할 것은 없고 Cgroup V1을 사용하는 코드정도만 해설하겠다.

 

기존 코드의 Cgroup을 컨트롤하는 부분을 보자.

 

컨테이너를 생성시 아래와 같은 cgroup 세팅을 진행한다.

pids 서브시스템의 하위로 cgroup 인터페이스 파일을 생성하는 것 보아 CgroupsV1 구조임을 알 수 있다.

  • /sys/fs/cgroup/pids/liz/pids.max
  • /sys/fs/cgroup/pids/liz/notify_on_release
  • /sys/fs/cgroup/pids/liz/cgroup.procs

 

CgroupsV2에서는 이를 단일 계층(/sys/fs/cgroup)에서 직접 제어한다.

만약 pids 서브시스템을 제어하고 싶다면 다음과 같이 사용하면 된다.

echo "20" > /sys/fs/cgroup/liz/pids.max

이후 해당하는 그룹에 cgroup.procs 인터페이스 파일에 pid를 씀으로써 제한을 수행할 수 있다.

echo "20" > /sys/fs/cgroup/liz/cgroup.procs

 

이에 맞게 cg함수를 변경하고, 함수명등도 setupCgroups로 변경했다.

 

기타 변경사항이 적용된 코드는 다음과 같다.

 

다음 포스트에서 더 설명하겠지만 컨테이너 프로세스의 stdio, stdout, stderr을 현재 터미널에 연결하는 걸 볼 수 있다.

 

이는 OCI Runtime 스펙에 정의된 내용으로, 정확히는 아래와 같이 명시되어있다.

https://github.com/opencontainers/runtime-spec/blob/34a39b90707d979c56f834071c77ef60941d7688/runtime-linux.md

 

때문에 컨테이너 프로세스의 라이프사이클은 그 부모 프로세스(터미널)와 동일해진다.

이 말인즉슨 터미널이 종료되면 컨테이너 프로세스도 같이 종료된다는 말과 같다. 

그 때문에 CRI 스펙을 구현하는 고수준 컨테이너 런타임들은 터미널에게서 독립적으로 만드는 shim을 두어 이를 해결한다.

 

이런 부분은 살을 붙여갈 때 더 알아보도록 하고, 이를 실행시켜 보겠다.

이 프로그램을 실행시키기 위해서는 ubuntufs가 필요하다. 고수준 런타임을 사용할 때 사용하는 이미지의 실체가 바로 이것이다.

실제로는 bundle directory 외에도 여러 설정등이 명시되어 있다.

 

ubuntufs는 ubuntu:22.04 이미지에서 export 하여 사용한다.

sudo docker create --name temp-ubuntu ubuntu:22.04
sudo mkdir -p /root/ubuntufs
sudo docker export temp-ubuntu -o /tmp/ubuntu.tar
sudo tar -xf /tmp/ubuntu.tar -C /root/ubuntufs
sudo rm /tmp/ubuntu.tar
sudo docker rm temp-ubuntu
sudo /root/ubuntufs/mytemp

 

그리고 빌드 후 실행시켜 보면 정상적으로 동작한다.

[root@rocky9 containeruntime]# go build -o containeruntime .
[root@rocky9 containeruntime]# ls
containeruntime  go.mod  main.go  README.md
[root@rocky9 containeruntime]# ./containeruntime run /bin/bash
Running: [/bin/bash]
Running: [/bin/bash]
root@container:/# ls
bin   dev  home  lib32  libx32  mnt     opt   root  sbin  sys  usr
boot  etc  lib   lib64  media   mytemp  proc  run   srv   tmp  var
root@container:/# cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
root@container:/#
root@container:/# echo "on the container" 
on the container

 

간단한 컨테이너 실행 데모를 완료했으니 살을 붙이며 OCI Runtime을 구현할 예정이다.

bundle 및 config 설정, cli설정 등..

 

https://github.com/yoonhyunwoo/containeruntime/commit/aa99af3b6f102b6e99f04e7b35053c8bf6e003bc

 

Init commit · yoonhyunwoo/containeruntime@aa99af3

+ must(syscall.Mount("tmpfs", "mytemp", "tmpfs", 0, ""))

github.com

 

 

참고자료

https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md#design-docs-and-proposals

https://github.com/opencontainers/runtime-spec

https://github.com/lizrice/containers-from-scratch

https://www.youtube.com/watch?v=MHv6cWjvQjM&t=1316s

https://www.alibabacloud.com/help/en/alinux/support/differences-between-cgroup-v1-and-cgroup-v2

https://man7.org/linux/man-pages/man7/cgroups.7.html

+ Recent posts