
GitHub Actions로 CI/CD 구축 #2에서는 일반적인 프로젝트에서 주로 사용하는 CI/CD 구축 방법을 소개했습니다.
이번 글에서는 AWS의 CodeDeploy를 사용하여 확장성을 고려한 프로젝트에서 많이 사용하는 CI/CD 구축 방법을 알아보도록 하겠습니다.
AWS의 배포 자동화 서비스인 CodeDeploy를 활용하므로, CodeDeploy를 먼저 세팅합니다.
CodeDeploy가 다른 리소스인 EC2에 접근 가능하도록 IAM 역할을 생성합니다.




IAM 역할 생성을 완료했으면, CodeDeploy 애플리케이션을 생성합니다.
- CodeDeploy에서 애플리케이션은 배포 작업을 관리하기 위한 단위입니다.
- CodeDeploy는 애플리케이션 단위로 배포를 수행합니다.
- 애플리케이션에는 배포할 대상, 배포 방법, 배포 파일 등이 연관되어 있습니다.
- 애플리케이션은 배포할 대상, 즉, 배포할 하나의 서비스라고 생각하면 편합니다.

- CodeDeploy에서 배포 그룹은 애플리케이션을 어디에, 어떻게 배포할지를 정의합니다.
- 배포 그룹에서는 배포 대상, 배포 방식, 배포 실패 처리 방식을 설정할 수 있습니다.




CodeDeploy 세팅을 완료한 후, EC2가 S3에 접근 가능하도록 IAM 역할을 생성합니다.
이 역할은 S3 권한을 포함하는 IAM 정책을 연결해 EC2가 S3에 접근할 수 있도록 설정합니다.
역할 생성에 앞서, 먼저 정책을 생성하여 필요한 권한을 정의해야 합니다.
// 이 정책은 EC2 인스턴스가 S3 버킷 및 객체에 대해 "읽기 권한"을 가지도록 정의합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:Get*",
"s3:List*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}







// 아래 내용을 EC2 인스턴스 내부에 입력합니다.
$ sudo apt update && \
sudo apt install -y ruby-full wget && \
cd /home/ubuntu && \
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install && \
chmod +x ./install && \
sudo ./install auto
// CodeDeploy 에이전트 실행 확인
$ systemctl status codedeploy-agent





AWSCodeDeployFullAccess, AmazonS3FullAccess 정책을 선택합니다.








