포스트

Necesse Helm Chart 자동 버전 업데이트 및 Harbor OCI 배포 자동화

Necesse Docker 이미지 최신 버전 감지부터 Helm Chart 자동 업데이트, 그리고 Harbor OCI Registry로의 자동 배포까지 전체 자동화 파이프라인 구축 과정 기록

Necesse Helm Chart 자동 버전 업데이트 및 Harbor OCI 배포 자동화

이 포스트는 brammys/necesse-server Docker 이미지를 기반으로 Helm Chart를 만들고, GitHub Actions를 통한 버전 자동 업데이트, 그리고 Harbor OCI Registry에 자동 배포하도록 만든 전체 과정을 정리한 것이다.

전체 코드는 dakim/necesse-helm에서 확인할 수 있습니다.

시작 전 준비 사항


  • Kubernetes 1.24+
  • Harbor OCI Registry
  • GitHub Actions (CI/CD)
  • Necesse Dedicated Server용 컨테이너 이미지
  • Helm 3.10+

Helm Chart 기본 구조


necesse-helm/
├── Chart.yaml                  # Helm chart metadata (appVersion 자동 업데이트)
├── values.yaml                 # 서버 설정, 이미지 태그, PVC 설정 등
│
├── templates/                  # 쿠버네티스 템플릿
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml (optional)
│   └── _helpers.tpl
│
├── scripts/
│   └── update-necesse-version.py   # Docker Hub 최신 태그 조회 스크립트
│
└── .github/
    └── workflows/              # GitHub Actions CI/CD
        ├── update-necesse-version.yaml   # appVersion / image.tag 자동 갱신
        └── publish-helm-oci.yaml         # Helm chart → Harbor OCI 자동 배포

버전 업데이트 스크립트 작성


다음 로직을 기준으로 최신 태그 조회:

  1. Docker Hub에서 last_updated 기준으로 가장 최근 태그 수집
  2. major, minor, patch, build 순으로 숫자 비교
  3. Chart.yamlvalues.yaml에 태그 반영
1
2
3
4
5
6
7
8
9
10
def select_latest_tag(tag_records):
    # 1) 날짜 기준 후보 필터링
    parsed.sort(key=lambda x: x["last_updated"])
    latest_time = parsed[-1]["last_updated"]
    candidates = [p for p in parsed if p["last_updated"] == latest_time]

    # 2) 숫자 비교
    candidates.sort(key=lambda x: (x["major"], x["minor"], x["patch"], x["build"]))
    chosen = candidates[-1]
    return chosen["base"], chosen["name"]

스크립트를 GitHub Actions에서 실행하여

  • Chart.yaml의 appVersion
  • values.yaml의 image.tag

값을 자동으로 수정하도록 구성했다.

GitHub Actions 워크플로우 구성


1. Necesse 버전 자동 업데이트

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
name: Update Necesse version

on:
  schedule:
    - cron: "0 21 * * *"       # 한국 기준 6시 자동 실행
  workflow_dispatch: {}        # 수동 실행도 허용

permissions:
  contents: write              # Chart.yaml / values.yaml 변경 후 commit & push를 위해 필요

jobs:
  update-necesse-version:
    runs-on: ubuntu-latest     # GitHub 제공 기본 러너

    steps:
      # -----------------------------
      # 1. 저장소 소스 체크아웃
      # -----------------------------
      - name: Checkout
        uses: actions/checkout@v4
        with:
          token: $   # 자동 커밋 & 푸시를 위한 권한 제공

      # -----------------------------
      # 2. Python 실행 환경 구성
      # -----------------------------
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.x"                # Python 3 최신 버전 사용

      # -----------------------------
      # 3. 스크립트 실행에 필요한 패키지 설치
      # -----------------------------
      - name: Install dependencies
        run: |
          pip install requests pyyaml          # Docker Hub API 요청 + YAML 파일 수정용 라이브러리

      # -----------------------------
      # 4. Docker Hub 최신 버전 조회 & Chart 업데이트 스크립트 실행
      # -----------------------------
      - name: Update Necesse versions
        run: |
          python scripts/update-necesse-version.py

      # -----------------------------
      # 5. 변경사항이 있을 경우 자동 커밋 & 푸시
      # -----------------------------
      - name: Commit and push changes
        run: |
          # 변경사항이 없으면 종료
          if git diff --quiet; then
            echo "No changes to commit."
            exit 0
          fi

          # GitHub bot 계정으로 커밋
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          
          git commit -am "chore: bump Necesse version (automated)"  # 버전 업데이트 커밋 메시지
          git push                                                  # 변경사항 푸시

