Jenkins 도입기

Corca
Corca
Published in
8 min readJul 5, 2023

Written by: 설지원(Back-end Engineer)

안녕하세요! 저는 코르카에서 Back-end Engineer로 일하고 있는 설지원입니다!

지속적 통합(Continuous Integration, CI)과 지속적 배포(Continuous Delivery, CD)를 위한 도구로 젠킨스를 이용했는데, 오늘 그 과정을 소개하려고 합니다!

도입 배경

그동안 Github actions를 사용하여 CI/CD를 진행했었는데 각각 develop, main branch 머지 기준으로 완전히 자동화 되어있었습니다. 그중 CD에서 특히 production 배포는 매우 중요한 과정인데, 사용자에게 보여질 어플리케이션이 배포되는 과정이기 때문이죠!

CD를 main branch 병합 기준으로 하니 몇 가지 문제가 있었어요.

  1. 어느 시점에 배포가 성공적으로 되었는지 알기가 힘들었고,
  2. Hotfix를 하다가 발생하는 실수 등 우리가 잘못한 커밋이 포함되어 자동으로 배포에 이어지는 과정을 막고 싶었습니다.
  3. 코르카는 NX를 이용하여 mono repo로 하나의 repo 안에서 여러 개의 pacakage들을 다루고 있기 때문에 NX를 이용하여 affected된 package만 자동으로 빌드 및 배포하고 있어요. 특정 언어를 사용한 package의 경우에 affexted 되었는지 잘 잡지 못하는 경우도 있었습니다.

위 이유들과 더불어 Production 배포는 중요한 과정이고, 개발자 뿐 아니라 Product Manager 등 다른 팀과도 협의하여 진행하는 과정이기 때문에 CD를 할 수 있는 trigger 버튼만 만들어놓고 버튼을 누르는 시점은 수동으로 결정하고 싶었습니다!

Github actions에서는 CI를 원래대로 하고, CD 과정은 다른 곳에서 trigger 할 수 있도록 하기 위해 여러 가지 툴들을 검토했습니다. 그 중 Jenkins가 초기 서버 설치에 대한 코스트만 감수하면 무료이고 오픈 소스인데다 생태계가 커서 가장 활용하기 좋을 것으로 판단했습니다!

Installing Jenkins

가장 먼저 젠킨스 설치를 해야 했는데, 어느 정도 사양의 머신 위에 어떻게 서버를 설치할지 결정해야 했어요.

먼저, 도커를 사용하여 설치하기로 했습니다. 젠킨스를 돌리기 위해 JDK 설치, 포트 설정, 방화벽 설정 등 복잡한 과정이 존재했는데 도커를 사용하면 해당 컨테이너 실행을 위해 필요한 환경이 다 Setup 되어었기 때문이죠.

참고하실 수 있게, 젠킨스 공식 문서의 Prerequisites를 가져왔어요!

Prerequisites

  • Minimum hardware requirements:
    256 MB of RAM, although more than 2 GB is recommended
    10 GB of drive space (for Jenkins and your Docker image)
  • Recommended hardware configuration for a small team:
    4 GB+ of RAM
    50 GB+ of drive space

우리는 AWS EC2 t4g.micro에 Ubuntu 22.04(ARM64), 50GiB 스토리지를 구성했습니다.

Install Docker

도커로 젠킨스 설치를 할 것이니 도커부터 우분투에 설치합니다.

Run Jenkins

https://hub.docker.com/r/jenkins/jenkins

위 이미지를 사용했습니다!

docker pull jenkins/jenkins:lts-jdk11

Jenkins image를 pull 받았으니 docker run만 하면 됩니다. 아래와 같은 docker-compose 파일을 통해 더 편리하게 run 합니다.

  • Volumes: 볼륨 마운트를 통해 도커 컨테이너의 데이터가 날아가지 않도록합니다.
  • Ports: 포트 매핑을 통해 호스트로 들어온 요청을 컨테이너 내부로 포워딩해줍니다.
  • Environment: TZ 환경 변수를 넣어 우리가 원하는 타임존으로 설정합니다.
version: "3"
services:
jenkins:
image: jenkins/jenkins:lts-jdk11
user: root
volumes:
- ./jenkins-data:/var/jenkins_home
ports:
- 8080:8080
environment:
- TZ=Asia/Seoul
docker-compose up -d

