티스토리 뷰

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

  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/create-json@1.1.2
        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
  • main 브랜치에 push 이벤트가 발생할때 자동 배포가 이루어진다. (PR을 통해 main 브랜치에 반영될때 포함)

Step 1 : JDK 설정

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
  • Java 17 버전을 설치하고 설정한다.

Step 2 : 배포에 필요한 설정 파일을 추가한다.

      # 설정 파일 추가
      - 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/create-json@1.1.2
        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

  • DB 비밀번호 등 git에 올릴 수 없는 환경 변수 파일을 git의 seceret 환경변수에서 가져온다.
  • 가져온 후, application-prod.yml 이라는 파일을 만들어 내용을 덮어씌우고 ./src/main/resources경로에 붙여 넣는다.

Step 3 : gradlew 실행 권한 설정

- name: Grant execute permission for gradlew
        run: chmod +x gradlew
  • git의 ubuntu가 gradlew 명령을 실행할 수 있도록 권한을 추가한다.

Step 4 : Gradle을 이용해 빌드를 수행

      - name: Build with Gradle
        run: ./gradlew build -x test

Step 5 : 이미지 빌드, 푸시

      - name: Docker build
        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
  • Docker를 이용하여 이미지를 빌드하고, Docker Hub에 푸시하는 단계이다. 이를 통해 AWS에서 해당 이미지를 가져올 수 있다.
  • secret.~를 볼 수 있는데, 이는 git에 등록해놓은 환경 변수이다. 이는 외부에서 볼 수 없다.

 

 

Step 6 : EC2 인스턴스에 배포

      - name: AWS 배포
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ec2-user
          key: ${{ secrets.PRIVATE_KEY }}
          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
  • EC2 인스턴스에 배포하는 단계이다.
  • AWS 배포: appleboy/ssh-action을 사용해 EC2 인스턴스에 접근하여 배포 작업을 수행한다.
  • 최신 Docker 이미지 Pull: 최신 애플리케이션 이미지를 가져온다.
  • 기존 컨테이너 중지 및 삭제: 동일한 이름의 기존 컨테이너가 있으면 종료하고 삭제한다.
  • 새 컨테이너 실행: 최신 이미지를 기반으로 새로운 컨테이너를 실행한다.

정리

이 파이프라인은 GitHub Actions를 통해 애플리케이션 빌드 및 배포를 자동화하여 새로운 변경사항을 손쉽게 EC2 인스턴스에 반영할 수 있도록 설계되었다.

주요 구성 요소는:

  1. 코드 변경이 main 브랜치에 푸시될 때 자동 실행.
  2. 애플리케이션 빌드 및 Docker 이미지 생성 후, Docker Hub에 푸시.
  3. EC2 인스턴스에 배포하고 기존 컨테이너를 갱신하여 최신 버전의 애플리케이션이 제공되도록 함.

이 구성은 반복적인 수작업을 최소화하고 변경사항이 신속히 서비스에 반영되도록 한다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함