작성한 워크플로우는 다음 역할을 수행한다:

  • Docker Hub에서 brammys/necesse-server 태그 목록 조회
  • 날짜 및 버전 기준으로 최신 태그 선택
  • Chart.yamlappVersion, values.yamlimage.tag 갱신
  • 변경이 있으면 자동 커밋 후 GitHub 저장소에 push

2. Helm Chart를 Harbor OCI Registry로 배포

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
name: Publish Helm chart to Harbor (OCI)

on:
  push:
    branches:
      - main
    paths:
      - "Chart.yaml"                      # Chart 메타데이터 변경 시
      - "values.yaml"                     # values 변경 시
      - "templates/**"                    # 템플릿 변경 시
      - ".github/workflows/publish-helm-oci.yaml"
  workflow_dispatch: {}                   # 수동 실행 가능

jobs:
  publish-helm-chart:
    runs-on: ubuntu-latest

    env:
      HARBOR_URL: $           # Harbor OCI endpoint
      HARBOR_USERNAME: $ # Robot 계정
      HARBOR_PASSWORD: $

    steps:
      # 저장소 체크아웃
      - name: Checkout
        uses: actions/checkout@v4

      # Helm CLI 설치
      - name: Set up Helm
        uses: azure/setup-helm@v4
        with:
          version: v4.0.1

      - name: Show Helm version
        run: helm version

      # Harbor(OCI) 로그인
      - name: Login to Harbor OCI registry
        run: |
          echo "${HARBOR_PASSWORD}" | helm registry login "${HARBOR_URL}" \
            --username "${HARBOR_USERNAME}" \
            --password-stdin

      # yq 설치 (Chart.yaml에서 name/version 추출용)
      - name: Install yq
        run: |
          sudo wget -qO /usr/local/bin/yq \
            https://github.com/mikefarah/yq/releases/download/v4.44.3/yq_linux_amd64
          sudo chmod +x /usr/local/bin/yq

      # Helm chart 패키징 (.tgz 생성)
      - name: Package Helm chart
        run: |
          CHART_NAME=$(yq '.name' Chart.yaml)
          CHART_VERSION=$(yq '.version' Chart.yaml)

          echo "Packaging chart: ${CHART_NAME} (version: ${CHART_VERSION})"
          helm package . --version "${CHART_VERSION}"

      # Harbor에 OCI Artifact로 Push
      - name: Push Helm chart as OCI artifact
        run: |
          CHART_NAME=$(yq '.name' Chart.yaml)
          CHART_VERSION=$(yq '.version' Chart.yaml)
          CHART_TGZ="${CHART_NAME}-${CHART_VERSION}.tgz"

          if [ ! -f "${CHART_TGZ}" ]; then
            echo "Chart package ${CHART_TGZ} not found"
            exit 1
          fi

          echo "Pushing ${CHART_TGZ} to oci://${HARBOR_URL}/dedicated-server"
          helm push "${CHART_TGZ}" "oci://${HARBOR_URL}/dedicated-server"

작성한 워크플로우는 다음 역할을 수행한다:

  • 코드 변경 감지 → 워크플로우 실행
  • Helm 설치
  • Harbor OCI 로그인
  • Chart.yaml에서 Chart 이름 및 버전 추출
  • Helm Chart 패키징 (.tgz 생성)
  • Harbor OCI Registry로 Push

최종 시스템 동작 구조


flowchart TD

  %% --- External Source Group ---
  subgraph EXTERNAL["External Source"]
    A["Docker Hub<br/>brammys/necesse-server"]
  end

  %% --- GitHub Automation System Group ---
  subgraph GITHUB["GitHub Automation System"]
    B["update-necesse-version.py<br/>(Version Update CI)"]
    C["GitHub Repo<br/>necesse-helm"]
    D["Publish Workflow<br/>(helm package → push OCI)"]
  end

  %% --- Artifact Storage Group ---
  subgraph REGISTRY["Artifact Storage"]
    E["Harbor OCI Registry<br/>dedicated-server"]
  end

  %% --- Flows ---
  A -->|Check latest tag| B
  B -->|Update appVersion & image.tag| C
  C -->|Commit & Push| D
  D -->|Publish chart| E