본문 바로가기
DevOps/Docker

[Docker] DinD(Docker in Docker) 와 DooD(Docker out of Docker)

by 방배킹 2025. 2. 2.

 

프로젝트를 진행하며 EC2에 Jenkins를 배포하여 CI/CD 파이프라인을 구축하였다. 이 과정에서 컨테이너 환경에서 실행 중인 Jenkins에서 docker-compose: not found 오류가 발생하는 문제가 있었고, 이를 해결하는 과정에서 DinD(Docker-in-Docker)와 DooD(Docker-out-of-Docker) 방식에 대해 학습하게 되었다.

 

CI/CD 파이프라인 흐름

  1. 개발자가 GitLab에 코드를 push한다.
  2. GitLab에 설정된 webhook을 통해 push 이벤트가 Jenkins로 전달된다.
  3. Jenkins는 GitLab의 최신 코드를 가져와 테스트 및 빌드를 수행한다.
  4. 빌드 과정에서 docker-compose up -d --build 명령을 실행하여 새로운 코드 기반으로 컨테이너를 빌드 및 실행한다.

그러나 4번 과정에서 docker-compose 명령어가 실행되지 않는 문제가 발생 하였다.

결론적으로, Docker 컨테이너로 실행된 Jenkins에서 docker-compose 명령어를 사용할 수 있어야 한다.

 

Docker의 구성요소

Docker는 여러 주요 구성 요소로 이루어져 있다.

  • 클라이언트(Client): 사용자가 docker 명령어를 실행하는 인터페이스
  • 데몬(Daemon): 백그라운드에서 컨테이너를 관리하는 서버 프로세스 (dockerd)
  • 이미지(Image): 컨테이너 실행을 위한 파일 시스템 및 실행 정보 패키지
  • 컨테이너(Container): 실행 중인 애플리케이션 인스턴스
  • 레지스트리(Registry): Docker 이미지를 저장하는 저장소 (예: Docker Hub)
  • 네트워크(Network): 컨테이너 간 네트워크 통신을 위한 기능
  • 볼륨(Volume): 컨테이너가 사용하는 데이터 저장소
  • Docker Compose: 여러 컨테이너를 정의하고 관리하는 도구
  • Docker Swarm: 컨테이너 오케스트레이션을 위한 기능

이 중 클라이언트와 데몬이 어떻게 연결되는지 이해하는 것이 중요하다.

 

Docker CLI와 Docker Host 연결 방식

Docker CLI는 여러 방식으로 Docker Host와 연결될 수 있지만, 기본적으로 /var/run/docker.sock 소켓을 통해 로컬 Docker 데몬과 통신한다.

 

/var/run/docker.sock이란?

docker.sock은 Docker 데몬과 통신하기 위한 Unix Domain Socket 파일이다.

이 소켓을 통해 Docker CLI가 Docker 데몬에게 API 요청을 전달하고, 응답을 받아 명령을 수행한다.

 

Docker 명령어 실행 과정

docker ps

 

위 명령어가 실행되는 내부 과정은 다음과 같다.

 

  1. Docker CLI가 요청을 생성
    • 사용자가 docker ps 명령을 실행하면, Docker CLI는 "실행 중인 컨테이너 목록을 가져오라"는 요청을 생성한다.
  2. Docker CLI → /var/run/docker.sock으로 요청 전달
    • Docker CLI는 이 요청을 /var/run/docker.sock을 통해 Docker 데몬(dockerd)에게 전달한다.
  3. Docker 데몬이 요청을 처리
    • Docker 데몬(dockerd)은 현재 실행 중인 컨테이너 목록을 가져와 응답을 생성한다.
  4. Docker CLI가 결과를 출력
    • Docker 데몬은 컨테이너 목록을 /var/run/docker.sock을 통해 Docker CLI로 반환한다.
    • Docker CLI는 이를 사용자 화면에 출력한다.

즉, /var/run/docker.sock은 Docker CLI와 Docker 데몬이 서로 데이터를 주고받는 통신 채널 역할을 한다.

 

Jenkins에서 docker-compose 명령어를 사용하려면?

docker-compose도 Docker 데몬과의 통신이 필요하다.

 

이를 위해 Docker Compose는 내부적으로 /var/run/docker.sock을 사용하여 Docker 데몬과 통신한다.

즉, 명령어를 실행하면 내부적으로 여러 개의 docker run 명령이 수행되며, 이 과정에서도 /var/run/docker.sock을 통해 Docker 데몬과 직접 통신한다.

 

따라서 Jenkins에서 docker-compose를 실행하려면 다음 두 가지 방법 중 하나를 선택해야 한다.

 

  • DooD (Docker-out-of-Docker) 방식
    • /var/run/docker.sock을 마운트하여 호스트 Docker 데몬을 직접 사용
    • 성능이 좋지만 보안 이슈가 존재함
  • DinD (Docker-in-Docker) 방식
    • Jenkins 컨테이너 내부에 Docker와 Docker Compose를 설치하여 독립 실행
    • 보안성이 높지만 성능 저하 가능

