이 글은 Travis CI나 GitHub Action에 대해 자세히 설명하지 않습니다.
이동욱 님의 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스'라는 책으로 Travis CI를 통해 CI/CD를 구현한 적이 있다. 이 경험을 살려 진행하는 팀 프로젝트에서 Travis CI로 CI 환경을 구축했는데 문제가 발생했다.
Travis CI는 무료인 줄 알았는데 아니었다...
처음 가입할 때 1달러도 빼갔었다.
여하튼 프로젝트는 진행해야 해서 대안을 알아보던 중 Jekins와 GitHub Action 두 가지 선택지가 나왔다.
Jenkins
어디서 들은 말인데, "퇴사할 때 네가 하던 일을 젠킨스한테 시키고 나가"라는 농담을 할 정도로 주기적인 일을 하는 시종이라고 생각하면 된다.
하지만 진입 장벽이 높다는 단점을 가지고 있다. Jekins에 대해 좀 더 자세히 알고 싶다면 다음 영상을 추천한다.
https://www.youtube.com/watch?v=JPDKLgX5bRg
GitHub Action
GitHub에서 제공하는 무료 솔루션으로 Travis CI와 비슷하게 CI 환경을 구축할 수 있다고 한다. 즉, 배우기 쉽고 현재 쓰는 구현되어있는 방식과 비슷하다. 따라서 Travis CI를 GitHub Action으로 마이그레이션 했다.
프로젝트 환경
프로젝트는 Front-End로 React, Back-End로 Spring Boot를 사용하였고 둘을 한 번에 Jar로 빌드한 후 배포하는 방식으로 CI/CD를 구축하였다.
내가 원하는 CI/CD 동작은 다음과 같다.
1. Front-End 프로젝트를 빌드 후 Spring Boot의 resources/static 폴더로 빌드 파일 이동
2. Spring Boot를 jar 파일로 빌드
3. 빌드된 jar 파일과 빌드 스크립트들(scripts/.)을 묶어서 압축(. zip)
4. 압축 파일을 AWS S3에 업로드
5. S3에 업로드가 완료되면 AWS Codedeploy에 배포 요청
6. S3에 업로드된 압축파일을 AWS EC2에 배포
7. 배포 스크립트 동작
위 단계를 하나씩 비교하며 GitHub Action으로 옮겨보겠다.
0 단계: 환경 세팅
Travis CI
language: java
jdk:
- openjdk11
branches:
only:
- main
GitHub Action
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
CICD:
name: CI/CD with Gradle
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
우선 Java 11 버전 설정과 main 브랜치에 푸시할 경우 동작을 하도록 설정한다.
1단계: Front-End Back-End 빌드
Travis CI
이 부분은 Travis CI에서 문제가 있었다. 0단계의 환경 세팅에서 language: java를 설정하면 java가 설치된 runner에서 작업을 진행하는데 이 runner의 node.js와 npm의 버전이 프로젝트의 버전과 맞지 않아 Front-End 빌드에 문제가 생겼다. 원래는 Gradle에서 빌드를 모두 진행하였지만 Travis CI에서는 Front-End 빌드를 따로 진행했다.
before_install:
- chmod +x gradlew
- npm cache clean -f
- curl -s https://deb.nodesource.com/setup_16.x | sudo bash
- sudo apt-get -y install nodejs
- npm install -g nvm
- nvm install 16
- nvm use 16
- npm install -g npm@8.5.0
- node --version
- npm --version
#Travis Ci 서버의 Home
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
script: "./gradlew clean build"
nvm을 이용해 npm 버전을 관리할 수 있는데 이를 활용하여 Front-End를 빌드하였다. 그리고 Gradle을 이용해 Spring Boot를 빌드하였다.
GitHub Action
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
with:
arguments: build
GitHub Action의 steps 아래에 다음 코드를 이어서 넣는다.
GitHub Action에서는 Front-End 빌드 관련 이슈가 없었기 때문에 Gradle을 통해 빌드하였다.
3단계: 빌드 스크립트와 빌드 파일 압축
Travis CI
프로젝트 루트에 scripts라는 폴더에 있는 빌드 스크립트들을 jar, appspec.yml(Codedeploy에서 사용)와 함께 압축한다.
before_deploy:
- mkdir -p before-deploy # zip에 포함시킬 파일들을 담을 디렉토리 생성
- cp scripts/*.sh before-deploy/
- cp appspec.yml before-deploy/
- cp build/libs/*SNAPSHOT.jar before-deploy/
- cd before-deploy && zip -r before-deploy * # before-deploy로 이동 후 전체 압축
- cd ../ && mkdir -p deploy # 상위 디렉토리로 이동 후 deploy 디렉토리 생성
- mv before-deploy/before-deploy.zip deploy/facealbum-springboot-server.zip # deploy로 zip파일 이동
GitHub Action
- name: Before deply
run: |
mkdir -p before-deploy
cp scripts/*.sh before-deploy/
cp appspec.yml before-deploy/
cp build/libs/*SNAPSHOT.jar before-deploy/
cd before-deploy && zip -r before-deploy *
cd ../ && mkdir -p deploy
mv before-deploy/before-deploy.zip deploy/facealbum-springboot-server.zip
4단계: 압축 파일을 AWS S3에 업로드 및 Codedeploy로 배포
이 부분은 암호키가 필요하기 때문에 키를 환경변수로 설정하여 사용하였다.
Travis CI
Settings에서 환경변수를 저장하여 사용하였다.
deploy:
- provider: s3
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: static-build-bucket # S3 버킷
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근을 private으로
local_dir: deploy # before_deploy에서 생성한 디렉토리
wait-until-deployed: true
on:
all_branches: true
- provider: codedeploy
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: static-build-bucket # S3 버킷
key: facealbum-springboot-server.zip # 빌드 파일을 압축해서 전달
bundle_type: zip
application: facealbum-springboot-server # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
deployment_group: facealbum-springboot-server-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
region: ap-northeast-2
wait-until-deployed: true
on:
all_branches: true
GitHub Action
Secrets > Actions에 환경변수를 저장하여 사용하였다.
- name: AWS config
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 AWS S3
run: aws s3 cp deploy/facealbum-springboot-server.zip s3://faceablum-springboot-build
- name: Codedeploy
run: aws deploy create-deployment --application-name facealbum-springboot-server --deployment-group-name facealbum-springboot-server-group --s3-location bucket=faceablum-springboot-build,bundleType=zip,key=facealbum-springboot-server.zip
전체 파일
Travis CI
language: java
jdk:
- openjdk11
branches:
only:
- main
#빌드 전 권한 추가
before_install:
- chmod +x gradlew
- npm cache clean -f
- curl -s https://deb.nodesource.com/setup_16.x | sudo bash
- sudo apt-get -y install nodejs
- npm install -g nvm
- nvm install 16
- nvm use 16
- npm install -g npm@8.5.0
- node --version
- npm --version
#Travis Ci 서버의 Home
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.gradle'
script: "./gradlew clean build"
before_deploy:
- mkdir -p before-deploy # zip에 포함시킬 파일들을 담을 디렉토리 생성
- cp scripts/*.sh before-deploy/
- cp appspec.yml before-deploy/
- cp build/libs/*SNAPSHOT.jar before-deploy/
- cd before-deploy && zip -r before-deploy * # before-deploy로 이동 후 전체 압축
- cd ../ && mkdir -p deploy # 상위 디렉토리로 이동 후 deploy 디렉토리 생성
- mv before-deploy/before-deploy.zip deploy/facealbum-springboot-server.zip # deploy로 zip파일 이동
deploy:
- provider: s3
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: static-build-bucket # S3 버킷
region: ap-northeast-2
skip_cleanup: true
acl: private # zip 파일 접근을 private으로
local_dir: deploy # before_deploy에서 생성한 디렉토리
wait-until-deployed: true
on:
all_branches: true
- provider: codedeploy
access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
bucket: static-build-bucket # S3 버킷
key: facealbum-springboot-server.zip # 빌드 파일을 압축해서 전달
bundle_type: zip
application: facealbum-springboot-server # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
deployment_group: facealbum-springboot-server-group # 웹 콘솔에서 등록한 CodeDeploy 배포 그룹
region: ap-northeast-2
wait-until-deployed: true
on:
all_branches: true
GitHub Action
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
CICD:
name: CI/CD with Gradle
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
with:
arguments: build
- name: Before deply
run: |
mkdir -p before-deploy
cp scripts/*.sh before-deploy/
cp appspec.yml before-deploy/
cp build/libs/*SNAPSHOT.jar before-deploy/
cd before-deploy && zip -r before-deploy *
cd ../ && mkdir -p deploy
mv before-deploy/before-deploy.zip deploy/facealbum-springboot-server.zip
- name: AWS config
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 AWS S3
run: aws s3 cp deploy/facealbum-springboot-server.zip s3://faceablum-springboot-build
- name: Codedeploy
run: aws deploy create-deployment --application-name facealbum-springboot-server --deployment-group-name facealbum-springboot-server-group --s3-location bucket=faceablum-springboot-build,bundleType=zip,key=facealbum-springboot-server.zip
정리 및 회고
개인적으로 GitHub Action이 좀 더 쉬웠다. 그리고 Travis CI의 경우 Front-End 빌드가 되지 않아 하룻밤을 샜다... 근데 GitHub Action에서 한 번에 되니 그 고생을 왜 했나 싶었다. 어찌 되었건 두 가지 CI 툴을 조금 사용(체험?)해보았는데 Travis CI와 GitHub Action 모두 공식 Document는 잘 되어있었지만 그래도 쓰기 편한 건 GitHub Action이었던 것 같다.
그리고 일반적인 workflow를 GitHub 홈페이지에서 검색을 통해 찾을 수 있어서 도움이 되었다!
'DevOps' 카테고리의 다른 글
[쿠버네티스 인 액션] - 실습 트러블슈팅 (0) | 2022.12.07 |
---|