Jenkins docker pipeline

목차 

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 서버 접속

cat /var/jenkins_home/secrets/initialAdminPassword 명령어를 실행해 표시되는 초기 관리자 비밀번호를 입력하고, 모든 플러그인 설치와 관리자 계정 생성이 완료되면 Jenkins 사이트에 정상적으로 접속할 수 있다 
 
그 후 Docker 이미지를 빌드하기위해선 "Docker pipeline" 플러그인이 필요하기 때문에 다음과 같이 플러그인 까지 설치해준다. 
 

ngrok 활용 하여 GIthub web hook 설정 

Github web hook 설정은 VM ip로 불가하기 때문에 ngrok을 활용하여 따로 임시 도메인을 등록해준다. 
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'