![[Github Actions] 기본 문법과 배포 자동화 방법](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV9vtv%2FbtsOoeQNQkb%2FbrLXmSeYlnYOfRwnKwhFHK%2Fimg.png)
이 글은 인프런의 지식 공유자 박재성님의 강의를 듣고 개인적으로 정리하는 글임을 알립니다.
기본 문법
GitHub Actions에서 가장 중요한 파일은 deploy.yml인데, 이 파일의 위치는 git으로 관리되는 최상위 루트의 .github/workflows/ 폴더에 저장되어 있어야 한다.
-> 최상위루트/.github/workflows/deploy.yml(deploy.yml 의 이름은 변경해도 상관없다.)
# Workflow의 이름 # Workflow : 하나의 yml 파일을 하나의 Workflow라고 부른다. name: Github Actions 실행시켜보기 # Event : 실행되는 시점을 설정 # main이라는 브랜치에 push 될 때 아래 Workflow를 실행 on: push: branches: - main # 하나의 Workflow는 1개 이상의 Job으로 구성된다. # 여러 Job은 기본적으로 병렬적으로 수행된다. jobs: # Job을 식별하기 위한 id My-Deploy-Job: # Github Actions를 실행시킬 서버 종류 선택 runs-on: ubuntu-latest # Step : 특정 작업을 수행하는 가장 작은 단위 # Job은 여러 Step들로 구성되어 있다. steps: - name: Hello World 찍기 # Step에 이름 붙이는 기능 run: echo "Hello World" # 실행시킬 명령어 작성 - name: 여러 명령어 문장 작성하기 run: | echo "Good" echo "Morning" # 참고: https://docs.github.com/en/actions/learn-github-actions/variables - name: Github Actions 자체에 저장되어 있는 변수 사용해보기 run: | echo $GITHUB_SHA echo $GITHUB_REPOSITORY - name: Github Actions Secret 변수 사용해보기 run: | echo ${{ secrets.MY_NAME }} echo ${{ secrets.MY_HOBBY }} |
시크릿 변수는 아래와 같이 지정할 수 있다.
배포 자동화
EC2에서 빌드하는 방식
이 방식은 깃허브에 저장된 소스코드를 PULL 하고 EC2에서 빌드하는 방식이다.
빌드는 컴퓨터의 자원을 많이 소모하는 작업이다.
장점
- git pull을 활용해서 변경된 부분의 프로젝트 코드에 대해서만 업데이트 하기 때문에 CI/CD 속도가 빠르다.
- CI/CD 툴로 Github Actions만 사용하기 때문에 인프라 구조가 복잡하지 않고 간단하다.
단점
- 빌드 작업을 EC2에서 직접 진행하기 때문에 운영하고 있는 서버의 성능에 영향을 미칠 수 있다.
- Github 계정 정보가 해당 EC2에 저장되기 때문에 개인 프로젝트 또는 믿을만한 사람들과 같이 진행하는 토이 프로젝트에서만 사용해야 한다.
EC2에 깃허브 액세스 토큰 저장
이렇게 하면 프라이빗 저장소의 git pull을 비밀번호를 입력하지 않아도 된다.git config --global credential.helper store git pull origin main # Github 계정 및 비밀번호 입력 git pull origin main # 더 이상 비밀번호를 안 묻는 걸 확인할 수 있다.
하지만 ~/.git-credentials 경로에 엑세스 토큰이 그대로 저장되기 때문에 개인 프로젝트 또는 믿을만한 사람들과 같이 진행하는 토이 프로젝트에서만 사용해야 한다.
이 방법은 주로 개인 프로젝트에서 CI/CD를 심플하고 빠르게 적용시키고 싶을 때 적용한다.
name: Deploy To EC2 on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: SSH로 EC2에 접속하기 uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.EC2_HOST }} # EC2의 주소 username: ${{ secrets.EC2_USERNAME }} # EC2 접속 username key: ${{ secrets.EC2_PRIVATE_KEY }} # EC2접속을 위한 pem 파일 내부 정보 script_stop: true # 아래 script 중 실패하는 명령이 하나라도 있으면 실패로 처리 script: | cd /home/ubuntu/instagram-server # 여기 경로는 자신의 EC2에 맞는 경로로 재작성 git pull origin main ./gradlew clean build sudo fuser -k -n tcp 8080 || true # || true를 붙인 이유는 8080에 종료시킬 프로세스가 없더라도 실패로 처리하지 않기 위해서이다. # jar 파일을 실행시키는 명령어이다. 그리고 발생하는 로그들을 ./output.log 파일에 남기는 명령어이다. nohup java -jar build/libs/*SNAPSHOT.jar > ./output.log 2>&1 & |
이렇게 설정하면 output.log를 통해서 로그 데이터를 읽을 수 있다.
민감한 값을 따로 application.yml로 분리하는 경우가 많다. 민감한 값이기에 .gitignore에 추가해서 application.yml가 버전관리 되지 않게 세팅한다.
.gitignore
... 프로젝트경로/src/main/resources/application.yml |
deploy.yml
name: Deploy To EC2 on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: SSH로 EC2에 접속하기 uses: appleboy/ssh-action@v1.0.3 env: APPLICATION_PROPERTIES: ${{ secrets.APPLICATION_PROPERTIES }} with: host: ${{ secrets.EC2_HOST }} # EC2의 주소 username: ${{ secrets.EC2_USERNAME }} # EC2 접속 username key: ${{ secrets.EC2_PRIVATE_KEY }} # EC2의 Key 파일의 내부 텍스트 envs: APPLICATION_PROPERTIES script_stop: true # 아래 script 중 실패하는 명령이 하나라도 있으면 실패로 처리 script: | cd /home/ubuntu/instagram-server # 여기 경로는 자신의 EC2에 맞는 경로로 재작성 rm -rf src/main/resources/application.yml git pull origin main echo "$APPLICATION_PROPERTIES" > src/main/resources/application.yml ./gradlew clean build sudo fuser -k -n tcp 8080 || true # || true를 붙인 이유는 8080에 종료시킬 프로세스가 없더라도 실패로 처리하지 않기 위해서이다. nohup java -jar build/libs/*SNAPSHOT.jar > ./output.log 2>&1 & |
이렇게 하면 application.yml이 git에 추적되지 않고, 배포를 자동화 할 수 있다.
당연히 깃허브 액션의 시크릿에 APPLICATION_PROPERTIES 이름으로 yml 파일의 내용을 추가해야한다.
GitHub Actions에서 빌드하는 방식(SCP)
이 방식은 빌드를 GitHub Actions에서 하는 방식이다.
장점
- 빌드 작업을 Github Actions에서 하기 때문에 운영하고 있는 서버의 성능에 영향을 거의 주지 않는다.
- CI/CD 툴로 Github Actions만 사용하기 때문에 인프라 구조가 복잡하지 않고 간단하다.
단점
- 무중단 배포를 구현하거나 여러 EC2 인스턴스에 배포를 해야 하는 상황이라면, 직접 Github Actions에 스크립트를 작성해서 구현해야 한다.
이 방법은 현업에서 초기 서비스를 구축할 때 이 방법을 많이 활용한다.
처음 서비스를 구현할 때는 대규모 서비스에 적합한 구조로 구현하지 않는다.
deploy.yml
name: Deploy To EC2
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Github Repository 파일 불러오기
uses: actions/checkout@v4
- name: JDK 17버전 설치
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: application.yml 파일 만들기
run: echo "${{ secrets.APPLICATION_PROPERTIES }}" > ./src/main/resources/application.yml
- name: 테스트 및 빌드하기
run: ./gradlew clean build
- name: 빌드된 파일 이름 변경하기
run: mv ./build/libs/*SNAPSHOT.jar ./project.jar
- name: SCP로 EC2에 빌드된 파일 전송하기
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
source: project.jar
target: /home/ubuntu/instagram-server/tobe
- name: SSH로 EC2에 접속하기
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script_stop: true
script: |
rm -rf /home/ubuntu/instagram-server/current
mkdir /home/ubuntu/instagram-server/current
mv /home/ubuntu/instagram-server/tobe/project.jar /home/ubuntu/instagram-server/current/project.jar
cd /home/ubuntu/instagram-server/current
sudo fuser -k -n tcp 8080 || true
nohup java -jar project.jar > ./output.log 2>&1 &
rm -rf /home/ubuntu/instagram-server/tobe
테스트 및 빌드
- ./gradlew clean build 명령어를 사용해 Gradle로 프로젝트를 빌드하고, 테스트한다.
빌드된 파일 이름 변경
- 빌드된 JAR 파일의 이름에 SNAPSHOT이 포함된 경우, 이 파일을 project.jar로 이름을 변경한다.
SCP로 EC2에 빌드된 파일 전송
- appleboy/scp-action@v0.1.7 액션을 사용해 EC2 인스턴스로 project.jar 파일을 전송한다.
- host, username, key는 GitHub Secrets에 저장된 값들을 사용하여 SSH 접속을 설정한다.
- source는 전송할 파일(project.jar), target은 EC2 인스턴스의 경로이다.
SSH로 EC2에 접속하여 명령어 실행
appleboy/ssh-action@v1.0.3 액션을 사용해 SSH로 EC2에 접속한 후, 다음 작업을 수행한다
- 현재 실행 중인 서버 파일 삭제: /home/ubuntu/instagram-server/current 디렉터리를 제거하고 새로 생성한다.
- 파일 이동: SCP로 전송된 파일을 /tobe 디렉터리에서 /current 디렉터리로 이동시킨다.
- 현재 실행 중인 프로세스 종료: 현재 8080 포트에서 실행 중인 프로세스를 강제로 종료한다.
- 새로 배포한 파일 실행: nohup 명령어를 사용해 백그라운드에서 project.jar을 실행한다.
- 배포 완료 후 불필요한 파일 삭제: /tobe 디렉터리를 삭제한다.
Docker를 이용한 컨테이너 기반 CI/CD(중단 후 배포)
이 방식은 Docker 이미지를 빌드하고 Docker Hub에 푸시한 후, 원격 EC2 서버에서 Docker Compose를 이용해 배포하는 방식이다.
이미지를 외부에 노출시키고 싶지 않다면, 도커 허브가 아니라 프라이빗 레지스트리를 사용해야한다.
도커 허브의 프라이빗 레지스트리를 사용하거나 아마존 ECR를 사용하면 된다.
deploy.yml
name: CI/CD for test
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Create application.yml from secrets
run: echo "${{ secrets.R2R_APPLICATION_YML }}" > ./SpringBoot/src/main/resources/application.yml
- name: Build with Gradle
run: ./gradlew clean build
working-directory: ./SpringBoot # SpringBoot 디렉터리에서 빌드 실행
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to DockerHub
run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
# SpringBoot 애플리케이션 이미지 빌드 및 푸시
- name: Build and push test image
run: |
docker build -t <도커 허브 계정>/test ./SpringBoot/
docker push <도커 허브 계정>/test
# MySQL 이미지 빌드 및 푸시
- name: Build and push MySQL image
run: |
docker build -t <도커 허브 계정>/test-db ./mysql/
docker push <도커 허브 계정>/test-db
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- name: SSH to EC2 and Deploy
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.R2R_EC2_HOST }}
username: ${{ secrets.R2R_EC2_USERNAME }}
key: ${{ secrets.R2R_EC2_PRIVATE_KEY }}
script: |
cd /home/ubuntu/test
docker compose down
docker compose pull
docker compose up -d
compose.yml
services:
<서비스 이름>:
image: <도커 허브 계정>/<이미지 이름>
platform: linux/amd64
ports:
- 80:8080
depends_on:
<앱이름-db>:
condition: service_healthy
networks:
- <네트워크 이름>
<앱이름-db>:
image: <도커 허브 계정>/<이미지 이름>
platform: linux/amd64
environment:
MYSQL_ROOT_PASSWORD: <루트 패스워드>
MYSQL_DATABASE: <데이터베이스 이름>
MYSQL_USER: <유저 이름>
MYSQL_PASSWORD: <유저 패스워드>
volumes:
- <볼륨 이름>:/var/lib/mysql
ports:
- 3306:3306
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
retries: 10
networks:
- <네트워크 이름>
networks:
<네트워크 이름>:
volumes:
<볼륨 이름>:
'DevOps > CI & CD' 카테고리의 다른 글
[Github Actions] CI/CD 기본 개념 (0) | 2024.09.13 |
---|