GitHub Actions + ArgoCD로 k8s 클러스터 환경에서 CI/CD 구축하기

2025. 1. 5. 06:37·DevOps

지난 포스팅에서 ArgoCD를 통해 애플리케이션의 자동 배포를 구축했습니다.

ArgoCD로 자동 배포를 구축하는 과정은 이 링크를 확인해 주세요!

 

이번 포스팅은 GitHub Actions를 통해 CI 부분을 자동화하는 부분을 중점적으로 다루며, 이전 포스팅과 함께 읽으신다면 최종적으로 GitOps 방식으로 쿠버네티스 클러스터 환경에서 CI/CD 파이프라인을 구축하는 과정을 보실 수 있습니다.

CI/CD 파이프라인 전체 흐름

  1. 단일 진실의 원천(SSOT)인 GitHub Repository에 코드를 Push 합니다.
  2. GitHub Actions의 workflow가 트리거 됩니다.
  3. JDK로 Spring 애플리케이션을 빌드하고, 빌드된 JAR 파일로 Docker 이미지를 만든 다음 Docker Hub에 Push 합니다.
  4. Docker 이미지 버전이 변경되었음을 알리기 위해 쿠버네티스 매니페스트 파일을 수정합니다.
  5. 변경 사항을 GitHub Actions Bot이 Commit / Push 합니다.
  6. ArgoCD가 Git 저장소에 있는 쿠버네티스 매니페스트 파일의 변경을 감지합니다.
  7. ArgoCD가 Git 저장소와 쿠버네티스 클러스터의 Sync를 맞춥니다.

이제 위의 1번부터 5번까지 GitHub Actions의 workflow를 작성해서 CI 부분을 처리하는 워크로드를 다루겠습니다.

Step 1. GitHub Repository에 Push 했을 때 워크플로우 트리거 시키기

name: CI  # 지속적 통합(CI) 워크플로우 이름

on:  # 워크플로우를 트리거하는 이벤트를 정의
  push:  # push 이벤트 발생 시 트리거
    branches: [ "main", "dev" ]  # 'main' 또는 'dev' 브랜치로 push될 때만 실행
  • GitHub Repository에 `main` 브랜치나 `dev` 브랜치에 Push 이벤트가 발생했을 때 트리거를 발생시키는 부분입니다.

Step 2. JDK로 Spring 애플리케이션을 빌드하기

jobs:
  build:
    name: Build and Push Docker Image  # 작업에 대한 설명 이름
    runs-on: ubuntu-latest  # 작업이 실행될 환경(운영체제)
    steps:  # 작업에서 실행할 단계 정의
      - uses: actions/checkout@v4  # GitHub 저장소 코드를 체크아웃

      - name: Setup Java JDK  # Java JDK 설정
        uses: actions/setup-java@v4.6.0  # GitHub 제공 Java 설정 액션 사용
        with:
          distribution: 'corretto'  # Amazon Corretto JDK 사용
          java-version: '17'  # Java 17 버전 사용

      - name: Grant execute permission for gradlew  # gradlew 파일 실행 권한 부여
        run: chmod +x gradlew  # 실행 가능하도록 권한 변경

      - name: Build with Gradle  # Gradle을 사용하여 빌드
        run: ./gradlew clean build -x test  # 테스트를 제외하고 클린 빌드 실행
  • `runs-on` 부분에 워크플로우 작업들을 실행할 운영체제를 작성합니다. Public Repository인 경우 GitHub에서 제공하는 인스턴스에서 작성한 운영체제로 실행됩니다. (Private Repository인 경우 제한된 무료 사용 시간을 제공하고, 초과 시 분당 요금이 청구되니 주의하세요!)
  • `steps` 원하는 작업들을 작성합니다. job은 병렬적으로 실행되지만 step은 작성한 순서대로 실행됩니다.
  • GitHub Actions 마켓플레이스에서 제공하는 `actions/setup-java@v4.6.0` Docker 이미지를 사용해서 JDK를 설치합니다.
    • distribution과 java-version은 프로젝트에 알맞게 설정할 수 있으며, 마켓플레이스에서 `name`으로 검색할 수 있습니다. Documentation 부분에서 필요한 옵션을 설정합니다.
  • JDK를 설치한 후엔 `chmod +x gradlew`로 실행 권한을 부여한 후 Spring 애플리케이션을 빌드합니다.

Step 3. JAR 파일을 Docker 이미지로 빌드 후 Docker Hub에 Push 하기

