Container Orchestration/Kubernetes

Public Helm Chart Repository 구축하기 — 로컬 차트에서 ArtifactHub까지

Somaz 2026. 4. 24. 00:00
728x90
반응형

Overview

사내에서 관리하던 Helm 차트를 여러 클러스터에서 공유하다 보면 결국 한계에 부딪힌다. 한 클러스터용으로 작성한 차트가 다른 클러스터에서도 유용한데, 복사해서 쓰다 보면 버전이 어긋나고 CI가 깨지고, 팀 밖으로는 공유할 방법이 없다.

 

이번 포스팅에서는 사내 차트 하나를 공개된 Dual Distribution Helm Chart Repository로 추출하는 전체 과정을 정리한다. GitHub Pages(커스텀 서브도메인) 기반의 전통적인 Helm Repo와 OCI Registry(GHCR)에 동시에 배포하고, ArtifactHub에 등록해 카탈로그에서 검색 가능하게 만들며, CI/CD와 거버넌스 체계까지 갖추는 방법이다.

 

예시로 사용한 차트는 nginx-gateway-cr이다. NGINX Gateway Fabric 컨트롤러 Chart를 보완하는 Custom Resource(Gateway, NginxProxy, ServiceMonitor)를 묶은 차트다. 여기서 다루는 패턴은 공개하고 싶은 어떤 차트에도 그대로 적용할 수 있다.

 

 

 

 


 

왜 별도의 Charts Repository를 만들었는가?

차트를 분리하기 전에, 정말 별도 레포가 필요한지 솔직하게 점검할 필요가 있었다. 세 가지 질문으로 결정했다.

다른 클러스터나 다른 팀이 이 차트를 재사용할 가능성이 있는가? 있다. 복사/붙여넣기는 바로 drift가 발생한다. 차트를 추출해야 한다.

 

나중에 추가로 공개할 사내 차트가 더 있는가? 최소 2~3개는 더 있다. 레포 하나당 차트 하나가 아니라 모노레포 구조(`charts/<name>/`)가 맞다.

 

카탈로그에서 검색되는 것도 목표인가? 그렇다. 깔끔한 공개 URL과 적절한 메타데이터가 필요하다.

 

모노레포 결정은 중요하다. 업계 표준인 `bitnami/charts`, `prometheus-community/helm-charts`, `grafana/helm-charts` 모두 이 패턴을 쓴다. ArtifactHub는 개별 차트를 `Chart.yaml`의 name 필드로 색인하기 때문에 레포 이름이 차트 검색에 영향을 주지 않는다. 차트 이름만 중요하다.

 

 

 

레포지토리 네이밍

helm-charts는 다소 일반적이긴 하지만 `username/org` 와 결합하면 충분히 명확해진다. `github.com/<user>/helm-charts` 전체 경로는 모호할 여지가 없다. 고려했던 다른 후보들은 다음과 같다.

  • `<user>-charts` — 브랜딩은 명확하지만 약간 중복
  • `helm-deck` — 창의적이지만 기존 helm-*-template 레포들과 패턴이 맞지 않음
  • `<chart-name>-chart` — 단일 차트 레포에만 적합

기존에 helm-chart-template, helm-base-app-template 같은 *-template 계열 레포가 있다면, helm-charts(복수형, "실제 컬렉션")가 자연스러운 네이밍 쌍을 이룬다.

 

 

 

 

Repository 레이아웃

최종 레이아웃은 다음과 같다. N개 차트까지 확장 가능한 구조다.

