AWS EC2 Jenkins를 통한 CI 환경 구축

서론

이전 글을 통해 Docker-compose 구성을 통한 각 서버별 Docker Container들을 구성하였는데, 진행하다 보니 CI 자동화도 알아보고 싶어 이에 대해 다루고자 한다. 본 글에서는 AWS EC2 인스턴스 내 Jenkins를 통해 CI 자동화에 대해 다루고자 한다. 현재 회사 특성상 CD 까지 진행하는데 어려움이 있을 듯 해 ‘CI/CD’가 아닌, CI만을 다루고자 한다.

 
 

본론

CI/CD 란?

CI = 지속적인 통합(Continuous Integration)을 의미하는 단어로 ‘빌드와 테스트’를 자동화한 것이다.

CD = 지속적인 전달(Continuous Delivery) or 지속적인 배포(Continuous Deployment)를 의미하는 단어로 CI가 완료된 이후 사용자에게 전달하는 배포 과정을 자동화한 것이다.

즉, CI는 개발 과정에서 필요한 빌드 및 테스트 단계를 자동화한 것이고 CD는 테스트가 완료된 코드를 사용자에게 전달하기 위한 도구이다.

 

프로젝트 구성

스크린샷 2024-01-11 오전 7 47 01
프로젝트 전체 구성도

 

전체 프로젝트 구성은 위 사진과 같다. 사용자가 코드를 깃 저장소에 올릴 경우 Github Webhook을 통해 해당 코드 내용을 Jenkins Docker Container에 반영한다. 이후 JenkinsMigration, Test를 비롯한 일련의 작업들을 수행하는 구조이다.

위에서 언급했듯이 본 프로젝트에서는 CI 환경 구축만을 목표로 하였기 때문에 별도의 Docker Hub와의 연결 없이 테스트 단계에서 마무리하도록 하겠다.

 

AWS EC2 인스턴스 생성

EC2에 대해 보다 자세히 다룬 글들이 존재하기에 참조할만한 링크만 추가한 뒤 본 글에서는 EC2 인스턴스를 통해 자동화하는 과정에 대해 집중적으로 다루고자 한다.

Amazon EC2란 무엇인가요?

기존 활용하던 인스턴스가 아닌 새로 인스턴스를 생성하여 설명하도록 하겠다. 추가로 본 글에서는 ‘프리 티어’를 기준으로 설명하므로 이를 인지하고 살펴볼 필요가 있다.

 

신규 인스턴스 생성

상단 레드박스 내 버튼을 통해 신규 인스턴스를 생성할 수 있다

스크린샷 2024-01-09 오전 8 26 50
인스턴스 목록 화면

 

Application and OS Images

AMI(Amazon Machine Image)는 해당 EC2 인스턴스의 베이스가 될 OS를 선택하는 것인데, 본 글에서는 Amazon Linux 2를 기준으로 설명하고자 한다. Ubuntu를 기준으로 하고자 할 경우 다른 AMI를 선택하면 된다.

스크린샷 2024-01-09 오전 8 28 42
인스턴스 생성 초기 화면

 

스크린샷 2024-01-09 오전 8 36 06
Amazon Linux 2 기준

 

인스턴스 유형 및 키 페어

인스턴스도 마찬가지로 기본 인스턴스인 t2.micro를 활용할 예정이다. 다음으로 중요한 부분은 키페어에 관련된 부분인데, 추후 해당 키 페어를 통해 SSH 접속에 활용할 예정이니 주의깊게 살펴야 한다.

스크린샷 2024-01-09 오전 8 38 53
키 페어 설정 화면

 

키페어 이름을 설정한 뒤 생성을 하면 로컬에 키페어가 다운로드 된다. 추후 해당 키페어가 위치한 디렉토리에서 SSH 접속을 해야하므로 안전한 곳에 보관하면 된다.

스크린샷 2024-01-09 오전 8 44 34
키 페어 생성 화면

 

네트워크 구성

네트워크 구성의 경우 외부에서 Jenkins 접근을 허용하기 위해 8080 포트를 추가로 개방해주었다. 특정 포트에 대해 외부로부터의 접근을 허용하는 것을 ‘인바운드 규칙’이라 하는데, 본 글에서는 8080 포트만 활용할 예정이므로 추가적으로 이에 대해 다루진 않겠다.

스크린샷 2024-01-09 오전 8 49 22
인바운드 보안 그룹 설정 화면

 

인스턴스 생성 완료

