프로젝트 진행 당시에 경험해 보지 못했던 CI/CD 진행해보려했다.
여러 방법들이 있지만 나는 Docker와 GitHub Actions를 경험해 보고 싶었고 또한 컨테이너를 이용해 무중단 배포를 경험해 보고 싶었기 때문에 두가지를 이용해서 CI/CD를 경험해 보기로 했다.
그러기 위해선 GitHub Actions과 Docker에 대한 이해가 필요했다.
아직은 무중단 배포까지 진행하지는 않았고 배포 자동화까지만 진행하였다.
Workflow
자동화된 전체 프로세스. 하나 이상의 Job으로 구성되고, Event에 의해 예약되거나 트리거될 수 있는 자동화된 절차
Workflow 파일은 YAML으로 작성되고, Github Repository의 .github/workflows 폴더 아래에 저장
Github에게 YAML 파일로 정의한 자동화 동작을 전달하면, Github Actions는 해당 파일을 기반으로 그대로 실행
Event
Workflow를 실행하는 특정 활동이나 규칙.
e.g. 누군가가 커밋을 리포지토리에 푸시하거나 풀 요청이 생성 될 때 GitHub에서 활동이 시작
Job
Job은 여러 Step으로 구성되고, 단일 가상 환경에서 실행된다. 다른 Job에 의존 관계를 가질 수도 있고, 독립적으로 병렬로 실행될 수도 있다.
Step
Job 안에서 순차적으로 실행되는 프로세스 단위. step에서 명령을 내리거나, action을 실행할 수 있다.
Action
job을 구성하기 위한 step들의 조합으로 구성된 독립적인 명령. workflow의 가장 작은 빌드 단위이다. workflow에서 action을 사용하기 위해서는 action이 step을 포함해야 한다. action을 구성하기 위해서 레포지토리와 상호작용하는 커스텀 코드를 만들 수도 있다. 사용자가 직접 커스터마이징하거나, 마켓플레이스에 있는 action을 가져다 사용할 수도 있다.
Runner
Gitbub Action Runner 어플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스
위 내용들은 주요 내용들을 간단히 정리한 것이다.
GitHub Actions에 대해 간단한 설명을 하자면 소프트웨어 workflow를 자동화할 수 있도록 도와주는 도구로 정리할 수 있다.
더 쉽게 표현하면 github로부터 작은 서버를 일시적으로 대여하여 해당 서버에서 여러 동작을 코드로 자동화하여 실행시키는 동작을 하는 서비스라고 생각하면 좋을 것 같다.
name: Build and Deploy
on:
push:
branches:
- develop
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Gradle execute permission for gradlew
run: chmod +x ./gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:latest
- name: Get Github Actions IP
id: ip
uses: haythem/public-ip@v1.3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Add Github Actions IP to Security group
run: |
aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
- name: Deploy to Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
./deploy.sh
- name: Remove Github Actions IP From Security Group
if: always() # 실패하더라도 실행됨
run: |
aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
AWS_SG_ID : EC2 보안그룹으로 지정되어있는 '보안그룹의 ID' 이다. (보안그룹에서 확인 후 설정)
EC2_HOST : EC2의 퍼블릭 IPv4 주소.
EC2_PASSWORD : EC2에 접근/연결 설정해두었던 패스워드.
EC2_SSH_PORT : SSH로 접근할 port.
EC2_USERNAME : EC2의 계정명.
AWS_REGION: 리전 (e.g. ap-northeast-2)
AWS_ROLE: ROLE의 ARN (Role 을 사용하는 것은 AWS 권고 사항)
EC2_SSH_KEY: .pem키 전체내용.
Docker는 인프라와 애플리케이션을 분리하여 컨테이너로 추상화함으로써 소프트웨어를 효율적이고 빠르게 배포할 수 있게 해준다. 각 컨테이너는 독립적인 프로그램처럼 격리된 환경에서 실행되며, 하나의 호스트 OS에서 여러 개의 컨테이너를 동시에 실행할 수 있다.
컨테이너는 불필요한 OS 생성을 피하면서 애플리케이션을 실행하고 관리할 수 있어, 인프라를 별도로 분할하지 않아도 된다. 이는 확장성과 속도가 뛰어난 특징을 제공한다.
또한 Docker는 가상 머신보다 자원 효율적이며, 성능적으로도 뛰어난 장점이 있다. 특히 개발 환경을 쉽게 동기화할 수 있어, 다양한 환경에서 동일한 애플리케이션을 실행할 수 있도록 지원한다.
결론적으로, Docker를 사용하는 주요 이유는 개발 환경을 쉽게 동기화할 수 있기 때문이다.
레포지토리가 있다고 가정하고 방법에 대해 작성하면 아래와 같은 방식으로 진행이 된다.
GitHub Actions의 Runner와 DockerHub를 이용해서 진행되는데 핵심만 간단히 요약하면
참고
[1] aws-actions/configure-aws-credentials 인증 설정(iam role을 제어하는것을 권장)
[2] [CI/CD] Github Actions 에서 AWS Credential 을 AccessKey 로??
[3] Plain jar vs Executable jar(feat. plain jar 생성 방지)