개발 일기

[Docker] 컨테이너 런타임(Container Runtime) 본문

DevOps/Docker Kubernetes

[Docker] 컨테이너 런타임(Container Runtime)

개발 일기장 주인 2024. 5. 21. 16:49

공부하게된 계기

Docker를 사용하다보니 컨테이너 런타임(Container Runtime)이라는 단어를 종종 듣게 됐다. 도커 관련 개발 블로그를 봐면 종종 컨테이너 런타임이라는 용어를 당연하다는 듯이 쓰는 것을 봤다. 처음에 그냥 뭐 컨테이너가 돌아가고 있는 것이나 컨테이너가 돌아가기 위한 소프트웨어? 환경? 이런 것이라고 대강 이해하고 넘어 갔었는데 Kubernetes에 관한 영상을 봤는데도 쓰는데 뭔가 자세히 한번 짚고 넘어가야겠다는 생각이 들어 정리해보게 됐다.


도커 아키텍처(Docker Architecture)

전에도 한번 다뤘지만 다른 그림으로 도커의 아키텍처를 다시 보자.

도커 클라이언트-서버 아키텍처

우선은 이게 전에 봤던 것과 유사하다. 기본적으로 도커는 클라이언트-서버 구조로 동작하는데 우선 클라이언트 즉, 사용자가 도커 명령어를 입력하면 도커 CLI(Command Line Interface)는 해당 명령어를 도커 데몬(dockerd)에게 API 요청 형식으로 전달한다. 이때,
도커는 보통 JSON 형태로 응답을 한다. 도커 CLI(Command Line Interface)는 이 JSON 응답을 받아, 보기 좋은 형식으로 변환하여 이용자에게 보여준다.

이 그림만 보면 도커 데몬에서 호스트 OS와 소통하며 컨테이너 생성, 시작, 중지, 삭제하는 등의 작업을 수행하는 것처럼 보이지만 아래를 보면 사실 컨테이너 런타임이 그 사이에 끼어있다. 

컨테이너 런타임

직접 커널과 통신하며 실제로 격리된 공간을 만드는 역할을 하는 것은 컨테이너 엔진이 아닌 "컨테이너 런타임"이였다.

도커는 기본적으로 runc라는 컨테이너 런타임을 사용하는데 도커에서 기본으로 사용하는 컨테이너 런타임이지만, 반드시 runc를 사용해야 하는 것은 아니다. 다른 런타임도 사용가능하다.
runc는 cgroup, namespace와 같은 리눅스 커널 기술을 활용하여 컨테이너를 격리하고 관리합니다. 다양한 리눅스 커널 버전에서 일관되게 동작할 수 있도록 LXC 혹은 libvirt를 사용하여 간접적으로 관리할 수 있다. 호스트 OS인 커널은 컨테이너 런타임이 격리된 환경을 만들기 위해 상호작용하는 핵심 계층이다. cgroup과 namespace와 같은 기능을 통해 리소스 제어와 격리를 제공한다.

 

그래서 도커 런타임이 뭔데?

컨테이너의 생성, 실행, 관리 등을 담당하는 소프트웨어 계층으로 도커에서 직접 커널과 통신하며 실제로 격리된 공간을 만드는 역할을 한다.크게 저수준 런타임과 고수준 런타임으로 나눌 수 있다.

  • 저수준 런타임
    저수준 런타임은 컨테이너의 핵심 기능을 담당한다.
    주로 namespace와 cgroup을 이용하여 프로세스를 격리하고 리소스를 제한하는 역할한다.
    즉, 컨테이너 자체를 생성하는 역할을 하는 것으로 이미지로부터 컨테이너를 실행하는 기능은 없다.
    저수준 런타임의 대표적인 예로는 runc가 있습니다. runc는 컨테이너를 실제로 생성하고 실행하는 기능을 수행하지만, 이미지를 다운로드하거나 컨테이너의 라이프사이클을 관리하는 기능은 없습니다.
  • 고수준 런타임
    고수준 런타임은 저수준 런타임 위에 위치하며, 이미지로부터 컨테이너를 실행하고 관리하는 기능을 제공한. 주로 컨테이너의 라이프사이클 관리, 이미지 관리, 네트워크 설정 등을 담당한다.
    대표적으로 containerd, cri-o 등이 고수준 컨테이너 런타임이다.

 

