일반적으로 서비스가 배포 되기 위한 과정은 크게 다음과 같다.

  1. 프로젝트 디렉토리에서 ./gradlew build후, .jar파일을 생성
  2. .jar 파일을 이미지화 하여 Docker Hub에 push
  3. EC2 인스턴스에 key pair를 이용하여 EC2 인스턴스에 접근
  4. EC2 인스턴스에서 Docker Hub에 있는 이미지를 pull
  5. EC2 인스턴스에서 Docker 이미지를 컨테이너화 하여 서버를 실행

git의 main 브랜치에 있는 최신 변경 사항을 서버에 반영하기 위해서는 매번 위 과정을 수행하여 배포해야 할 것이다.

이 과정은 반복적이며 자동화 할 필요가 있다.

배포 자동화가 필요한 이유

빠르게 변화하는 개발 환경에서는 배포 자동화가 필수적이라고 볼 수 있겠다.

파이프라인 : friendship-pipeline.yml

FriendShip 서비스가 제공하고 있는 자동 배포 파이프라인은 다음과 같다.

name: FriendShip - Deploy to Amazon EC2

on:
  push:
    branches:
      - main

env:
  APP_NAME : friendship
  BUILD_NAME : friendship

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest

    services:
      test-mongo:
        image: mongo:latest
        ports:
          - 27017:27017
        options: --name test-mongo --health-cmd="mongosh --eval 'db.adminCommand(\\"ping\\")'" --health-interval=10s --health-timeout=5s --health-retries=5

    steps:
      - uses: actions/checkout@v3

      - name: JDK 17 설치
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      # 설정 파일 추가
      - name: application-secret.yml 구성
        run: |
          cd ./src/main/resources
          touch ./application-secret.yml
          echo "${{ secrets.APPLICATION_SECRET }}" > ./application-secret.yml

      - name: firebase_service_key.json 구성
        run: |
          cd ./src/main/resources
          touch ./firebase_service_key.json
          echo "${{ secrets.FCM_KEY }}" > ./firebase_service_key.json

      - name: firebase_service_key.json 파일 생성
        id: create-json
        uses: jsdaniell/[email protected]
        with:
          name: "firebase_service_key.json"
          json: ${{ secrets.FCM_KEY }}

      - name: JSON 파일 이동
        run: |
          mv ./firebase_service_key.json ./src/main/resources/firebase_service_key.json

      - name: gradlew 실행 권한 부여
        run: chmod +x gradlew
        
      - name : asciidoc 플러그인 실행 및 생성된 html 파일 정적 저장소로 이동
        run: |
          ./gradlew asciidoctor
          mkdir -p src/main/resources/static/docs
          cp -r build/docs/asciidoc/* src/main/resources/static/docs/

      - name: Gradle 빌드
        run: ./gradlew build -x test

      - name: Docker 이미지 파일 PUSH
        run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build --platform linux/arm64/v8 -t app .
          docker tag app ${{ secrets.DOCKER_USERNAME }}/${{ env.APP_NAME }}:latest
          docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.APP_NAME }}:latest

      - name: AWS 배포
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }} # EC2 인스턴스 퍼블릭 DNS
          username: ec2-user
          key: ${{ secrets.PRIVATE_KEY }} # pem 키
          # 도커 작업
          script: |
            # 최신 friendship 이미지 pull
            sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ env.APP_NAME }}:latest
            
            # 기존 friendship 컨테이너 중지 및 삭제
            sudo docker stop friendship || true
            sudo docker rm friendship || true
            
            # 새 friendship 컨테이너 실행
            sudo docker run -v /home/ec2-user/elk/logs:/logs -d --log-driver=syslog -p 443:8080 --name friendship --network friendship-network -e spring.profiles.active=prod -e TZ=Asia/Seoul ${{ secrets.DOCKER_USERNAME }}/${{ env.APP_NAME }}:latest
            
            # friendship 관련 종료된 컨테이너 삭제
            sudo docker container prune -f
            
            # 사용하지 않는 friendship 이미지만 삭제 (최신 버전 제외)
            sudo docker image prune -f
            
            # 이전 버전의 friendship 이미지 삭제
            sudo docker images ${{ secrets.DOCKER_USERNAME }}/${{ env.APP_NAME }} -q | awk 'NR>1' | xargs -r sudo docker rmi -f

      - name: 테스트용 MongoDB 컨테이너 종료 및 삭제
        run: |
          sudo docker stop test-mongo || true
          sudo docker rm test-mongo || true

언제 자동 배포가 이루어 지는가?

on:
  push:
    branches:
      - main

Step 1 : JDK 설정

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'