jobs:
  build:
    name: Build and Push Docker Image
    runs-on: ubuntu-latest
    steps:
      # 윗 단계 생략 ..
      - name: Get current time  # 현재 시간 가져오기
        uses: josStorer/get-current-time@v2.1.2  # 현재 시간을 가져오는 액션 사용
        id: current_time  # 이 단계의 ID를 설정하여 이후 사용 가능
        with:
          format: YYYY-MM-DDTHH-mm-ss  # 시간 형식 지정
          utcOffset: "+09:00"  # UTC+9 시간대 설정(한국 시간)

      - name: Docker Login  # Docker 로그인
        uses: docker/login-action@v3.3.0  # Docker 로그인 액션 사용
        with:
          username: ${{ secrets.DOCKER_USERNAME }}  # GitHub Secrets에 저장된 Docker 사용자 이름
          password: ${{ secrets.DOCKER_PASSWORD }}  # GitHub Secrets에 저장된 Docker 비밀번호

      - name: Build and push Docker images  # Docker 이미지 빌드 및 푸시
        uses: docker/build-push-action@v6.10.0  # Docker 빌드 및 푸시 액션 사용
        with:
          context: .  # 현재 디렉터리를 컨텍스트로 사용
          file: Dockerfile-dev  # Docker 빌드에 사용할 Dockerfile 지정
          tags: myRegistry/upup-radio-backend:${{ steps.current_time.outputs.formattedTime }}  # 이미지 태그에 현재 시간을 포함
          push: true  # 빌드된 이미지를 Docker Hub로 푸시
  • Gradle로 빌드를 하면 `${프로젝트 루트}/build` 디렉터리에 JAR 파일이 생성됩니다. 이 JAR 파일을 프로젝트에 있는 Dockerfile를 이용해 Docker 이미지로 빌드해야 합니다.
  • 먼저 마켓플레이스에 있는 `josStorer/get-current-time@v2.1.2`를 통해 현재 시간을 가져옵니다.
    • 이 값은 빌드한 Docker 이미지의 `tag`로 사용될 예정입니다.

secret 생성 화면

  • 그리고나서 Docker 이미지로 빌드하기 전 Docker Hub 계정으로 로그인하는 작업을 수행했습니다.
    • 아이디와 비밀번호는 프로젝트 Repository의 Secret 설정을 통해 입력을 합니다.
    • Secret 설정은 프로젝트 Repository의 `Settings`-왼쪽 사이드 바에서 `Secrets and variables`-`Actions` 부분에서 설정할 수 있습니다.
    • Repository secrets 부분에서 `New repository secret` 버튼을 클릭해 아이디와 비밀번호를 시크릿으로 등록합니다.
  • 마지막으로 GitHub Actions 마켓플레이스에서 제공하는 `docker/build-push-action@v6.10.0`를 이용해 Docker 이미지를 빌드하고 `tags`에 해당하는 Docker Hub 레지스트리에 이미지를 Push 합니다. 저는 개발 서버 전용 Dockerfile을 이용해 빌드하길 원해 `context` 부분을 추가했습니다.
    • 빌드할 때 윗 단계 step에서 가져온 시간 정보를 이미지의 tag로 설정합니다.
    • `push` 값은 기본값이 `false`이므로 `true`로 설정합니다.

Step 4. 쿠버네티스 매티페스트 파일 수정하기

jobs:
  build:
    name: Build and Push Docker Image
    runs-on: ubuntu-latest
    steps:
      # 윗 단계 생략 ..
	  - name: Update deployment version
        run: |
          sed -i "s|image: myRegistry/upup-radio-backend:.*|image: myRegistry/upup-radio-backend:${{ steps.current_time.outputs.formattedTime }}|g" ./kubernetes/api.yaml

 

  • 이 `step`은 Docker 이미지의 버전이 변경되었음을 ArgoCD에 알리기 위한 작업입니다.
  • linux의`sed`(stream editor)를 이용해 `./kubenetes/api.yaml` 파일의 내용을 치환합니다.
    • `s|`와 `|g`의 사이에서 구분자 `|`를 사용해 치환합니다.
    • 치환 대상 문자열 (from): `image: myRegistry/upup-radio-backend:.*`부분
    • 치환할 문자열 (to): `image: myRegistry/upup-radio-backend:${{ steps.current_time.outputs.formattedTime }}` 부분

Step 5. 변경 사항을 GitHub Actions Bot을 통해 Commit / Push 시키기

jobs:
  build:
    name: Build and Push Docker Image
    runs-on: ubuntu-latest
    steps:
      # 윗 단계 생략 ..
      - name: Configure Git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Commit and push changes
        run: |
          git add ./kubernetes/api.yaml
          git commit -m ":tada: Update deployment version to ${{ steps.current_time.outputs.formattedTime }}"
          git push origin dev
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  • 이제 매니페스트 파일의 변경 사항을 Commit / Push를 해서 ArgoCD에게 알려야 합니다.
    • ArgoCD에서 매니페스트 파일의 변경 사항을 감지해 쿠버네트스 클러스터 환경과 Sync 작업을 수행할 것입니다.
  • 이 과정에서 Commit / Push 작업을 위해 GitHub Actions Bot을 이용합니다.
    • `41898282+github-actions[bot]@users.noreply.github.com` 계정은 GitHub Actions Bot의 계정입니다.
    • GitHub Actions Bot을 이용하기 위해 권한을 설정해야 합니다. 프로젝트의 `Settings`-왼쪽 사이드 바의 `Actions`-`General` 메뉴로 이동한 뒤 `Workflow permissions`에서 `Read and write permissions`에 체크한 후 `Save` 버튼을 눌러 저장합니다.

