이쿠의 슬기로운 개발생활

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

Kubernetes/Kubernetes 이론

Kubernetes docker 지원 중단 관련 설명

이쿠우우 2021. 10. 5. 20:33
반응형

 

kubernetes docker 지원 중단 관련 설명

 

목표

kubernetes 에서 2021년 하반기 1.23 version 부터 docker를 지원하지 않는다고 하는데

그 이유를 자세히 알고싶어서 리서치를 진행했던 과정을 정리해봄.

이유와 대처 방법을 알아봄.

 


 

2022-02 월 내용 추가 사항

현재 Docker는 Mirantis(미란티스)가 인수해서 cri-docker를 지원함.

그래서 k8s 1.23에서 Docker를 사용할 수 있음.

프로젝트 링크 : https://github.com/Mirantis/cri-dockerd

 

공식 관련 내용 확인

2020.12.02 글 확인

https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/

 

결론 요약

kubernetes의 default container runtime 이었던 docker 가 v1.20 부터 deprecated 되고, 

2021년도 하반기에 출시되는 v1.23 부터는 docker를 container runtime 으로 활용할 수 없음. 

docker를 지원하지 않는 이유는 docker가 CRI(Container Runtime Interface) 를 구현하지 않았고, 

kubernetes에서 dockershim으로 변환하는데 비효율적이라고 판단했기 때문임.

즉 좀더 자세히 말하자면 kubernetes는 docker 지원을 중단한게 아니라

Kubelet에서 docker-shim의 지원이 Deprecation 됨.

아직 docker-shim이 어떤 역할을 하는 것인지 모르겠으니

한번 알아보도록함.

 

 

 

 


 

 

왜 Kubernetes에서 Docker 사용을 중단할까?

 

해당 원인을 알아보기 위해서 먼저 Docker의 구조를 알아 볼 필요가 있음.

 

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임.

 

 

 

docker-shim의 어떤 부분이 문제였던건가?

 

[첫 번째]

docker-shim은 위에서 설명한 바와 같이 kubelet 과 docker간의 CRI를 위해 만들어진 프로젝트임.

하지만 docker 구조에서 알아본 것 처럼 docker 자체가 container를 생성하는 것이 아니라 

docker 구조에 있던 containerd가 container 생성에 직접적인 관여를 함.

즉 containerd 상위의 dockerd는 kubelet과 통신할 필요없이 containerd와 직접하면 됨.

어떻게 보면 이 첫 번째 이유가 docker가 Deprecation되는 가장 큰 이유임.

 

[두 번째]

docker-shim에서 많은 이슈가 발생함.

kubelet에서 docker-shim 때문에 유지보수 에 어려움을 느끼게됨.

이에 따라 kubernetes가 docker에게 1년 넘게 CRI를 따르라고 시간을 줌(docker에게 압박을 가한 것 같기도 함).

하지만 Docker는 자체적으로 CRI를 지원하지 않고 docker-shim을 계속 고집하고

19년 03월 19.03버전 출시 이후로 아무런 업데이트를 하지 않았음.

 

[세 번째]

쿠버네티스는 더 많은 업데이트를 하고싶고, 더 많은 개선을 하고싶은데

Docker는 19년 3월 이후 사실상 Comminity Edition을 업데이트하지 않았고 

당연히 dockershim도 업데이트 되지 않음.

docker의 dockershim은 kubernetes 생태계를 발목을 잡게 됨.

 

[네 번째]

docker의 모든 명령은 root 권한을 가지고 있어야 실행 가능했음.

이로 인해 보안 문제가 발생할 수 있음.

docker 이외의 cri-o같은 tool들은 일반 권한으로도 실행이 가능함.

 

[다섯 번째]

docker는 daemon 형태로 실행됨.

daemon 형태로 실행되는 docker는 container image build, 관리, 공유, 실행 등 많은 기능을 담당하고 있음.

그리고 해당 daemon하위에 모든 컨테이너를 자식 프로세스로 소유함. 

이로 인해 무거울 뿐 아니라 장애가 발생하면 모든 자식 프로세스에 영향을 끼쳐 

단일 실패점(Single point of failure)이 될 위험이 있음.

즉 docker daemon이 중지되면 kubernetes 전체에 영향이 미침.

 

 

 

 

결국 docker-shim 지원이 중단

 

kubernetes는 1년이 넘도록  docker의 업데이트를 기다리다가 docker-shim 을 deprecated하기로 결정함.

kubernetes는 2021년 말에 출시 예정인 1.23 version부터 docker-shim 을 deprecated하기 때문에

kubelet에서 container runtime으로 docker를 사용 시 

docker-shim, dockerd를 통하지 않고

CRI를 준수하는 containerd 또는 CRI-O 사용을 권장하게 됨.

 

 

 


 

 

 

 

Docker가 deprecated 된 이후 궁금증

 

 

 

