[DevOps] Jenkins를 통한 CI/CD 구축기 2편 (Backend CI/CD 구축)

2022년 07월 17일

TOC

🔗 연관 게시글

이전 포스트에서는 젠킨스의 설치에 대해 다루어 봤다. 이번 포스트에서는 본격적으로 젠킨스를 통한 CI/CD 구축 과정을 다뤄보려고 한다.

Untitled

팀에서 구축하고자 하는 CI/CD 구조는 다음과 같다. 현재 포스트에서는 5번 프론트엔드 프로젝트의 빌드 과정을 제외한 1번에서부터 6번까지의 구축 과정을 담아보았다.

Jenkins의 Item

젠킨스에서 하나의 CI/CD 프로젝트를 구축하기 위해서는 아이템(Item)을 생성하여야 한다. 하나의 젠킨스 서버에 여러개의 아이템을 만들 수 있고 각각의 아이템들은 개발자가 설정하는 것에 따라 다르게 동작한다. 젠킨스에서 아이템을 만드는 방법은 대표적으로 FreeStyle과 Pipeline이 존재한다. 과거에는 Freestyle 방법을 많이 사용하였다고 한다. 하지만 요즘은 Freestyle보다는 Pipeline을 많이들 사용하고 있다. 두 방법의 장단점을 비교해보자.

Freestyle vs Pipeline

Freestyle

  • 장점

    • 웹 기반의 GUI를 통해 여러 플러그인을 쉽게 사용할 수 있다.
  • 단점:

    • CI 파이프라인에 변경 사항을 만들기 위해서는 젠킨스에 로그인해 각각의 프리스타일 잡의 설정을 변경해야만 한다.
    • CI/CD의 과정을 콘솔을 통해서만 확인할 수 있다.
    • 각각의 과정들을 한번에 보기 어렵다.

Pipeline

  • 장점:

    • 파이프라인은 코드로 프로젝트 설정을 할 수 있어 프리스타일과 다르게 젠킨스 웹에 직접 접근하지 않아도 설정 변경이 가능하다. (웹을 통한 설정도 가능하다.)
    • CI/CD 파이프라인 설정을 하나의 스크립트 파일(Jenkinsfile)로 프로그래밍을 통해 할 수 있다.
    • Freestyle과 다르게 하나의 스크립트 파일(Jenkinsfile)을 통해 흐름 제어를 할 수 있다.
    • 일반 코드처럼 버전 관리가 가능하게 된다.
    • 아래와 같이 GUI를 통해 현재 어떤 과정을 진행중이고 평균적으로 얼마만큼의 시간이 걸렸는지 통계와 피드백을 준다.

    Untitled

  • 단점:

    • 프리스타일과 다르게 스크립트를 짜야하는 번거로움이 있다. 즉, 파이프라인 구축을 위해서는 스크립트 문법을 학습하여야 한다.

우리 팀은 두 방법 모두 사용을 해보았으나 Pipeline이 하나의 스크립트를 통해 전체 흐름을 알 수 있기에 더욱 관리가 편하고 GUI를 통한 진행과정을 피드백을 받을 수 있다는 장점이 있어 최종적으로 Pipeline을 사용하기로 하였다.

이제 Pipeline 프로젝트를 구축한 과정을 다뤄보겠다.

Jenkins Pipeline 구축하기

1. CI 구축하기

1.1. 아이템 생성

Jenkins에 접속 후 아이템 생성을 클릭하면 아래와 같은 페이지가 나온다. 생성하고자하는 아이템의 이름을 입력 후, Pipeline을 선택하여 아이템을 생성하겠다.

Untitled

아이템을 생성하면 아래와 같이 생성한 파이프라인의 설정 페이지로 넘어갈 것이다. 우선 앞의 설정들은 건너 뛰고 Script 쪽으로 넘어가보겠다.

Untitled

1.2. 스크립트 작성 방법

앞서 파이프라인 방법은 스크립트를 프로그래밍하여 일련의 과정을 세팅한다고 하였다. 젠킨스의 파이프라인은 스크립트를 설정하는 2가지 방법을 제공하고 있다.

Untitled

  1. 젠킨스 웹 내에서 스크립트를 작성하여 관리 → Pipeline Script(default)
  2. 프로젝트 내에서 Jenkinsfile에 스크립트를 작성하여 관리 → Pipeline Script from SCM

우리는 일단 1번 방법을 사용하여 파이프라인 구축을 하고 추후 2번 방법으로 변경해보려 한다.

1.3. 스크립트 문법 종류

Pipeline Script는 2가지 타입으로 작성이 가능하다. 두 방법 모두 Groovy문법 기반이며 둘의 차이는 아래와 같다.

  • Declarative Pipeline : 쉽게 작성이 가능하며 Groovy 문법 기반이지만 해당 문법을 몰라도 작성 가능하다고 한다. (최상단에 pipeline 이라고 되어 있으면 declarative 문법으로 작성된 것이다.)
  • Scripted Pipeline : Groovy 문법 기반이며 Declarative 보다 효과적이고 많은 기능으로 포함해서 작성 가능한데 어렵다. (최상단에 node 지시어가 있으면 scripted 문법으로 작성된 것이다.)

