내맘대로 개발 일기/뽀모로 Do!

[CICD 구축] GitHub Actions 워크플로우 구성하기

bu119 2024. 6. 14. 18:00
728x90
반응형

워크플로우 개요

  • 이 GitHub Actions 워크플로우는 Java, Spring Boot, Ubuntu, Docker를 사용하여 CI/CD(Continuous Integration/Continuous Deployment)를 자동화하는 설정이다.
  • 이 워크플로우는 develop-be 브랜치에 push 또는 pull request 이벤트가 발생할 때 실행된다.
  • 두 개의 작업(Job)  build, deploy 로 구성되어 있다.
  • Docker를 사용하기 때문에 Docker 이미지를 빌드하는 단계에서 Dockerfile이 필요하다.

 

Dockerfile 준비하기

Dockerfile은 애플리케이션의 실행 환경을 코드로 정의하여, 어디서나 동일한 환경에서 애플리케이션을 실행할 수 있도록 한다. Dockerfile을 사용하여 Docker 이미지를 빌드한다.

FROM openjdk:17-alpine
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENV TZ=Asia/Seoul
ENTRYPOINT ["java","-jar","/app.jar","-Duser.timezone=Asia/Seoul"]
# -Duser.timezone=Asia/Seoul 옵션: 기본 타임존을 Asia/Seoul로 강제 설정
  • Dockerfile은 애플리케이션을 컨테이너화하는 데 필요한 명령어들을 포함하고 있다.
  • 여기에는 베이스 이미지를 설정하고, 필요한 종속성을 설치하고, 애플리케이션 코드를 추가하고, 실행 명령을 정의하는 내용이 포함된다

 

워크플로우 구성

# deploy.yml

name: CI/CD with Gradle, Docker, AWS

# develop-be 브랜치에 push 또는 PR 이벤트가 발생하면 워크플로우가 실행된다.
on:
  push:
    branches: [ "develop-be" ]
  pull_request:
    branches: [ "develop-be" ]

# 해당 Workflow의 Job 목록
jobs:
  build:
  # 이 작업이 실행되는 환경을 정의: 최신 Ubuntu 환경
    runs-on: ubuntu-latest
    # 리포지토리에 대한 읽기 권한을 부여한다.
    permissions:
      contents: read
      
    # build Job 내의 step 목록
    steps:
    ### CI
    # 레포지토리 체크아웃하여 레포지토리에 접근할 수 있게 한다.
    - name: Checkout Repository
      uses: actions/checkout@v4
      
    # JDK 버전 17 설정 (temurin 배포판 사용)
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'
        
    # Gradle 캐싱 설정으로 의존성 다운로드 시간을 줄인다.
    - name: Gradle Caching
      uses: actions/cache@v3
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('backend/pomoro-do/**/*.gradle*', 'backend/pomoro-do/**/gradle-wrapper.properties') }}
        restore-keys: |
          ${{ runner.os }}-gradle-
          
    # Gradle 래퍼 스크립트에 실행 권한 부여
    - name: Grant Execute Permission For Gradlew
      run: chmod +x backend/pomoro-do/gradlew
    
    # 테스트를 제외한 Gradle 빌드 수행 (CI 단계)
    - name: Build with Gradle
      run: cd backend/pomoro-do && ./gradlew build -x test

    ### CD
    # Docker Hub에 로그인 (보안) (CD 단계 준비)
    - name: Log in to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    # SpringBoot 어플리케이션의 Docker 이미지를 빌드하고 Docker Hub에 푸시 (CD 단계 준비)
    - name: Build and Push Docker Image for SpringBoot
      run: |
        cd backend/pomoro-do
        set -e # 명령 실패 시 스크립트 종료
        # 현재 디렉토리에 있는 Dockerfile을 사용하여 Docker 이미지를 빌드
        docker build -t ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest .
        # Docker Hub에 이미지 푸시
        docker push ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest

  deploy:
    # 이 작업은 build 작업이 성공적으로 완료된 후에만 실행다.
    needs: build
    runs-on: ubuntu-latest
    steps:
    # SSH를 통해 원격 서버에서 명령을 실행하여 Docker 컨테이너 배포
    - name: Docker Run
      uses: appleboy/ssh-action@v0.1.8
      with:
        host: ${{ secrets.SSH_HOST }}  # 원격 서버의 호스트명
        username: ${{ secrets.SSH_USERNAME }}  # 원격 서버의 사용자명
        key: ${{ secrets.SSH_PRIVATE_KEY }}  # 원격 서버의 비밀 키
        port: 22  # SSH 포트
        sync: false
        use_insecure_cipher: false
        timeout: 30s
        command_timeout: 10m
        debug: true  # 디버그 모드 활성화
        script: |
          docker stop pomorodo-server || true  # 실행 중인 pomorodo-server 컨테이너 중지
          docker rm pomorodo-server || true  # 기존 컨테이너 삭제
          docker rmi ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest || true # 기존 이미지 삭제
          docker pull ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest # 최신 이미지 다운로드
          docker run -d -p 8080:8080 --name pomorodo-server -e DB_URL=${{ secrets.DB_URL }} -e DB_USERNAME=${{ secrets.DB_USERNAME }} -e DB_PASSWORD=${{ secrets.DB_PASSWORD }} ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest

 