상기 과정을 완료했다면 아래 이미지처럼 인스턴스가 성공적으로 생성되었음을 확인할 수 있다

스크린샷 2024-01-09 오전 8 53 21
인스턴스 생성 완료시 화면

 

인스턴스 접근

정상적으로 인스턴스 생성이 완료되었다면 이전 과정에서 생성한 키 페어를 통해 인스턴스 내부에 접근할 수 있다. 본인의 키 페어가 위치한 디렉토리에서 ssh -i <키페어 이름>.pem ec2-user@<퍼블릭 IP> 를 통해 인스턴스 내부로 접근할 수 있다.

스크린샷 2024-01-09 오전 8 59 25
SSH를 통한 인스턴스 접근 화면

 

그런데 접근을 시도할 경우 위 이미지처럼 에러가 발생함을 확인할 수 있다. 이는 최초 다운로드된 키 페어의 경우 접근 권한이 외부인에게도 허용된 상태이기 때문인데, sudo chmod 400 test.pem 명령어를 통해 해당 키 페어의 권한을 변경해주면 정상적으로 인스턴스에 접근할 수 있다.

스크린샷 2024-01-09 오전 9 09 25
인스턴스 접근에 성공할 시 화면

 
 

Jenkins 설치 & 설정

본 글에서는 EC2 인스턴스 내 도커를 설치한 뒤, Jenkins Container를 띄울 예정이다. Docker 내부에 Jenkins를 설치하지 않고 EC2 자체에 Jenkins를 설치하는 방법도 존재하나, 설치 및 관리의 편의성으로 인해 이러한 방법을 채택하였다.

아래 일련의 명령어를 수행함으로써 필수 패키지 및 Docker를 설치할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 패키지 업데이트
sudo yum update

# Docker 관련 패키지 설치
sudo yum install docker docker.io

# Docker 서비스 실행
sudo service docker start

# 사용자를 User Group에 추가
sudo usermod -a -G docker $USER

# 부팅 시 Docker 자동 시작
sudo systemctl enable docker

# 권한 설정
newgrp docker

# docker-compose 설치
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

# docker-compose 권한 부여
sudo chmod +x /usr/local/bin/docker-compose

위 과정을 완료하였다면 아래 명령어들을 통해 정상적으로 설치 및 실행이 완료되었음을 확인할 수 있다

1
2
3
4
5
6
7
8
9
10
11
# Docker 설치 확인
[ec2-user@ip-172-31-6-223 ~]$ docker -v
Docker version 20.10.25, build b82b9f3

# Docker 실행 확인
[ec2-user@ip-172-31-6-223 ~]$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since 화 2024-01-09 00:54:38 UTC; 33min ago

(중략)

이후 docker-compose.yml 파일을 통해 Jenkins Container를 구성해보겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# pwd : /home/ec2-user/docker-compsoe.yml

version: '3'

services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
volumes:
- /var/run/docker.sock:/var/run/docker.sock. # 컨테이너 내 Docker 실행을 위함
- /jenkins:/var/jenkins_home
ports:
- "8080:8080"
privileged: true # 컨테이너 내부 자원 접근을 위함
user: root # root 권한 설정

아래 명령어들을 통해 도커 생성 및 확인을 할 수 있다

1
2
3
4
5
6
7
8
9
# pwd : /home/ec2-user

# Docker-compose 실행
docker-compose up -d

# Docker container 확인
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
07b7e38fdd22 jenkins/jenkins:lts "/usr/bin/tini -- /u…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 50000/tcp jenkins

상기 과정을 모두 정상적으로 수행하였다면 브라우저 상에서 <퍼블릭 IP>:8080 을 통해 Jenkins를 사용할 수 있다.

스크린샷 2024-01-09 오전 10 39 21
Jenkins 초기 화면

 

Jenkins를 활성화하기 위한 비밀번호는 docker logs <container 이름> 명령어를 통해 확인할 수 있다.

스크린샷 2024-01-09 오전 10 45 05
Jenkins 활성화를 위한 비밀번호

 

이후 패키지를 설치할 수 있는데, 대부분 좌측 기본 패키지 설치하는 것을 권장한다

스크린샷 2024-01-09 오전 10 47 03
Jenkins 패키지 설치 화면

 

패키지 설치를 완료한 뒤 로그인에 사용할 관리자 계정을 생성해주면 Jenkins 설정은 끝이다.

스크린샷 2024-01-09 오전 10 48 26
설정을 완료한 뒤 Jenkins 화면

 
 

