AWS EC2와 Jenkins를 사용한 릴리즈 노트 자동화하기

기능 릴리즈 후 변경사항을 정리해 사용자에게 공유하는 일은 반복되는 중요한 작업입니다. 하지만 커밋 로그를 하나하나 확인하며 수동으로 정리하다 보면, 누락이 생기기 쉽고 시간이 많이 들 수 있습니다.

매번 수동으로 문서를 작성하고 업로드하는 과정을 반복하는 대신, Jenkins와 Git 트리거를 활용해 커밋이 발생하면 자동으로 릴리즈 노트가 생성되고, Nginx로 배포되는 시스템을 구현했습니다.

이 글에서는 그 과정을 하나씩 정리하며, 비슷한 자동화를 시도할 때 도움이 될 수 있도록 공유하고자 합니다. 먼저 전체 자동화 흐름을 한눈에 볼 수 있도록 다이어그램으로 정리해보았습니다.

[이미지1] 시스템 아키텍처 다이어그램


1. Nginx 설치 및 릴리즈 노트용 웹페이지 준비

Nginx 설치 방법은 이전 글에서 다루었기 때문에, 이 글에서는 Ubuntu 기반 AWS EC2 인스턴스에 Nginx가 설치 후 릴리즈 노트를 서비스하기 위한 웹페이지 설정 위주로 설명하겠습니다.

> Nginx 설치 방법 참조글 “Nginx를 활용한 파일 다운로드 링크 제공하기”(https://blog.cslee.co.kr/providing-file-download-links-using-nginx/)

1) 사이트 설정파일 확인하기

– Nginx의 기본 사이트 설정 파일은 `/etc/nginx/sites-available/default` 경로에 위치합니다.  

sudo nano /etc/nginx/sites-available/default

– Ubuntu에서 Nginx를 설치하면 기본 웹 루트 경로는 /var/www/html로 설정되어 있습니다. 아래는 default 설정 파일의 일부 예시입니다.

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;

2) 테스트용 인덱스 생성
echo ‘<h1>Hello Nginx!</h1>’ | sudo tee /var/www/html/index.html

3) 웹브라우저에서 접속확인

EC2의 퍼블릭 IP 주소 또는 연결된 도메인을 사용해 아래 주소로 접속하면, 방금 생성한 HTML 페이지가 브라우저에 출력되는지 확인할 수 있습니다. 

이 프로젝트에서는 EC2를 웹 서버로 사용하기 때문에, 브라우저 주소창에 http://<EC2-IP>을 입력해 정상 작동 여부를 확인합니다.

[이미지2] 웹브라우저 접속화면

2. Jenkins 설치하기

1) 방화벽열기

Jenkins가 설치될 EC2의 8080포트를 열어 웹으로 접속할 수 있도록 AWS EC2 보안 그룹에서 인바운드 규칙을 편집합니다.

[이미지3] AWS EC2 인바운드 규칙 편집화면

2) java설치

Jenkins는 Java 기반이므로 먼저 JDK를 설치합니다.

sudo apt update
sudo apt install openjdk-17-jdk -y
java -version  # 설치 확인

3) Jenkins설치

Jenkins 공식 저장소에서 제공하는 패키지를 설치하기 위해, 먼저 Jenkins의 공개 GPG 키를 등록하고 APT 저장소를 추가합니다. 이후 APT 패키지 관리자를 통해 Jenkins를 설치합니다.

wget -q -O – https://pkg.jenkins.io/debian-stable/jenkins.io.key |sudo gpg –dearmor -o /usr/share/keyrings/jenkins.gpg
sudo sh -c ‘echo deb [signed-by=/usr/share/keyrings/jenkins.gpg] http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list’
sudo apt update
sudo apt install jenkins -y

4) Jenkins실행

– 아래 명령어로 Jenkins를 실행하고 브라우저 주소창에 http://<EC2-IP>:8080을 입력해 정상 작동 여부를 확인합니다.

sudo systemctl start jenkins

– 처음 브라우저로 Jenkins에 접속하면 관리자 비밀번호 입력 화면이 표시됩니다. Jenkins는 설치 시 임시 비밀번호를 자동 생성하며, 아래 명령어로 확인할 수 있습니다.

sudo cat /var/lib/jenkins/secrets/initialAdminPassword
[이미지4] Jenkins 첫 접속화면

– Skip and continue as admin을 하게 될 경우 Jenkins가 생성한 기본 관리자 계정을 그대로 사용하게 됩니다. 보안을 위해 Admin계정을 새로 생성하여 Jenkins실행 설정을 마무리합니다.