CI (Continuous Integration)

CI 단계에서는 코드의 변경사항이 빌드되고 테스트된다.

여기서는 주로 코드가 정상적으로 동작하는지 확인하는 작업이 이루어진다. 여기서 코드를 빌드하여 문제가 없는지 확인한다.

 

워크플로우 설정

워크플로우 이름을 정의한다.

name: CI/CD with Gradle, Docker, AWS
  • name: CI/CD with Gradle, Docker, AWS - 워크플로우의 이름이다.

 

이벤트 트리거

develop-be 브랜치에 push되거나 pull request가 생성될 때 워크플로우가 실행된다.

on:
  push:
    branches: [ "develop-be" ]
  pull_request:
    branches: [ "develop-be" ]
  • on: - 워크플로우를 트리거하는 이벤트를 정의한다.
  • push:  branches: -  해당 브랜치로 푸시 이벤트가 발생하면 워크플로우가 실행한다.
  • pull_request: branches: - 해당 브랜치를 대상으로 한 풀 리퀘스트 이벤트가 발생하면 워크플로우가 실행한다.

 

Job 1: build

기본 설정

이 작업은 최신 Ubuntu 환경에서 실행된다. 리포지토리에 대한 읽기 권한을 부여한다.

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
  • runs-on: ubuntu-latest - 최신 버전의 Ubuntu 가상 머신에서 작업이 실행한다.
  • permissions: contents: read - 저장소 내용에 대한 읽기 권한을 부여한다.

 

Steps (단계)

1. 레포지토리 체크아웃 (Checkout Repository)

레포지토리의 코드를 체크아웃하여 접근할 수 있게 한다.

- name: Checkout Repository
  uses: actions/checkout@v4
  • actions/checkout 액션을 사용하여 저장소를 러너에 클론한다.

 

2. JDK 17 설정 (Set up JDK 17)

JDK 17을 설정한다.

- name: Set up JDK 17
  uses: actions/setup-java@v4
  with:
    java-version: '17'
    distribution: 'temurin'
  • actions/setup-java 액션을 사용하여 JDK 17 (Temurin 배포판)을 설치한다.

 

3. Gradle 캐싱 (Gradle Caching)

Gradle 캐싱을 설정하여 빌드 시간을 줄인다.

- name: Gradle Caching
  uses: actions/cache@v3
  with:
    path: |
      ~/.gradle/caches
      ~/.gradle/wrapper
    key: ${{ runner.os }}-gradle-${{ hashFiles('backend/pomoro-do/**/*.gradle*', 'backend/pomoro-do/**/gradle-wrapper.properties') }}
    restore-keys: |
      ${{ runner.os }}-gradle-
  • actions/cache 액션을 사용하여 Gradle 종속성을 캐싱하여 빌드 속도를 높인다.
  • path - 캐싱할 디렉토리를 지정한다.
  • key - OS 및 Gradle 파일의 해시를 기반으로 한 고유한 캐시 키를 정의한다.
  • restore-keys - 기본 키가 없을 때 사용할 대체 키를 정의한다.

 

