목차
Jenkins를 이용해 Docker Pipeline을 구성하여, Docker 이미지를 빌드하고 Docker Hub에 푸시하는 CI 작업을 진행해보겠다.
Jenkins는 워낙 유명한 CI/CD 툴이기 때문에 설명은 생략하도록 하겠다.
Server에 Jenkins 설치
Dockerfile
FROM jenkins/jenkins
USER root
# install linux packages
COPY configuration.sh /root/configuration.sh
RUN chmod +x /root/configuration.sh && /root/configuration.sh
EXPOSE 8080/tcp
EXPOSE 50000/tcp
configuration.sh
#!/bin/sh
# jenkins 컨테이너에 도커 설치하기
# install docker
# 패키지 리스트 업데이트 및 필수 패키지 설치
apt update
apt install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
# Docker GPG 키 추가
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
# Docker 저장소 추가
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
# 패키지 리스트 업데이트 후 Docker 설치
apt update
apt install -y docker-ce docker-ce-cli containerd.io
# Jenkins 사용자를 Docker 그룹에 추가
usermod -aG docker jenkins
먼저, 서버에 Docker를 설치하여 Jenkins를 컨테이너 형태로 띄워준다. 그 후, mkdir -p /tmp/docker/jenkins_home 명령어를 사용하여 디렉토리를 생성한다. 그리고 Jenkins 컨테이너에 볼륨 마운트를 해주어 Jenkins 컨테이너와 호스트 간에 데이터를 지속적으로 공유할것이다. 그다음 다음 명령어를 통해 Jenkins 컨테이너를 띄워주면 된다.
docker run -d --name jenkins -p 8080:8080 -p 50000:50000 \
-v /tmp/docker/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
--env "TZ=Asia/Seoul" jenkins
왜 /var/run/docker.sock 파일을 컨테이너에 마운트하냐면 /var/run/docker.sock은 Docker 호스트와 컨테이너 간의 통신을 담당하는 소켓 파일로, 컨테이너가 호스트 시스템의 Docker 데몬과 상호작용할 수 있도록 해주기 위함이다. 이렇게 마운트를 해주면 실제로 Jenkins 컨테어니내부에서 Docker run을 하여 컨테이너를 띄우면 Host에서 똑같이 컨테이너가 띄워진다.
docker exec-it 명령어로 컨테이로 접속해보면 다음과 같이 Host와 Jenkins컨테이너에 같은 컨테이너들이 동작하는 모습을 확인할 수 있다.
root@dj-master:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b0ef9e18d6b9 jenkins "/usr/bin/tini -- /u…" 3 days ago Up 3 days 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins
aad953b3817a my-diary-3-mydiary-front "docker-entrypoint.s…" 2 weeks ago Up 7 hours 0.0.0.0:3000->3000/tcp rolling-front
6e56e0a7224a mysql:5.7-debian "docker-entrypoint.s…" 2 weeks ago Up 3 days 33060/tcp, 0.0.0.0:13306->3306/tcp rolling-db
root@dj-master:~# docker exec -it b0ef9e18d6b9 /bin/bash
root@b0ef9e18d6b9:/# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b0ef9e18d6b9 jenkins "/usr/bin/tini -- /u…" 3 days ago Up 3 days 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp jenkins
aad953b3817a my-diary-3-mydiary-front "docker-entrypoint.s…" 2 weeks ago Up 7 hours 0.0.0.0:3000->3000/tcp rolling-front
6e56e0a7224a mysql:5.7-debian "docker-entrypoint.s…" 2 weeks ago Up 3 days 33060/tcp, 0.0.0.0:13306->3306/tcp rolling-db
Jenkins 서버 접속


ngrok 활용 하여 GIthub web hook 설정
root@dj-master:~# mkdir ngrok && cd $_
root@dj-master:~# wget -c https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
root@dj-master:~# unzip ngrok-stable-linux-amd64.zip
root@dj-master:~# sudo snap install ngrok
https://ngrok.com/ 회원 가입하여 개인 token을 받고 사용해야 한다.
root@dj-master:~# ngrok config add-authtoken 3S3ld......
Authtoken saved to configuration file: /home/kevin/snap/ngrok/108/.config/ngrok/ngrok.yml
그리고 현재 컨테이너의 jenkins가 8080포트로 띄워져 있기 떄문에 ngrok http 8080 명령어를 사용해준다.
그러면 다음과 같이 창이 뜬다. https://ea4a-223-130-159-67.ngrok-free.app -> http://localhost:8080 이주소로 들어가면 이제 jenkins 사이트로 접속할 수 있다.