helm-charts/
├── .github/
│   ├── CODEOWNERS                                 # * @<user>
│   ├── dependabot.yml                             # github-actions weekly
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── config.yml                             # issue chooser
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   └── workflows/
│       ├── lint.yml                               # ct lint + helm template + kubeconform
│       ├── release.yml                            # chart-releaser + GHCR OCI
│       ├── gitlab-mirror.yml                      # backup mirror
│       ├── dependabot-auto-merge.yml
│       ├── stale-issues.yml
│       ├── issue-greeting.yml
│       └── contributors.yml
├── charts/
│   └── nginx-gateway-cr/
│       ├── Chart.yaml
│       ├── values.yaml
│       ├── values.schema.json                     # JSON Schema (draft-07)
│       ├── README.md
│       ├── .helmignore
│       └── templates/
│           ├── _helpers.tpl
│           ├── gateways.yaml
│           ├── nginxproxies.yaml
│           ├── servicemonitor-controller.yaml
│           ├── servicemonitor-dataplane.yaml
│           ├── referencegrants.yaml
│           └── NOTES.txt
├── README.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md                                # auto-generated
├── SECURITY.md
├── Makefile                                       # local automation
├── LICENSE                                        # Apache-2.0
├── artifacthub-repo.yml                           # ownership claim
└── .gitignore
  • 라이선스는 Apache-2.0을 선택했다. Kubernetes/Helm 생태계의 사실상 표준이다(Helm, Kubernetes, NGINX Gateway Fabric, 주요 차트 컬렉션 모두 Apache-2.0).
  • 명시적인 특허 조항이 있어서 기업이 도입할 때 법무팀과의 확인 과정이 적다는 실무적 이점도 있다.

 

 

 

 


 

 

 

 

 

Chart 설계 — 확장성 우선

사내용 nginx-gateway-cr은 단순했다. 두 클러스터에서 쓸 만큼의 필드만 있으면 됐다. 공개로 전환하려면 한 번도 만나본 적 없는 사용자의, 한 번도 본 적 없는 클러스터를 염두에 두고 설계해야 한다.

 

 

 

다섯 가지 확장성 패턴

 

 

1. Empty-by-default (기본값은 비어있음)

# values.yaml
gateways: []
referenceGrants: []
serviceMonitor:
  controller:
    enabled: false
  dataplane:
    enabled: false
  • `values` 없이 `helm install` 을 실행하면 어떤 리소스도 생성되지 않는다. 안전한 No-op 기본값이다.
  • 사용자는 `gateways` 를 채워서 명시적으로 `opt-in` 한다. default 네임스페이스에 예상치 못한 리소스가 생기지 않는다.

 

 

 

2. Defaults + Per-entry Overrides

proxy:                          # 글로벌 defaults
  replicas: 1
  service:
    externalTrafficPolicy: Cluster

gateways:
  - name: app
    loadBalancerIP: 10.0.0.10
    proxy:                      # per-gateway override (deep-merge)
      replicas: 3
      service:
        annotations:
          metallb.universe.tf/address-pool: prod-pool
  • 템플릿에서 `mergeOverwrite (deepCopy $defaultProxy) (default dict $gw.proxy)` 를 쓰면 최종 spec이 만들어진다.
  • 사용자는 기본값과 다른 부분만 설정하면 된다.

 

 

3. Shorthand + Full-control 공존

# 일반적인 케이스(HTTP + HTTPS 종료)를 위한 shorthand
gateways:
  - name: app
    https:
      enabled: true
      hostname: "*.example.com"
      tlsSecretName: wildcard-example-tls

  # 복잡한 케이스(TLS Passthrough, TCP, gRPC 등)는 전체 listener 오버라이드
  - name: tcp
    listeners:
      - name: tls-passthrough
        protocol: TLS
        port: 8443
        tls:
          mode: Passthrough
        allowedRoutes:
          kinds:
            - kind: TLSRoute
  • `listeners` 가 설정되면 shorthand는 무시된다. 쉬운 기본값과 완전한 제어력을 모두 제공하는 방식이다.

 

 

 

4. Escape Hatch — 예외 상황을 위한 탈출구

