Workfolio - Jenkins

eslerkang·2021년 12월 22일
1

Workfolio

목록 보기
3/4
post-thumbnail

Jenkins

CI/CD

  • CI → 지속적인 통합(Countiruous integration)으로 다수의 개발자들의 코드를 계속 통합하는 것이다.
  • CD → 지속적 배달(Countinuous delivery)로 코드를 계속해서 작성하면 사용자들이 계속 쓸 수 있도록 하는 것이다.

Jenkins

젠킨스(Jenkins)는 자바 런타임 위에서 동작하는 자동화 서버로 다양한(정말 많은) 플러그인들을 활용하여(말이 활용이지 사용할 플러그인을 고르는 것부터 시작이다) CI/CD 파이프라인(Pipeline)을 만들어서 자동화 작업을 할 수 있게 해준다. 파이프에서 물이 흘러가는 것 처럼 파이프라인을 통해 물이 흘러가듯 코드 작성 후 푸쉬하면 설정한 트리거가(예시에서는 푸쉬) 실행되어 테스트 → 빌드 → 배포하는 과정이 실행되는 것이다.

이번 Workfolio 프로젝트에서는 생각보다 빠르게 AWS에의 배포가 완료되어서(물론 API Gateway를 통해 MSA로 구성하지는 못했지만 ALB를 사용하여 Private 서브넷 안에 있는 EC2로 부하가 분산되게 구성해봤다) CI/CD를 직접 조금이라도 접해보고자 Jenkins를 사용해보기로 하였다.

첫 목표는 'feature/*' 브랜치에 푸쉬가 되면 테스트만 실행, 'dev' 브랜치에 푸쉬되면 테스트/배포를 실행하도록 구성할 생각이었지만 생각보다 프리티어의 EC2는 약했고, 젠킨스 서버를 EC2 에 띄우고 docker container 안에서 테스트 코드를 돌려보는 정도가 한계였다(이것마저 두번정도 트리거가 실행되면 EC2가 뻗어버렸다... 인스턴스 Stop → Start의 연속이었다).
그랬기에 이번에는 Pipeline을 통해 dev 브랜치에 푸쉬가 되면 docker container를 띄워 그 안에서 깃헙의 코드를 가지고 유닛테스트를 진행하는 과정까지만 진행했다(후에 자금이 여유가 생기거나 크레딧을 받게되면 t2 medium 정도로 다시 진행해보고 싶다).

Jenkins로 테스트 자동화!

우선 젠킨스 서버가 돌아갈 EC2를 구성해줘야 한다. 나 같은 경우엔 대역이 '172.16.0.0/16'인 VPC를 만들어 그 안에 2a, 2c AZ에 서브넷을 두고 진행했다. 2a, 2c의 서브넷 모두 퍼블릭 서브넷이고 2a에 젠킨스 서버가 돌아갈 EC2를 두었다(본래는 같은 서브넷에 배포가 되어 백앤드 서버가 돌아갈 EC2를 하나 배치했었으나 테스트까지만 진행 후 EC2 t2.micro의 성능 한계로 뒷 과정을 생략했기에 terminate 했다). 또한 VPC를 하나 더 만들어 RDS와 EC2를 분리하려 했으나 연습용으로 진행하였기에 EC2(젠킨스)가 있는 VPC 안에 RDS도 같이 배치하여 2a, 2c의 서브넷 두개에 걸쳐두었다.

Jenkins Installation

우선 젠킨스 서버를 돌릴 EC2에 당연하게도 젠킨스를 설치해주겠다.
젠킨스 공식 홈페이지에 가면 상황/OS 별로 자세히 설명되어 있으니 참고할 수 있고, 이번의 경우 EC2를 우분투 이미지를 통해 만들었으므로 우분투 기준으로 설명하겠다.

처음으로 jdk를 설치해줘야 하는데, 이는 젠킨스가 자바 런타임 위에서 동작하기 때문이다.

sudo apt update
# 가능한 jdk 전체 보기
sudo apt search openjdk
# 11 버전 jdk(예제에서는 openjdk-11-jdk를 사용하겠다)
sudo apt install openjdk-11-jdk