이 주소를 github webhook에 연결 시켜줄 것이다. 참고로 ngrok 창을 나가면 url이 사라지기 때문에 백그라운 명령어를 사용하던가 계속 키고 있어야한다.

그다음 "ngrok 주소/github-webhook/" 을 입력해주면된다. github webhook은 코드가 변경될 때마다 Jenkins 도구에서 자동 빌드와 배포가 이루어질 수 있도록 트리거 역할을 한다.
Github token, Docker hub token으로 Jenkins credential 생성

Jenkins 관리 → Manage Credentials → Stores scoped to Jenkins 에 있는 Jenkins 선택 → Global credentials → Add credentials
Jenkinsfile에서 입력할 값을 ID에 적어주고 비밀번호로는 Dockerhub. github 토큰을 입력해주면 된다. 그래야 Jenkinsfile을 통해 github저장소에 접근하여 소스코드를 가져오고 빌드한 후 Dockerhub에 생성한 이미지를 올릴 수 있다.
Jenkins Docker Pipeline 생성


다음은 New item에서 pipeline을 통해 프로젝트를 생성해준다. 자신의 소스코드를 올릴 github url를 입력해주고 Build Triggers로 "Github hook trigger for GITScm polling" 를 선택해준다. 다음으로 파이프라인 관련 설정을 해주면 된다.

"Pipeline script from SCM"을 선택한 뒤 SCM은 git을 선택한 후 리포지토리 주소를 입력하면 된다. 그럼 선택한 git 리포지토리에 "Jenkinsfile"을 파이프라인 스크립트로 사용하겠다는 의미를 갖는다. 자신의 branch에 맞게 설정해주면 된다.
다음으로 Jenkinsfile를 git 리포지토리로 올려주면된다.
Jenkinsfile
pipeline {
agent any
environment {
DOCKER_HUB_CREDENTIALS_ID = 'docker' // Docker Hub 자격증명 ID
GITHUB_CREDENTIALS_ID = 'github' // GitHub 자격증명 ID
DOCKER_IMAGE = 'dongjukim123/nodejs-ci' // Docker 이미지 이름
IMAGE_TAG = 'latest' // 원하는 이미지 태그
}
stages {
stage('Checkout') {
steps {
// Git 리포지토리에서 소스 코드 체크아웃
checkout([
$class: 'GitSCM',
branches: [[name: '*/main']],
userRemoteConfigs: [[
url: 'https://github.com/dongjucloud/nodejs-ci', // GitHub 저장소 URL로 대체
credentialsId: GITHUB_CREDENTIALS_ID
]]
])
}
}
stage('Build Docker Image') {
steps {
// Docker 이미지를 빌드하고 태그 추가
script {
sh "docker build -t ${DOCKER_IMAGE}:${IMAGE_TAG} ."
}
}
}
stage('Push to Docker Hub') {
steps {
// Docker Hub에 로그인하고 이미지를 푸시
script {
docker.withRegistry('', DOCKER_HUB_CREDENTIALS_ID) {
sh "docker push ${DOCKER_IMAGE}:${IMAGE_TAG}"
}
}
}
}
}
post {
always {
// 빌드가 완료된 후 작업 디렉토리에서 Docker 이미지를 정리
sh "docker rmi ${DOCKER_IMAGE}:${IMAGE_TAG} || true"
}
success {
echo 'Build and deployment succeeded!'
}
failure {
echo 'Build or deployment failed.'
}
}
}
//
이렇게 Jenkinsfile 설정을 끝낸 후 소스 코드를 변경하고 자신의 Repository에 올리면 파이프라인이 작동할 것이다.


Webhook이 정상 작동하여 Docker 이미지가 성공적으로 빌드되고 레지스트리로 푸시까지 완료되었다. Git 리포지토리의 소스코드는 /tmp/docker/jenkins_home/workspace 경로로 다운로드된 후 빌드 및 푸시 과정을 거친다.
root@dj-master:/tmp/docker/jenkins_home/workspace# ls
nodejs-ci nodejs-ci@2 nodejs-ci@2@tmp nodejs-ci@tmp 'nodejs example app'