이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Docker

15. Docker 구조

이쿠우우 2021. 10. 20. 21:31
반응형

 

Docker 구조

 

원래 최초에 docker는 이러한 구조를 가지고 있지 않고 docker 하나로 구성되어이 있었음.

하지만 이후 고수준(high-level) 컨테이너 런타임,  저수준(Low-level) 컨테이너 런타임

프로젝트로 분리되며 위와같은 구조를 가지게 되었음.

 

해당 구조를 직접확인해볼려면 

아래와 같은 명령어로 확인 가능함.

 

 

[구조 확인 명령어 ]

ps aux | grep docker

 

[결과]

상위 그림과 같이 실제로 dockerd, containerd, runc를 확인 할 수 있음

 

dockerd란?

일반적으로 docker를 사용하기 위해 실행했던 명령어(docker image, docker ps등)는 docker cli를 사용했던 것임.

docker cli를 이용해서 명령어를 사용하면 최초로 dockerd으로 전달됨.

dockerd는 Daemon Process 형태로 실행되고 있으며 docker cli로 부터 RESTful API 형식의 요청을 수신하여 처리하며

UNIX, TCP, FD의 세 가지 소켓 유형을 통해 API 요청을 수신할 수 있음.

dockerd는 container build, security, volume, networking, secrets, orchestration, distributed state와 같은 docker 기능을 담당하고 있음

default 경로로 /usr/bin/dockerd에 위치하고 있음

containerd에 의존적이며 containerd 없이 단독으로 실행할 수 없음.

dockerd는 docker engine이라고 부르기도 함.

 

containerd 란?

일반적으로 container runtime tool의 대표라고 하면 docker를 떠올릴 것임.

하지만 docker 구조를 더 자세히 파고들어서 따지고 보면 docker가 container runtime tool이라기 보단

docker 를 구성하고 있는 것 중 containerd가 container runtime tool에 해당함.

docker는 version 1.11 이후 버전부터 containerd를 container runtime tool로 사용하고 있음.

 

network, volume과 같은 것들은 dockerd에게 맡기고 

오로지 container에 대한 기능만 가지고 있는 것이 containerd임.

 

containerd 또한 dockerd와 마찬가지로 Daemon Process 형태로 실행됨.

containerd는 ctrcli이라고 불리는 containerd 전용 CLI Client를 제공함. 

containerd는 Namespace 기능을 제공하는데 Docker를 통해 실행되는 containerd는 

"moby"라는 이름의 Namespace에 모든 Container들을 생성함.

 

[containerd 기능]

1. container 생성 

더 자세히 말하자면 containerd 자체가 container를 구동하지는 않음. 

containerd는 containerd-shim, runC를 이용하여 container를 생성하는 역할을 수행함.

 

2. image pull

containerd는 container 구동에 필요한 Image가 존재하지 않는다면 

OCI Spec을 기반으로 Image register Server로부터 Container Image를 Pull 하는 역할을 수행함.

 

3. container lifecycle 관리

container start, stop 상태를 관리함.

 

containerd는 독립 실행형 고수준(high-level) 컨테이너 런타임에 해당함.

 

[고수준(high-level) 컨테이너 런타임]

High Level Container Runtime은 이미지 관리, 압축해제, Low Level Container Runtime으로 전달 등 
조금 더 고수준 작업을 수행함.
보통은 Daemon 방식으로 동작하며 Remote API를 제공하기 때문에, 
외부에서 컨테이너를 실행하거나 모니터링이 가능함.

 

 

containerd-shim이란?

위에서 설명한 바와 같이 containerd가 container 생성에 관여하지만

실제 container를 생성하는 것은 containerd가 아닌 containerd-shim, runC가 수행함.

이 둘 중 실제 container를 생성하는 것은 runC가 담당함.

하지만 runC는 container 생성을 완료하면 exit됨.

그럼 생성된 container는 어떻게 관리가 될까?

바로 runC가 종료되도 container를 관리할 수 있도록 해주는 것이 containerd-shim임.

 

containerd는 containerd-shim을 통해 runC를 호출하고

runC는 container를 생성하고 종료함.

그럼 생성된 container의 stdin/out/err 및 Init Process의 Exit Code를 담당하는 Process가 필요한데

container를 생성했던 runC가 종료되어 버렸으니 해당 기능을 담당하는 Process가 없어지게 되어 관리가 어려워짐.

이러한 문제를 해결하기 위해 

containerd-shim 프로세스를 subreaper로 만들어서

