[CI/CD] Jenkins로 배포 자동화하기 (feat. Docker)

General Dong·2024년 10월 23일
0

CI/CD

목록 보기
2/12
post-thumbnail

Jenkins 배포 환경 : Amazon Linux, Java 17, Git, Docker, Gradle

배포 자동화 아키텍처

  1. Github Repository의 main branch에 merge가 되면 GitHub Webhook을 통해 Jenkins에게 알린다.
  2. Jenkins는 다음과 같은 동작을 수행한다.
    • Git clone -> Gradle로 Spring Boot 프로젝트 Build -> Docker Image화 -> GHCR (GitHub Container Registry)에 Push -> WAS 서버에 원격 접속 -> GHCR로부터 Pull -> Docker Container Run

WAS와 원격 연결

먼저 원격으로 서버에 배포를 하기 위해서는 SSH 프로토콜로 접근해야 한다.
또한 GitHub에서 Webhook을 받기 위해서 플러그인이 필요하다.
해당 작업들을 도와주는 플러그인을 설치해보자.

처음에는 Publish over SSH 플러그인을 사용하려 했으나, 보안 문제로 인해 이제는 Jenkins에서 지원을 안 해준다고 한다.

플러그인 설치

Dashboard -> Jenkins 관리 -> Plugins -> Available plusgins 순으로 들어가 SSH Agent, GitHub IntegerationGeneric Webhook Trigger를 검색하여 설치하자.

SSH로 접근할 정보 입력

Dashboard -> {item name} -> Pipeline Syntax로 이동한 다음 Sample Step 항목에서 sshagent: SSH Agent를 선택하고 아래 +Add 버튼을 클릭하고 Jenkins를 클릭한다.

  • Kind : SSH Username with private key 선택
  • Scope : Global로 선택
  • ID : 해당 Credential을 식별할 값
  • Username : 해당 서버에 접근할 계정명 (Amazon Linux는 ec2-user / 우분투는 ubuntu가 디폴트)
  • Private Key Enter directly : 체크
  • Key : ssh Private Key(.pem) 추가

연결하기 전에 인바운드 규칙에 22번(SSH) 포트에 접근을 Jenkins IP도 허용해야한다!!

배포용 Pipeline 생성

Build 설정

Jenkins에서 GradleSpring Boot 프로젝트를 Build해서 배포할 계획이다.

Dashboard -> Jenkins 관리 -> Tools -> Gradle installations -> Add Gradle

  • name : 마음대로 설정
  • Version : Spring Boot 프로젝트와 버전을 맞춰준다!

설정이 끝나면 Save 버튼을 눌러 저장하면 된다!

Credential 생성

Dashboard -> Jenkins 관리 -> Credentials -> System -> Global credentials (unrestricted)에서 + Add Credentials 버튼을 클릭한다.

  • Username : GitHub 아이디
  • Password : GitHub Access Token
  • ID : 해당 자격증명을 식별할 용도로써 원하는 이름을 지정해주면 된다.

다 작성했으면 Create 버튼 누르기

Jenkins Project 생성

+ 새로운 Item을 클릭해준다.

item 이름을 설정해주고 Pipeline을 선택한 후 OK를 클릭해준다.
item을 생성하면 해당 item의 Configuration로 넘어갈 것이다.

이제 Configuration을 설정해보자!

GitHub project를 체크하고, 배포하려는 GitHub Repository URL을 적어준다.

우리는 GitHub Webhook 이용할 것이기 때문에
Build Triggers에서 GitHub hook trigger for GITScm polling을 체크해준다.
아래 Pipeline 부분으로 이동해준다.

나는 Spring Boot 프로젝트 내에 Pipeline Script를 작성할 것이다.

  • Definition : Pipeline script from SCM
  • SCM : Git으로 설정
  • Repository URL : 배포하려는 GitHub Repository URL
  • Credentials : 위에서 만든 Credential을 설정
  • Branch Specifer : 트리거 받을려는 브랜치
  • Script Path : .jenkinsfile 위치 (프로젝트의 루트에 위치할 경우 파일명.jenkinsfile로 설정)

특정 브랜치에 대해서만 동작시키고 싶으면 여기를 확인해주세요!

Jenkinsfile 작성

프로젝트에서 Script Path에 설정한 경로에 jenkinsfile을 작성하면 된다!

Jenkins에서 환경 변수를 사용하고 싶으면 Dashboard -> Jenkins 관리 -> System로 이동한 다음 Global properties에서 Environment variables를 체크하여 환경 변수를 추가하면 된다.

