개발 기록
> [ CI/CD ] Docker & Jenkins & Spring boot CI/CD - 2. 호스트 도커에 젠킨스 구축하기 (DIND, DOOD) 본문
> [ CI/CD ] Docker & Jenkins & Spring boot CI/CD - 2. 호스트 도커에 젠킨스 구축하기 (DIND, DOOD)
1z 2024. 3. 8. 19:09

CI(빌드/테스트 자동화) 서버로 jenkins 이미지를 Docker 에서 컨테이너로 실행시키고 있을 때, CD(배포 자동) 를 위해 젠킨스 컨테이너에서 도커 데몬을 제어해야 하는데 Jenkins 이미지에는 Docker 가 설치되지 않기 때문에 불가하다. 이때 DOOD, DIND 방법으로 Jenkins 컨테이너에서 내부에서 Docker 데몬을 실행시키는 방법이 있다.
☞ DIND (DOCKER IN DOCKER)
① Docker 가 설치된 Jenkins 이미지 빌드
② DIND 이미지를 실행하고 해당 TCP 소켓을 Jenkins 컨테이너에 노출 (젠킨스 공식문서에 나와있는 방법)
☞ DOOD (DOCKER OUT DOCKER)
① Host Docker Unix 소켓을 Jenkins 컨테이너에 마운트
■
1. DIND - DIND 컨테이너 TCP 소켓 연결
(1) 구조
도커 호스트에는 2개의 컨테이너가 올라간다. 두 컨테이너는 네트워크와 볼륨을 통해 상호작용한다.
1. 도커-인-도커(docker:dind 이미지) 컨테이너 : 도커 자체에서 엑세스 하기 위함
2. 젠킨스(jenkins 이미지) 컨테이너 : 젠킨스가 수신 대기하는 포트 8080을 외부 트래픽에 노출한다.

(2) Docker host 에서 네트워크 생성
젠킨스 컨테이너와 도커-인-도커 컨테이너가 공유할 도커 네트워크 브리지를 만든다
* 도커 네트워크(Docker Network) 란 각각의 Docker 컨테이너 간의 통신을 관리하고 격리하기 위한 기능을 제공한다.
같은 네트워크 안에서는 컨테이너의 IP를 지정해주거나 할 필요 없이 name 만으로 손쉽게 네트워크를 연결할 수 있다는 장점이 있다. 또한 아웃바운드 포트를 오픈하지 않는 이상 내부적으로만 통신하게 된다.
# docker network create {name}
docker network create jenkins
(3) Pull the DinD Image (dind 이미지)
docker pull docker:20.10-dind