proxy:
  kubernetesExtra: {}     # spec.kubernetes로 merge
  specExtra: {}           # spec(top-level)로 merge
  • NGF의 `NginxProxy` CRD에는 수십 개의 필드가 있다. 모두 values에 1급 필드로 노출하면 스키마가 지저분해진다.
  • 자주 쓰는 것들(replicas, resources, externalTrafficPolicy, logging, rewriteClientIP, telemetry, ipFamily)만 노출하고, Escape Hatch 키 두 개(`kubernetesExtra`, `specExtra`)로 다른 필드에도 접근할 수 있게 했다.
  • 차트 업데이트를 기다리지 않고도 어떤 필드든 사용 가능하다.

 

 

5. 스키마로 검증되는 입력값

// values.schema.json (일부)
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "proxy": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "replicas": { "type": "integer", "minimum": 0 },
        "service": {
          "type": "object",
          "properties": {
            "externalTrafficPolicy": {
              "type": "string",
              "enum": ["Cluster", "Local"]
            }
          }
        }
      }
    }
  }
}
  • `helm install` 시 이 스키마가 자동으로 실행된다.
  • 음수 replicas, enum 오타, 알 수 없는 최상위 키 — 모두 Kubernetes 오브젝트가 생성되기 전에 거부된다.
  • 덤으로 ArtifactHub가 이 스키마를 기반으로 차트 페이지에 values 입력 폼을 렌더링해 준다.
 
 
$ helm template t charts/nginx-gateway-cr --set proxy.replicas=-1
Error: values don't meet the specifications of the schema(s) in the following chart(s):
nginx-gateway-cr:
- proxy.replicas: Must be greater than or equal to 0

 

 

 

 

 


 

 

 

 

CI/CD Workflow 구성

워크플로우는 총 8개다. 각자 하나의 일만 한다.

 

 

`lint.yml` — PR 검증

PR이 들어오면 3단계로 검증한다.

  1. `ct lint` — chart-testing의 표준 검증(Chart.yaml, README, 버전 증가 여부)
  2. `helm template` — 템플릿 문법 오류와 스키마 위반 감지
  3. `kubeconform` — 렌더링된 매니페스트를 Kubernetes core + CRD 스키마로 검증(datreeio CRDs catalog 사용)

 

 

`release.yml` — main push가 모든 걸 트리거

두 가지 중요한 포인트가 있다.

 

트리거는 `Chart.yaml` 의 `version` 증가지, `git tag` 가 아니다. `chart-releaser-action` 이 `Chart.yaml` 의 `version` 을 읽고 이전 릴리즈보다 높으면 자동으로 tag(`<chart>-<version>`)를 만든다. git tag를 수동으로 실행할 일이 없다.

 

`concurrency.cancel-in-progress: false` — 릴리즈는 반드시 끝까지 완료되어야 한다. 중간에 취소되면 일관성 없는 상태가 된다(tag는 생성됐는데 .tgz는 없거나, 반대거나).

 

 

 

 

보조 Workflow

Workflow 용도
gitlab-mirror.yml main push마다 GitLab으로 백업 미러링
dependabot-auto-merge.yml minor/patch 의존성 PR을 squash-merge로 자동 병합
stale-issues.yml 30일간 활동 없는 이슈를 stale 표시, 7일 후 close
issue-greeting.yml 새 이슈에 환영 메시지 자동 댓글
contributors.yml 릴리즈 후 CONTRIBUTORS.md 자동 업데이트

 

 

 

Distribution — Static + OCI Dual 배포

Helm Chart를 공개 배포하는 방법은 두 가지다. 대부분의 프로젝트는 하나만 고른다. 여기서는 둘 다 한다.