[이미지5] Jenkins 초기 관리자 설정화면

3. Git 연동 설정 및 Jenkins 프로젝트생성

1) GitHub 인증정보 등록

Jenkins에서 Github에 접근할 수 있도록 인증 정보를 등록하기위해서 Jenkins관리 > Credentials > (global)을 클릭 한 후 + Add Credentials버튼을 누르면 New credentials 입력 화면이 나타납니다. 입력 화면에 아래내용을 입력합니다.

  • Username : GitHub ID
  • Password : GitHub에서 발급받은 token
  • ID : 식별을 위해 사용할 ID정보

[이미지6] Jenkins 인증정보등록 화면

입력을 완료하면 아래와 같이 인증정보가 등록됩니다.

[이미지7] Jenkins 인증정보 목록 화면

2) 프로젝트 생성

새로운 Item > 프로젝트명 입력 > Pipeline을 선택하고 OK버튼을 눌러 프로젝트 생성을 진행합니다.

– 커밋이 발생할 때마다 Jenkins와 Git 트리거를 통해 자동으로 릴리즈 노트를 생성하고, 이를 Nginx를 통해 배포하는 시스템을 구현하기 위해 Jenkins Job을 Pipeline 타입으로 구성했습니다.

[이미지8] Jenkins 프로젝트생성 화면

– 프로젝트 설정화면이 나타나면 GitHub project를 체크한 후 연결하려는 Git Repository url을 넣어줍니다.

[이미지9] Jenkins 프로젝트생성 설정화면1

– Triggers는 커밋이 발생하면 빌드를 유발하도록 GitHub hook trigger for GITScm polling를 체크합니다.

[이미지10] Jenkins 프로젝트생성 설정화면2

– 마지막 하단의 Pipeline은 아래내용을 참고하여 입력 후 Save를 눌러 프로젝트 생성을 완료합니다.

  • Definition : Pipeline script from SCM(Pipeline script를 외부저장소에서 가져옴) 선택
  • SCM : Git선택
  • Repository URL : 가져오려는 Git Repository url 입력
  • Credentials : 1)에서 등록한 GitHub 인증정보 선택
  • Branch Specifier (blank for ‘any’) : Jenkins가 가져올 Git Branch입력
  • Script path : 가져올 Pipeline script 파일명 입력

[이미지11] Jenkins 프로젝트생성 설정화면3

3) Webhook 설정

Github상에서 Settings – Webhooks – Add webhook을 눌러 webhook설정을 진행합니다. 아래 내용을 입력 후 Add webhook을 눌러 설정을 완료합니다.

  • Payload URL : Jenkins 도메인 뒤에 /github-webhook/을 붙여 입력
  • content type : application/json 선택

[이미지12] Jenkins 프로젝트생성 설정화면4

4. 릴리즈 노트 자동 생성 스크립트 작성

.iss 파일은 Windows 설치 프로그램을 만들기 위한 스크립트로, 버전 정보 및 설치 설정이 포함되어 있습니다.

GitHub에서 모든 파일의 커밋 메시지를 릴리즈 노트에 반영하면 정보 전달의 의미가 약해질 수 있기 때문에, .iss  파일의 버전 정보가 변경된 경우에만 릴리즈 노트를 자동 생성하도록 파이프라인을 구성했습니다.

1) Jenkins스크립트 주요코드 요약

– test_setup.iss 파일의 최근 커밋을 가져와서 추출한 버전 정보를 비교하여 버전 변경 감지

// 최근 변경된 커밋2개의 커밋해시값 불러오기
def commits = sh(script: “git log -2 –pretty=format:’%H’ –test_setup.iss”, returnStdout: true).trim().split(‘\n’)

// 직전커밋, 그이전커밋
def latestCommit = commits[0]
def prevCommit = commits[1]

// 두 커밋의 .iss 파일에서 버전 문자열 뽑기 함수
def getVersion = { commitHash ->  
def content = sh(script: “git show ${commitHash}:test_setup.iss”, returnStdout: true)
//버전정보 추출      
def matcher = content =~#define\s+MyAppVersion\s+”([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)”/      return matcher ? matcher[0][1] : null
}

– 해당 커밋의 메세지와 커밋 날짜를 릴리즈 노트 콘텐츠로 사용하기 위해 분리하여 추출

//커밋메세지 내용 || 날짜 불러오기
def log = sh(script: “git log -1 –pretty=format:’%s||%cd’ –date=short  ${LATEST_COMMIT}”, returnStdout: true).trim()
def parts = log.split(“\\|\\|”) //메세지, 날짜 분리
env.NOTE = parts[0].trim() //메세지
env.COMMIT_DATE = parts[1].trim() //날짜

