안녕하세요. 카페인 팀의 제이입니다. 저희 팀에서 CI/CD는 어떻게 진행되는지 작성하겠습니다.
CI (지속적 통합)
카페인 팀에서는 지속적 통합 즉 CI를 진행하기 위해서 위에 사진과 같이 Github Actions를 사용합니다.
main, develop 브랜치에 Push, Pull Request 요청이 들어간다면 이벤트가 발생하고, Github Actions를 통해 저희가 작성해둔 스크립트가 실행 됩니다.
이 스크립트에 여러가지를 등록할 순 있지만, 저희는 자동으로 테스트를 진행하도록 하였습니다. 자동으로 테스트를 돌리면서 테스트가 통과를 해야지만 Merge를 진행할 수 있습니다.
이를 통해 개발자의 실수를 줄일 수 있고 안정적으로 지속적 통합을 이룰 수 있게 됩니다.
CD (지속적 배포)
저희의 지속적 배포 아키텍처입니다.
순서를 요약하자면 다음과 같습니다.- Release 브랜치에 Push를 한다.
- Github Actions를 통해 Docker Hub에 레포지토리의 소스코드를 Docker Image로 빌드해서 Push 한다.
- 인프라 서버에서 Self Hosted Runner가 작동한다.
- 인프라 서버에서 배포 서버로 들어간다.
- 배포 서버 안에서 Docker Hub에 미리 업로드한 Docker Image를 Pull 해온다.
- 배포 서버 안에서 Docker Image를 컨테이너에 띄운다.
배포 자동화 툴 선택하기
먼저 배포 자동화 과정을 구축하기 위해서 여러가지 툴이 있습니다.
Travis, Jenkins, Github Actions 등등 여러가지가 있는데요.
저희 팀은 Github Actions
를 선택했습니다.
이를 선택한 여러가지 이유가 있었지만 저희 팀 누누를 제외하고 CI/CD 경험이 부족해서 비교적 쉽고 설치 및 큰 세팅이 없는 점이 저희한테는 매력적으로 다가왔습니다.
또한 Docker를 사용하는데, 이유는 다음과 같습니다.
- JDK 혹은 Node 버전을 관리할 수 있다.
- Docker Image를 빌드한 후 배포하기 때문에 서버 환경 차이로 발생하는 문제를 최소화할 수 있다.
- 배포 서버에서 Docker만 설치하고 Image를 받고 실행시키면 돼서 빠르고 쉽게 배포 환경을 구축할 수 있다.
과정
본격적으로 저희의 배포 자동화를 구축하는 과정을 설명하겠습니다.
1. Github Actions에 Runners 등록
먼저 Self Hosted Runner를 이용하기 때문에 저희는 위에 사진과 같이 Runners를 등록을 해줬습니다.
이를 등록을 할 때 제공해주는 설정 코드가 나오는데요. 이 코드들을 infra 서버에 모두 입력을 해주면서 설정을 해주시면 됩니다.
2. Github workflow 만들기다음으로는 저희가 수행하고자 하는 Task를 등록해주기 위해서 yml 파일을 만들어줍니다.
yml 파일의 경로는 ./github/workflows/
안에 만들어주면 됩니다.
name: deploy
# release/backend push 할 때
on:
push:
branches:
- release/backend
jobs:
# Docker
docker-build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
# Docker Hub 로그인
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'adopt'
- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew bootjar
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: Docker Hub 사용자명/이미지 이름
# Build 및 Docker image를 Docker Hub에 push
- name: Build and push Docker image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
with:
context: .
file: ./backend/Dockerfile
push: true
platforms: linux/arm64
tags: woowacarffeine/backend:latest
labels: ${{ steps.meta.outputs.labels }}
deploy:
runs-on: self-hosted
if: ${{ needs.docker-build.result == 'success' }}
needs: [ docker-build ]
steps:
# EC2 배포 서버로 접속
- name: Join EC2 dev server
uses: appleboy/ssh-action@master
env:
JASYPT_KEY: ${{ secrets.JASYPT_KEY }}
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_KEY }}
port: ${{ secrets.SERVER_PORT }}
envs: JASYPT_KEY
# 1. 도커 이미지 받기
# 2. 기존에 켜진 백엔드 서버(도커 이미지) stop
# 3. 최신 백엔드 서버 run
# 4. 사용하지 않는 이미지와 컨테이너 삭제
script: |
sudo docker pull woowacarffeine/backend:latest
sudo docker stop backend || true
sudo docker run -d --rm -p 8080:8080 \
-e "ENCRYPT_KEY=${{secrets.JASYPT_KEY}}" \
--name backend \
Docker Hub 사용자명/이미지 이름:latest
sudo docker image prune -f
저희 팀은 위와 같이 backend-deploy.yml 파일을 만들어주었습니다.
위에 yml에서 저희는 키를 숨겼는데요.
위에 사진과 같이 설정을 해주시면 됩니다.
그리고 이를 yml에서 사용하기 위해선 secrets.Key이름
으로 사용해주시면 됩니다.
이제 마지막으로 Dockerfile
을 만들어줍니다.
저희는 /backend/
경로에 만들어주었습니다.
FROM amazoncorretto:17-alpine-jdk
ARG JAR_FILE=./backend/build/libs/carffeine-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-Dspring.profiles.active=dev", "-jar","/app.jar"]
저희는 위처럼 절대 경로를 기준으로 JAR_FILE 위치를 지정하고, profiles는 dev로 설정해서 만들어주었습니다.
3. 배포하기
트리거를 작동시켜서 저희가 yml 파일에서 지정해준 것들이 잘 작동하는지 확인합니다.
위에 사진처럼 모든 Job이 성공적으로 통과하는 것을 보실 수 있습니다.
이렇게 인프라 서버에서 배포 서버로 들어가서 성공적으로 서버를 도커로 띄운 것을 보실 수 있습니다.
EC2 배포 서버에서 docker ps
를 입력했을 때에도 잘 실행이 되네요!
CD 배포 과정 요약
지속적 배포 과정을 요약 하자면 다음과 같습니다.
- Self Hosted Runner를 EC2 인프라 서버에 등록해준다.
- yml 파일과 Dockerfile을 만들어준다.
- 트리거를 작동시켜서 Github Actions의 태스크가 모두 잘 되는지 확인한다.
- 잘 됐다면 EC2 배포 서버에 Docker image가 성공적으로 띄워진다.