본문 바로가기

CICD 무중단배포

Git Actions를 이용한 CI/CD 이용하기 ( 1 )

반응형
 
  1. CI/CD란?
  2. 프로젝트 설정
  3. GitHub Actions 설정
  4. 자동화 배포 설정
  5. 결론

CI/CD 무중단 자동화 배포 설정하기

GitHub Actions와 appleboy의 SSH Action을 사용하여 CI/CD 파이프라인을 구성하고

무중단 자동화 배포를 설정하는 방법

1. CI/CD란?

지속적 통합(CI): 개발자가 코드를 자주 통합하여 테스트 및 빌드 자동화를 수행하는 과정입니다. 이를 통해 빠르게 피드백을 받고, 코드 충돌을 조기에 발견할 수 있습니다.

지속적 배포(CD): CI 과정이 끝난 후, 자동으로 프로덕션 환경에 배포하는 과정입니다. 이를 통해 새로운 기능이나 버그 수정이 사용자에게 빠르게 전달됩니다.

2. 프로젝트 설정

이번 예제에서는 Gradle을 사용한 자바 스프링부트를 이용한 프로젝트를 대상으로 합니다.

프로젝트 루트에 gradlew 스크립트가 있는지 확인합니다.

세팅 이전에는 사진과 같이 workflows 폴더안에 **.yml 파일이 없으므로 
Git Actions를 이용할  GitHub Repositories를 들어간후 상단 메뉴에 Actions메뉴를 누른후에

사진과 같은 화면이 나오는데  선택한 리포지토리의 프로그래밍 언어를 가지고 추천을 해주니 
방향에 맞는 것을 선택을 하면 

 

 

이런 화면이 나오게 되면 저장을 한후에는

해당하는 리포지토리 .github/workflows 폴더안에 **.yml 파일이 생기게 됩니다.

 

3. GitHub Actions 설정

GitHub Actions를 사용하여 CI/CD 파이프라인을 설정합니다.

.github/workflows 디렉토리에 ci-cd.yml 파일을 생성하고 아래의 내용을 추가합니다.

 

name: Java CI with Gradle and Deploy

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

이벤트 트리거 설정

이 부분은 GitHub Actions가 실행되는 조건을 설정합니다.

push와 pull_request 이벤트가 main 브랜치에서 발생할 때마다 워크플로가 실행됩니다.

 

jobs:
  prepare:
    runs-on: ubuntu-latest
    steps:
      - name: executing remote ssh commands using ssh key
        uses: appleboy/ssh-action@v0.1.10
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SECRET_KEY }}
          port: ${{ secrets.PORT }}
          passphrase: ${{ secrets.PASSPHRASE }}
          script: |
            whoami
            ls -al
            cd /home/ubuntu/danum-backend
            git pull origin main

준비 작업 (prepare job)

  • runs-on: ubuntu-latest 가상 환경에서 실행됩니다.
  • steps: 개별 작업 단계입니다.
    • name: 단계의 이름입니다. 여기서는 "executing remote ssh commands using ssh key"로 설정했습니다.
    • uses: appleboy/ssh-action@v0.1.10 액션을 사용하여 SSH를 통해 원격 서버에 명령을 실행합니다.
    • with: SSH 연결에 필요한 매개변수를 설정합니다.                                                                                                 여기서 GitHub Secrets에 저장된 호스트, 사용자 이름, SSH 키, 포트, 암호구 등을 사용합니다.
    • script: 원격 서버( AWS EC2등 ) 에서 실행할 명령입니다. 현재 사용자를 확인하고(whoami), 파일 목록을 출력하며(ls -al), 프로젝트 디렉토리로 이동하여( cd /home/ubuntu/프로젝트명 ), 최신 코드를 가져옵니다(git pull origin main).

 

  build:
    needs: prepare
    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: Setup Gradle
        uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
      - name: List files
        run: |
          pwd
          ls -al
      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
      - name: Build with Gradle
        run: ./gradlew build

빌드 작업 (build job)

  • needs: 이 작업이 prepare 작업 이후에 실행되도록 설정합니다.
  • runs-on: ubuntu-latest 가상 환경에서 실행됩니다.
  • permissions: 컨텐츠 읽기 권한을 부여합니다.
  • steps: 개별 작업 단계입니다.
    • uses: actions/checkout@v4: 코드를 체크아웃합니다.
    • Set up JDK 17: actions/setup-java@v4 액션을 사용하여 JDK 17을 설치하고 설정합니다.
    • Setup Gradle: gradle/actions/setup-gradle@v3.1.0 액션을 사용하여 Gradle을 설정합니다.
    • List files: 현재 디렉토리의 파일 목록을 출력합니다.
    • Grant execute permission for gradlew: gradlew 스크립트에 실행 권한을 부여합니다.
    • Build with Gradle: Gradle을 사용하여 프로젝트를 빌드합니다.

 

  dependency-submission:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Generate and submit dependency graph
        uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

의존성 제출 작업 (dependency-submission job)

  • needs: 이 작업이 build 작업 이후에 실행되도록 설정합니다.
  • runs-on: ubuntu-latest 가상 환경에서 실행됩니다.
  • permissions: 컨텐츠 쓰기 권한을 부여합니다.
  • steps: 개별 작업 단계입니다.
    • uses: actions/checkout@v4: 코드를 체크아웃합니다.
    • Set up JDK 17: JDK 17을 설치하고 설정합니다.
    • Generate and submit dependency graph: Gradle을 사용하여 의존성 그래프를 생성하고 제출합니다.

 

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
    - name: Deploy danum Application
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.SECRET_KEY }}
        passphrase: ${{ secrets.PASSPHRASE }}
        script: |
          cd /home/ubuntu/danum-backend
          # danum.service를 재시작하여 변경사항 적용
          sudo systemctl stop danum.service
          sudo systemctl daemon-reload
          sudo systemctl start danum.service

 

