졸업 작품을 하면서 서버를 배포를 하려고 했다.
이전에 했던 프로젝트에서는 젠킨스를 활용해 CI/CD 를 하여 ec2에 서버를 띄우도록 구축을 해두었다.
이번에는 CI 를 깃헙 액션으로 했였고, 기존에 젠킨스로 했던 것들을 깃헙액션으로 하고 싶어 깃헙 액션으로 CI/CD 작업을 해주었다.
또한 이전에는 git pull 을 활용해 ec2에서 깃허브를 통해 코드를 내려받고, 서버를 띄우도록 하였는데, 이번에는 AWS 의 CodeDeploy 와 AWS S3 를 활용하여 CD 를 동작시켰다.
하면서 조금 꼬인 부분도 많았고, 과정이 쉽지가 않았기 때문에 간단하게 한 과정을 기록해두었다.
github actions 에서 공식적으로 권장하는 배포프로세스는 2가지가 있다.
나는 1번 방법으로 서버를 띄웠다. 기존에 이미 배포용으로 EC2를 만들었고, RDS 설정 까지 해두었기 때문에 이 방법을 활용해야 미리 만들어둔 EC2를 사용할 수 있을 것이라 판단했다.
2번 방법은 간단하게 말해 AWS ECR 이라는 도커 이미지 저장 레지스트리에 도커 이미지를 정장 한 후, Task Definition 을 기반으로 클러스터에 인스턴스를 생성한 후, AWS ECR에 도커 이미지 자체를 배포하는 개념이다. → 개념적으로 모르는 내용이 많아서 공부하고 적용하는데 시간이 오래걸린다 판단했다.
이 방법은 여러 서버 인스턴스를 관리하는데 편하다는 장점이 있다고 한다.
큰 그림을 위와 같다.
용어 정리
애플리케이션 배포를 자동화
하는 배포 서비스
이다. 쉽게 말해 배포를 자동화할 수 있도록 해주는 서비스온라인 스토리지
웹 서비스. 저장소라 이해하면 될 것 같다.나는 기존에 미리 띄워둔 EC2에 해당 내용을 적용했다.
리눅수 우분투 20.04
버전 확인 필요!! → CodeDeploy Agent 설치 시 버전에 따라 설치 방법이 다르다. 자세한 내용은 뒤에 명시할 예정Mysql
로 적용CodeDeploy 를 생성하기 전에 어떤 인스턴스에서 수행할지 구분하는 값으로 태그를 사용해야한다.
사용하려는 ec2 인스턴스에서 태그를 직접 만들어 주면된다.
나는 mokindang-ec2-tag
라는 이름으로 태그를 만들어 주었다.
IAM역할 이란?
계정에 생성할 수 있는, 특정 권한을 지닌 IAM 자격 증명입니다.
역할을 만들어주는 이유
IAM → 역할 → 역할 생성
EC2 에서 사용할 것이니 위와 같이 만들어 준다.
S3에 있는 파일을 접근해야하니 AmazonS3FullAccess 권한
을 추가한다.
권한 추가 후 생성 완료. 나는 mokindang-ec2-iam-role
라는 이름으로 IAM 역할을 만들어 주었다.
이제 만들어 둔 IAM 역할을 EC2에 적용 해주자.
ec2 → 인스턴스 → 인스턴스 상세 → 작업 → 보안 → IAM 역할 설정
위와 같이 바꿔주면된다.
이제 해당 EC2 인스턴스는 S3에 접근할 권한이 주어진다.
CodeDeploy Agnet 를 설치해햐한다. 설치는 CodeDeploy 를 통해 배포할 EC2의 인스턴스에 설치 해야한다.
이 과정은 공식문서를 보고 따라 하면 된다. 사용하는 ec2에 설치된 os 의 종류와 버전에 따라서 설치방법이 다르기 때문에 꼭 공식문서를 보고 설치를 하는 것을 추천한다.
Ubuntu Server용 CodeDeploy 에이전트 설치 - AWS CodeDeploy
추가적으로 Agent 설치 시 버킷과 리전에 대한 명령어를 적는 부분이 있는데.
wget https://[mokindang-github-actions-s3-bucket](https://s3.console.aws.amazon.com/s3/buckets/mokindang-github-actions-s3-bucket?region=ap-northeast-2).s3.ap-northeast-2.amazonaws.com/latest/install
나는 다음과 같이 입력해주었다.
또한 리눅스 우분투 20.04 사용시
공식문서에서 언급한대로 설치를 한번에 제대로 하지 않으면 ruby 버전을 체크하라는 에러가 발생한다. AWS CodeDeploy 는 Ruby 2.xx
버전으로 설치를 해주어야한다.
일반적으로 리눅스 우분투 20.04
에서 ruby
를 설치한 경우 ruby 3.xx
버전이 설치된다. 공식문서에서 리눅스 우분투 16 버전 이상
방법대로 설치하지 않고, 다른 우분투 버전에서 사용하는 방법으로 AWS CodeDeploy 를 설치하면 ruby 3.xx 버전
이 설치된다.
이 경우 다시 제대로 16 버전 이상 방법
으로 설치를 해도 기존에 설치된 ruby 3.xx
버전 때문에 AWS CodeDeploy 가 제대로 설치가 되지 않는다.
이 경우 해결책은 2가지가 있다.
나는 2번째 방법으로 해결함
# Ubuntu 22.04에서의 CodeDeploy 설치
mkdir codedeploy-agent_1.3.2-1902_ubuntu22 #폴더명은 자율
dpkg-deb -R codedeploy-agent_1.3.2-1902_all.deb codedeploy-agent_1.3.2-1902_ubuntu22
sed 's/Depends:.*/Depends:ruby3.0/' -i ./codedeploy-agent_1.3.2-1902_ubuntu22/DEBIAN/control
dpkg-deb -b codedeploy-agent_1.3.2-1902_ubuntu22/
sudo dpkg -i codedeploy-agent_1.3.2-1902_ubuntu22.deb
systemctl list-units --type=service | grep codedeploy
sudo service codedeploy-agent start
설치가 완료 되면 아래 명령어로 CodeDeploy 를 실행한 후
sudo service codedeploy-agent start
아래 명령어로 제대로 동작하는지 확인해주면 된다.
sudo service codedeploy-agent status
AWS S3 버킷을 만들어주자.
빌드한 프로젝트를 압축해서 이 버킷에 저장을 해둔다. 이후 ec2 에서 버킷 내부에있는 파일을 가져와 사용할 수 있다.
Amazon S3 → 버킷 → 버킷 만들기
버킷 이름을 설정하고 리전을 선택한다.
ACL 은 비활성화 하였다.
그외 설정은 모두 default 상태로 두었다.
생성된 버킷 확인
Code Deploy 전용 IAM 만들기
IAM → 역할 → 역할 생성
AWS 서비스 선택 후, CodeDeploy를 적용한다.
역할 이름을 mokindang-codedeploy-iam
으로 만들어 주었다.
이제 CodeDeploy 애플리케이션을 생성 해주자.
개발자 도구 → CodeDeploy → 애플리케이션 → 애플리케이션 생성
하단과 같이 애플리케이션 이름과 컴퓨팅 폴랫폼을 지정해주자.
나는 mokindang-codedeploy-app
라는 이름으로 만들었고, 컴퓨팅 플랫폼은 EC2/온프레이스
를 선택 했다.
CodeDeploy 애플리케이션에서 사용하는 배포그룹을 생성해주자.
만들어두었던 CodeDeploy 애플리케이션에서 배포 그룹 생성을 누루면
배포 그룹 생성창이 나온다.
배포 그룹 이름을 입력하고, 해당 서비스의 역할을 설정 해주자. 이전에 만들어 두었던 mokindang-codeploy-iam
을 역할로 지정해주었다.
이후 어떤 인스턴스에서 동작할 지 선택한다.
사전에 EC2 인스턴스에서 태그를 추가해야 선택이 가능하다.
나는 기존에 태그를 추가해주었기 때문에 바로 적용하였다.
배포 설정 을 해준다. 로드밸런서는 사용하지 않았다.
이후 배포 그룹 생성을 눌러 배포 그룹을 생성해주었다.
이제 Github Actions 에서 사용할 IAM 사용자를 추가해주어야 한다.
GitHub Acions 에서 AWS 에 접근을 할때는 당연하게 권한이 필요하다.
이런 권한을 IAM 사용자로 추가하여 생성해줄 것 이다.
IAM → 사용자 → 사용자 생성
다음과 같이 입력해주었다.
위와 같이 AmazonS3FullAccess
AWSCodeDeployFullAccess
에 대한 권한을 주었다.
이제 생성을 해주자
생성 후 암호가 생성되는데 이를 기억해두자!!
나중에는 확인이 불가하다.
github 액션에서 CD 스크립트 실행 시, 키값을 기반으로 aws 에 접근을 하기 위해서 적용해주기 위해 액세스 키 를 만들어야 한다.
iam → 사용자 →사용자 선택 → 보안 자격 증명 → 액세스 키 만들기
액세스키를 만들었다.
저장해두자!
GitHub - action 에 키를 등록하자.
이전에 만들어둔 access key id 와 secret access key를 등록하자
CodeDeploy 에서 배포를 위해 참조할 AppSpec 파일을 작성하자.
AppSpec 는 로컬에서 작성한다.
AppSpec 파일을 이용해 프로젝트에 어떤 파일들을 EC2의 어떤 경로에 복사할지 설정이 가능하고, 배포 프로세스 이후에 수행할 스크립트를 지정하여 서버를 띄울 수 있다.
AppSpec 파일은 기본적으로 루트 디렉터리(프로젝트 최상단)에 위치해야한다.
AWS CodeDeploy는 appspec.yml에 명시된 어떤 파일들
을, 어느 위치
에 배포
하고, 이후 어떤 스크립트를 실행
시킬 것인지를 알수 있다. Appspec.yml 파일은 CodeDeploy 를 사용하기 위해서는 필수적이다.
#appspec.yml
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/app
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
hooks:
AfterInstall:
- location: scripts/stop.sh
timeout: 60
runas: ubuntu
ApplicationStart:
- location: scripts/start.sh
timeout: 60
runas: ubuntu
files:
- source: /
destination: /home/ubuntu/app
overwrite: yes
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
hooks:
AfterInstall:
- location: scripts/stop.sh
timeout: 60
runas: ubuntu
ApplicationStart:
- location: scripts/start.sh
timeout: 60
runas: ubuntu
배포 이후 수행할 스크립트를 지정할 수 있음.
AfterInstall
에서 기존에 실행중이던 애플리케이션을 종료시키고 ApplicationStart
에서 새로운 애플리케이션을 실행하도록 할 것임.
stop.sh
#!/usr/bin/env bash
PROJECT_ROOT="/home/ubuntu/app"
JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
TIME_NOW=$(date +%c)
# 현재 구동 중인 애플리케이션 pid 확인
CURRENT_PID=$(pgrep -f $JAR_FILE)
# 프로세스가 켜져 있으면 종료
if [ -z $CURRENT_PID ]; then
echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG
else
echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG
kill -15 $CURRENT_PID
fi
start.sh
#!/usr/bin/env bash
PROJECT_ROOT="/home/ubuntu/app"
JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"
APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"
TIME_NOW=$(date +%c)
# build 파일 복사
echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG
cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE
# jar 파일 실행
echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG
nohup java -jar -Dspring.profiles.active=deploy $JAR_FILE > $APP_LOG 2> $ERROR_LOG &
#프로파일 명시!!
CURRENT_PID=$(pgrep -f $JAR_FILE)
echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG
기존에 CI 작업을 해두었으니, github Actions 워크플로우에서 이미 빌드가 마쳤기 때문에 JAR 파일만 복사 후 실행한다.
이때 사용하는 프로파일을 꼭 명시할 것!!
위 스크립트를 보면 Jar 파일 복사 시, /build/libs/*.jar
파일을 $JAR_FILE
파일로 복사를 한다.
Spring Boot 2.5 부터는 빌드 시 일반 jar 하나와 -plin.jar 파일 하나가 만들어진다. 따라서 하나의 jar 파일만 만들어지도록 하기 위해 build.gradle 파일에 다음의 내용을 추가해야한다.
jar {
enabled = false
}
이제 Gitbub Actions Workflow 를 작성해보자
main 브랜치에 push 되는경우 ci/cd 를 모두 하도록 작성하였다.
name: Deploy to Amazon EC2
on:
push:
branches:
- main
pull_request:
branches:
- main
# 리전, 버킷 이름, CodeDeploy 앱 이름, CodeDeploy 배포 그룹 이름
env:
AWS_REGION: ap-northeast-2
S3_BUCKET_NAME: mokindang-github-actions-s3-bucket
CODE_DEPLOY_APPLICATION_NAME: mokindang-codedeploy-app
CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: mokindang-codedeploy-deployment-group
permissions:
contents: read
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
# (1) 기본 체크아웃
- name: Checkout
uses: actions/checkout@v3
# (2) JDK 11 세팅
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
# (3) Gradle build(빌드, 테스트는 ci 에서 하기 때문에 x)
- name: Build with Gradle
uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
with:
arguments: clean build -x test
# (4) AWS 인증 (IAM 사용자 Access Key, Secret Key 활용)
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
# (5) 빌드 결과물을 S3 버킷에 업로드
- name: Upload to AWS S3
run: |
aws deploy push \
--application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
--ignore-hidden-files \
--s3-location s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip \
--source .
# (6) S3 버킷에 있는 파일을 대상으로 CodeDeploy 실행
- name: Deploy to AWS EC2 from S3
run: |
aws deploy create-deployment \
--application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
--s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip
main 브랜치에 있는 코드를 배포할 것이니 트리거를 main에 push 되는 경우로 설정해주었다.
코드를 push 하면 다음과 같이 배포가 실행 된다.
배포 완료!
중간 중간 작은 이슈들
프로파일 설정을 해두는 것을 깜빡하고,,, 배포시 문제가 발생했음. 다음부턴 배포시 프로파일 적용 상태를 제대로 확인하기
참고한 내용
https://bcp0109.tistory.com/363
https://velog.io/@tjddnths0223/Github-Actions로-AWS-EC2에-Spring-Boot-배포하기-정리