DevKim
[Webtooniverse] CI/CD 구조 변경 - GitHub Action 본문
[Webtooniverse] CI/CD 구조 변경 - GitHub Action
on_doing 2021. 8. 11. 23:59더 나은 배포 프로세스를 적용하기 위해 docker + jenkins로 배포해보고,
jenkins + webhook으로 배포 자동화까지 간단한 실습에 적용해보면서
내부적으로 어떻게 동작하는지에 대해 생각할 수 있는 시간이었다.
과금 문제로 인해, 지금 당장은 Jenkins 대신 Github Action을 프로젝트에 적용해보려고한다.

현재 프로젝트 구조는 이렇다.
아직 캐싱 서버와, nginx는 도입하지 않았다. 필요하다고 느껴질 때 그 이유와 함께 도입할 예정이다.
[ GitThub Action ]
Github Action은 Github에서 바로 소프트웨어 Workflow를 자동화할 수 있는 도구이다.
간단하게 말하면 Github에서 직접 제공하는 CI/CD 도구이다.

장점
사용하면서 느꼈던 가장 장점은,
- seperate된 CI/CD 서비스를 사용하지 않아도된다는 점
- webhook을 set up하지 않아도된다는 점
- 다른 CI툴보다 단순하다는 점
용어
- Workflows
- : Github 저장소에서 발생하는 build, test, package, release, deploy 등 다양한 이벤트를 기반으로 직접 원하는 Workflow를 만들 수 있다. 즉, 작업 뭉치이다.
- 기본적인 방법은 저장소에 .github/workflows 폴더를 만들어서 .yml 형식 파일 만든 뒤 Workflow를 정의하는 것이지만, Github Action 툴에서 다양한 환경에 맞는 기본적인 workflows를 제공하고있다.
- Events: 작업 뭉치 동작하는 이벤트, push, release, pull request, 스케줄, 등
- Jobs: 동시 수행 가능한 작업 단위
- Runners: Job 실행되는 곳. GitHub이 제공하는 가상 머신(Linux, macOS, Windows)
- Steps: 순서대로 실행되는 쉘 커맨드 또는 action
- Actions: 재사용할 수 있는 job의 step.
[ 환경 ]
- ubuntu
- worker instance : aws ec2 t2.micro
- Java 8, Gradle 7.1.1
[ Workflow ]
직접 파일을 생성해도되지만, 제공해주는 Java with Gradle로 set up 해주었다.

생성된 gradle.yml 파일을 보자.
name: workflow이름
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 8
uses: actions/setup-java@v2
with:
java-version: '8'
distribution: 'adopt'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name
- 원하는 workflow의 이름을 적어주면된다.
- on
- Event that trigger workflows이다. 즉 특정 이벤트를 할 때 작동한다.
- 현재 test 환경에서는 main 브랜치에 push하거나 pr을 보냈을 때 작동하도록 설정했다.
- jobs
- Event가 발생하는 경우 jobs가 작동한다.
- runs-on : machine의 type을 정한다. (Ubuntu,macOs,Windows..)
- steps : 일련의 단계별 작업 (JDK 권한 설정 후 빌드)

[ github Action & S3, CodeDeploy 접근 키 생성 ]
S3는 AWS에서 제공하는 일종의 파일 서버이다. 현재 구조에서 Github Action애서 S3로 Jar파일을 전달해야기 때문에 외부 서비스가 AWS 서비스에 접근할 수 있는 권한 생성 해줘야한다. 이때 이러한 인증과 관련된 기능을 제공하는 서비스로 AWS에 IAM이 있다.
IAM을 통해 Github Action이 S3와 배포를 담당하는 CodeDeploy에 접근할 수 있도록하자.
[ 사용자 추가 ]
우선 사용자 추가를 해준다. 엑세스 유형은 프로그래밍 방식 엑세스이며, 권한 설정은 기존 정책 직접 연결을 눌러주고 S3full - & CodeDeployfull- 을 선택하여 각각의 권한을 추가해준다.
실제 서비스라면 '이 두개의 권한을 분리해서 관리'하기도 한다고한다.
권한 최종 확인을 마치면, 엑세스 키 ID와 비밀 엑세스 키가 생성되는데 이 두개가 Github Action에서 사용될 키이다.