(4) DIND 컨테이너 실행
참고 (Docker Environment variables)
docker run \
# Docker 컨테이너 이름
--name jenkins-docker \
# 컨테이너가 종료될 때 컨테이너와 관련된 리소스(파일 시스템, 볼륨)까지 제거(선택사항)
--rm \
# 해당 컨테이너를 백그라운드에서 실행
--detach \
# 권한 확장: Host 시스템 자원 접근 권한 부여 (default : Unprivileged)
--privileged \
# jenkins 컨테이너와 통신하기 위한 network
# 컨테이너를 네트워크에 연결
--network jenkins \
# 컨테이너에 대한 네트워크 범위 별칭
--network-alias docker \
# TLS(HTTPS) 사용하여 Docker Daemon Socket 보호
# CA가 서명한 인증서로 인증된 클라이언트만 연결 허용
# 환경변수로 지정된 디렉터리에 인증서를 생성한다.
--env DOCKER_TLS_CERTDIR=/certs \
# host 의 jenkins-docker-certs 디렉터리와 컨테이너의 /certs/client 디렉터리 간 마운트
--volume jenkins-docker-certs:/certs/client \
# host 의 jenkins-data 디렉터리와 컨테이너의 /var/jenkins_home 디렉터리 간 마운트
--volume jenkins-data:/var/jenkins_home \
# ( 선택 사항 ) 호스트에 Docker Daemon 포트를 노출 (Host 에서 해당 컨테이너의 Docker Daemon 을 제어할경우)
# 2375(암호화되지않은 트래픽), 2376(암호화된 트래픽)
# --publish 2376:2376 \
# docker:dind 이미지
docker:dind \
# Docker 볼륨용 스토리지 드라이버
--storage-driver overlay2
※ 참고 : 주석 제거 명령문 ↓
▶ 젠킨스 이미지 빌드
: DIND 컨테이너 내부 Docker Daemon 에 명령어를 보내기 위해 Docker Client 설치
- Dockerfile
FROM jenkins/jenkins:2.440.1-jdk17
USER root
RUN apt-get update && apt-get install -y lsb-release
RUN curl -fsSLo /usr/share/keyrings/docker-archive-keyring.asc \
https://download.docker.com/linux/debian/gpg
RUN echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/usr/share/keyrings/docker-archive-keyring.asc] \
https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
# Docker client 만 설치
RUN apt-get update && apt-get install -y docker-ce-cli
USER jenkins
RUN jenkins-plugin-cli --plugins "blueocean docker-workflow"
- image build
docker build -t myjenkins-blueocean:2.440.1-1 .
▶ 젠킨스 컨테이너 실행
참고 (Docker Environment variables)
docker run \
--name jenkins-blueocean \
# container가 정상적으로 종료되지 않은 경우(exit code가 0이 아님)에만 재시작 (with max-retries)
# default = no
--restart=on-failure \
# 현재 컨테이너를 백그라운드에서 실행
--detach \
# DIND 컨테이너와 통신하기 위한 network
# 컨테이너를 네트워크에 연결
--network jenkins \
# 연결할 Daemon Socket
# TCP : Docker Daemonn 에 원격으로 access 하는 경우
# 2376(TLS), 2375(일반)
--env DOCKER_HOST=tcp://docker:2376 \
# 인증키 위치 (CLI와 Daemon 에서 사용)
--env DOCKER_CERT_PATH=/certs/client \
# TLS 사용 (CLI와 Daemon 에서 사용)
--env DOCKER_TLS_VERIFY=1 \
# 현재 컨테이너의 포트 8080을 호스트의 포트 8080에 매핑. (host port:container port)
--publish 8080:8080 \
# Jenkins agents 포트는 50000 로 Jenkins "controller" 매핑한다. (복수의 jenkins 시 설정 필요)
--publish 50000:50000 \
# 컨테이너의 /var/jenkins_home 디렉터리를 host 시스템의 디렉터리 jenkins_home 에 매핑
--volume jenkins-data:/var/jenkins_home \
# Docker 데몬에 연결하는 데 필요한 클라이언트 TLS 인증서는 DOCKER_CERT_PATH 에서 사용할 수 있다
--volume jenkins-docker-certs:/certs/client:ro \
myjenkins-blueocean:2.440.1-1
※ 참고 : 주석 제거 명령문 ↓
ⓛ 장점
☞ 기본 Jenkins 이미지로 직접 작업 가능
☞ 호스트와 Jenkins 컨테이너 간의 격리
② 단점
☞ Jenkins 컨테이너의 도커 명령어를 받는 컨테이너를 추가해야 한다.
☞ DIND 컨테이너에 --privileged 옵션을 설정하여 호스트 시스템의 모든 장치에 액세스 권한을 부여해야한다. 이 부분은 보안상 좋지 않다. 그래도 Jenkins 컨테이너에 직접 부여하는 것보다 낫다.
■
2. DIND - Jenkins 컨테이너 내 Docker 설치

