본문 바로가기
Web Development/MicroService Architecture

[MSA / Jenkins / Gitea] Webhook 이벤트 감지와 Jenkins 파이프라인을 통한 빌드/배포 자동화 방법 A to Z(feat. CI/CD)

by 감자맹고우 2023. 5. 3.
728x90
반응형

MSA 프로젝트를 진행하며, 코드를 수정하고 반영할 때마다 각각의 서비스를 수동적으로 다시 빌드하고 컨테이너로 올려야 하는 문제를 해결하기 위해, 빌드/배포를 자동화하도록 Jenkins를 도입하여 구축하게 되었다.

 

▼ 이를 위해 이전 글에서 Docker Desktop을 통해 Jenkins를 설치했다.

 

[MSA / Jenkins / Docker] Docker Desktop에 Jenkins 설치 방법 A to Z(feat. CI/CD)

MSA 프로젝트를 진행하면서, 여러 개의 서비스를 일일히 실행하고 관리해야하는 번거로움을 탈피하기 위해서, 계획과는 다르게 Docker를 빠르게 도입하게 되었다. 그리고, 한번에 실행하고 관리할

devlifetestcase.tistory.com

 

이제 설치한 Jenkins를 활용하여 CI/CD를 구축해볼 것이다.

먼저 단계를 세분화해보자.

0. Plugin 설치
1. Jenkins 파이프라인 생성
2. Jenkins 토큰 생성

3. Git 웹훅 생성
4. Jenkins 파이프라인 실행을 위한 Jenkins 컨테이너 내부 설정

 

위와 같이 진행하여 CI/CD를 구축할 것이며, 각각의 단계에서 상세히 설명하려고 한다.

테스트 프로젝트이기 때문에, CI/CD에서 배포 전 테스트 과정은 생략되었으니 참고 부탁드린다.

 

 

[ 구축 방법 ]

 

0. Plugin 설치

 

Jenkins에 접속하여 대시보드 왼쪽에 보면 Jenkins 관리 메뉴가 있다.

클릭하면 여러가지 메뉴가 있는데 그 중에서 Plugins를 선택하면 Jenkins Plugin을 관리할 수 있다.

Plugins 메뉴를 선택하면 아래와 같은 화면이 출력된다.

이 때, Available plugins를 선택하면 설치 가능한 플러그인을 검색할 수 있는데 아래와 같은 플러그인을 검색하여 설치해준다.

 

- 설치 목록 : Docker, Docker Pipeline, Gitea, Publish Over SSH

(만약, Gitea 외에 다른 git을 사용한다면 github 등을 검색해보면 되겠다. 검색 결과로 나오지 않으면 설치되었을 확률이 높다. Installed plugins 메뉴에서 설치가 되었는지 확인해보자)

 

 

1. Jenkins 파이프라인 생성

 

(1) 아이템 생성

우선 Jenkins는 Project나 서비스를 생성하는 것을, Item을 생성하는 것으로 진행한다.

Jenkins 대시보드에서 왼쪽에 보면 '새로운 Item' 이라는 항목을 볼 수 있다.

클릭해보면 아래와 같은 창이 출력된다.

 

 

말그대로 Item을 생성하는 창인데, 여기서 Item 명과 Item을 어떤 종류로 생성할 것인지 선택할 수 있다.

여러 가지 종류를 선택할 수 있지만, 나는 파이프라인을 선택했다. 선택한 이유는 자세히 몰라서 선택한 부분도 있지만, 파이프라인은 스크립트로 동작하고 스크립트를 따라서 순서대로 진행되기 때문에, 스크립트만 잘 짜면 내가 원하는 커스텀으로 그대로 진행할 수 있을 것 같아 선택했다.

 

그래서 Item명은 대충 test-service-pipeline과 같이 서비스명에 뒤에 pipeline을 붙여 지었고, Pipeline 타입을 선택하고 OK 버튼을 클릭하여 Item을 생성했다.

 

(※ 참고 : Copy from 부분은 생성한 Item이 있다면 Item명을 입력하여 기존 Item의 설정을 그대로 불러들이는데, 비슷한 설정의 Item을 생성할 때 매우 편리하다!)

 

 

(2) 파이프라인 설정

아이템을 생성했으니 이제 설정을 해야 한다. 설정만 완료하면 파이프라인 생성 작업은 완료된다.

 

 

먼저 필요한 항목만 간단히 살펴보자. 설명은 아이템에 대한 설명을 간단히 작성하는 공간이다.

예로, Deploy Test Service와 같이 Test Service를 배포하는 아이템이라고 작성할 수 있다.

 