4. Gradlew 실행 권한 부여 (Grant Execute Permission For Gradlew)

Gradle 래퍼 스크립트에 실행 권한을 부여한다.

- name: Grant Execute Permission For Gradlew
  run: chmod +x backend/pomoro-do/gradlew

 

 

5. Gradle로 빌드 (Build with Gradle)

- name: Build with Gradle
  run: cd backend/pomoro-do && ./gradlew build -x test
  • Gradle 빌드 명령을 실행하여 테스트를 제외한 빌드를 수행한다.

 

CD (Continuous Deployment)

CD 단계에서는 빌드된 애플리케이션을 실제 환경에 배포한다.

 

6. Docker Hub에 로그인 (Log in to Docker Hub)

Docker Hub에 로그인한다.

- name: Log in to Docker Hub
  uses: docker/login-action@v2
  with:
    username: ${{ secrets.DOCKER_USERNAME }}
    password: ${{ secrets.DOCKER_PASSWORD }}
  • docker/login-action 액션을 사용하여 Docker Hub에 로그인한다.
  • GitHub Secrets에 저장된 자격 증명 사용한다.

 

7. SpringBoot용 Docker 이미지 빌드 및 푸시 (Build and Push Docker Image for SpringBoot)

- name: Build and Push Docker Image for SpringBoot
  run: |
    cd backend/pomoro-do
    set -e
    docker build -t ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest .
    docker push ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest
  • 이 단계에서는 backend/pomoro-do 디렉토리에 있는 Dockerfile을 사용하여 Spring Boot 애플리케이션의 Docker 이미지를 빌드한다.
  • 빌드된 Docker 이미지를 Docker Hub에 푸시한다.

 

Job 2: deploy

기본 설정

deploy:
  needs: build
  runs-on: ubuntu-latest
  • needs: build - 이 작업은 build 작업이 성공적으로 완료된 후에만 실행된다.
  • 최신 Ubuntu 환경에서 실행다.

 

Steps (단계)

1. Docker 실행 (Docker Run)

SSH를 통해 원격 서버에 접속하여 Docker 컨테이너를 배포한다.

기존 컨테이너와 이미지를 중지하고 삭제한 후, 최신 이미지를 다운로드하여 새로운 컨테이너를 실행한다.

 

  • 이 단계에서는 Docker Hub에서 최신 이미지를 풀(pull) 한 후, 컨테이너를 실행한다.
  • 이 이미지도 Dockerfile을 통해 빌드된 것이므로, 이전 단계에서 만든 Dockerfile이 중요하다.

 

