SU_DING_GI

[AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 3. CI/CD 코드 및 여러 설정 파일 수정하기 본문

INFRA

[AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 3. CI/CD 코드 및 여러 설정 파일 수정하기

Soonga00 2025. 2. 6. 17:21
728x90

2025.02.06 - [INFRA] - [AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 2. EC2 인스턴스 생성하기

 

[AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 2. EC2 인스턴스 생성하기

2025.02.04 - [INFRA] - [AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 1. RDS 생성 및 백업 [AWS] 기존 서버에서 새로운 서버로 마이그레이션 하기 - 1. RDS 생성 및 백업기존에 운영하던 알고리즘

sua-su-ding-gi.tistory.com

이전 글에서 EC2와 RDS 연결까지 완료했었다. 원래 우리는 깃 action을 통해 CI/CD를 구현해놨지만 내가 그 설정을 담당하지 않았어서.. 처음 부터 다시 공부하면서 EC2 서버에 배포할 수 있도록 설정해 보겠다!

 

1. CI/CD란?

CI(Continuous Integration)/CD(Continuous Deployment)는 소프트웨어 개발 및 배포를 자동화하는 과정이다.

CI/CD를 적용하면 코드 변경이 자동으로 테스트 되고, 빌드되며, 배포까지 자동으로 이루어질 수 있기 때문에 이를 통해 개발 속도를 높이고, 오류를 줄이며, 운영 효율성을 개선할 수 있다.

1-1. CI(Continuous Integration, 지속적 통합)

CI는 코드 변경이 생길 때마다 자동으로 테스트하고 빌드하는 과정이다.

 

✅ CI의 핵심 목표

  • 코드 변경 사항을 빠르게 검증하여 오류를 미리 발견
  • 여러 개발자가 동시에 작업하더라도 코드 충돌을 최소화
  • 자동화된 테스트로 안정적인 코드 유지

✅ CI 동작 방식

  • 코드 변경 사항을 Git에 푸시
  • Github Actions, Jenkis, GitLab CI/CD같은 CI 서버가 자동으로 실행
  • 코드가 자동으로 빌드되고, 테스트 실행
  • 테스트를 통과하면 배포를 준비, 실패하면 즉시 개발자에게 알림

1-2. CD(Continuous Deployment, 지속적 배포)

CD는 CI이후, 테스트를 통과한 코드를 자동으로 서버에 배포하는 과정이다

CD는 Continuous Deployment라는 지속적 배포외에도 추가로 Continuous Delivery라는 지속적 전달 방식도 있다.

  1. Continuous Delivery(지속적 전달)
    • 배포 준비까지 자동화되지만, 배포는 사람이 승인 후 진행
    • 일반적으로 "배포 버튼"을 눌러야 실제 서버에 반영됨
    • 대기업에서는 주로 이 방식을 사용하여 실수를 방지
  2. Continuous Deployment(지속적 배포)
    • 완전히 자동으로 배포까지 진행
    • 사람이 개입할 필요 없음
    • 스타트업이나 DevOps 환경에서 많이 사용됨

CD의 핵심 목표

  • 코드가 안전하게 배포될 준비가 되었는지 확인
  • 배포 프로세스를 자동화하여 운영 부담 감소
  • 코드 변경이 빠르게 실제 서비스에 반영

CD에서 수행하는 작업 (aloc 웹서버 기준)

  • CI 단계를 통과한 코드르 자동으로 Docker 이미지로 빌드
  • Docker 이미지를 업로드
  • EC2 서버에서 최신 Docker 이미지를 받아 실행

2.  CI/CD 스크립트 및 EC2 서버 초기 설정하기

2-1. CI/CD 스크립트 수정

우선, 우리 CI 관련 스크립트는 프로젝트 폴더에 .github/workflows/gradle.yml에 작성되어있다.

기존에는 lightsail로 사용하고 있어서 PostgreSQL을 로컬로 설치하고 있었는데, 이제는 RDS를 사용해야 하므로 해당 내용을 수정해보겠다.

 

✅ application.yml 수정

application.yml의 db관련 설정(url, username, password) 정보를 반영한다.

 

그외에는 gradle.yml을 그대로 사용해도 되기때문에 특별한 수정은 안했다.

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle

name: CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read

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

    - name: Make gradlew executable
      run: chmod +x gradlew

    - name: Cache Gradle packages
      uses: actions/cache@v3
      with:
        path: ~/.gradle/caches
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
        restore-keys: ${{ runner.os }}-gradle-

    - name: Create application.yml
      run: |
        mkdir -p ./src/main/resources
        cd ./src/main/resources
        echo "${{ secrets.APPLICATION }}" base64 -d > application.yml
        echo "${{ secrets.JWT_APPLICATION }}" base64 -d > application-jwt.yml
        echo "${{ secrets.TEST_APPLICATION }}" base64 -d > application-test.yml

    - name: Install PostgreSQL client
      run: sudo apt-get install -y postgresql-client

    - name: Build with Gradle Wrapper
      run: ./gradlew build --parallel --daemon

 

✅ CD 스크립트를 수정

우리 CD 관련 스크립트는 프로젝트 폴더에 .github/workflows/deploy.yml에 작성되어있는데,

기존 코드에 수정해야될 점은

  1. CI/CD 스크립트로 수정 및 작업 분리
  2. lightsail 에서 EC2로변경
  3. EC2에서는 필요없는 네트워크 설정을 제거

이다. 따라서 해당 수정사항을 반영해서

name: CI/CD

on:
  push:
    branches: [ "develop" ]  # develop 브랜치에 푸시되면 실행

jobs:
  build:
    name: CI - Build & Test
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v4

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

      - name: Make gradlew executable
        run: chmod +x gradlew

      - name: Cache Gradle packages
        uses: actions/cache@v3
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
          restore-keys: ${{ runner.os }}-gradle-

      - name: Create application.yml
        run: |
          mkdir -p ./src/main/resources
          cd ./src/main/resources
          echo "${{ secrets.APPLICATION }}" base64 -d > application.yml
          echo "${{ secrets.JWT_APPLICATION }}" base64 -d > application-jwt.yml
          echo "${{ secrets.TEST_APPLICATION }}" base64 -d > application-test.yml

      - name: Install PostgreSQL client
        run: sudo apt-get install -y postgresql-client

      - name: Build with Gradle Wrapper
        run: ./gradlew build --parallel --daemon

      - name: Save JAR Artifact
        uses: actions/upload-artifact@v4
        with:
          name: built-jar
          path: build/libs/*.jar

  deploy:
    name: CD - Deploy to EC2
    needs: build
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Download JAR Artifact
        uses: actions/download-artifact@v4
        with:
          name: built-jar
          path: build/libs/

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ secrets.DOCKER_USERNAME }}/aloc-spring:latest

      # 🔥 SSH 키 설정 (base64 디코딩 없이 원본 그대로 저장)
      - name: Setup SSH Key
        run: |
          echo "${{ secrets.EC2_SSH_KEY }}" > key.pem  # 🔴 디코딩 없이 저장
          chmod 600 key.pem

      # ✅ SSH 연결 테스트
      - name: Test SSH Connection
        run: ssh -i key.pem -o StrictHostKeyChecking=no ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} "echo '✅ SSH 연결 성공!'"

      # ✅ SSH 실행 시 key_path 사용
      - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key_path: key.pem  # 🔥 key_path로 직접 지정
          script: |
            echo "🔄 Starting Deployment on EC2..."
            docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}

            echo "🛑 Stopping and removing old container..."
            if [ "$(docker ps -q -f name=aloc-spring)" ]; then
              docker stop aloc-spring
              docker rm aloc-spring
            fi

            echo "📦 Pulling the latest image..."
            docker pull ${{ secrets.DOCKER_USERNAME }}/aloc-spring:latest | exit 1

            echo "🚀 Running new container..."
            docker run -d --name aloc-spring \
              -e TZ=Asia/Seoul \
              -p 8080:8080 \
              -v /home/ubuntu/upload/user:/app/upload \
              ${{ secrets.DOCKER_USERNAME }}/aloc-spring:latest

            echo "✅ Deployment completed!"

      - name: Send deployment status to Discord
        if: success()
        uses: tsickert/discord-webhook@v5.3.0
        with:
          webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
          content: |
            [aloc-spring]
            🚀 배포가 성공적으로 완료되었어요!
            커밋한 사람 🙋🏻 ${{ github.actor }}
            ${{ github.event.head_commit.message }}

      - name: Send deployment failure to Discord
        if: failure()
        uses: tsickert/discord-webhook@v5.3.0
        with:
          webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
          content: |
            [aloc-spring]
            ❌ 배포 중 문제가 발생했어요!
            확인이 필요합니다. 👀
            잘못한 사람 🙋🏻 ${{ github.actor }}
            URL: https://github.com/ALOC-UOS/aloc-backend/actions/runs/${{ github.run_id }}
            ${{ github.event.head_commit.message }}

이렇게 수정해주고 github setting에서 secrets를 업데이트 해줬다.

 

2-2. EC2 초기 설정

그후, EC2의 보안 그룹에 가서 22, 80, 443, 8080 포트를 개방해주고

EC2에 접속해 docker 와 docker-compose를 설치해준다!

그후 develop 브랜치에 push 해주면

짜잔 완성이다~

이제는 DNS에 EC2 퍼블릭 ip 등록하고 Nginx 사용해서 ssl 설정까지 해주면 진짜 완성~

사실 ssl 인증서 등록하는 것도 작성하려 했지만.. 너무 지친 관계로 작성하지 못했다

생각보다 되게 오래걸렸지만 다음에 또 이런 설정을 하게되면 순차적으로 잘 할 수 있을 것 같다!