Docker로 생성했던 image는 사용가능한가?

 

의문점

지금까지 docker의 dockerfile로 image를 생성해서 kubernetes에서 사용했었는데

docker가 deprecated 된다면 docker image 또한 더 이상 사용이 불가능한건가?

 

결론

docker가 CRI는 준수하지 않았지만 OCI(Open Container Initiative)는 준수했기 때문에

docker를 통해서 생성했던 image는 다른 container runtime tool에서도 정상적으로 사용할 수 있음.

 

OCI(Open Container Initiative)란?

Container라는 개념이 생긴 초반에는 많은 IT 회사들이 Container기반의 제품을 출시함.
하지만 Container 기술에 대한 특정한 규격이 없다 보니 개념 자체가 혼란스러워짐.
문제를 해결하기 위해 2015년 6월 22일 도커, Redhat, 구글, 마이크로소프트, IBM 등 
주요 업체들이 Container 포맷과 런타임에 대한 개방형 업계 표준을 만들기 위해 
OCI(Open Container Initiative)를 구성함.
이후 OCI는 Container 개발의 기준이 됨.
OCI는 표준 요구 사항, 구성 요소 및 특징을 가지고 있음
참고 : http://www.opennaru.com/kubernetes/open-container-initiative/

 

 

 

 

k8s를 1.23 version으로 Update한다면 container runtime tool을 교체해야할까?

 

의문점

그렇다면 k8s 1.19 version 환경에서 container runtime tool을 docker로 사용하고 있다가 

곧 출시 되는 k8s 1.23으로 update하면 더 이상 docker를 사용하지 못하고 

cri-o, containerd 와같은 다른 tool로 교체해야할까?

 

결론

글을 쓰고 있는 현 시점 2021-09월 20일은 아직 k8s 1.23 version이 출시되지 않아서 정확하진 않을 수 있지만..

현재 docker deprecated 관련 글을 리서치 해보면 docker를 다른 containerd, CRI-O와 같은 것으로 교체를 해야할 것으로 보임.

이와같이 생각하는 이유는

docker deprecated 관련해서 docker shim을 대체할 수 있는 프로젝트가 많이 생성되고 있음.

즉 이러면 프로젝트를 통해 누군가 별도로 도커쉼을 개발해주는게 아니라면 docker는 kubernetes에서 사용 불가능함이 증명됨.

[docker shim 대체 프로젝트] 

미란티스 cri-dockerd : https://github.com/mirantis/cri-dockerd

 

 

 

containerd는 docker에서 사용되는 containerd와 같은건가?

 

의문점

추천 container runtime tool로 containerd, cri-o가 있던데

containerd는 위의 docker 구조에서 리서치한 바와 같이

docker 구조에 포함되어있는 containerd와 같은건가?

 

결론

같음.

 

 

containerd가 docker 구조에 포함되어있는 containerd와 같다면
docker에서 쓰던 containerd를 그대로 쓰면되지 않는가?

 

의문점

위에서 알아본 바와 같이 docker는 이미 containerd를 포함하고 있음.

그리고 해당 containerd는 앞으로 kubernetes가 권장하는 container runtime tool인 

containerd와 동일한 tool이 맞음.

그렇다면 docker에서 동작하고 있는 containerd를 그대로 바라보게 한다면

k8s 1.23 version이 출시되더라도 문제가 없을 것 같은데 

containerd 또는 cri-o 변경을 권장하는가?

 

 

결론

먼저 결론 부터 말하자면 

어차피 docker도 내부적으로 containerd -> runc로 container를 실행하기 때문에

docker에서 사용하는 containerd를 그대로 사용할 수 있음.

단 그대로 사용하기 위해서는 관리자가 수동으로 

containerd의 namespace에 대한 특정 작업을 진행해줘야함.

 

kubelet 소스를 살펴보면 container runtime detect 로직이 있는데

거기서 어떤 .sock이 시스템에 있는지 판별해서 container runtime tool을 선정했었음.

그동안은 dockershim이 priority 가 제일 높았었지만

k8s 1.23 version부터는 docker shim이 없어지고

containerd가 있다면 해당 containerd를 detect 할 것임.

 

containerd의 namespace란?

docker에서 사용되는 containerd의 namespace 때문에 문제가 있음.

dockerd -> containerd 같은 경우에는 moby namespace를 물고 들어가고

kubelet -> containerd 는 k8s namespace 를 물고 들어감.

이렇게 되면 update 이후 containerd가 바라보는 namespace 때문에 문제가 발생함.

 

[실제 docker 20.10.8 - kubernetes 에서 확인]

ctr namespace list

명령으로 확인해보면 namespace가 moby 만 있는 것을 확인할 수 있고

모든 container가 moby namespace에서 실행되고 있는 것을 확인할 수 있음.

 

[실제 containerD - kubernetes 환경에서 확인]

 

containerd namespace 이슈

[1.]