와 같이 설치할 수 있다. 그 다음 젠킨스를 설치해야 하는데 설치 스크립트가 공식 문서에 잘 되어있어 그대로 붙여넣기를 하면 된다.

curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update
sudo apt-get install jenkins

와 같이 설치 후 systemctl status/start/stop jenkins을 통해 젠킨스를 제어할 수 있다.

또한 우리의 경우 도커를 사용해 젠킨스가 테스트/배포 할 수 있도록 할 것이므로 EC2에 도커를 설치하고 chown 777 /var/run/docker.sock를 통해 젠킨스가 도커를 다룰 수 있도록 해준다.

Initial-setting

설치를 모두 마치고 EC2의 공인 IP:8080으로 들어가면 다음과 같은 화면을 볼 수 있다.

쓰여진 위치에서 Administrator Password를 가지고 와 인증하면


과 같이 플러그인을 설치할 수 있는 페이지로 이동한다. 이 경우 필요한 플러그인들만 골라서 다운받을 수도 있지만 Suggested Plugins에 필요한 플러그인들이 많이 포함되어 있기에 추천 플러그인을 설치하도록 하겠다.

그 후 잠시 몇 분 정도 플러그인들을 설치하고 관리자 계정을 만들면 기본적인 초기 설정은 끝나게 된다.

그 후 우리에게 필요한 몇가지 플러그인을 설치하기 위해 Jenkins 관리 → 플러그인 관리를 통해 플러그인 설치 페이지로 들어간다.

우리는 파이프라인에서 이 EC2 내에서 깃헙의 파일들을 가져와 테스트하지 않고 젠킨스 서버 내에서 도커 컨테이너를 띄워 테스트/배포 환경 설정 및 테스트(+배포)를 진행할 것이기 때문에 'docker pipeline'과 'blue ocean' 플러그인을 다운로드 받아준다. 아래의 'Install without restart', 'Download now and install after restart'의 경우에는 무엇을 선택해도 이 플러그인들에서는 문제가 발생하지 않는 것 같다.
그러나 이 버튼들 옆에 다시 젠킨스를 시작해야 한다는 문구가 적혀있다면 경고 문구를 따라 젠킨스를 restart 해주도록 하자.

Credentials

이제 플러그인과 관련된 모든 설정은 완료했다. 다음으로 해 줄 것은 깃헙과 젠킨스를 이어주기 위한 과정이다.
우리는 젠킨스 파이프라인이 특정 브랜치의 푸쉬를 인지하고 깃헙과 작업을 하기 위해서 webhook, credentials 설정을 해주도록 하겠다. 우선 깃허브 ssh 인증을 위해 EC2 ssh에서

ssh-keygen

을 실행하고 모든 항목들은 기본값/원하는 값을 넣어준다. 그 후 키가 생성되었다는 위치로 이동하면(permission 문제가 발생하면 sudo su root로 사용자를 바꿔 접근할 수 있다) [키 이름], [키 이름].pub 두 파일이 있는 것을 알 수 있다. 우선 [키 이름] 파일을 cat으로 조회하여 내용을 모두 복사해두자. 그 후 다시 젠킨스로 복귀해준다.

다시 한번 Jenkins 관리에서 Manage Credentials로 이동하게 되면 2번째 사진과 같은 곳으로 이동하고 Stores scoped to Jenkins 아래의 (global)에서 Credential을 추가할 수 있다.

추가를 눌러서 들어가면 아래와 같은 화면을 볼 수 있다.

여기서 Kind를 SSH Username with private key로 변경하고 ID는 Jenkins에서 보여질 이름, Username은 깃허브 Username, Private Key에는 방금 복사한 [키 이름] 파일의 내용을 Enter directly를 통해 입력해준다. 그 후 저장하고 나온다.

또한 우리는 젠킨스 서버에서 도커를 띄워 그 안에서 Repo를 클론 받아 테스트를 진행할 것이기에 secret text로 환경변수로 설정한(DATABASES, SECRET_KEY, ALGORITHM) 값들을 저장해준다.

