DinD와 DooD를 공부하기 앞서 Docker Architecture에 대해 알아볼 필요가 있다.
Docker는 Docker Client와 Docker Server로 구성되어 있다.
Docker Client 측에서 명령어를 입력받고 이를 REST API를 통해 Docker Server 측으로 보냄으로써 Docker Server 내부에 있는 Daemon이 명령어를 수행하는 구조를 가진다.
예를 들어 "docker ps"를 통해 실행되고 있는 Container List를 받아오고 싶다고 해보자.
그렇다면 우리는 Docker Client 측에 "docker ps"라는 명령어를 직접 입력할 것이다. 이후 엔터 버튼을 누르면 REST API를 통해 Docker Server에 "docker ps" 명령어가 보내질 것이다.
Docker Server는 명령어를 받고 이를 Docker Deamon 측에서 수행하게 하여 Container List를 반환할 것이다. 이후 반환한 Container List 결과를 REST API를 통해 다시 Docker Client 측에 보냄으로써 Docker Client에서 Container List를 볼 수 있게 되는 것이다.
CMD에 "docker version"을 입력해보면 2가지에 대한 Version이 출력됨을 알 수 있는데, 바로 Docker Client와 Docker Server에 대한 Version이다.
현재 필자는 "Docker Desktop"이라는 Tool을 활용하여 Docker Service를 활용하고 있다.
사진을 설명하기 앞서 Docker는 Linux 위에서 동작되어야 하므로 Windows에서 가상화된 Linux Server를 이용하기 위해 Docker Destop을 설치하기 이전 WSL2라는 것을 설치해야 했다는 것을 상기하고 가자.
위 사진을 잘 살펴보자.
Docker Client는 CLI 환경과 동일하다. 결국 Docker Client는 단순히 명령어를 Docker Server 측에 보내주는 역할만 수행하므로 우리가 Docker 명령어를 입력하는 CMD의 운영 환경이 Docker Client의 OS가 될 것이다.
실제로 windows/amd64가 Docker Client OS임을 볼 수 있다.
Docker Server는 Docker Daemon을 가지고 있으면서 Client 측에서 온 명령문을 직접 실행한다. 우리는 Docker Desktop이 동작할 때 가상화된 Linux 서버 위에서 동작함을 알고 있다. 즉, CMD를 통해 입력한 명령문이 직접 실행되는 환경은 Linux 위에 존재하는 Docker Deamon이므로 OS를 보면 linux/amd64라고 기입된 것을 볼 수 있다.
Docker Daemon을 dockerd라고도 부른다.
dockerd는 Docker Server 측에서 지속적으로 Running 되면서 항상 Docker Client의 요청을 기다리고 Docker Process들을 관리하고 있다. 또한 Docker Service들을 관리하기 위하여 다른 Daemon과 통신하기도 한다.
Docker Image를 저장하는 저장소 역할을 수행한다.
우리는 Docker Image를 사용할 때 "docker pull ~" 명령어를 통해 Docker Hub에 등록되어 있는 이미지를 가져오는데 이때 Docker Hub 측에서 Docker Image를 저장하고 있으므로 Docker Hub가 Registry 역할을 수행하는 것이다.
실제로 Docker Hub는 가장 유명한 Public Docker Registry이며, Docker 측에서도 Docker Registry 기본 값으로 Docker Hub를 활용한다.
위 사진에선 Docker Host라고 나와 있지만, 개인적으로 Docker Server라고 말하는 것이 이해가 더 쉬운 것 같다.
Docker Server는 내부에 Docker Daemon(dockerd)를 가지고 있으며 Docker Registry에서 가지고 온 Image들과 Image를 통해 생성한 여러 개의 Container를 보유 및 관리한다.
"docker ps -a"나 "docker images" 명령어를 통해 보유하고 있는 Container와 Image들을 볼 수 있는데 이들이 Docker Server 측에 저장되어 있는 것이다.
Docker Registry와 헷갈리지 않게 다시 강조하자면, Docker Image를 처음 가지고 올 때는 Docker Registry에 등록되어 있는 Docer Image를 복사하여 가지고 오는 것이고 이렇게 복사한 Image를 Docker Server측에 저장함으로써 Docker Server 측에서 사용할 Docker Image를 관리하게 된다.
또한 Docker Server에 저장한 Docker Image를 통해 Docker Container를 생성함으로써 Docker Server가 직접 Container를 관리할 수 있게 되는 것이다
위에 나온 사진과 설명을 보자면 결국 Docker Client - Docker Server - Docker Registry 사이에 데이터나 명령어를 주고받음으로써 Docker가 동작됨을 알 수 있다. 그리고 3개의 Architecture 요소 간 통신 과정에서 REST API를 활용한다.
Docker Daemon과 Docker Client 사이에서 통신을 할 때 Local에서는 Unix Socket을 활용하며 원격에서는 TCP Socket을 활용한다.
CI 툴들은 Agent라는 것을 활용한다.
Jenkins Agent는 Jenkins Master Node가 작업에 대한 명령을 수행하면 실제로 작업을 수행하는 Node이다.
Jenkins Master Node가 임원이라면 Jenkins Agent는 임원이 내린 명령을 처리하는 평사원이라고 할 수 있는 것이다.
Jenkins Master Node에서도 직접 작업을 처리할 수는 있으나 이 경우 Master Node에서 실행되는 작업들이 겹쳐 충돌이 발생할 수 있으므로 각각의 Jenkins Agent가 1개의 일만 처리하도록 분할시키는 방식을 활용한다.
Jenkins는 Jenkins Agent를 통해 CI 과정을 수행할 것이고, 당연히 Jenkins Agent가 Docker Command에 대한 처리도 수행해야 할 것이다.
즉, Jenkins Agent가 Docker 명령문을 처리하는 Docker Client 역할을 수행해야 한다는 것이다.
Jenkins Agent가 Docker Client 역할을 한다면 어떤 Container가 명령어를 처리할 Docker Server(dockerd) 역할을 수행해야 할까?
이를 위해 나온 방법 중 2가지가 다음 Section에서 배울 DooD와 DinD 되시겠다.