항목 GitHub Pages (Static) GHCR (OCI)
사용자 명령어 `helm repo add` 후 `helm install` `helm install oci://...` (한 번에)
Helm 버전 3.0+ 3.8+ 필요
ArtifactHub `index.yaml` 로 자동 색인 수동 등록 필요
검색성 강함(ArtifactHub 검색 가능) 약함(URL을 알아야 함)
인증 모델 Public HTTP 토큰 인증(Private 지원)
호스팅 GitHub Pages(무료) GHCR 스토리지(Free tier)
  • 왜 둘 다? ArtifactHub 검색성은 Static Repo에서 온다.
  • OCI는 현대적 표준이며, 커스텀 도메인에 문제가 생겼을 때의 Fallback 역할도 한다(뒤에서 자세히). 비용은 거의 없다.
  • `chart-releaser` 가 이미 `.tgz` 를 만드니, 두 번째 목적지로 밀어넣는 건 bash 몇 줄 추가면 된다.

 

 

 

 


 

 

 

 

 

Bootstrap 함정 #1 — gh-pages Chicken-and-Egg 문제

 

처음 push하면 릴리즈 워크플로우가 다음과 같이 실패한다.

fatal: invalid reference: origin/gh-pages
Error: exit status 128
  • `chart-releaser-action` 은 `gh-pages` 브랜치가 이미 존재한다고 가정한다.
  • 이 브랜치에 새 `index.yaml` 과 `.tgz` 를 일반 커밋으로 추가하는 방식이다. 갓 만든 레포에는 gh-pages 브랜치가 없다.

 

 

 

해결 방법 — 빈 orphan 브랜치를 한 번만 수동으로 만든다.

git switch --orphan gh-pages
git commit --allow-empty -m "chore: initialize gh-pages branch"
git push -u origin gh-pages
git switch main
  • 그 다음 실패한 릴리즈 워크플로우를 다시 실행한다. 이후부터는 `chart-releaser` 가 알아서 `gh-pages` 를 관리한다.
  • 주의할 점은 `chart-releaser-action` 이 이걸 자동 부트스트랩하지 않는다는 것이다. 이 액션은 사용자가 이미 GitHub Pages를 설정했다고 가정한다.
  • 대부분은 첫 릴리즈 전에 설정해 두기 때문이다. Pages 설정이 전혀 없는 갓 만든 레포에서만 이 Edge Case가 발생한다. 자동 부트스트랩 관련 Open Discussion이 있긴 하지만, 현재는 수동 Workaround가 권장 경로다.

 

 

 

Custom Domain 설정

기본 URL은 `https://<user>.github.io/helm-charts` 다. 이것도 잘 작동한다. 다만 (대부분의 개인 계정 사용자가 그렇듯) `<user>.github.io` 프로필 사이트에 커스텀 도메인(예: 블로그)을 연결해 뒀다면, 모든 프로젝트 페이지가 그 커스텀 도메인으로 301 리다이렉트된다.

https://<user>.github.io/helm-charts/index.yaml
  → 301 Moved Permanently
  → http://<your-blog-domain>/helm-charts/index.yaml
  • 문제는 ArtifactHub가 URL을 확인할 때 301 리다이렉트를 따라가지 않는다는 점이다. 등록이 다음 에러로 실패한다.
 
 
An error occurred adding the repository: invalid input:
the url provided does not point to a valid Helm repository
  • 해결책은 전용 서브도메인을 쓰는 것이다. 3단계로 처리한다.

 

 

1. DNS — CNAME 레코드 추가

DNS 제공자에서 레코드를 추가한다.

Type Host Value TTL
CNAME charts <user>.github.io 180

 

 

전파 확인

dig +short charts.example.com
# 예상 결과:
# <user>.github.io.
# 185.199.108.153
# 185.199.109.153
# 185.199.110.153
# 185.199.111.153
  • (IP들은 GitHub Pages anycast 대역이다. DNS가 제대로 해석되면 보인다.)

 

 

 

2. gh-pages 브랜치에 CNAME 파일 추가

GitHub Pages는 서빙할 브랜치의 루트에서 CNAME 파일을 읽어 도메인을 결정한다. gh-pages에 추가한다.