대부분의 CI/CD 환경에서는 /var/run/docker.sock을 마운트하는 DooD 방식이 일반적으로 사용됨.

 

DinDDooD 방식의 차이점을 이해하고, 왜 DooD 방식이 일반적으로 사용되는지 자세히 살펴보자.

 

DinD (Docker-in-Docker) 방식

1. 개념

DinD(Docker-in-Docker)는 컨테이너 내부에 또 다른 Docker 데몬을 실행하는 방식이다.

즉, Jenkins 컨테이너 안에서 Docker 데몬을 직접 실행하고, 그 내부에서 추가적인 컨테이너를 실행하는 구조이다.

이를 구현하려면 Jenkins 컨테이너 내부에서 Docker를 설치하고, Docker 데몬을 실행해야 한다.

2. 특징

보안성

  • Docker 데몬이 Jenkins 컨테이너 내부에서 실행되므로, 호스트 Docker 환경과 격리됨
  • /var/run/docker.sock을 마운트하지 않기 때문에 호스트 환경에 대한 직접적인 접근이 불가능함

성능 저하

  • Docker 컨테이너 내부에서 또 다른 Docker 데몬을 실행하기 때문에 추가적인 가상화 계층이 필요함
  • CI/CD에서 여러 개의 컨테이너를 빌드하고 실행하는 경우 빌드 시간이 증가하고, 캐시 공유가 어려움

캐시 미활용 문제

  • 컨테이너 내부에서 실행된 Docker 데몬은 호스트의 Docker 캐시를 공유하지 않음
  • 매번 새로운 빌드가 발생할 때마다 캐시를 활용할 수 없어 성능이 저하됨

컨테이너 유지 문제

  • 컨테이너 내부에서 실행된 Docker 데몬을 관리해야 하므로, Jenkins 컨테이너가 재시작되면 내부 Docker 데몬이 함께 종료될 위험이 있음

3. DinD 방식의 사용 예시

services:
  jenkins:
    image: jenkins/jenkins:lts
    privileged: true  # DinD 사용 시 필수
    volumes:
      - /var/jenkins_home:/var/jenkins_home

 

Docker 컨테이너 내부에서 또 다른 Docker 환경을 실행하는 구조로, CI/CD 파이프라인에서 성능 저하 및 캐시 활용 문제를 유발할 수 있음

 

DooD (Docker-out-of-Docker) 방식

1. 개념

DooD(Docker-out-of-Docker)는 컨테이너 내부에서는 Docker CLI만 실행하고, 실제 Docker 명령은 호스트의 Docker 데몬을 사용하는 방식이다.

즉, Jenkins 컨테이너는 호스트의 Docker 데몬과 직접 통신하며, 컨테이너를 생성하고 관리한다.

이 방식에서는 호스트의 /var/run/docker.sock을 컨테이너에 마운트하여 Docker 명령을 실행한다.

2. 특징

빠른 성능

  • 추가적인 가상화 계층이 없으므로 빌드 속도가 빠름
  • 호스트의 Docker 캐시를 그대로 활용 가능

컨테이너 독립성 유지

  • Jenkins 컨테이너가 종료되더라도, 실행된 컨테이너는 호스트에서 계속 유지됨
  • CI/CD 파이프라인에서 실행된 컨테이너가 별도로 관리될 수 있음

보안 문제

  • /var/run/docker.sock을 마운트하면 Jenkins 컨테이너에서 호스트의 Docker 데몬을 완전히 제어할 수 있음
  • 악성 코드가 실행될 경우, 호스트의 모든 컨테이너 및 이미지가 위험에 노출될 가능성이 있음

3. DooD 방식의 사용 예시

services:
  jenkins:
    image: jenkins/jenkins:lts
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock  # 호스트의 Docker 데몬을 사용
      - /var/jenkins_home:/var/jenkins_home

 

DooD 방식은 성능과 캐시 활용 측면에서 유리하지만, 보안 문제가 존재하므로 권한 관리가 필요함

 

결론: CI/CD에서는 왜 DooD 방식이 더 많이 사용될까?

  1. 성능이 뛰어나다
    • 추가적인 가상화 계층이 없기 때문에 빌드 속도가 빠름
    • 호스트의 Docker 캐시를 그대로 활용할 수 있어 빌드 최적화가 가능
  2. CI/CD 환경과 잘 맞는다
    • CI/CD에서 빌드된 컨테이너는 Jenkins 컨테이너와 독립적으로 실행되는 것이 일반적
    • Jenkins 컨테이너가 종료되더라도 배포된 컨테이너는 유지됨
  3. Kubernetes 및 클러스터 환경과의 연계가 용이하다
    • Kubernetes에서 배포할 때 호스트의 Docker 데몬을 직접 사용하는 것이 더 자연스러움
    • Kubernetes 환경에서는 DinD 방식보다 DooD 방식이 더 많이 사용됨

댓글