생성된 container의 stdin/out/err 및 Init Process의 Exit Code를 담당하는 Process가 containerd-shim이 되도록함.

즉 container와 containerd의 모든 통신은 containerd-shim을 통해서 이루어짐.

 

container가 생성된 후 containerd-shim을 통해 container를 관리하니

containerd가 종료되어도 container는 정상적으로 동작할 수 있고

runC가 종료되어도 container를 정상적으로 관리할 수 있게됨.

 

runC란?

container를 실제 생성하는 역할을 수행함.

runC는 containerd가 아닌 containerd-shim으로 부터 실행되고,

위에서 설명한 바와 같이 container를 생성하고 종료됨.

runC는 OCI Runtime Spec(Open Container Initiative)을 준수하고 있는 저수준(Low-level) 컨테이너 런타임에 해당함.

최초 이름은 libcontainer이였는데 docker project에서 OCI에 기부되었고, 이후 이름이 runC가 됨.

즉 runC는 libcontainer의 리팩토링 구현체임.

docker는 1.8version 이전에는 LXC 드라이버를 기준으로 동작했었는데

이후 docker에서 자체 개발한 libcontainer Go library를 사용하면서 host kernel의 namesapce,  cgroups에 의존되지 않고

OS에서 독립되어 동작할 수 있음.

libcontainer에서 cgroups 관리 모듈로는 cgroupfs 또는 systemd 둘중 하나를 사용할 수 있지만

모든 모듈이 systemd를 사용하는 쪽으로 update되고있음.

 

[저수준(Low-level) 컨테이너 런타임]

OCI Runtime으로 부르기도 하는 Low Level Container Runtime은 오로지 컨테이너를 실행하는 기능만 제공함.
컨테이너는 linux namespace와 cgroups를 통해 구현됨.
linux namespace는 시스템 리소스(Filesystem, Network 등)의 가상화하고, 
cgroups는 컨테이너 안에서 사용할 수 있는 리소스의 양을 제한하는데 사용됨. 
저수준 컨테이너 런타임은 이러한 namespace와 cgroup을 설정한 다음 해당 namespace 및 cgroup 내에서 명령을 실행

lmctfy, rkt, railcar 등 다양한 Low Level Container Runtime이 존재했지만,
현재 OCI 표준 스펙을 지키면서 살아남은 건 많지않으며 runC가 사실상 시장을 지배했다고 보면 됨.

 

 

docker-shim이란?

 

docker 구조를 확인해봤는데 docker-shim은 확인해볼 수 없었음.

그럼 docker-shim이란 어떤 역할을 담당하고 있는가에 대해 알아봄.

 

결론 부터 말하자면 docker-shim은 docker 내부 구조에 포함되어 있는 것 아니고

kubernetes의 kubelet과 docker간의 통신을 위해 구현된 프로젝트임.

kubelet과 docker간의 통신을 직접하지 못하고 docker-shim을 사용하는 이유는 뭘까?

그 이유는 CRI 때문임.

Kubernetes는 CRI(Container Runtime Interface)를 지원하는 모든 컨테이너 런타임을 통해 컨테이너를 실행하는 것을 선호함.

 

 

CRI(Container Runtime Interface)란?

CRI가 생기기 전에는 대표적인 container runtime tool인 Docker 또는 rkt가 kubelet에 직접 통합되어있었음.
하지만 문제가 발생함.
새로운 container runtime tool이 docker, rkt이외에도 많이 생겨남에 따라,
kubernetes도 다양한 container runtime tool을 지원했어야 했는데
container runtime tool 마다 kubelet을 수정하기는 무리가 있었음.
그래서 kubelet 수정없이 다양한 container runtime tool을 지원하기 위해 
kubelet과 container runtime간의 인터페이스를 통일화할 수 있게 해주는 CRI(Container Runtime Interface)가 등장함.
CRI가 등장한 이후 container runtime tool은 해당 CRI 스펙에 맞춰서 CRI 컴포넌트를 구현하면 
쉽게 kubelet과 호환될 수 있게되었음.

근데 Docker와 Kubernetes 중 어떤 프로젝트가 먼저 개발되었는가를 생각하면 당연히 Docker임.

Kubernetes보다 더 오래된 Docker는 CRI를 준수하여 개발되지 않음.

그래서 Kubernetes의 kubelet과 docker간의 CRI를 이용하기 위해 사용했던 것이 바로 docker-shim임.

 

 

 


제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!


 

 

반응형