docker가 무엇인지에 대한 정의는 다른 블로그의 글이나 다양한 곳에서 언제든 쉽게 찾아볼 수 있으므로 따로 설명으로 기록하려 하지 않는다.
docker의 설명을 찾아보면 핵심적인 키워드는 "container"이다. 그렇다면 이 "container"라는 친구는 어떤 기능이며 어떻게 돌아가는지에 대한 궁금증으로 시작되었다.
https://docs.docker.com/get-started/
위의 docker 공식 reference를 참고하여 글을 써내려가므로 이해가 가지 않거나 궁금한 내용이 생겼다면 위의 사이트를 직접 참고하시는것을 추천드립니다.
docker에 대해서 공부했거나 읽어봤다면 container라는 단어를 아주 눈에 익도록 보았을텐데 대부분의 설명을 보면
다 맞는말인데 무엇인가 부족하다. 그래서 저 container라는게 어떻게 가상화를 하는건가 궁금해지게 만든다.
docker 공식 문서에서의 설명을 해석해보면
docker는 Go 언어로 작성되었으며 Linux kernel의 다양한 기능을 활용하여 기능을 제공합니다. Docker는 namespace라고 불리는 기술을 사용하여 container라는 격리된 공간을 제공합니다.
단순히 docker의 핵심 기능은 container! 라는 설명만으로는 부족한 이유가 여기에 있다.
namespace = container라는 사실을 공식 문서에서 명시하고 있다.
그럼 namespace라는 친구는 어떻게 동작하는걸까?
docker-compose.yml 파일에서 host와 container의 공유 폴더(volume)를 만들어서 테스트를 진행해보자
sunghkim 사용자는 현재 host 환경이고 (~/date/mariadb)
ctr 사용자는 container안에서의 환경이다. (/var/lib/mysql)
container에서 "this_is_test_file"이라는 파일을 한 번 생성해보자 .
사용자의 이름은 다르지만, UID가 같은 상황에서 host에서 살펴본 파일의 소유주와 container에서 보는 소유주가 서로 본인을 나타내고 있다.
watch "ps ax"
프로세스는 어떨까? watch 명령어로 2초마다 "ps ax"의 출력을 감시해보자
그리고선 host에서는 watch를 빼고 "ps ax" 명령어만 입력을 해보면
container에서는 PID가 1813번인데 host에서는 32040번임을 확인할 수 있다.
이것이 docker가 virtual machine이랑 다른 이유이다.
host와 container는 자원을 공유함을 알 수 있고 일정 수준의 격리가 이루어 져있음을 짐작할 수 있다.
또 namespace가 이런 프로세스들을 독립적으로 서로에게 간섭없이 실행될 수 있도록 기능하는 것을
엿볼 수 있었다.
정리해보면 여기서 우리는 "일정 수준의 격리"가 container라는 공간임을 알게 되었고
namespace는 독립된 가상의 공간 즉 container를 만들어 주는 핵심적인 기술임을 알 수 있다.
namespace = container 라고 위에서 언급했었는데 정확하게는
namespace에 의해 만들어진 공간이므로 container가 종속적인 의미로 느껴진다.
pstree
pstree 명령어로 현재 precess의 트리 구조를 볼 수 있다. watch ps ax를 컨테이너에서 돌려놓고 Host에서 pstree를 살펴보자.
systemd를 시작으로, containerd와 containerd-shim을 관찰할 수 있는데
자세히 보면 containerd-shim에서 watch가 돌아가고 있는것을 볼 수 있다.
여기서나온 containerd는 또 무엇일까?
https://containerd.io
https://github.com/containerd/containerd
단순성, 견고성, 이식성에 중점을 둔 산업 표준 컨테이너 런타임
위의 링크를 타고 들어가면 설명이 보다 자세하게 나와있는데 핵심 대목은
" containerd는 호스트 시스템의 전체 컨테이너 수명 주기를 관리한다. "
" containerd의 runtime 요구사항은 매우 적다.
Linux 컨테이너와 대부분의 상호작용은 runc를 통해 처리된다."
개념이 계속해서 하나씩 새로 나오고 있고 더 깊게 들어가면 끝이 없기 때문에 runc는 나중에
따로 더 알아보도록하고 간단하게 도커의 전체적인 구조를 보면
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/get-started/overview/
사진에 나와있는 구조는 client가 CLI 명령어인
위와 같이 명령어를 입력하면 "docker daemon(dockerd)" 으로 전달되고
dockerd는 자동(기본)으로 containerd를 실행시킨다고 한다.
근데 ubuntu 환경에서 테스트 할 때 containerd가 없었는데
pstree를 하면 containerd 자리에 dockerd가 나오긴 하더라..!
아마 containerd가 없어도 아주 기본적인 기능은 하는것 같은데 이 부분은 어떤식으로
설계가 되어있는지 확실히 모르겠다. 찾아봐도 잘 안나오기도 하고!
containerd를 수동으로 설치하니깐 containerd를 사용하는 모습을 볼 수 있었다.
추측하기론 어차피 runc가 실제적인 컨테이너 관리, 생성을 하니깐 돌아가는것 같기도..?
어쨌든 정리해보면
Client(CLI) 명령어 <-> dockerd -> containerd -> runc
우리가(CLI) 명령어를 사용하면 dockerd 와 통신하게 되고
dockerd가 시작되기전에, containerd가 컨테이너를 관리하고 생성하기 때문에
dockerd에 의해서 containerd가 자동으로 먼저 실행이 되고 있고
containerd는 실제로 컨테이너를 생성하고 실행하는데 runc를 사용한다.
그럼 실제로 도커가 이렇게 돌아가는지 확인해보려 하는데
너무 길어질것 같으니 다음 포스팅에서 계속 이어서 쓰려한다.