배포 작업 (deploy job)

  • needs: 이 작업이 build 작업 이후에 실행되도록 설정합니다.
  • runs-on: ubuntu-latest 가상 환경에서 실행됩니다.
  • steps: 개별 작업 단계입니다.
    • Deploy danum Application: appleboy/ssh-action@master 액션을 사용하여 SSH를 통해 원격 서버에 배포 작업을 수행합니다.
    • with: SSH 연결에 필요한 매개변수를 설정합니다. 여기서 GitHub Secrets에 저장된 호스트, 사용자 이름, SSH 키, 암호구 등을 사용합니다.
    • script: 원격 서버에서 실행할 명령입니다. 프로젝트 디렉토리로 이동하여(cd /home/ubuntu/danum-backend), danum.service를 재시작하여 변경사항을 적용합니다(sudo systemctl stop danum.service, sudo systemctl daemon-reload, sudo systemctl start danum.service).

 

Git Actions의 yml파일 작성은 마무리하고 

 

AWS EC2 서버에서 작성한 danum.service 유닛 파일에 대한 자세한 설명입니다.

이 파일은 시스템 서비스 관리 도구인 systemd를 사용하여 Java 애플리케이션을 관리하는 방법을 정의합니다

해당 service파일은 /etc/systemd/system 안에 위치하고있습니다.

 

/etc/systemd/system 해당 경로 접속후

sudo vi  이름.sh 으로 파일 생성후 코드 작성

 

 

전체 파일 구조

[Unit]
Description=Danum Java Application
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/프로젝트명/build/libs
ExecStartPre=/bin/sh -c '! lsof -t -i:8080 || kill -9 $(lsof -t -i:8080)'
ExecStart=/usr/bin/java -jar /home/ubuntu/프로젝트명/build/libs/프로젝트-0.0.1-SNAPSHOT.jar
SuccessExitStatus=143
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

 

 

[Unit] 섹션

[Unit]
Description=Danum Java Application
After=network.target

 

 

  • Description: 서비스의 설명을 정의합니다. 여기서는 "Danum Java Application"으로 설정되어 있습니다.
  • After: 이 서비스가 network.target 이후에 시작되도록 설정합니다. 이는 네트워크가 활성화된 후에 이 서비스가 시작된다는 것을 의미합니다.

 

[Service] 섹션

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/프로젝트명/build/libs
ExecStartPre=/bin/sh -c '! lsof -t -i:8080 || kill -9 $(lsof -t -i:8080)'
ExecStart=/usr/bin/java -jar /home/ubuntu/프로젝트명/build/libs/프로젝트-0.0.1-SNAPSHOT.jar
SuccessExitStatus=143
Restart=always
RestartSec=5

 

 

  • User: 서비스를 실행할 사용자를 지정합니다. 여기서는 AWS EC2 default인  ubuntu 사용자로 설정되어 있습니다.
  • WorkingDirectory: 서비스가 실행되는 작업 디렉토리를 지정합니다. 여기서는 /home/ubuntu/프로젝트명/build/libs 디렉토리로 설정되어 있습니다. 
  • ExecStartPre: ExecStart 명령을 실행하기 전에 실행되는 명령을 지정합니다. 이 경우, 포트 8080을 사용하는 프로세스를 종료하는 명령입니다.
    • /bin/sh -c '! lsof -t -i:8080 || kill -9 $(lsof -t -i:8080)': 포트 8080을 사용하는 프로세스를 찾고, 있으면 강제로 종료합니다.
  • ExecStart: 서비스가 시작될 때 실행되는 명령을 지정합니다. 여기서는 danum-0.0.1-SNAPSHOT.jar 파일을 실행하는 Java 명령입니다.
    • /usr/bin/java -jar /home/ubuntu/프로젝트명/build/libs/프로젝트-0.0.1-SNAPSHOT.jar: 해당 JAR 파일을 Java로 실행합니다.
  • SuccessExitStatus: 서비스가 정상적으로 종료될 때의 종료 상태를 정의합니다. 여기서는 143으로 설정되어 있습니다.
  • Restart: 서비스가 비정상 종료된 경우 재시작할 정책을 정의합니다. 여기서는 항상 재시작하도록 설정되어 있습니다.
  • RestartSec: 서비스가 비정상 종료된 후 재시작하기 전에 대기하는 시간을 초 단위로 설정합니다. 여기서는 5초로 설정되어 있습니다.

 

 

[Install] 섹션

[Install]
WantedBy=multi-user.target

 

WantedBy: 이 서비스가 어느 대상에서 활성화될지 정의합니다.

여기서는 multi-user.target에서 활성화되도록 설정되어 있습니다. 이는 다중 사용자 모드에서 이 서비스가 활성화됨을 의미합니다.

 

 

이 danum.service 유닛 파일은 네트워크가 준비된 후 danum-0.0.1-SNAPSHOT.jar Java 애플리케이션을 실행하고

Tomcat의 기본포트 8080을 사용하는 기존 프로세스를 종료하며

애플리케이션이 비정상 종료될 경우 자동으로 재시작하는 등의 기능을 제공합니다.

이를 통해 애플리케이션이 항상 가동 상태를 유지할 수 있습니다.

 

 

 

 

 

 

 

 

참고 : https://github.com/appleboy/ssh-action 

반응형