[ github Action & S3 연동 ]
S3 버킷을 하나 만들어주자!
이때 jar 파일이 public일 경우 누구나 내려받게되면 코드나 설정값, 주요 키값들이 다 탈취될 수 있으므로 권한 설정에서 모든 퍼블릭 엑세스 차단을 선택해주어야한다.

[ Gradle.yml 파일 설정 ]
- name: Make Directory
run: mkdir -p deploy
- name: Copy Jar
run: cp ./build/libs/*.jar ./deploy
- name: Make zip file
run: zip -r ./webtooniverse.zip ./deploy
#Deploy
- name: Deploy Webtooniverse
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
aws-region: ap-northeast-2
- name: Upload to S3
run: aws s3 cp --region ap-northeast-2 --acl private ./webtooniverse.zip s3://webtooniverse/
- build 후 생성된 jar파일을 넣을 폴더 생성
- Jar파일 deploy 폴더로 복사
- CodeDeploy는 Jar 파일을 인식하지 못하기 때문에 압축한다.
- 외부 서비스와 연동될 행동들을 선언한다. (S3로 해당 zip 파일을 업로드)
Push하자마자 Github Action이 동작한다. 만약 빌드에 성공했다면 S3버킷에 업로드가 된 것을 확인할 수 있다.
[ EC2에 IAM 역할 추가 ]
EC2가 CodeDeploy를 연동 받을 수 있게 IAM 역할을 하나 생성해주자
* 앞에서 만든 사용자와의 차이점은 무엇일까? *
- 역할
- AWS 서비스에만 할당할 수 있는 권한
- Ec2.CodeDeploy 등
- 사용자
- AWS 서비스 외에 사용할 수 있는 권한
- 로컬 PC 등
CodeDeploy의 연동을 할 것이기 때문에, 정책에서 'AmazonEC2RoleforAWSCodeDeploy'를 선택해준다.
이렇게 만든 역할을 EC2 서비스에 등록하기 위해선, worker 인스턴스에서 해당 IAM 역할을 선택해준 뒤, 재부팅해준다.
[ EC2 서버에 CodeDeploy 에이전트 설치 ]
- Java 8 설치
sudo apt install openjdk-8-jdk
- 타임존 변경
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
- 기본 설치
sudo apt update
sudo apt install ruby-full
sudo apt install wget
- home/ubuntu 경로에 CodeDeploy 리소스 키트
cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
- install 파일에 권한 추가 & codeDeploy 에이전트 설치
chmod +x ./install
sudo ./install auto
#실행확인
sudo service codedeploy-agent status
[ CodeDeploy -> EC2 접근 권한 생성 ]
AWS 서비스 이므로 IAM 역할을 생성해준다.

[ CodeDeploy 생성 ]
- CodeDeploy는 AWS 의 배포 서비스로, 대체재가 없다. 현재 Code commit의 역할은 깃허브가, Code Build는 Github Action이 해주고있다.
애플리케이션 생성을 눌러서 컴퓨팅 프랫폼은 EC2/온프래미스를 선택해주자.

생성 후엔 배포 그룹을 생성해야하는데 이때 서비스 역할은 위에서 생성한 CodeDeploy용 IAM 역할을 선택하면된다.
현재는 배포 할 서비스가 1대의 Ec2이므로 배포 유형은 현재 위치로 선택해주었다.
배포 설정은 다음과 같이 선택해주자

설정하는 과정에서 태그를 선택 사항으로 입력할 수 있는데, 이때 태그 Name을 지정해줬다면 사용하고자하는 EC2서버에도 태그 Name을 지정해줘야 인식하는 이슈가 있었다. (EC2에 Name 태그를 지정해주지 않았을 땐, CodeDeploy가 EC2를 인식하지 못해 빌드에 실패했다. )
[ appspec.yml 생성 ]
Github Action의 설정은 gradle.yml에 넣어주었고, CodeDeploy 설정은 appspec.yml에 넣어주자
먼저, S3에서 넘겨줄 zip 파일을 저장할 디렉토리를 하나 생성해주자
/home/ubuntu/app
appspec.yml 파일은 build.gradle 파일과 동일선상의 위치에 하나 만들어주면된다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/app/
overwrite: yes
- source : /
- CodeDeploy에 전달해 준 파일 중 전체 파일을 이동
- destination: /home/ubuntu/app/
- source에서 보낸 파일을 저장 할 위치
- overwrite: yes
- 기존 파일이 있다면 덮어쓰기
[ gradle.yml 추가 ]
# appspec.yml 파일 복사
- name: Copy appspec.yml
run: cp appspec.yml ./deploy
...
# s3에 있는 zip 파일을 Deploy
- name: CodeDeploy
run: aws deploy create-deployment --application-name webtooniverse --deployment-group-name webtooniverse-group --s3-location bucket=webtooniverse,key=webtooniverse.zip,bundleType=zip
해당 브랜치로 push하면 AWS에서 배포 성공 여부를 알 수 있다. 성공했으면 Jar 파일을 배포해보자
[ deploy.sh 생성 ]
src파일과 동일선상에 scripts 파일을 하나 만들어서 그 안에 deploy.sh 파일을 만들어주자
#!/usr/bin/env bash
REPOSITORY=/home/ubuntu/app
echo "> 현재 구동 중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -fl webtooniverse | grep jar | awk '{print $1}')
echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then
echo "현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 애플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/webtooniverse-0.0.1-SNAPSHOT.jar | tail -n 1)
echo "> JAR NAME: $JAR_NAME"
echo "> $JAR_NAME 에 실행권한 추가"
chmod +x $JAR_NAME
echo "> $JAR_NAME 실행"
nohup java -jar $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
[ gradle.yml 추가 ]
script 파일도 S3에 보내준다.
# script files 복사
- name: Copy script
run: cp ./scripts/*.sh ./deploy
[ appspec.yml 추가 ]
permissions:
- object: /
pattern: "**"
owner: ubuntu
group: ubuntu
hooks:
ApplicationStart:
- location: deploy.sh
timeout: 60
runas: ubuntu
- permissions : CodeDeploy에서 EC2 서버로 넘겨준 파일들이 모두 ubuntu 권한을 갖게한다.
- hooks
- CodeDeploy 배포 단계에서 실행할 명령어를 지정
- 스크립트 실행시간이 60초가 넘어가면 실패
- deploy.sg를 ubuntu 권한으로 실행
Main 브랜치에 push시, 잘 배포되어진다.

[ properties 파일 은닉 ]
RDS 정보나, OAuth 같은 정보는 보호해야 할 정보이니 Git ignore에 포함되어있는 정보들이다.
따라서 RDS 접속 정보, OAuth 정보들을 EC2 서버에 직접 설정 파일 생성해야한다.
먼저, 기존의 properties와 같은 경로에 application-real.properties를 추가해준다.
real로 파일을 만들면 profile=real인 환경이 구성된다고 보면된다고한다. 보안상 이슈가 될 만한 설정들은 모두 제거하며 RDS 환경 profile 설정이 추가된다.
spring.profiles.include=real-db
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.session.store-type=jdbc
vim을 이용하여 특정 경로에 properties 파일을 만들어서 원래 properties에 있던 RDS 설정 코드를 넣어주자
vim /home/ubuntu/app/application-real-db.properties
deploy.sh가 real profile을 쓸 수 있도록 다음과 같이 sh파일을 변경한다.
nohup java -jar \
-Dspring.config.location=classpath:/application.properties,classpath:/application-real.properties,/home/ubuntu/app/application-real-db.properties \
-Dspring.profiles.active=real \
$JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
[ CodeDeploy 로그확인 ]
vim /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
'Spring Project > Webtooniverse' 카테고리의 다른 글
[ webtooniverse ] 프로젝트 구조 변경 - 무중단 배포 도입 (0) | 2021.08.16 |
---|---|
[Refactor] 쿼리 성능 개선 (0) | 2021.08.12 |
[Webtooniverse] Jenkins+webhook으로 Build&Deploy 자동화 적용 (0) | 2021.08.11 |
[ Webtooniverse ] 프로젝트에 CI/CD 를 도입하고자하는 이유 (0) | 2021.08.10 |
[ Webtooniverse ] 2차 DB 구축- 카카오 웹툰 크롤링 (0) | 2021.08.05 |