Github Webhook 연결

Github Webhook을 통해 Github 저장소에 코드가 변경될 경우(push, merge request, …) 자동적으로 빌드를 수행하게끔 하고자 한다. Github 저장소와의 연결을 위해 Credential을 만들어야 한다.

스크린샷 2024-01-09 오전 10 54 08
Jenkins Credential 설정 화면 - 1

 

스크린샷 2024-01-09 오전 10 55 40
Jenkins Credential 설정 화면 - 2

 

스크린샷 2024-01-09 오전 10 55 47
Jenkins Credential 설정 화면 - 3

 

아래 페이지에서 username : Github ID, Password : Github Access Token 를 입력한 뒤 ID 는 자유롭게 입력하면 된다. ID 를 입력하지 않을 경우 자동으로 생성되나 구분을 위해 별도로 선언하는 편이 좋다.

스크린샷 2024-01-09 오전 10 56 14

Jenkins Credential 설정 화면 - 4

 

이후 연결하고자 하는 저장소에 들어간 뒤 Webhook을 설정할 수 있다.

스크린샷 2024-01-09 오전 11 19 23
Github 저장소 내 Webhook 설정 - 1

 

Payload URL에 http://<퍼블릭 IP>:8080/github-webhook/ 로 설정하면 된다.

스크린샷 2024-01-09 오전 11 23 01
Github 저장소 내 Webhook 설정 - 2

 

Github와 연결을 완료하였으니 코드가 수정될 경우 수행할 작업들을 정의하도록 하겠다.

스크린샷 2024-01-09 오후 3 30 01
Jenkins Job 설정 화면 - 1

 

스크린샷 2024-01-09 오후 3 30 29
Jenkins Job 설정 화면 - 2

 

아래 이미지와 마찬가지로 ‘소스 코드 관리’ 탭에서 연결하고자 하는 Git 주소, Credential, Branch를 설정해주었다. 필자는 main branch 기준이므로 수정하였다.

스크린샷 2024-01-09 오후 3 43 07
Jenkins Job 설정 화면 - 브랜치 설정

 

이후 ‘빌드 유발’ 탭에서 Github Webhook과 관련된 GitHub hook trigger for GITScm polling 옵션을 활성화해준 뒤, ‘Build Steps’에서 Execute Shell 탭을 선택한 뒤 수행할 일련의 명령어들을 정의하였다.

스크린샷 2024-01-09 오후 3 48 20
Jenkins Job 설정 화면 - 빌드 명령어 설정

 

참고로 필자의 경우 실제론 Postgresql, Django, Nginx 3개의 Docker-compose로 구성된 환경이나 Jenkins 내부에서는 Django에 대한 테스트를 수행할 것이므로 위와 같이 빌드 명령어를 작성하였다.

또한 위 명령어는 Jenkins Docker Container 내부에서 수행되는데, 현재 해당 Conatiner 내부에는 파이썬을 비롯한 별도의 패키지들이 설치되어 있지 않다. 따라서 아래 명령어를 통해 이를 설치하고자 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Container 진입을 위한 명령어
docker exec -it --user root <container 이름> /bin/bash

# 패키지 업데이트
apt-get update -y

# 필수 패키지 설치
apt-get install -y

# Docker 설치
apt-get install docker.io -y

# python & pip 설치
apt-get install python3 pip -y

# Django 설치
apt-get install python3-django

이후 연결한 Github 저장소에 들어가 코드를 수정하면 아래와 같이 Jenkins에서 빌드가 수행됨을 확인할 수 있다.

스크린샷 2024-01-09 오후 4 58 32
Jenkins 빌드 수행 화면 - 진행중

 

스크린샷 2024-01-09 오후 4 58 41
Jenkins 빌드 수행 화면 - 성공

 

참고 문헌

Amazon EC2란 무엇인가요?)

젠킨스란 무엇인가, CI(Continuous Integration) 서버의 이해)

Django + jenkins + docker CI/CD 구축기)

AWS에 Jenkins를 구축해보자)

젠킨스(Jenkins) Github Webhooks 연결)

비공개 도커 레지스트리(private docker registry) 만들기)

End to End Project: Deployed Django Application with Jenkins CI/CD Pipeline)

Springboot Gradle + AWS EC2 + Jenkins + Docker로 배포까지)

[AWS EC2] Jenkins in Docker 배포 자동화 구축)

Jenkins를 활용한 DevOps 환경 구축)

AWS EC2에 Docker-Compose 구축하기)