– 추출한 정보들을 인자로 넘겨  Python 스크립트를 실행하고 HTML 릴리즈 노트를 업데이트

sh “””python3 ${env.PY_SCRIPT} \\
    –version “${env.VERSION}” \\
    –note “${env.NOTE}” \\
    –date “${env.COMMIT_DATE}” \\
    –file “${env.HTML_FILE_PATH}””””

2) Python스크립트 주요코드 요약

– 쉼표로 나뉜 메세지를 키워드를 기준으로 ‘신기능’, ‘버그수정’,‘업데이트’로 분류

# 3-1. 업데이트인지 버그인지 판단
notes = args.note.split(‘,’) #쉼표기준으로 split
#리스트 초기화
bugs = []new_features = []updates = []
#키워드 설정
bug_keywords = [‘버그’, ‘오류’, ‘에러’]new_feature_keywords = [‘신기능’]
#포함키워드 확인
for note in notes:
    note = note.strip() #공백제거
    if any(keyword in note for keyword in bug_keywords):
        bugs.append(note)
    elif any(keyword in note for keyword in new_feature_keywords):
        new_features.append(note)
    else:
        updates.append(note)

– HTML에 새로운 릴리즈 섹션을 추가하고, 버전 및 날짜 정보를 h3태그로 구성

# 1. 새로운 버전 섹션 생성
main_content = soup.find(‘main’, class_=’main-content’)
# 2-1. 버전 정보
sectionversion_section = soup.new_tag(‘section’, attrs={‘class’: ‘version-info’, ‘id’: version_id})
# 2-2. h3태그
h3 = soup.new_tag(‘h3’)
# 2-3. version span태그
span_version = soup.new_tag(‘span’, attrs={‘class’: ‘version’})
span_version.string = f”Version {args.version}”h3.append(span_version)h3.append(soup.new_string(” “)) # 공백
# 2.4. released span태그
span_released = soup.new_tag(‘span’, attrs={‘class’: ‘released’})
span_released.string = f”Released on {args.date}”
h3.append(span_released)
# 2-5. section에 h3 추가
version_section.append(h3)

– HTML의 사이드바에도 새로운 릴리즈 버전 링크를 최상단에 추가

sidebar_ul = soup.find(‘aside’, class_=’sidebar’).find(‘ul’)
sidebar_li = soup.new_tag(‘li’)
a = soup.new_tag(‘a’, href=f”#{version_id}”)
a.string = args.versionsidebar_li.append(a)
sidebar_ul.insert(0, sidebar_li)

5. 릴리즈 노트 자동 배포 테스트

1) Git hub commit 발생

– Git hub test_setup.iss파일에서 새로운 커밋이 발생함

[이미지13] Git hub 메인화면

2) Jenkins 빌드

– Permission오류 발생

오류가 발생하여 정상적으로 빌드되지않았습니다. 오류가 발생했을때 Jenkins상의 Console Output으로 로그를 확인할 수 있습니다. Jenkins가 빌드하여 HTML파일을 갱신하여야 하는데 권한이 없어 Permission가 발생하였습니다.

[이미지14] Jenkins Console Output1

– Jenkins에게 디렉토리 권한부여

오류를 해결하기 위해 터미널로 돌아와 디렉토리에 권한을 부여하였습니다. 

sudo chown -R jenkins:jenkins /var/www/html

– 권한부여 후 정상적으로 빌드되어 직전커밋과 이전커밋이 추출

[이미지15] Jenkins Console Output2

3) HTML갱신
[이미지16] 빌드전 HTML파일
[이미지17] 빌드 후 HTML파일 갱신

4) 웹브라우저 확인
[이미지18] 빌드 전 웹브라우저
[이미지19] 빌드 후 웹브라우저

마무리하며..

Jenkins와 Git을 연동하고, 버전 정보를 감지하여 자동으로 HTML 릴리즈 노트를 생성한 후, 이를 실시간으로 Nginx 서버에 반영함으로써 반복적인 문서 작업을 줄이고, 변경사항을 사용자에게 빠르게 전달할 수 있게 되었습니다.
이 구조는 이번 글에서 사용한 .iss 파일 외에도 특정 경로나 파일 타입을 감지하여 다양한 방식으로 확장할 수 있으며, 향후 Slack 알림이나 버전 태깅 자동화 등으로도 발전시킬 수 있습니다. 전반적인 자동화 흐름과 관련된 코드는 ChatGPT의 도움을 받아 정리하였습니다.