설치 후 Unlocking 과정이 있습니다.

먼저 젠킨스가 설치된 서버의 ip, port 주소(ex. localhost:8080)를 통해 접근하면 Unlock Jenkins 페이지를 볼 수 있는데 docker logs <container name> 를 통해 초기 생성된 비밀번호를 확인하여 넣으면 됩니다!

다음으로 Install suggested plugins를 선택하고 관리자 계정을 생성하면 끝이 납니다.

Controller & Agent

젠킨스에서는 controller node에서 빌드가 이뤄지는 것을 추천하지 않고 있습니다. 실제로 controller node에서 빌드까지 했을 때 CPU 부하로 인해 Jenkin 메인 대시보드도 접근이 어려워 불편을 겪은 사례도 있다고 하더라구요!

따라서 분산하여 controller는 각 Agent 및 빌드를 관리하기만 하고, Agent가 실제 빌드를 수행하도록 했습니다.

조금 전까지 설정해둔 인스턴스를 controller로 사용하고 Agent를 위해 새로운 우분투 인스턴스를 생성했습니다.

Jenkins Dashboard에서 pem key와 Agent를 등록합니다!

  • Manage Jenkins → Manage Credentials Add Credentials
    여기에 ssh로 접속하기 위해 필요한 pem key를 등록하는데, 이때 username도 신경 써야 하는데 접속 가능한 user로 해야 합니다.
  • Manage Jenkins → Manage Nodes and clouds → New node
    Number of executors는 1로 하고, Remote root directory는 /home/ubuntu로 했습니다. label은 사용 용도에 맞게 달아주면 됩니다. Launch agents via SSH로 하여 Agent host 정보를 입력해주고, 위에서 생성한 Credentials를 연결해줍니다. Host Key Verification Strategy는 Manually trusted key Verification Strategy로 했습니다.

이렇게 하면 Agent successfully connected and online로 정상적으로 Agent가 연결된 것을 볼 수 있습니다.

이제 controller에서 pipeline 스크립트를 쓰고 빌드를 시작하면 Agent에서 빌드가 실행되는 것을 볼 수 있습니다. 우리는 Agent에 라벨을 달고, pipeline을 아래와 같이 구성하여 해당 라벨의 Agent에서 빌드가 이루어지도록 했습니다.

pipeline {
agent {
label '<label-name>'
stages {
...
}
}

우리는 dev-cd, prod-cd 파이프라인을 젠킨스에 만들어두었습니다.

Dev 환경 배포는 feature branch → develop branch 머지 시점에 자동화하기로 했지만, dev-cd 파이프라인은 특별한 상황에서 trigger하는 것이 필요한 경우에 사용합니다.

prod 환경 배포는 develop branch → main branch 머지가 된 후 내부적으로 검토를 마치고 젠킨스 파이프라인을 통해 진행하기로 했습니다.

또한, 모두 아래와 같은 스크립트를 통해 성공과 실패 시 slack으로 alerting하여 해당 프로젝트에 참여 중인 모든 팀원들이 확인할 수 있도록 했습니다.

post {
success {
script{
def res = slackSend (
channel: '#<channel-name>',
color: '#11c220',
message: """
✅ SUCCESS\\n*${env.JOB_NAME} (${env.BUILD_NUMBER})*
...
*"""
)
slackSend(
channel: res.threadId,
message: "${OUTPUT}"
)
}
}
failure {
slackSend (
channel: '#<channel-name>',
color: '#FF0000',
message: """
❌ FAIL\\n*${env.JOB_NAME} (${env.BUILD_NUMBER})*
...
"""
)
}
}

이렇게 배포를 쉽게, 그리고 그 결과를 확인하기 쉽게 하도록 파이프라인을 구성하였고 보다 더 안정적으로 배포 과정이 이루어질 수 있도록 했습니다!

우리가 살아가는 세상을 AI 기술로 변화시키는 팀 Corca는 고도화된 기술력과 기획력을 토대로 새로운 가치를 창출하고 있습니다.

Corca의 여정에 함께하실 분들은 corca.team 페이지를 확인해주세요!

--

--

Corca
Corca

Published in Corca

우리가 살아가는 세상을 AI로 변화시키는 팀, 코르카

Corca
Corca