그림(참고1)

Untitled

컨테이너 기술은 계층적으로 구성되어 있다. 당연히 컨테이너 기술 활성화에 크게 기여한 도커도 예외가 아니다(참고1). 위 그림의 Docker 박스에서 출발하는 화살표를 따라가 보자. 도커 엔진(from1:도커와 도커 엔진)은 도커 데몬(docker-containerd)에게 일을 시킨다(참고8). 도커 데몬고수준 컨테이너 런타임이라고도 불린다(참고2,참고6). 도커의 고수준 컨테이너 런타임인 도커 데몬은 컨테이너 이미지를 빌드하고, 공유하고, 컨테이너 실행을 관리한다(참고7:컨테이너를 실질적으로 실행하는 것이 아니라 실행을 관리한다).

저수준 컨테이너 런타임은 컨테이너를 실제 실행하는 역할을 하지만 이미지로부터 컨테이너를 실행하려면 이미지와 관련된 API 같은 기능이 필요합니다. 이러한 기능은 고수준 컨테이너 런타임에서 제공됩니다.(참고5)

다시 도커 데몬은 저수준 컨테이너 런타임(참고3)이라고도 불리는 ‘진짜’ 도커 컨테이너 런타임(from2)에게 일을 시킨다(참고8). ‘진짜’ 도커 컨테이너 런타임은 격리된 환경을 구성하고(참고17) 컨테이너를 실행한다(참고5,참고9). 위 그림과 조금 다른 방식으로 이 관계를 표현하면 아래와 같다.

그림(참고4)

Untitled

그렇다면 사람들이 컨테이너 기술을 설명할 때 이렇게 여러 추상 계층으로 나누고 쪼개어 설명하는 이유가 무엇일까? 가령 앞서 설명했던 컨테이너 런타임은 두 가지였다. 고수준 컨테이너 런타임과 저수준 컨테이너 런타임(저수준 컨테이너 런타임을 ‘컨테이너 런타임’ 이라고 부르는 경우가 많아서 이 글에서는 ‘진짜’ 라고 표현한 것)이다. 그런데 컨테이너 기술을 설명한 인터넷의 수많은 사람들이 둘을 가리지 않고 ‘컨테이너 런타임’ 이라고 퉁쳐 부르곤 하여 사람을 헷갈리게 만드는 것 같다. 하지만 그들의 실수나 잘못이 아니다. 이렇게 누군가는 퉁쳐서 부르고, 때로는 이렇게 세분화된 추상화 수준에서 기술을 다루어야 하는 이유가 무엇일까?

그 의도가 선량하다는 것을 받아들일 수 있다면 헷갈리는 단어들을 받아들이기 조금 더 쉬울 것 같다. 내가 이해한 바에 따르면 가장 큰 이유는 뒤늦게 표준화가 이루어졌기 때문이다. 다양한 엔지니어링 요구를 충족시켜줄 수 있는 도구를 만들 수 있는 자유를 보장하면서도, 컨테이너의 본래 사용 목적인 이식성을 해치지 않도록 표준을 수립할 필요성이 뒤늦게 생겨났다.

도커가 세상에 공개되고 컨테이너 기술을 이끌어 갔던 시절에, 컨테이너 런타임에는 원래 고수준 저수준 같은 것들이 없었다(참고18). 그렇게 고민할 필요도 나눌 필요가 없었기 때문이다. 그때 당시에 작성된 글들은 고수준 컨테이너 런타임이고 저수준 컨테이너 런타임이고 별로 고민할 것이 없었을 것이다. 그런 개념이 존재하지 않았기 때문이다.

하지만 그 이후 컨테이너 사용이 점점 확산되며, 다양한 엔지니어링 수요들이 생겨나기 시작했다. 예를 들어, 빌드된 컨테이너 이미지를 실행하기만 하면 되는 서버에서는 이미지를 빌드하는 기능이 필요가 없는 상황이 있다고 생각해 보자(참고20). 리소스가 충분하지 않은 서버에서는 거대한 컨테이너 런타임이 너무 무겁게 느껴질 수 있기에 본질적이지 않은 요소들을 분리할 필요가 있다고 주장할 것이다. 그래서 원래 하나처럼 여겨지던 것들이 다양한 수요를 충족시키기 위해 여러 추상화 단계로 나뉘며 고수준 저수준 따위의 용어가 생겨나게 된 것이다(참고18:Portability). 그 어떤 데몬이든 OCI(Open container Initiative)스펙만 맞추면 ‘진짜 컨테이너 런타임’(저수준 컨테이너 런타임)에 명령을 내려 OCI스펙을 준수하는 컨테이너를 실행할 수 있게 되었다(참고11:당연히 컨테이너 이미지도 OCI스펙을 준수해야 한다). 도커의 RunC는 물론이고, Curn(참고12), Kata(참고13), gVisor(참고14)같은 도구들도 OCI 표준을 준수(OCI-Complient)하는 저수준 컨테이너 런타임과 컨테이너 이미지라고 볼 수 있다.

미니쿠브 설치 문서 스크린샷. 미니쿠브를 설치 시 다양한 (고수준) 컨테이너 런타임을 선택할 수 있도록 제공하고 있다.

미니쿠브 설치 문서 스크린샷. 미니쿠브를 설치 시 다양한 (고수준) 컨테이너 런타임을 선택할 수 있도록 제공하고 있다.

그 어떤 엔진이든 CRI(Kubernetess Container Runtime Interface) 스펙(참고19,참고22:쿠버네티스 표준)만 맞추면 그 어떤 쿠버네티스(from3) 도구들이든 상관없이(참고21) 그 어떠한 고수준 컨테이너 런타임(데몬)에든 명령을 내릴 수 있게 되었다(참고10,참고16:컨테이너 런타임 인터페이스라고 해서 저수준 컨테이너 런타임 인터페이스라고 헷갈리지 말자). 중간에 껴 있는 고수준 컨테이너 런타임은 자연스레 CRI 와 OCI 를 동시에 준수해야 쿠버네티스는 물론 저수준 컨테이너 런타임과도 상호작용할 수 있게 된다. 대표적으로는 CRI-O 가 있다(참고15).

실제로 이와 관련하여 재미있는 에피소드가 있는데, 군 내 서비스들을 docker compose 기반에서 kubernetess 기반으로 옮겨야 하는 상황이었다. docker compose로 실행되던 서비스들이 있는 상태에서 k3s를 컴퓨터에 설치하니, CRI-O와 Containerd 모두가 먹통이 되면서 kubernetess와 docker 모두가 응답하지 않는 문제가 나타났다. 추측컨데 둘의 컨테이너 런타임이 다르기 때문에, 충돌 위험이 있는 서로의 런타임을 끄려고 시도했던 것 아닐까.

아무튼, 정리하면 아래 표와 같다.

Container engine Interface High level container runtime Interface Low level container runtime
- 표준: CRI 별명: Daemon 표준: OCI 별명: Container runtime
역할: 다양한 기능 역할: 이미지 빌드(참고20:구현체마다 다름), 공유(참고20:구현체마다 다름), 실행명령 역할: 컨테이너 실행
대표적 구현체: docker 대표적 구현체: docker-containerd(참고22) 대표적 구현체: RunC
대체체: LXC(from1) 대체제: CRI-O(참고20) 대체제: Curn, Kata, gVisor

컨테이너 런타임과 컨테이너 엔진의 관계는 커널과 셸에 대응할 수 있지 않을까?


parse me : 언젠가 이 글에 쓰이면 좋을 것 같은 재료들.