기존의 k8s가 docker를 사용하고 있었다면

containerd는 moby namespace를 사용하고 있었을 것임.

해당 환경에서 k8s를 1.23 version으로 update한다면

kubelet은 자동으로 host를 CRI에 해당하는 container runtime tool을 찾을 것이고

기존 host의 docker가 사용하던 containerd를 찾게 될 것임.

update 후 kubelet -> containerd의 k8s namespace를 확인하게 될탠데

host는 기존에 docker를 통해서 containerd가 실행되고 있었기 때문에 

기존에 동작하고 있던 container 정보들은 moby namespace에 있어서

k8s namespace에는 정보가 없을 것임.

 

[2.]

k8s namespace에 생성된 containerd의 container 정보는

docker ps 같은 명령으로 확인이 안됨.

즉 kubelet -> containerd 구조의 kubernetes 환경이라면

docker 명령으로 container를 확인할 수 없음.

 

[3.]

docker CLI를 통해서 로컬에 pull 받은 image라던가 local에서 build한 image는

moby namespace를 사용하는 containerd 에서 사용할 수 있지만, (docker)

k8s namespace를 사용하는 containerd 에서는 사용할 수 없음. (k8s)

 

 

docker containerd의 namespace가 moby인 이유?

docker에서 containerD를 사용하면

당연히 conatainerD namespace를 docker라고 할줄 알았는데

moby인 이유가 궁금해져서 docker opensource로 알아봄.

docker는 다양한 opensource project의 집합체임.

docker에서 container 관련 작업을 담당하는 project가 moby임으로

containerD namespace default값이 moby로 설정되어있음.

참고 : https://github.com/moby/moby#relationship-with-docker

 

 

코드에서 직접확인

 

[docker(moby) 프로젝트]

https://github.com/moby/moby/blob/master/daemon/daemon.go

containerd default namespace

[kubernetes containerD CRI]

https://github.com/containerd/cri/blob/release/1.4/pkg/constants/constants.go

containerd default namespace

 

containerd namespace 변경방법

 

containerd도 마찬가지로 namespace 개념을 사용할 수 있음.

kubernetes 1.23관련해서 containerd namespace 작업이 필요한데

해당 작업은 별도로 containerd cli tool을 사용해서 

containerd에서 moby namespace에 있는 container를

k8s namespace로 변경하는 작업을 사용하기 보단

kubernetes 자체에서 작업을 진행하는 편이 더 효율적임.

이유는 k8s에서 containerd를 API로 호출하는 과정에

namespace를 명시하기 때문에

kubelet -> containerd 형태로 설정 후 작업을 진행하면

자연스럽게 k8s namespace에 container들이 생성됨.

 

해당 작업을 실제 진행한다면 그때 별도의 글로 더 자세히 정리하도록 하겠음.

지금은 간단히 flow만 요약해서 설명하겠음.

 

[1.]

1.23 version으로 변경을 원하는 kubernetes node 차단 및 drain 작업 진행

관련 명령어 : kubectl cordon, kubectl drain

해당 작업을 진행하면 containerd에서 moby namespace에 있는 container정보들이 제거됨.

 

[2.]

kubelet 중지

 

[3.]

kubernetes container runtime tool을 docker에서 containerd로 변경

docker를 삭제 후 containerd를 재설치해도 되고

docker를 유지한 상태로 kubernetes 설정만 변경해서 containerd를 container runtime tool로 변경해도 됨.

 

[4.]

kubelet 시작

 

[5.]

node 차단 취소 후 container들이 재생성되면

containerd에서 k8s namespace에 container를 생성함.

 

 

 

 


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


 

 

 

 


참고

https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/

https://kubernetes.io/blog/2020/12/02/dockershim-faq/#what-should-i-look-out-for-when-changing-cri-implementations

https://www.tutorialworks.com/difference-docker-containerd-runc-crio-oci/

https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2221-remove-dockershim

https://dev.to/inductor/wait-docker-is-deprecated-in-kubernetes-now-what-do-i-do-e4m

https://fransoaardi.github.io/posts/k8s_docker_deprecated/

https://www.samsungsds.com/kr/insights/docker.html

https://waspro.tistory.com/679

https://www.youtube.com/watch?v=XXH0Ocm_9Ro

https://www.lesstif.com/container/podman-98926748.html

https://subicura.com/k8s/2020/12/19/deprecate-docker/

http://www.opennaru.com/kubernetes/containerd/

https://wookiist.dev/83

https://kwiki.devserum.com/ko/articles/tech-articles/kubernetes-is-deprecating-docker-as-a-container-runtime-after-v1-20

https://acloudguru.com/blog/engineering/kubernetes-is-deprecating-docker-what-you-need-to-know

https://www.mirantis.com/blog/the-future-of-dockershim-is-cri-dockerd/

 

kubelet docker shim 제거 

https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2221-remove-dockershim

 

 

 

 

반응형