그리고 연동할 GitHub 정보와 사용할 인증 토큰 정보, Script 등을 작성해주면 된다. Script는 별도로 작성할 것이기 때문에 제외하고 나머지를 작성한 최종 정보는 아래와 같다.

 

 

1. 'GitHub Project' 체크 > Jenkins Item에 연결한 프로젝트의 git 주소 입력 (github의 url을 예시로 적어보았다)
2. 'GitHub hook trigger for GITScm polling' 체크
: hook 트리거 발생 시에 빌드를 하겠다는 의미, 다른 옵션을 선택하여 일정 기간마다 빌드하는 등의 선택 가능
3. '빌드를 원격으로 유발' 체크
: 특정 주소를 호출하여 빌드를 원격으로 유발하겠다는 의미, 밑줄을 보면 호출할 주소 양식을 확인 가능
4. Pipeline script 작성
5. Apply & Save(저장)

 

이제 Pipeline Script를 작성해보자.

파이프라인 스크립트는 Jenkinsfile 의 작성과 동일한 것 같다. Dockerfile을 작성해보았다면 이해하기가 쉽다.

일단 최종 작성은 아래와 같다.

 

pipeline {
    agent any
    environment {
        DOCKER_REGISTRY = "localhost:5000"
        IMAGE_NAME = "test-service"
        IMAGE_TAG = "latest"
        GIT_REPO = "https://github.com/test123/test-service.git"
        SAVE_REPO = "/c/Users/Administrator/MSA/test-service"
        MAVEN_HOME = "'/c/Program Files/Apache/apache-maven-3.8.7'"
    }
    stages {
        stage('Remove Previous Image') {
            steps {
                sh "sudo docker stop ${IMAGE_NAME} || true"
                sh "sudo docker rm ${IMAGE_NAME} || true"
                sh "sudo docker rmi ${DOCKER_REGISTRY}/${IMAGE_NAME} || true"
            }
        }
        stage('Clone Git Repository') {
            steps {
                script {
                    if (!fileExists("${SAVE_REPO}")) {
                        sh "git clone ${GIT_REPO} ${SAVE_REPO}" 
                    }
                }
            }
        }
        stage('Build Image') {
            steps {
                sh "cd ${SAVE_REPO} && git pull && sudo ${MAVEN_HOME}/bin/mvn clean compile package"
                sh "sudo docker build -t ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} ${SAVE_REPO}"
            }
        }
        stage('Push Image') {
            steps {
                sh "sudo docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
            }
        }
        stage('Deploy Image') {
            steps {
                sh "sudo docker run -d --name ${IMAGE_NAME} --network test-network -p 1111:1111 ${DOCKER_REGISTRY}/${IMAGE_NAME}"
            }
        }
    }
}

 

pipeline 내용을 설명하겠다.

- agent any : 에이전트 노드 제한 없이 스크립트를 실행하겠다는 의미
- environment : 환경 변수 설정
- stages : 파이프라인 실행할 단계 설정
- stage : jenkins 빌드 시에 각 단계를 순서대로 실행, 아래 이미지처럼 jenkins에서 아이템의 대시보드에서 Stage View를 통해 각 단계별 진행 상태 확인 가능
- steps : 각 단계별 진행할 내용 작성
- sh : 쉘에서 실행할 명령어 작성

====================================================

0. Environment
본인의 환경에 맞게 작성, 이미지 명이나 Local Private Registry/DockerHub, Maven 설치 경로, git 주소 등 직접 작성해야 함

1. Remove Previous Image 단계
서비스가 이미 Docker Container에 올라가 있다면, 컨테이너를 멈추고 제거하고 도커 이미지를 삭제한다.

2. Clone Git Repository 단계
만약 로컬 위치에 서비스가 클론 되어 있지 않으면, git clone하여 로컬 경로에 저장

3. Build Image 단계
로컬 경로로 이동하고 git pull 진행, Maven 빌드로 프로젝트 빌드 진행하고, Docker 이미지로 빌드 진행

4. Push Image 단계
Docker 이미지를 registry에 push

5. Deploy Image 단계
Docker 이미지를 컨테이너로 올림. 이 때 옵션을 지정하는데, --network는 Docker-Compose 처럼 각 컨테이너들이 같은 네트워크를 사용하도록 설정하는 옵션이며, -p는 호스트포트와 컨테이너포트를 각각 지정해주는 옵션이다.

 

 

이렇게 하면, Jenkins 파이프라인 생성 작업은 끝났다.

 

반응형

 

2. Jenkins 토큰 생성

 

이제 Jenkins 토큰을 생성해보자. 이 토큰은 Git 웹훅 생성 시 필요하다.