두 문법을 혼합해서 사용할 수는 없으며 상세한 내용은 공식 문서를 참고하길 바란다.

Pipeline Syntax

1.4. 스크립트 작성하기

스크립트 작성에 대한 방법은 다루게 된다면 해당 포스트의 글이 너무 길어지기에 이번 포스트에서도 다루지 않겠다. 이번 포스트에서는 각각의 스크립트가 어떤 작업을 하는지만 설명하겠다. 스크립트 작성법에 대해 학습하고 싶다면 위의 공식문서 또는 다른 블로그들을 참고하길 바란다.

pipeline {
    agent any

    tools {
        gradle 'gradle'
    }

    stages {
        stage('Git Clone') {
            steps {
                git branch: 'develop-be', url: 'https://github.com/woowacourse-teams/2022-momo.git'
            }
        }
        stage('BE-Build') {
            steps {
                dir("./backend") {
                    sh "./gradlew clean build"
                }
            }
        }
    }
}

위의 스크립트에서는 Git Clone단계에서 Git에 올라가있는 프로젝트의 develop-be 브랜치를 클론을 하도록 하였다. 그 후 BE-Build 단계를 통해 클론한 프로젝트의 backend 디렉터리로 이동 후 프로젝트 빌드를 통한 CI 작업을 진행하도록 하였다.

이와 같은 설정을 하면 CI 구축은 완료한 것이다. 지금까지 설정한 파이프라인을 실행해보겠다.

생성한 파이프라인으로 들어가 좌측에 있는 메뉴바에서 지금 빌드를 하게된다면 아래와 같이 Git Clone으로부터 Build까지 CI 과정이 정상적으로 동작하는 것을 확인할 수 있다.

Untitled

Untitled

2. CD 구축하기

이제 CI 작업을 마쳤으니 CD 작업을 진행해보도록 하겠다.

2. 1. SSH 를 위한 플러그인 설치하기

🚨 Publish Over SSH 접속 불가 이슈

구글에서 Jenkins Pipeline 생성을 다룬 게시글들을 검색하여 찾아보면 스프링이 실행될 운영서버 EC2의 접근을 위한 SSH 플러그인으로 Publish Over SSH를 사용하는 것을 볼 수 있었다. 하지만 Publish Over SSH 플러그인은 현재 아래 링크의 설명에서 볼 수 있듯이 최신의 openssh 버전이 ssh-rsa 방식의 암호화 키를 비활성화 시킨 것을 알 수 있다. 우리의 운영서버 EC2로의 접근을 하기 위한 pem는 rsa 방식으로 암호화되었기에 해당 플러그인에서 연결이 불가능하다는 것을 확인하여 다른 플러그인을 사용하기로 결정했다.

(우테코 크루인 루키는 다른 암호화 방식으로 접속키를 암호화하여 사용하였더니 해당 플러그인을 정상적으로 이용할 수 있었다고 한다.)

Failed to connect and initialize SSH to the Remote Server (Tried All the Possible Ways ) · Issue #247 · jenkinsci/publish-over-ssh-plugin

Publish Over SSH를 사용하지 않고서도 운영서버로 접속할 수 있는 방법은 현재 생각나는 방법으로는 아래와 같은 방법이 있다.

  1. Jenkins 서버에 pem 키를 보관하고 리눅스 명령을 통해 접속하는 방법
  2. 다른 SSH Plugin을 사용해 접속하는 방법

두 방법중 우리 팀은 다른 SSH Plugin을 사용하는 방법을 택하였다. 우리가 사용한 플러그인은 SSH Agent이다. 설치 방법은 아래와 같다.

📌  Jenkins 관리 → Plugin Manager → 설치 가능 앱 → SSH Agent 검색 및 설치

2.2. AWS 접속정보 추가하기

우리가 PC에서 AWS EC2에 접속을 할 때는 ssh -i {pem key 이름}.pem ubuntu@{ip주소}와 같은 명령어를 통해 pem 키를 통해 접속하였다. Jenkins 서버에서 운영서버로 접속을 할 때도 접속 키가 필요하기에 해당 키를 먼저 Jenkins에 등록해주겠다.

📌 Jenkins관리 → ManageCredential

Untitled

📌 jenkins에 접속

Untitled

📌 add credentials

Untitled

credential 설정은 아래와 같이 kind는 SSH Username with private key를 선택 후 아래와 같이 인증 정보를 입력해주면 된다.

key에는 pem 키 정보를 입력해주면 된다. 아래의 명령어를 통해 나오는 키 값을 -----BEGIN RSA PRIVATE KEY----- 부터 -----END RSA PRIVATE KEY-----까지 모두 복사해 입력하면 된다.

cat {key 이름}.pem