1. Jenkins 컨테이너 내부에 접속해서 Docker 를 수동 설치해도 되고, Dockerfile 을 사용하여 jenkins 기반 이미지를 만들면서 RUN 명령어을 사용하여 Docker 를 설치해도 된다.
- Dockerfile
from jenkins/jenkins:lts
USER root
RUN apt-get update -qq && apt-get install -qqy apt-transport-https ca-certificates curl gnupg2 software-properties-common
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
RUN apt-get update -qq && apt-get install -qqy docker-ce docker-ce-cli containerd.io
RUN usermod -aG docker jenkins
# Build image
docker build --no-cache /path/to/Dockerfile --tag jenkins-docker
# Run container
docker run --rm -d -v ./jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --privileged jenkins-docker
* 중요한점
[ - -v ./jenkins_home:/var/jenkins_home]
:Jenkins 컨테이너가 재시작 및 삭제 될 때 데이터가 날라가지 않다록, 호스트의 디렉토리와 마운트하여 데이터를 유지한다.
ⓛ 장점
☞ 호스트와 Jenkins 컨테이너의 프로세스 격리
② 단점
☞ 기존 jenkins 이미지를 이용하는 것이 아니라, docker 설치가 포함된 jenkins 이미지를 빌드해야한다.
☞ --privileged 권한 부여 옵션 설정
☞ docker에 docker 를 설치하는 것은 권장하지 않는다. (참고)
■
3. DOOD (Docker out of Docker)
: 컨테이너 내부에서 도커를 설치하지 않고 외부의 도커를 사용하는 방식이다.

호스트의 Docker Unix 소켓을 Jenkins 컨테이너에 마운트 함으로써 Jenkins 컨테이너는 호스트의 docker 데몬에 액세스 할 수 있다. 반대로 Jenkins 컨테이너에서 생성된 모든 컨테이너/이미지 또한 호스트에서 액세스할 수 있다.
(1) Pull Jenkins Image
$ docker pull jenkins/jenkins:lts

☞ 문제 발생 : 권한 error

☞ 해결 방법: Docker 그룹에 현재 계정 추가

(2) 볼륨 생성
docker volume create jenkins-volume
(3) 젠킨스 컨테이너 실행
$ docker run -d \
# port 연결
-p 8080:8080 \
-p 50000:50000 \
# 젠킨스 컨테이너의 설정을 호스트 서버와 공유함으로써, 컨테이너가 삭제되는 경우에도 설정을 유지할수 있게 해준다.
-v /jenkins:/var/jenkins_home \
# 젠킨스 컨테이너에서도 호스트 서버의 도커를 사용하기 위한 바인딩
-v /usr/bin/docker:/usr/bin/docker \
# docker.sock 마운트
-v /var/run/docker.sock:/var/run/docker.sock \
--name jenkins \
-u root \
jenkins/jenkins:lts
ⓛ 장점
☞ 기본 Jenkins 이미지로 직접 작업 가능
☞ --privileged 권한 부여 옵션 설정 필요 없음
☞ 많이 사용하는 방식
② 단점
☞ 호스트와 Jenkins 컨테이너가 격리되지 않아 결합도가 높다.
☞ Jenkins 컨테이너가 호스트 도커 소켓에 관한 권한이 없을 수 있다.
참고
https://www.jenkins.io/doc/book/installing/docker/
https://www.tiuweehan.com/blog/2020-09-10-docker-in-jenkins-in-docker/
'인프라' 카테고리의 다른 글
| > [ CI/CD ] Docker & Jenkins & Spring boot CI/CD - 4. Github Webhook 설정, 젠킨스 파이프라인 & Dockerfile 작성 (0) | 2024.03.08 |
|---|---|
| >[ CI/CD ] Docker & Jenkins & Spring boot CI/CD - 3. Jenkins 접속 및 Github 연동 (0) | 2024.03.08 |
| > [ CI/CD ] Docker & Jenkins & Spring boot CI/CD - 1.설계 (0) | 2024.03.08 |
| > [AWS] EC2 인스턴스 SSH 접속 (0) | 2024.03.08 |
| > [AWS] AWS 프리티어 EC2 인스턴스 생성 방법 (0) | 2024.03.05 |