
프로젝트를 진행하며 EC2에 Jenkins를 배포하여 CI/CD 파이프라인을 구축하였다. 이 과정에서 컨테이너 환경에서 실행 중인 Jenkins에서 docker-compose: not found 오류가 발생하는 문제가 있었고, 이를 해결하는 과정에서 DinD(Docker-in-Docker)와 DooD(Docker-out-of-Docker) 방식에 대해 학습하게 되었다.
CI/CD 파이프라인 흐름
- 개발자가 GitLab에 코드를 push한다.
- GitLab에 설정된 webhook을 통해 push 이벤트가 Jenkins로 전달된다.
- Jenkins는 GitLab의 최신 코드를 가져와 테스트 및 빌드를 수행한다.
- 빌드 과정에서 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
위 명령어가 실행되는 내부 과정은 다음과 같다.
- Docker CLI가 요청을 생성
- 사용자가 docker ps 명령을 실행하면, Docker CLI는 "실행 중인 컨테이너 목록을 가져오라"는 요청을 생성한다.
- Docker CLI → /var/run/docker.sock으로 요청 전달
- Docker CLI는 이 요청을 /var/run/docker.sock을 통해 Docker 데몬(dockerd)에게 전달한다.
- Docker 데몬이 요청을 처리
- Docker 데몬(dockerd)은 현재 실행 중인 컨테이너 목록을 가져와 응답을 생성한다.
- 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 방식이 일반적으로 사용됨.
DinD와 DooD 방식의 차이점을 이해하고, 왜 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 방식이 더 많이 사용될까?
- 성능이 뛰어나다
- 추가적인 가상화 계층이 없기 때문에 빌드 속도가 빠름
- 호스트의 Docker 캐시를 그대로 활용할 수 있어 빌드 최적화가 가능
- CI/CD 환경과 잘 맞는다
- CI/CD에서 빌드된 컨테이너는 Jenkins 컨테이너와 독립적으로 실행되는 것이 일반적
- Jenkins 컨테이너가 종료되더라도 배포된 컨테이너는 유지됨
- Kubernetes 및 클러스터 환경과의 연계가 용이하다
- Kubernetes에서 배포할 때 호스트의 Docker 데몬을 직접 사용하는 것이 더 자연스러움
- Kubernetes 환경에서는 DinD 방식보다 DooD 방식이 더 많이 사용됨
'DevOps > Docker' 카테고리의 다른 글
| Docker Registry와 Docker 실행 구조 (Harbor, Desktop, Engine, WSL2) (0) | 2026.01.24 |
|---|---|
| [Docker] Container의 불변성과 Volume, Bind Mount (0) | 2025.01.29 |
| [Docker] docker compose - redis 연결 불가 오류 (2) | 2024.02.27 |
| [Docker] docker compose로 Spring + MySQL 배포 환경 만들기 (1) | 2024.02.22 |
| [Docker] 도커 개념, 기본 명령어 정리 (0) | 2024.02.19 |
댓글