// 결과
-----BEGIN RSA PRIVATE KEY-----
{키 값들}
-----END RSA PRIVATE KEY-----

Untitled

2.3. 스크립트 작성하기

앞서 CI 스크립트에 이어서 작성한 스크립트는 아래와 같다.

pipeline {
    agent any

    tools {
        gradle 'gradle'
    }

    stages {
        stage('Git Clone') {
            steps {
                git branch: 'develop-be', url: 'https://github.com/woowacourse-teams/2022-momo.git'
            }
        }
        stage('BE-Build') {
            steps {
                dir("./backend") {
                    sh "./gradlew clean build"
                }
            }
        }
        stage('Deploy') {
            steps {
                sshagent(credentials: ['aws_key']) {
                    sh '''
                        ssh -o StrictHostKeyChecking=no ubuntu@192.168.xxx.xxx uptime
                        scp /var/jenkins_home/workspace/Momo/backend/build/libs/momo-0.0.1-SNAPSHOT.jar ubuntu@192.168.xxx.xxx:/home/ubuntu/momo

                        ssh -t ubuntu@192.168.xxx.xxx ./deploy.sh
                    '''
                }
            }
        }
    }
}

운영 서버로의 배포는 앞서 설치한 sshagent 플러그인을 이용해 진행하였다. Deploy 과정에서 진행한 내용은 다음과 같다. 먼저 젠킨스 서버에서 운영 서버에 접근할 수 있도록 StrictHostKeyChecking를 비활성화 시켜준다. 그 후 scp를 통해 빌드된 jar파일을 운영 서버로 전송시킨 후, 운영 서버에 미리 작성한 deploy.sh 파일을 실행하여 빌드된 파일을 실행시켜준다.

deploy.sh파일의 내용은 아래와 같다.

#!/bin/bash
pid=$(pgrep -f momo)

if [ -n "${pid}" ]
then
        kill -15 ${pid}
        echo kill process ${pid}
else
        echo no process
fi

chmod +x ./momo/momo-0.0.1-SNAPSHOT.jar
nohup java -jar ./momo/momo-0.0.1-SNAPSHOT.jar >> application.log 2> /dev/null &

지금까지 설정한 파이프라인을 실행하면 아래와 같이 Git Clone-Build-Deloy의 과정을 자동으로 실행되는 것을 확인할 수 있다.

Untitled

3. Webhook 등록하기

지금까지 Jenkins에서 CI/CD작업을 하는 하나의 Pipeline을 만들었다. 하지만 지금까지 작업한 내용은 Jenkins에서 직접 빌드를 하여야만 실행이 된다. 즉, CI/CD 파이프라인은 만들었으나 해당 작업업의 자동화는 등록되지 않은 것이다. 이번 단계에서는 Github의 Webhook을 통해 프로젝트 레포지토리에 푸쉬가 일어났을 때 자동으로 CI/CD가 동작하도록 만들어보려한다.

먼저 앞서 만든 젠킨스 파이프라인에 접속하여 설정 창에서 GitHub hook trigger for GITScm polling 을 활성화해준다. 해당 옵션을 활성화해주면 github에서 webhook이 들어왔을 때 자동으로 파이프라인이 빌드된다.

📌  {앞서 만든 파이프라인 아이템}접속 → 구성 → Build Triggers → GitHub hook trigger for GITScm polling 활성화

Untitled

이제 Github 프로젝트 레포지토리에 접속하여 Webhook을 만들어보겠다. 프로젝트 레포지토리의 Setting에 들어가면 Webhook 목록을 찾을 수 있다. Webhook에 들어가서 Add webhook을 클릭해보자.

📌  Setting → Webhook → Add webhook

Untitled

Payload URL에는 https://{젠킨스 접속 주소}/github-webhook/의 값을 입력하고 Content type은 application/json 을 선택해준다. webhook을 trigger할 선택은 3가지 선택지가 있는데 우리는 Just the push event로 설정해두고 Webhook을 추가해보겠다.

Untitled

이렇게 Webhook 등록이 끝났다.

Untitled

이제 PR이 머지되면 Github에서 Webhook을 날려서 아래와 같이 CI/CD작업이 자동으로 실행되게 되었다.

Untitled

마무리

지금까지 프로젝트를 진행하며 젠킨스를 통한 CI/CD작업을 정리해봤다. 현재는 백엔드가 아직 MySQL과 같은 DB연결을 하지 않고 인메모리 형태인 H2 DB를 사용하고 있어서 단순히 스프링 프로젝트를 운영서버에 배포 및 실행하는 단계까지만 진행하였다. 앞으로는 CI/CD작업을 하며 DB 인스턴스(EC2)를 연결하는 과정 및 프론트의 CI/CD작업을 추가해보려 한다.

Reference

Buy me a coffeeBuy me a coffee
Written by

@Seongwon

기술공유를 통해 새로운 가치 창조을 추구하는 백엔드 개발자 오성원입니다.
©SeongwonOh