프로젝트의 최상단 경로(루트 경로)에 appspec.yml 파일을 생성합니다.
AWS CodeDeploy는 appspec.yml 파일을 기반으로 동작합니다.
이 파일은 CodeDeploy가 애플리케이션을 배포할 때 수행해야 할 작업의 규칙을 정의하는 설정 파일입니다.
# CodeDeploy가 이 설정 파일(appspec.yml)을 기반으로 동작합니다.
# appspec.yml은 CodeDeploy가 애플리케이션을 배포할 때 따라야 할 작업의 규칙을 정의하는 파일입니다.
# 이 파일은 배포 파일의 위치, 파일 복사 경로, 권한 설정, 실행할 스크립트 등을 설정하여 CodeDeploy가 EC2에서 어떻게 작업해야 하는지 알려줍니다.
version: 0.0 # appspec.yml 파일의 버전 (기본값 0.0)
os: linux # 배포 대상 운영 체제 (여기서는 Linux)
files: # 배포 파일의 소스 경로와 대상 경로를 정의
- source: / # 소스 파일의 루트 경로 (S3에 업로드된 파일 전체)
destination: /home/ubuntu/test-server # EC2 인스턴스에서 파일이 복사될 경로
permission: # 복사된 파일/폴더의 소유자와 그룹을 설정
- object: / # 권한을 설정할 대상 파일/폴더 (여기서는 모든 파일/폴더)
owner: ubuntu # 파일/폴더의 소유자를 'ubuntu' 사용자로 설정
group: ubuntu # 파일/폴더의 소유 그룹을 'ubuntu' 그룹으로 설정
hooks: # 배포 과정 중 특정 단계에서 실행할 스크립트를 정의
ApplicationStart: # 애플리케이션 배포 후 실행되는 단계
- location: scripts/start-server.sh # 실행할 스크립트의 경로
timeout: 60 # 스크립트 실행 시간 제한 (초 단위)
runas: ubuntu # 스크립트를 실행할 사용자 ('ubuntu' 사용자로 실행)
version: 0.0 # appspec.yml 파일의 버전 (기본값 0.0)
os: linux # 배포 대상 운영 체제 (여기서는 Linux)
version: 0.0: appspec.yml 파일의 버전입니다. 항상 0.0을 사용합니다. os: linux: 배포할 대상 운영체제를 지정합니다. 여기서는 Linux 기반의 EC2 인스턴스입니다.files: # 배포 파일의 소스 경로와 대상 경로를 정의
- source: / # 소스 파일의 루트 경로 (S3에 업로드된 파일 전체)
destination: /home/ubuntu/test-server # EC2 인스턴스에서 파일이 복사될 경로
source:
destination:
/home/ubuntu/test-server 디렉토리로 복사됩니다. permission: # 복사된 파일/폴더의 소유자와 그룹을 설정
- object: / # 권한을 설정할 대상 파일/폴더 (여기서는 모든 파일/폴더)
owner: ubuntu # 파일/폴더의 소유자를 'ubuntu' 사용자로 설정
group: ubuntu # 파일/폴더의 소유 그룹을 'ubuntu' 그룹으로 설정
object:
/로 지정하면 모든 파일/폴더에 권한이 적용됩니다. owner와 group:
ubuntu 사용자로 설정합니다. hooks: # 배포 과정 중 특정 단계에서 실행할 스크립트를 정의
ApplicationStart: # 애플리케이션 배포 후 실행되는 단계
- location: scripts/start-server.sh # 실행할 스크립트의 경로
timeout: 60 # 스크립트 실행 시간 제한 (초 단위)
runas: ubuntu # 스크립트를 실행할 사용자 ('ubuntu' 사용자로 실행)
hooks:
ApplicationStart:
start-server.sh 스크립트를 실행해 애플리케이션 서버를 시작합니다. location:
scripts/start-server.sh를 실행하도록 설정했습니다. timeout:
runas:
ubuntu 사용자로 실행합니다. files 섹션에 따라 소스 파일이 EC2 인스턴스의 지정된 경로로 복사됩니다. permissions 섹션에 따라 파일 소유권 및 권한이 설정됩니다. hooks 섹션에 정의된 스크립트가 지정된 단계에서 실행됩니다. ApplicationStart 단계에서 scripts/start-server.sh 스크립트가 실행되어 서버가 시작됩니다.start-server.sh는 서버를 실행하는 스크립트입니다. 루트 경로 아래에 위치한 scripts 디렉토리에 저장하고, appspec.yml의 ApplicationStart 섹션에서 호출됩니다.
scripts/start-server.sh
#!/bin/bash
echo "--------------- 서버 배포 시작 -----------------"
cd /home/ubuntu/test-server
sudo fuser -k -n tcp 8080 || true
nohup java -jar project.jar > ./output.log 2>&1 &
echo "--------------- 서버 배포 끝 -----------------"
실행 권한 부여
chmod +x scripts/start-server.sh경로 확인
로그 기록
#!/bin/bash
#!/bin/bash: echo "--------------- 서버 배포 시작 -----------------"
echo: cd /home/ubuntu/test-server
cd: /home/ubuntu/test-server는 appspec.yml 파일에서 설정한 배포 대상 경로와 일치합니다. sudo fuser -k -n tcp 8080 || true
fuser -k -n tcp 8080: -k: 해당 포트를 사용하는 프로세스를 종료합니다. -n tcp: TCP 프로토콜을 대상으로 합니다. sudo: || true: nohup java -jar project.jar > ./output.log 2>&1 &
nohup:
java -jar project.jar:
project.jar 파일을 실행합니다. java -jar 명령어를 사용해 애플리케이션을 실행합니다. > ./output.log 2>&1:
>: 표준 출력을 output.log 파일에 기록합니다. 2>&1: 표준 에러(2)를 표준 출력(1)과 같은 곳에 기록합니다. &:
echo "--------------- 서버 배포 끝 -----------------"
echo: 배포 시작 메시지 출력:
--------------- 서버 배포 시작 ----------------- 작업 디렉토리 이동:
/home/ubuntu/test-server 디렉토리로 이동합니다. 8080 포트 종료:
애플리케이션 실행:
project.jar 파일을 백그라운드에서 실행하고, 출력을 output.log에 저장합니다. 배포 완료 메시지 출력:
--------------- 서버 배포 끝 ----------------- fuser 명령어를 통해 기존 프로세스를 종료합니다. nohup과 &를 사용해 애플리케이션을 백그라운드에서 실행합니다. output.log에 표준 출력과 에러 로그를 저장해 디버깅 및 모니터링이 가능합니다. || true를 사용합니다. 루트 경로 아래에 .github/workflows 디렉터리를 생성한 후 그 아래에 deploy.yml 워크플로 파일을 생성합니다.
.github/workflows/deploy.yml
name: Deploy To EC2 # 워크플로 이름 정의, Actions 탭에서 이 이름으로 표시됨
on:
push:
branches:
- main # main 브랜치에 푸시될 때 워크플로 실행
jobs:
deploy:
runs-on: ubuntu-latest # 최신 Ubuntu 환경에서 실행
steps:
# 리포지토리의 코드를 작업 환경에 가져오기
- name: Github Repository 파일 불러오기
uses: actions/checkout@v4
# JDK(Java Development Kit) 설치
- name: JDK 17버전 설치
uses: actions/setup-java@v4
with:
distribution: temurin # OpenJDK의 temurin 배포판 사용
java-version: 17 # JDK 17 설치
# application.yml 파일 생성 (환경 변수 적용)
- name: application.yml 파일 만들기
run: echo "${{ secrets.APPLICATION_PROPERTIES }}" > ./src/main/resources/application.yml
# Gradle 빌드 도구를 사용해 프로젝트 테스트 및 빌드
- name: 테스트 및 빌드하기
run: ./gradlew clean build
# 빌드된 결과물의 이름을 변경 (사용하기 쉽게 관리)
- name: 빌드된 파일 이름 변경하기
run: mv ./build/libs/*SNAPSHOT.jar ./project.jar
# 배포 파일 및 설정을 하나의 tar.gz 파일로 압축
- name: 압축하기
run: tar -czvf $GITHUB_SHA.tar.gz project.jar appspec.yml scripts
# AWS에 접근하기 위한 자격 증명 설정
- name: AWS credentials 설정
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-2 # AWS 리소스가 위치한 리전 (서울 리전)
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS 액세스 키 ID
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS 시크릿 액세스 키
# 압축된 파일을 AWS S3 버킷에 업로드
- name: S3에 프로젝트 폴더 업로드
run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.tar.gz s3://mytestserverbucket/$GITHUB_SHA.tar.gz
# AWS CodeDeploy를 사용해 EC2에 배포 명령 실행
- name: CodeDeploy를 활용해 EC2에 프로젝트 배포
run: |
aws deploy create-deployment \
--application-name test-server \ # CodeDeploy 애플리케이션 이름
--deployment-config-name CodeDeployDefault.AllAtOnce \ # 배포 설정 (한 번에 배포)
--deployment-group-name Production \ # CodeDeploy 배포 그룹 이름
--s3-location bucket=mytestserverbucket,bundleType=tgz,key=$GITHUB_SHA.tar.gz # S3에서 파일 로드