아래와 같이 Jenkins에서 계정을 선택하고 설정으로 들어가서 API Token 항목에서 토큰을 생성할 수 있다. 과정에 따라 토큰을 생성하고 나면, 최초 1회에 한해 토큰값이 출력된다. 이 값을 복사해두자.

 

 

그리고 cmd 창을 켜서 아래와 같이 진행한다.

 

 

docker exec -itu 0 jenkins-server bash

 

위의 명령어를 입력하면 jenkins-server 컨테이너에 bash로 접속할 수 있다. 이 때, 일반 사용자로 접속하지 않고 u 옵션을 추가하여 root로 접속하게 하였다. 그리고나서 아래와 같이 입력한다.

echo -n "Jenkins ID:복사한 Token값" | base64

 

위 명령어를 입력하면 Base64로 인코딩된 값이 나오며, 최종적으로 이 값을 웹훅에 작성해줄 것이기 때문에 복사해준다.

 

반응형

 

3, Git 웹훅 생성

 

이제 웹훅을 생성할 차례이다. 웹훅은 Git에 이벤트가 발생했을 때, 정보를 전달하는 역할을 수행한다.

즉, Git에 push 같은 이벤트가 발생했다면 jenkins에 정보를 전달해준다. 웹훅은 git에서 생성할 수 있으며, 보통 레퍼지토리의 설정에서 생성할 수 있다.

 

github

 

gitea

 

위와 같이 github나 gitea에서 repository의 설정에 들어가보면 웹훅 메뉴가 있다. 여기서 웹훅을 추가해주면 된다.

근데 찾아보니 gitea와 github는 웹훅 설정 시 차이가 있다. 아래 이미지는 gitea의 웹훅 추가 화면인데, github에는 gitea와 다르게 Authorization Header를 설정하는 부분이 없다. 대신 github에서는 Secret 값을 설정하여 검증하는 것 같다.

 

 

나는 gitea로 진행했기 때문에 그대로 진행하겠다.

최종 작성 내용은 아래와 같다.

 

 

- 대상 URL : localhost 주소 대신 Jenkins를 설치한 본인의 IPv4나 서버의 IPv4와 Jenkins 포트를 적고, Jenkins 아이템명과 token 정보를 입력하면 된다. 이 정보는 위에서 1. Jenkins 파이프라인 생성 > (2) 파이프라인 설정 부분에서 작성하였으니 참고하면 된다.

- Authorization Header : 2. Jenkins 토큰 생성 부분을 진행하여 얻은 Base64 인코딩 값을 붙여넣기하여 작성하면 된다. 인코딩 값 앞에 'Basic ' 을 붙여 작성한다.

HTTP Method나 트리거 등 나머지는 본인의 의도에 맞게 작성하고 Webhook을 추가한다.

 

반응형

 

4. Jenkins 파이프라인 실행을 위한 Jenkins 컨테이너 내부 설정

 

할 것이 너무 많다... 그러나!

이제 마지막으로 파이프라인이 동작할 수 있도록, Jenkins 안에 프로그램 설치와 권한 설정만 해주면 된다.

해보자!

 

(1) docker mount 여부 확인

Docker Desktop을 설치했다면 Docker CLI가 자동으로 설치된다.

그렇기 때문에, 호스트 cmd 창에서 docker images 명령어를 실행하면, Docker 이미지 목록이 출력된다.

그렇다면, Jenkins 컨테이너에 호스트의 Docker가 마운트되었다면 Jenkins 컨테이너의 bash에서도 Docker 명령어가 정상 동작해야하며, 결과는 호스트의 결과와 같아야 한다.

docker exec -itu 0 jenkins-server bash

 

위의 명령어를 입력하여 jenkins 컨테이너의 bash에서 docker images 명령어를 동일하게 실행하여 마운트가 정상적으로 이루어졌는지 확인해보자. 오류가 발생하거나 결과가 동일하지 않다면 마운트가 정상적으로 이루어지지 않은 것이다.

아래와 같이 동작해야 한다.

 

 

(2)  docker 그룹 생성 & jenkins 유저 추가

jenkins가 파이프라인을 실행할 수 있도록 docker 그룹과 jenkins 유저를 추가해준다.

이를 위해 jenkins 컨테이너의 bash에 root로 접속한 상태에서 아래의 명령어를 순서대로 입력해보자.

groupadd docker
grep docker /etc/group
usermod -aG docker jenkins
grep docker /etc/group
docker restart jenkins-server

 

위의 명령어는 docker 그룹을 추가하고, docker 그룹을 확인하며, jenkins 유저를 추가하고 추가되었는지 다시 그룹을 확인하는 명령어이다. 그리고 마지막으로 jenkins-server를 재시작하여 결과가 반영되도록 하였다.