git switch gh-pages
git pull --rebase origin gh-pages
echo "charts.example.com" > CNAME
git add CNAME
git commit -m "chore: add CNAME for charts.example.com custom domain"
git push origin gh-pages
git switch main

 

 

 

3. GitHub Pages 설정에서 HTTPS 활성화

레포의 Settings → Pages로 이동한다.

  • CNAME 파일을 push하면 GitHub가 커스텀 도메인을 자동 감지한다.
  • 1~3분 기다리면 DNS 체크가 통과된다(도메인 옆에 ✓ 표시).
  • **"Enforce HTTPS"**에 체크한다. GitHub가 Let's Encrypt 인증서를 프로비저닝한다(1~15분).

 

 

4. 검증

curl -I https://charts.example.com/index.yaml
# HTTP/2 200
# content-type: text/yaml

 

 

 

 

 


 

 

 

 

ArtifactHub 등록

URL이 정상 작동하면 artifacthub.io에 등록한다.

 

 

 

Step 1 — 메타데이터 파일 생성

`artifacthub-repo.yml` 을 레포에 추가한다(ID는 일단 비워둬도 된다).

# artifacthub-repo.yml
repositoryID: ""
owners:
  - name: <your-github-handle>
    email: <your-email>

 

 

 

이 파일은 ArtifactHub가 fetch할 때 소유권을 증명한다. `index.yaml` 과 같은 URL에서 서빙되어야 한다. 즉 `main` 이 아니라 `gh-pages` 브랜치 루트에 있어야 한다. chart-releaser는 이 파일을 자동으로 복사하지 않으므로 한 번 수동으로 추가한다.

git switch gh-pages
git checkout main -- artifacthub-repo.yml
git add artifacthub-repo.yml
git commit -m "chore: add ArtifactHub repository metadata"
git push origin gh-pages
git switch main
  • chart-releaser는 `gh-pages` 의 파일을 삭제하지 않으므로 한 번 올려두면 영원히 유지된다. 자동화할 필요가 없다.

 

 

Step 2 — Repository 등록

artifacthub.io → 로그인(GitHub OAuth, 무료) → Control Panel → Add repository:

 

필드 비고
Kind Helm charts OCI는 별도 Kind
Name <your-handle> helm repo add alias로 쓰임
Display name <Your Handle> Helm Charts 카탈로그 표시명
URL https://charts.example.com 커스텀 도메인
Disabled OFF 카탈로그에 표시되게 유지
Security scanner disabled OFF 참조하는 컨테이너 이미지를 Trivy가 자동 스캔
  • ArtifactHub가 repositoryID(UUID)를 할당한다.
  • 이 값을 `artifacthub-repo.yml` 에 복사해 넣고 `main` 과 `gh-pages` 양쪽에 commit, push한다.

 

 

Step 3 — 기다리기

ArtifactHub는 약 30분마다 자동 sync한다. 첫 sync 후:

  • 차트가 카탈로그에 노출된다.
  • "Verified Publisher" 배지가 붙는다(repositoryID가 일치하면).
  • `values.schema.json` 기반 입력 폼이 각 차트 페이지에 활성화된다.

 

 

 

 


 

 

 

리스크 — 도메인이 만료되면 어떻게 되는가?

반드시 나오는 질문이다. 