이젠 깃허브의 젠킨스와 연결할 Repo로 가서 퍼블릭 키 등록과 webhook 등록을 하도록 하겠다.

위의 사진과 같이 깃헙 레포의 Settings → Deploy keys에서 키 추가를 통해 Title에는 원하는 표시 이름, Key에는 [키 이름].pub의 내용을 붙여넣고 Add key를 통해 키를 생성한다. 이제 SSH 키 등록은 모두 완료되었다.

Webhook의 경우 깃헙에서의 설정으로 간단하게 끝낼 수 있는데, 우선 EC2(젠킨스) 서버에 Inbound 규칙으로 깃허브 훅 용 IP에 대해 허용되어 있어야 한다. 깃허브의 훅 IP 주소 정보는 여기에서 'hooks'를 통해 알 수 있다.

Inbound 허용을 모두 마쳤으면 다시 깃허브의 Repo로 돌아와 Settings → Webhooks로 들어와 webhook 추가를 눌러준다. 그 후 Payload URL에는 외부 접속이 가능한(이게 제일제일 중요하다. 이것 때문에 Inbound 규칙을 열어준 것이다) Jenkins 서버의 주소(본 예제의 경우 도메인을 등록했기에 http://jenkins.kro.kr:8080이다)에 /github-webhook/을 붙여 써준다. /github-webhook/의 경우 앞 뒤의 슬래시 모두 들어가야 정상 작동한다. 그 후 Content-type을 application/json으로 변경하고 트리거를 푸쉬에 걸어준 후 저장해준다.

다시 Settings → Webhooks로 들어왔을 때 지정해준 주소(젠킨스 서버) 옆에 초록색 체크가 떠 있다면 정상적으로 연결이 된 것이다. 만약 정상 상태가 아니라면 보안그룹 규칙, 주소 등을 다시 한 번 확인하도록 하자.

Pipeline

이제 모든 준비가 끝났다. 간단하게 테스트까지만 진행하는 pipeline을 구성해보도록 하자.

젠킨스 메인페이지(Dashboard)에서 새로운 item을 누르면 위와 같은 화면을 볼 수 있는데 우리는 파이프라인을 구성하고자 하는 것이므로 이름을 정해주고 Pipeline을 눌러 OK로 다음으로 넘어간다.

그 후 다음과 같은 여러 설정이 있는 곳으로 이동하면 나머지 설정은 건드리지 않고 BUild Triggers의 'Github hook trigger for GITScm polling'에만 체크한다. 이는 우리가 설정한 webhook에 따라 파이프라인을 실행한다는 의미이다.

이제 모든 설정은 완료되었고, 아래의 Pipeline을 작성해주면 완료된다. 파이프라인의 문법, 사용법의 경우 빈 칸 상태에서 우측에 있는 Hello world 등과 같은 예제를 통해 살펴보거나 Pipeline syntax 등을 통해 확인할 수 있다(나도 완벽하게 이해하고 있지는 않기에 미흡할 수 있다).

pipeline {
    environment {
        DATABASES = credentials('databases')
        SECRET_KEY = credentials('secret_key')
        ALGORITHM = credentials('algorithm')
    }
    agent {
        docker {
            image "python:3.9"
            args "-e DATABASES=${env.DATABASES} -e SECRET_KEY=${env.SECRET_KEY} -e ALGORITHM=${env.ALGORITHM} -u root"
        }
    }

    stages {
        stage('Environment setting') {
            steps {
                git url: 'https://github.com/eslerkang/jenkins-test.git',
                    credentialsId: 'github',
                    branch: 'dev'
                
                sh 'pip install --upgrade pip'
                sh 'pip install -r requirements/requirements.txt'
            }
        }
        stage('Test') {
            steps {
                sh 'python manage.py test'
            }
        }
    }
}

와 같이 파이프라인을 작성해주면 된다. 이 코드의 경우 'environment'에서 젠킨스에서 설정해둔 credentials(secret text)를 가져와서 환경 변수로 지정해주고, 'agent'에서 도커를 실행시켜 이 아래의 'stages'의 과정은 도커 컨테이너 안에서 실행되도록 정해둔다. docker{}에서의 args의 경우 여러 속성들을 정해주는 부분으로 -e를 통해 환경변수들과 -u root를 통해 관리자로의 접속을 설정해주었다(관리자 접속을 하지 않으면 권한 문제가 매우 많이 발생하기에 피곤해질 수 있다).

'stages'는 'stage'의 모음으로 'stage'는 단계를 의미하고 stage안의 과정들이 하나의 지표로써 표시된다. 'stage'안의 'steps'는 그 'stage'안에서 순차적으로 실행되는 동작들이다.

여기서는 'Environment setting', 'Test' 두 개의 'stage'로 분리하였고, 첫번째는 깃헙 레포에서 클론을 받아오고 requirements.txt 안의 파이썬 라이브러리들을 다운받는 과정, 두번째는 manage.py test 명령어를 통해 테스트를 진행하는 과정이다.

첫번째 과정에서 git credentialsId: 'github' 부분은 아까 [키 이름] 파일의 내용을 붙여넣기 한 SSH 키에 대한 내용으로 'github'가 아까 내가 지정한 credential의 ID이고, branch의 경우 어떤 브랜치에서 푸쉬가 발생했을 때 파이프라인이 실행되는지에 대해 규정해주는 부분이다.

위와 같은 과정을 완료하고 dev 브랜치에 푸쉬를 하면 위의 사진 2장과 같이 파이프라인이 실행되는 것을 볼 수 있다. 잘 살펴보면 정상적으로 도커 컨테이너 안에서 테스트를 위한 환경이 구축되고, 테스트가 진행되어 통과했다는 것을 알 수 있다.

정리

젠킨스 파이프라인을 구성하는 방법은 정말 다양한 것 같다. 파이프라인의 문법부터 시작해서 Blue ocean을 사용해(본 연습과정에서는 파이프라인이 동작하는 과정을 보기 위해서만 사용했다) 파이프라인을 구성하는 방법 등 아직 공부할 것들은 많은 것 같다. 같은 기능을 하는 플러그인들도 상당히 다양하기에 어떤 것을 사용할 지 고르는 것부터 막혔었는데 이런 것에 대해서도 시각/정보를 넓혀나갈 필요가 있겠다.
이번 프로젝트에서 가장 아쉬웠던 것은 젠킨스를 도입해 사용할 수 있는 시간이 있었으나 EC2의 프리티어 성능 문제로 배포에 대한 실습을 제대로 해보지 못했다는 것이다.
다음에 크레딧/돈이 좀 생기게 되면 feature/* 브랜치의 푸쉬 트리거를 통해 테스트를 진행하고 dev 브랜치의 푸쉬 트리거를 통해 테스트/도커 이미지 빌드, 푸쉬를 한 후 publish over ssh를 통해 백앤드 서버 EC2에서 원래의 도커 컨테이너/이미지를 종료/삭제하고 방금 푸쉬한 이미지를 통해 서버 배포까지 완료하는 전체적인 과정을 진행해보고 싶다는 생각이 들었다. 또한 이번엔 Jenkinsfile을 만들지 않고 Jenkins에서 바로바로 제작해 진행했지만 다음엔 Blue ocean을 좀 더 적극적으로 사용하고 Dockerfile처럼 Jenkinsfile을 깃헙으로 관리하여 버젼관리까지 해보고 싶다는 생각이 들었다.
이 과정을 진행하며 무엇보다 AWS 구성부터 Docker, Jenkins를 한번에 사용하며 무엇을 좀 더 살펴봐야 할지 감이 좀 더 잡힌 것 같고 실제 Jenkins가 아니더라도(Gitlab?) CI/CD가 구축되어 있거나 구축해야 하는 곳에서 관리/생성을 해보고 싶다는 생각도 들었다.

profile
Surfer surfing on the dynamic flow of the world

0개의 댓글