GitHub Actions Bot 권한 설정

  • 권한 설정을 마치면 별도의 Secret을 등록하지 않아도 워크플로우 스크립트에서 `${{ secrets.GITHUB_TOKEN }}`를 사용할 수 있습니다.

최종 워크플로우 파일

name: CI

on:
  push:
    branches: [ "main", "dev" ]

jobs:
  build:
    name: Build and Push Docker Image
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Java JDK
        uses: actions/setup-java@v4.6.0
        with:
          distribution: 'corretto'
          java-version: '17'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

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

      - name: Get current time
        uses: josStorer/get-current-time@v2.1.2
        id: current_time
        with:
          format: YYYY-MM-DDTHH-mm-ss
          utcOffset: "+09:00"

      - name: Docker Login
        uses: docker/login-action@v3.3.0
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker images
        uses: docker/build-push-action@v6.10.0
        with:
          context: .
          file: Dockerfile-dev
          tags: myRegistry/upup-radio-backend:${{ steps.current_time.outputs.formattedTime }}
          push: true

      - name: Update deployment version
        run: |
          sed -i "s|image: myRegistry/upup-radio-backend:.*|image: myRegistry/upup-radio-backend:${{ steps.current_time.outputs.formattedTime }}|g" ./kubernetes/api.yaml

      - name: Configure Git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Commit and push changes
        run: |
          git add ./kubernetes/api.yaml
          git commit -m ":tada: Update deployment version to ${{ steps.current_time.outputs.formattedTime }}"
          git push origin dev
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

동작 확인

이제 `main` 브랜치 또는 `dev` 브랜치에 Push 이벤트가 발생할 때마다 워크플로우가 트리거되는지 확인해 보겠습니다.

프로젝트 Repository에서 `Actions` 탭에서 성공적으로 작동하는 것을 확인할 수 있습니다.

 

ArgoCD

ArgoCD에서도 `41898282+github-actions[bot]@users.noreply.github.com` 이메일을 사용하는 GitHub Actions Bot이 수정한 매니페스트 파일을 통해 CD가 작동한 것을 확인할 수 있습니다.

 

참고

  • https://zerohertz.github.io/cicd-init/
  • https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#job-context
  • https://docs.github.com/ko/actions/writing-workflows/workflow-syntax-for-github-actions

'DevOps' 카테고리의 다른 글

Argo Rollouts를 이용한 Blue-Green 배포 전략 적용하기  (0) 2025.01.07
GitOps에서 SealedSecret으로 안전하게 Secret 관리하기  (1) 2025.01.04
GitOps와 ArgoCD  (2) 2025.01.04
'DevOps' 카테고리의 다른 글
  • Argo Rollouts를 이용한 Blue-Green 배포 전략 적용하기
  • GitOps에서 SealedSecret으로 안전하게 Secret 관리하기
  • GitOps와 ArgoCD
옐리yelly
옐리yelly
  • 옐리yelly
    개발 갤러리
    옐리yelly
  • 전체
    오늘
    어제
    • 모든 글 보기 (84)
      • Project (22)
      • Java (4)
      • Spring (8)
      • Kubernetes (6)
      • Docker (2)
      • JPA (2)
      • Querydsl (2)
      • MySQL (9)
      • ElasticSearch (7)
      • DevOps (4)
      • Message Broker (3)
      • Git & GitHub (2)
      • Svelte (1)
      • Python (8)
        • Python Distilled (4)
        • Anaconda (1)
        • Django (0)
        • pandas (3)
      • Algorithm (1)
      • Computer Science (0)
      • 내 생각 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    pandas
    gitops
    Message Broker
    리팩토링
    k8s
    ncloud
    데드락
    elasticsearch
    비사이드
    MySQL
    pymysql
    JPA
    mybatis
    OOP
    RabbitMQ
    nks
    argocd
    blue-green 배포
    svelte
    docker
    Project
    devops
    Spring
    querydsl
    예약 시스템
    성능 테스트
    포텐데이
    프로젝트
    커넥션 풀
    Python
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
옐리yelly
GitHub Actions + ArgoCD로 k8s 클러스터 환경에서 CI/CD 구축하기
상단으로

티스토리툴바