리소스 charts.example.com이 죽었을 때 영향
helm repo add ... (신규 설치) DNS 실패
helm repo update 실패
이미 설치된 릴리즈 Helm이 .tgz를 로컬 캐시
ArtifactHub 카탈로그 엔트리 Unreachable 표시, 결국 제거됨
OCI 설치 (oci://ghcr.io/...) 영향 없음 — GitHub 소유 도메인

 

 

정확히 이것 때문에 양쪽에 배포하는 것이다. GHCR에 올린 OCI 배포물이 안전망이다. 커스텀 도메인이 다운되더라도 사용자는 이렇게 전환하면 된다.

helm install <release> oci://ghcr.io/<user>/charts/<chart-name> --version <ver>
  • 다른 건 아무것도 바꿀 필요 없다.
  • README에 두 가지 설치 방법을 모두 기재하고(OCI를 Backup 옵션으로 눈에 띄게), 도메인 등록기관에서 자동 갱신을 켜두자.
  • 대부분의 도메인 만료는 의도적 갱신 중단이 아니라 카드 정보 갱신 누락 때문이다.

 

 

 

로컬 자동화 — 다중 차트 대응 Makefile

Makefile은 단일 차트에서도 써볼 만큼 간단하고, 차트가 늘어나면 더 빛을 본다. 패턴은 CHART=<name>으로 한 차트로 범위를 좁히고, 기본값은 charts/ 아래 전체 차트로 동작하는 방식이다.

 

 

유용한 타겟들

Target 용도
make help 자동 생성 도움말(kubebuilder 스타일 awk 파서)
make charts 모든 차트와 현재 버전 나열
make lint [CHART=...] helm lint
make ct-lint chart-testing lint(CI와 동일)
make template [CHART=...] helm template 스모크 렌더
make validate [CHART=...] kubeconform으로 k8s + CRD 스키마 검증
make ci 위 전체를 순서대로 실행
make package [CHART=...] helm package → .cr-release-packages/
make version CHART=<name> 현재 버전 출력
make bump CHART=<name> LEVEL=patch|minor|major Chart.yaml 버전 증가, 다음 단계 안내 출력
make scaffold CHART=<new> FROM=<existing> 기존 차트를 시작점으로 복사

 

 

 

bump 활용 

릴리즈 플로우가 다음과 같이 정리된다.

make bump CHART=nginx-gateway-cr LEVEL=patch
# Chart.yaml의 artifacthub.io/changes annotation 편집
git commit -am "feat(nginx-gateway-cr): describe change"
git push    # release.yml 자동 트리거

 

 

 

 


 

 

 

거버넌스 파일

공개 차트 레포는 작은 추가 네 개만으로 완성도가 크게 올라간다.

 

 

 

CONTRIBUTING.md

PR 프로세스, 버전 bump 규칙, 로컬 검증 명령어를 문서화하고 `Makefile` 을 참조시킨다. 이게 없으면 의욕 넘치는 Contributor가 차트 버전을 올려야 한다는 걸 모른다.

 

 

SECURITY.md

취약점 신고를 위한 Private Channel(GitHub Security Advisories + 이메일 Fallback)을 제공하고 대응 시간을 명시한다. 인프라 차트처럼 프로덕션 트래픽에 영향을 줄 수 있는 경우, 일반 OSS 라이브러리보다 훨씬 중요하다.

 

 

.github/PULL_REQUEST_TEMPLATE.md

모든 PR에 차트 인식 체크리스트가 자동으로 채워진다.

- [ ] `Chart.yaml`의 `version`을 SemVer에 맞게 bump
- [ ] `Chart.yaml`의 `artifacthub.io/changes`에 엔트리 추가
- [ ] `values.yaml` 구조가 바뀌었다면 `values.schema.json` 업데이트
- [ ] values나 동작이 바뀌었다면 차트 `README.md` 업데이트
- [ ] 로컬에서 `helm lint`와 `helm template` 실행

 

 

 

.github/ISSUE_TEMPLATE/*

차트 이름, 차트 버전, helm 버전, k8s 버전, 사용한 values, 예상 동작과 실제 동작을 묻는 YAML 기반 폼 템플릿이다. 이게 없으면 디버깅이 5번의 주고받기 댓글로 시작된다.

 

`config.yml` 에 "Security vulnerability" 링크를 `SECURITY.md` 로 걸고, Blank Issue를 비활성화해서 사용자가 항상 템플릿을 선택하게 한다.

 

 

 

 


 

 

 

운영하면서 얻은 교훈

누군가 시작하기 전에 알려줬다면 좋았을 것들이다.

 

`chart-releaser-action` 은 `gh-pages` 를 부트스트랩해 주지 않는다. 첫 릴리즈 전에 orphan 브랜치를 수동으로 만들어야 한다. 안 그러면 애매한 에러 메시지로 10분을 디버깅하게 된다.

 

ArtifactHub는 HTTP 301 리다이렉트를 따라가지 않는다. `<user>.github.io` 프로필 사이트에 커스텀 도메인이 걸려 있다면 전용 서브도메인 없이는 프로젝트 레포를 등록할 수 없다.

`artifacthub-repo.yml` 은 `main` 이 아니라 `gh-pages` 에 있어야 한다. 소유권 검증을 위해 `index.yaml` 과 같은 URL에서 서빙되어야 하기 때문이다.

`chart-releaser-action` 은 `gh-pages` 의 파일을 삭제하지 않는다. 수동으로 추가한 파일(`CNAME`, `artifacthub-repo.yml`)은 한 번 올려두면 영원히 유지된다. 자동화할 필요가 없다.

 

릴리즈 트리거는 `Chart.yaml` 의 version 증가지 `git tag` 가 아니다. 관습적인 `v1.0.0` 태깅 방식은 여기선 적용되지 않는다. `chart-releaser` 가 `<chart>-<version>` 태그를 자동으로 만든다.

차트 `.tgz` 파일은 `gh-pages` 에 직접 호스팅되지 않고 GitHub Release Attachment로 호스팅된다. `gh-pages` 의 `index.yaml` 은 그 다운로드 URL을 참조한다. 이게 `chart-releaser-action` 의 표준 동작이다.

 

OCI 배포는 커스텀 도메인이 만료되는 날을 위한 보험이다. 구축 비용은 bash 몇 줄, 복구 가치는 측정 불가다.

`values.schema.json` 은 차트에 추가할 수 있는 가장 높은 ROI의 단일 아이템이다. 사용자 실수를 조기에 잡고, ArtifactHub 폼을 자동 생성하며, 스키마를 machine-readable하게 문서화한다. 이 모든 게 파일 하나에서 나온다.

 

차트별 `artifacthub.io/changes` annotation이 ArtifactHub의 "What's new" 탭을 채운다. 사용자에게 커밋 메시지를 읽으라고 하는 것보다 훨씬 나은 UX다.

 

`dependabot-auto-merge.yml` 은 첫날부터 넣어야 한다. 안 그러면 의존성 PR이 쌓여서 읽지 않게 되고, dependabot의 목적이 사라진다.

 

 


 

 

마무리

공개 Helm Chart Repository를 구축하는 건 대부분 배관 작업이다. 어려운 건 차트 작성이 아니라, GitHub Actions와 GitHub Pages, GHCR, 커스텀 DNS, ArtifactHub를 하나의 파이프라인으로 잘 작동하게 연결하는 것이다. 각 시스템 자체는 복잡하지 않다. 마찰은 이음새에서 발생한다.

 

이 포스팅의 패턴들은 실전에서 검증됐고, 앞으로의 나를 위해서라도 문서화해 둘 가치가 있었다. 차트를 공개하려는 사람이 바로 쓸 수 있는 청사진이 필요하다면, 이 Repository 구조와 두 개의 주요 Workflow, 그리고 Bootstrap 함정부터 시작하면 된다. 나머지는 차트 컬렉션이 커지면서 자연스럽게 확장된다.

 

실제 동작하는 결과물을 보고 싶다면 helm-charts 레포지토리(GitHub)에서 이 글에 설명된 그대로를 확인할 수 있다.

 

 

 

 

 

 

 

 


Reference

 

 

 

 

 

Somaz | DevOps Engineer | Kubernetes & Cloud Infrastructure Specialist

728x90
반응형