pipeline {
    agent any

    environment {
        VERSION = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
        DOCKER_IMAGE = "${DOCKER_REGISTRY}/test:${VERSION}"
    }

    stages {
        stage ('Git clone') {
            steps {
                git branch: 'main', credentialsId: 'github-access-token',
                url: ${GITHUB_REPO}
            }
        }

        stage ('Gradle Build') {
            steps {
                sh '''
                    chmod +x gradlew
                    ./gradlew clean bootJar
                '''
            }
        }

        stage ('Docker Image Build') {
            steps {
                sh '''
                    docker rmi -f \$(docker images -q) || true

                    docker build -t test .
                    docker tag test ${DOCKER_IMAGE}

                    export CR_PAT=${GITHUB_TOKEN}
                    echo \$CR_PAT | docker login ghcr.io -u ${GITHUB_ID} --password-stdin

                    docker push ${DOCKER_IMAGE}
                '''
            }
        }

        stage ('Deploy') {
            steps {
                sshagent (credentials: ['위에서_만든_SSH_Credential_ID']) {
                    sh '''
                        ssh -o StrictHostKeyChecking=no ${API_SERVER_USER}@${API_SERVER_IP} "docker login ghcr.io -u ${GITHUB_ID} --password ${GITHUB_TOKEN}"

                        ssh -o StrictHostKeyChecking=no ${API_SERVER_USER}@${API_SERVER_IP} "docker pull ${DOCKER_IMAGE}"

                        ssh -o StrictHostKeyChecking=no ${API_SERVER_USER}@${API_SERVER_IP} "docker stop test || true"
                        ssh -o StrictHostKeyChecking=no ${API_SERVER_USER}@${API_SERVER_IP} "docker rm test || true"
                        
                        ssh -o StrictHostKeyChecking=no ${API_SERVER_USER}@${API_SERVER_IP} "docker rm test || true"

                        ssh -o StrictHostKeyChecking=no ${API_SERVER_USER}@${API_SERVER_IP} "docker run -d --name test -p 8080:8080 ${DOCKER_IMAGE}"
                    '''
                }
            }
        }
    }
}

Jenkins 와 GitHub 연결

이제 GitHub Repository의 main 브랜치가 변경될 때마다 자동으로 배포가 될 수 있게 만들어보자!

GitHub Repository Token 생성

GitHub 로그인 후 본인 계정 페이지에서
Settings -> Developer settings -> Personal access tokens -> Tokens (classic) -> Generate new token -> Generate new token (classic)
순서대로 들어가 토큰을 생성한다.

  • Note : Token의 이름 (마음대로 짓기)
  • repoadmin:repo_hook은 꼭 체크하기!!
  • 그 외 필요한 권한과 유효 기간을 원하는대로 설정한 뒤 생성하면 된다!

생성된 토큰값은 해당 페이지를 나가면 다시 볼 수 없으므로 다른 곳에 저장해두길 바란다.

GitHub Webhook

Webhook을 받을려는 GitHub Repository -> Settings -> Webhooks -> Add webhook

  • Payload URL : http://{Jenkins 서버 IP}:{Jenkins Port}/github-webhook/ (/ 빠지면 안됨!!)
  • Content type : application/json
  • Secret : 발급 받은 GitHub Access Token
  • SSL verification : Jenkins 서버가 HTTPS가 적용되었다면 Enable로 해주고 Payload URLhttps로 변경해주면 된다!
  • Push EventTrigger로 받을 것이다!

나는 테스트 용도로만 하는 것이기에 HTTPS를 적용하지 않았다.

아마 별다른 보안 그룹을 설정하지 않았으면 ping 요청부터 실패했을 것이다.
아래 링크를 통해 Jenkins 서버의 보안 그룹을 수정하자!
GitHub Webhook 실패 시 참고!

Jenkins에 GitHub Token 등록

Dashboard -> Jenkins 관리 -> System

Add GitHub Server를 클린한 다음 GitHub Server를 클릭하자.

+Add를 클릭한 다음 Jenkins를 클릭하면 아래와 같은 창이 나온다.

  • Kind : Secret Text로 설정
  • Secret : 위에서 발급 받은 GitHub Token
  • ID : 해당 자격증명을 식별할 용도로써 원하는 이름을 지정

다 작성했으면 Add 버튼을 눌러 추가하고, 이전 창의 Credentials을 방금 만든 ID로 설정하고 저장하면 된다!

GitHub Webhook 동작 확인

Spring Boot 서버로 접속하면 페이지가 뜨는 것을 확인할 수 있었다!


소감

처음에는 Jenkins를 Docker Image로 받아서 사용했는데, Docker 컨테이너 내부에서 Docker 명령어를 사용할 수 없어서 로컬에 다시 설치해서 해결했다...

그 외에도 Linux 명령어를 잘 모르고, Pipeline도 처음 작성하다 보니까 수많은 에러를 많이 만난 것 같다.

확실히 설계까지의 과정이 오래 걸리긴 하지만, GitHub에 Push만 하면 자동으로 배포해줘서 진짜 편리하다.


참고

Jenkins/Nginx로 무중단 배포 하기 1편 | HYK
[DevOps] Jenkins를 통한 CI/CD 구축기 2편 (Backend CI/CD 구축) | Seongwon.dev
[CI/CD] Jenkins와 GitHub 연동하기 | rungoat.log
Jenkins pipeline + Docker SpringBoot프로젝트 SSHAgent로 배포하기(+Slack Notification) | devdo
[AWS] EC2 Jenkins -(2) Github와 연동, 자동 빌드 적용하기 | yunSeok
docker image를 삭제하는 다양한 방법들 | soonbee

profile
개발에 대한 기록과 복습을 위한 블로그 | Back-end Developer

0개의 댓글