아래와 같이 동작해야 한다. 1001이란 숫자는 다를 수 있다. 중요한 것은 docker 그룹과 jenkins 유저의 추가 여부이다.

 

 

(3) 프로그램 설치

jenkins 파이프라인의 명령어들이 동작할 수 있게 프로그램을 설치해주어야 한다. 설치를 위해 먼저 update를 하고 설치해준다. 아래의 명령어를 하나씩 실행하여 설치한다.

docker exec -itu 0 jenkins-server bash
apt-get update
apt-get install sudo
apt-get install -y vim
(필요 시, jdk 설치. 기본은 openjdk 11버전) apt-get install -y openjdk-17-jdk

 

나는 java 17버전으로 프로젝트를 작성하였기 때문에 maven build 시에 jdk 17을 사용하도록 추가 설치해주었다.

 

(4) 권한 설정

jenkins 파이프라인을 그냥 실행하면 권한 문제가 발생한다.

권한을 부여하기 위해 sudo 명령어를 사용해야하는데 명령어를 사용하려면 비밀번호를 입력해야하고, 파이프라인이 실시간으로 비밀번호를 입력할 수는 없기에 jenkins가 sudo 명령어를 비밀번호 없이 사용할 수 있도록 설정해준다.

보안 문제를 고려하여 비밀번호 입력이 자동으로 되도록 비밀번호를 파이프라인의 어디에 적는 방법을 생각해볼 수 있는데 그것이 더 큰 보안 문제를 초래할 수 있기에, 위와 같이 처리하는 것을 결정했다.

 

sudo 명령어 사용 권한을 설정하기 위해서, sudoers.d 파일을 수정한다. 이를 위해 vim을 위에서 설치했다.

vi /etc/sudoers

 

sudoers.d 파일이 열리면 #Allow members of group sudo to execute any command 아래에 jenkins를 추가해준다.

jenkins ALL=(ALL) NOPASSWD: ALL

위 내용을 추가해주면 된다.

수정된 파일 내용은 아래와 같다.

 

 

(5) JDK의 PATH 및 환경 변수 설정

JDK를 설치하였다면 기존 설치된 openjdk 11버전 대신 17버전을 사용하도록 PATH와 JAVA_HOME을 설정해주어야 한다.

 

export PATH=/usr/lib/jvm/java-17-openjdk-amd64/bin:$PATH
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

 

기존 PATH에 17버전의 경로를 맨 앞으로 붙여, 우선순위를 통해 17버전이 사용되도록 한다.

아래와 같이 동작해야 한다.

 

 

echo 명령어로 설정 결과를 확인할 수 있다.

 

(6) docker network 생성

위에서 pipeline script를 작성할 때 --network로 마이크로서비스 컨테이너가 test-network를 사용하도록 해주었다. 이는 각 컨테이너들이 같은 네트워크에서 동작하도록 옵션 설정을 해준 것이다.

그러나 실제 docker network는 생성해주지 않았다. 그렇기 때문에 미리 network를 생성해주는 작업이 필요하다.

(Docker-Compose 기능을 구현할 때는 network를 선언하여 자동 생성해주었다)

 

어려운 작업은 없고, 아래 명령어를 실행하면 된다.

docker network create test-network

 

이렇게 네트워크까지 생성해줬다면, 모든 설정이 완료되었다!

 

반응형

 

이제 만들었으니, 잘 돌아가는지 확인해보자.

만들어놓은 gitea의 웹훅에 들어가보면 아래쪽에 '전달 시험' 버튼이 있다.

버튼을 클릭하여 웹훅에서 jenkins로 전달이 잘되는지 살펴보고, 전달이 잘 되었다고 초록색 체크 표시가 뜨면, jenkins에서는 빌드가 잘 진행되는지 확인해보면 된다.

 

gitea 웹훅

 

jenkins 아이템 stage view

 

이렇게 결과가 초록 초록 하다면, 구축을 성공적으로 완료한 것이다!

 

=============================================================

[ Jenkins Pipeline Error 목록 ]

 

1. permission denied while trying to connect to the Docker daemon socket at

권한 문제입니다. 위의 '4. Jenkins 파이프라인 실행을 위한 Jenkins 컨테이너 내부 설정' 과정을 참고해주세요.

2. Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

마운트가 잘못되던지, 권한 등 기타 설정이 잘못되었을 것입니다. '4. Jenkins 파이프라인 실행을 위한 Jenkins 컨테이너 내부 설정' 과정을 참고해주세요.

 

 

🤞 도움이 되셨기를 바랍니다. 한 번의 클릭과 댓글은 어딘가의 누군가에게 진실로 큰 힘이 됩니다. 🐱‍🏍

 

728x90
반응형

댓글