- name: Docker Run
  uses: appleboy/ssh-action@v0.1.8
  with:
    host: ${{ secrets.SSH_HOST }}
    username: ${{ secrets.SSH_USERNAME }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    port: 22
    sync: false
    use_insecure_cipher: false
    timeout: 30s
    command_timeout: 10m
    debug: true
    script: |
      docker stop pomorodo-server || true
      docker rm pomorodo-server || true
      docker rmi ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest || true
      docker pull ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest
      docker run -d -p 8080:8080 --name pomorodo-server -e DB_URL=${{ secrets.DB_URL }} -e DB_USERNAME=${{ secrets.DB_USERNAME }} -e DB_PASSWORD=${{ secrets.DB_PASSWORD }} ${{ secrets.DOCKER_USERNAME }}/pomorodo:latest

 

  • appleboy/ssh-action 액션을 사용하여 SSH를 통해 원격 서버에서 명령을 실행한다.
  • 실행 중인 pomorodo-server 컨테이너를 중지하고 삭제한다.
  • 기존 Docker 이미지를 삭제한다.
  • Docker Hub에서 최신 이미지를 다운로드한다.
  • 환경 변수를 설정하고 새로운 Docker 컨테이너를 실행다.

 

CI/CD

CI는 코드의 품질을 보장하고 빌드 과정을 자동화하며,

CD는 빌드된 애플리케이션을 실제 운영 환경에 배포하는 과정을 자동화한다.

  • CI (지속적 통합): 코드 체크아웃, JDK 설정, Gradle 캐싱, Gradle 빌드
  • CD (지속적 배포): Docker Hub 로그인, Docker 이미지 빌드 및 푸시, Docker 컨테이너 배포

이 워크플로우는 코드가 develop-be 브랜치에 푸시되거나 PR이 생성될 때 자동으로 빌드와 배포 과정을 수행하여, 최신 버전의 애플리케이션이 원격 서버에 배포되도록 설정되어 있다.

 

docker-compose 사용하기

docker-compose.yml 파일은 Docker Compose를 사용하여 여러 컨테이너 서비스를 정의하고 설정할 수 있는 구성 파일이다.

docker-compose.yml

# docker-compose.yml

# Docker Compose 파일의 버전을 지정
version: '3.8'

services:
  app:
    build:
      context: ./backend/pomoro-do
      dockerfile: Dockerfile
    image: ${DOCKER_USERNAME}/pomorodo:latest
    ports:
      - "8080:8080"
    environment:
      DB_URL: ${DB_URL}
      DB_USERNAME: ${DB_USERNAME}
      DB_PASSWORD: ${DB_PASSWORD}
      S3_ACCESS_KEY: ${S3_ACCESS_KEY}
      S3_SECRET_KEY: ${S3_SECRET_KEY}
      REGION: ${REGION}
      BUCKET_NAME: ${BUCKET_NAME}
      JWT_ACCESS_SECRET: ${JWT_ACCESS_SECRET}
      JWT_REFRESH_SECRET: ${JWT_REFRESH_SECRET}

 

 

버전

version: '3.8'
  • version: Docker Compose 파일의 버전을 지정합니다. 여기서는 버전 3.8을 사용합니다. 이는 Docker Compose의 여러 기능을 사용할 수 있게 합니다.

서비스 정의

services:
  app:
  • services: 여러 컨테이너 서비스를 정의할 수 있는 섹션입니다.
  • app: 서비스의 이름입니다. 여기서는 app이라는 이름을 사용하여 Spring Boot 애플리케이션을 정의하고 있습니다.

 

빌드 설정

    build:
      context: ./backend/pomoro-do
      dockerfile: Dockerfile
  • build: Docker 이미지를 빌드하는 설정을 정의합니다.
    • context: Docker 빌드 컨텍스트를 지정합니다. ./backend/pomoro-do 디렉토리를 기준으로 빌드가 수행됩니다. 이 디렉토리에는 Dockerfile과 소스 코드가 있어야 합니다.
    • dockerfile: 사용할 Dockerfile을 지정합니다. 기본적으로 context 내의 Dockerfile을 사용합니다.

 

이미지 설정

    image: "${DOCKER_USERNAME}/pomorodo:latest"
  • image: 빌드된 Docker 이미지를 태그할 이름을 지정합니다. 여기서는 환경 변수 ${DOCKER_USERNAME}를 사용하여 Docker Hub의 사용자 이름과 pomorodo:latest 이미지를 결합하여 이름을 설정합니다. 예를 들어, 사용자 이름이 exampleuser인 경우 exampleuser/pomorodo:latest로 태그됩니다.

 

포트 설정

    image: "${DOCKER_USERNAME}/pomorodo:latest"
  • ports: 호스트와 컨테이너 간의 포트 매핑을 설정합니다.
    • "8080:8080": 호스트의 포트 8080을 컨테이너의 포트 8080에 매핑합니다. 이렇게 하면 호스트의 8080 포트로 접근할 때 컨테이너의 8080 포트로 요청이 전달됩니다.

환경 변수 설정

    environment:
      DB_URL: "${DB_URL}"
      DB_USERNAME: "${DB_USERNAME}"
      DB_PASSWORD: "${DB_PASSWORD}"
  • environment: 컨테이너에서 사용할 환경 변수를 설정합니다. 환경 변수는 주로 애플리케이션의 설정 값을 지정하는 데 사용됩니다.
    • DB_URL: 데이터베이스 URL을 지정합니다.
    • DB_USERNAME: 데이터베이스 사용자 이름을 지정합니다.
    • DB_PASSWORD: 데이터베이스 비밀번호를 지정합니다.

 

요약

이 docker-compose.yml 파일은 Spring Boot 애플리케이션을 정의하고, Docker 이미지를 빌드하고, 환경 변수를 설정하며, 포트 매핑을 통해 컨테이너가 호스트의 특정 포트에서 접근 가능하도록 합니다. 이를 통해 애플리케이션을 손쉽게 배포하고 관리할 수 있습니다. 각 구성 요소는 다음과 같은 역할을 합니다:

  • version: Docker Compose 파일의 버전 지정
  • services: 여러 컨테이너 서비스를 정의
  • app: 서비스 이름
  • build: Docker 이미지를 빌드하는 설정
  • image: 빌드된 이미지의 태그 이름
  • ports: 호스트와 컨테이너 간의 포트 매핑
  • environment: 컨테이너 내에서 사용할 환경 변수 설정

 

deploy.yml

# deploy.yml

name: CI/CD with Gradle, Docker, AWS

# develop-be 브랜치에 push 또는 PR 이벤트가 발생하면 워크플로우가 실행된다.
on:
  push:
    branches: [ "develop-be" ]
  pull_request:
    branches: [ "develop-be" ]

# 해당 Workflow의 Job 목록
jobs:
  build:
  # 이 작업이 실행되는 환경을 정의: 최신 Ubuntu 환경
    runs-on: ubuntu-latest
    # 리포지토리에 대한 읽기 권한을 부여한다.
    permissions:
      contents: read
      
    # build Job 내의 step 목록
    steps:
    ### CI
    # 레포지토리 체크아웃하여 레포지토리에 접근할 수 있게 한다.
    - name: Checkout Repository
      uses: actions/checkout@v4
      
    # JDK 버전 17 설정 (temurin 배포판 사용)
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'
        
    # Gradle 캐싱 설정으로 의존성 다운로드 시간을 줄인다.
    - name: Gradle Caching
      uses: actions/cache@v3
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('backend/pomoro-do/**/*.gradle*', 'backend/pomoro-do/**/gradle-wrapper.properties') }}
        restore-keys: |
          ${{ runner.os }}-gradle-
          
    # Gradle 래퍼 스크립트에 실행 권한 부여
    - name: Grant Execute Permission For Gradlew
      run: chmod +x backend/pomoro-do/gradlew
    
    # 테스트를 제외한 Gradle 빌드 수행 (CI 단계)
    - name: Build with Gradle
      run: cd backend/pomoro-do && ./gradlew build -x test

    ### CD
    # Docker Hub에 로그인 (보안) (CD 단계 준비)
    - name: Log in to Docker Hub
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    # SpringBoot 어플리케이션의 Docker 이미지를 빌드하고 Docker Hub에 푸시 (CD 단계 준비)
    - name: Build and Push Docker Image for SpringBoot
      run: |
        cd backend/pomoro-do
        set -e # 명령 실패 시 스크립트 종료
        # 현재 디렉토리에 있는 Dockerfile을 사용하여 Docker 이미지를 빌드
       	docker-compose build
        # Docker Hub에 이미지 푸시
        docker-compose push
        
  deploy:
    # 이 작업은 build 작업이 성공적으로 완료된 후에만 실행다.
    needs: build
    runs-on: ubuntu-latest
    steps:
    # SSH를 통해 원격 서버에서 명령을 실행하여 Docker 컨테이너 배포
    - name: Docker Run
      uses: appleboy/ssh-action@v0.1.8
      with:
        host: ${{ secrets.SSH_HOST }}  # 원격 서버의 호스트명
        username: ${{ secrets.SSH_USERNAME }}  # 원격 서버의 사용자명
        key: ${{ secrets.SSH_PRIVATE_KEY }}  # 원격 서버의 비밀 키
        port: 22  # SSH 포트
        sync: false
        use_insecure_cipher: false
        timeout: 30s
        command_timeout: 10m
        debug: true  # 디버그 모드 활성화
        script: |
          cd /path/to/deploy
          docker-compose down # 기존 컨테이너 중지 및 제거
          docker-compose pull # 최신 이미지 가져오기
          docker-compose up -d # Docker Compose를 사용해 컨테이너 시작

 

728x90
반응형