그렇다면 도커에서는?

도커의 경우에 containerd라는 고수준 런타임runc라는 저수준 런타임을 이용해서 기능을 제공한다.

도커는 원래 컨테이너 데몬의 기능을 가지고 있었지만, 최근 구조에서는 이러한 기능을 containerd에게 넘겨주고 도커 데몬(dockerd)는 이제 containerd의 클라이언트로서 작동하며, 사용자로부터 명령을 받아 containerd와 통신한다.

이 말이 위 그림들에서 알 수 있다.  도커 데몬(dockerd)이 요청을 받고 실제 컨테이너를 생성하고 실행하는 그런 작업들을 뒷 단의 컨테이너 런타임으(고수준:containerd/저수준:runc)로 넘겨서 처리하는 것으로 역할이 확실히 분리된 것으로 볼 수 있다.

 

처음에 LXC 라는 런타임을 사용했지만 나중에 libcontainer 라는 자체 런타임을 개발하고 나중에 runc 로 발전된 것이다. 점진적으로 가볍고 유연하고 안정적으로 바뀌게 됐다.

 


용어를 정리해보면

  • OCI(Open Container Initiative)
    : OCI는 컨테이너 기술의 표준화를 위해 만들어진 개방형 프로젝트이다. 이는 컨테이너 런타임과 이미지 사양의 표준을 정의하는 것으로 OCI 런타임 사양(Runtime Specification)은 컨테이너를 실행하기 위한 저수준 런타임의 표준을 정의하며 컨테이너가 어떻게 시작되고 실행되는지, 그리고 어떻게 상호작용하는지를 명시하고 OCI 이미지 사양(Image Specification)에서는 컨테이너 이미지를 빌드, 저장, 배포하기 위한 표준을 정의하는 것이다.

  • containerd
    : 도커에서 분리된 프로젝트로, 컨테이너의 라이프사이클을 관리하는 데 집중합니다. 이미지 관리, 컨테이너 실행, 네트워크 설정 등을 처리합니다.
  • runc
    : runc는 OCI(Open Container Initiative) 사양을 준수하는 표준 저수준 컨테이너 런타임이다. 

  • LXC
    : LXC(Linux Containers)는 리눅스 운영 체제 수준의 가상화 기술로, 격리된 리눅스 환경(컨테이너)을 제공하는 데 사용된다. LXC는 namespace와 cgroup을 사용하여 애플리케이션을 격리하고, 호스트 시스템의 커널을 공유합니다. 이는 하드웨어 가상화보다 더 가볍고 효율적인 방법으로 컨테이너를 관리할 수 있게 해줍니다.
  • Namespace
    : namespace 리눅스 커널 기능으로, 시스템 리소스를 격리하여 각각의 컨테이너가 독립된 환경에서 실행되도록 한다. 다양한 유형의 namespace가 있으며, 각각의 유형은 특정 시스템 리소스를 격리한다. 
  • libvirt
    : libvirt는 하이퍼바이저, 컨테이너 및 기타 가상화 기술을 관리하는 오픈 소스 API 및 툴킷이다. libvirt는 다양한 백엔드(VMware, Xen, KVM, QEMU, LXC 등)를 지원하며, 가상 머신 및 컨테이너의 생성, 모니터링, 제어를 위한 일관된 인터페이스를 제공한다. 이는 가상화 환경을 관리하기 위한 추상화 계층을 제공한다
  • cgroup (Control Group)
    : 리눅스 커널 기능으로, 시스템 리소스(CPU, 메모리, 디스크 I/O, 네트워크 대역폭 등)를 제한하고 격리하는 데 사용됩니다. cgroup은 프로세스 그룹을 관리하고, 각 그룹이 사용할 수 있는 리소스를 제어
  • CRI-O
    : Kubernetes의 CRI(Container Runtime Interface)를 구현한 런타임으로, 컨테이너를 관리하고 실행하는 기능을 제공합니다.