Next.js 앱을 위한 Helm 차트
charts/<app>/ 디렉토리와 Argo CD가 이를 어떻게 소비하는지에 대해 설명합니다. 실행되는 이미지는 docker-nextjs에서 제공되며, 두 항목을 게시하는 파이프라인은 github-actions입니다.
템플릿의 charts/__APP__/ 디렉토리가 진실 공급원(source of truth)입니다. 모든 값에 대한 설명은 references/values.md를 참조하세요. Argo CD 연결 방법은 references/argocd.md를 참조하세요.
규칙
-
앱당 하나의 차트, 앱 리포지토리 루트의
charts/<app>/에 위치합니다. 이 위치를 사용하는 이유: Argo CD는 개발 환경에서는 Git에서 직접(path: charts/<app>) 동기화하고, 운영 환경에서는 OCI 레지스트리에서 동기화할 수 있으며 동일한 파일을 사용합니다.apps/밖에 있으므로 Docker 빌드 컨텍스트나 pnpm 워크스페이스 패키지에 포함되지 않으며, 차트 변경 사항이 필요한 코드와 동일한 PR에서 함께 변경됩니다. 차트는 단일 앱 전용으로 설계되었습니다(엄브렐라 차트나 서브차트가 없음). 단일 앱에서 인그레스, HPA, PDB, 토폴로지 스프레드와 같은 스위치가 필요한 경우에만 제네릭하고 값 기반의 템플릿(예: 백엔드 엄브렐라 차트)을 사용합니다. -
version또는appVersion을 수동으로 편집하지 마세요. 둘 다 Git에서는0.0.0으로 유지됩니다.release.yaml은 태그vX.Y.Z로부터helm package --version X.Y.Z --app-version vX.Y.Z를 실행하므로, 게시된 차트와 이미지 태그가 항상 일치합니다. -
image.tag는 기본적으로appVersion을 사용하며, Git 동기화 환경에서만 설정합니다. OCI에서appVersion은 릴리스 태그이며 이미지는ghcr.io/nuvraxis/<app>:vX.Y.Z입니다. Git에서 동기화할 때는appVersion이0.0.0이므로 Argo CD Application이image.tag: sha-<short-sha>(또는main)로 설정합니다.image.repository는 항상 소문자입니다. -
프로브: startup, liveness, readiness 모두
/api/health를 사용합니다. startup 프로브(2초 간격 × 30회)는 부팅을 담당하며, liveness와 readiness는 startup이 완료된 후에만 시작됩니다. 이 경로는 어떤 종속성에도 접근하지 않으므로 데이터베이스 장애가 파드 재시작을 유발하지 않습니다. 프로브를/에 설정하지 마세요: 페이지를 렌더링합니다. -
보안 컨텍스트는 이미지와 일치해야 합니다: uid/gid 1001,
runAsNonRoot,readOnlyRootFilesystem, 권한 상승 금지, 모든 Capability 드롭,RuntimeDefaultseccomp. ServiceAccount와 파드 모두에서automountServiceAccountToken: false로 설정합니다: 앱이 Kubernetes API를 호출하지 않습니다. -
.next/cache는sizeLimit이 있는emptyDir입니다. 이것이 유일한 쓰기 가능 경로입니다(/app/apps/<app>/.next/cache). 제한을 두면 이미지 캐시가 노드를 가득 채우는 것을 방지합니다. -
envFrom을 통한 구성:values.config의 ConfigMap과 이미 존재하는 Secret.config: API_BASE_URL: https://api.nuvraxis.com existingSecret: portal-secrets차트는 절대로 Secret 값을 템플릿으로 만들지 않습니다:
kind: Secret에data를 value로 사용하거나--set secret=…를 사용하지 않습니다. Secret은 External Secrets, Sealed Secrets 또는 연산자(operator)에 의해 생성되며, 그 이름만 values에 있습니다. ConfigMap이 변경되면checksum/config어노테이션을 통해 파드가 롤링 업데이트됩니다. -
차트에
NEXT_PUBLIC_*을 포함하지 마세요. 이 값들은 이미지 빌드 시에 내장됩니다(docker-nextjs참조); 런타임에 설정해도 아무 효과가 없습니다. -
리소스: CPU와 메모리 요청, 메모리 제한, CPU 제한 없음. CPU 제한은 버스트 중에 Node 서버를 스로틀링하고 꼬리 지연 시간(tail latency)을 증가시킵니다. 메모리 제한은 유지됩니다(노드의 힙(heap)이 주어진 만큼 커지기 때문).
-
고가용성 기본값: 2개의 레플리카, PDB
maxUnavailable: 1, 롤링 업데이트maxUnavailable: 0, 영역 및 노드 간 토폴로지 스프레드(ScheduleAnyway).autoscaling.enabled가 활성화되면 Deployment에서replicas를 생략하여 Argo CD와 HPA가 충돌하지 않도록 합니다. -
인그레스는 옵트인(opt-in)이며, cert-manager 어노테이션을 통해 TLS를 설정합니다.
ingress.enabled,className, hosts,secretName이 있는tls, 그리고 어노테이션에cert-manager.io/cluster-issuer를 포함합니다. ingress-nginx에서 스트리밍 응답을 위해nginx.ingress.kubernetes.io/proxy-buffering: "off"를 추가하고, rate limiting을 위해limit-rps를 추가합니다(nextjs-security참조). -
Helm 훅(hook) 없음,
lookup없음, 무작위 값 없음. Argo CD는 차트를helm template으로 렌더링하고 결과를 적용합니다: 훅은 다른 의미론(semantics)을 가진 Argo 훅이 되고,lookup은 아무것도 반환하지 않으며,randAlphaNum은 동기화할 때마다 변경되어 앱이 영구적으로 동기화되지 않은 상태(out of sync)로 표시됩니다. 렌더링된 모든 것은 값들(values)의 순수 함수여야 합니다. -
비공개 GHCR 패키지는 풀(pull) 자격 증명이 필요합니다. GHCR 패키지는 처음 푸시할 때 비공개입니다. 이미지의 경우:
read:packages권한이 있는 토큰이 포함된docker-registry타입의 Secret을imagePullSecrets에서 참조합니다(운영 값은ghcr-pull을 참조). Argo CD의 차트의 경우:enableOCI: "true"가 설정된 리포지토리 Secret을 사용합니다(references/argocd.md참조). 또는 코드가 공개되어 있다면 GitHub에서 패키지를 공개로 만드세요. -
values-production.yaml에는 운영 환경에서 다른 값만 포함합니다. 계층화하여 사용합니다:-f values.yaml -f values-production.yaml. 기본값을 복사하지 마세요. -
helm lint --strict와helm template | kubeconform -strict로 검증합니다. helm-unittest 대신 kubeconform을 사용하는 이유: 단일 앱 차트가 실제로 가지는 버그는 렌더링된 출력에서 잘못된 필드 이름, 타입 또는 API 버전이며, kubeconform은 실제 Kubernetes 스키마에 대해 이를 테스트 코드 없이 잡아냅니다. helm-unittest는 템플릿 로직을 테스트하는데, 이 차트에는 템플릿 로직이 거의 없습니다. 두 values 파일 모두 린트(lint)하고 검증합니다(pr.yaml에서 하는 것처럼).
추가 사항
| 필요 사항 | 변경 사항 |
|---|---|
| 런타임 환경 변수 | values의 config: (비밀 아닌 값) 또는 기존 Secret의 키 |
| 비밀(secret) | 외부 Secret에 추가; 차트 변경 없음 |
| 다른 포트 또는 사이드카 | 먼저 정당성 설명; Next 앱은 하나의 포트만 필요 |
| 크론잡 | 별도의 차트 또는 앱, 이 Deployment가 아님 |
| NetworkPolicy | templates/networkpolicy.yaml 추가, networkPolicy.enabled로 전환 |
검증
helm lint --strict charts/<app>
helm lint --strict charts/<app> -f charts/<app>/values-production.yaml
helm template <app> charts/<app> -f charts/<app>/values-production.yaml \
| docker run --rm -i ghcr.io/yannh/kubeconform:v0.8.0 -strict -summary -kubernetes-version 1.34.0 -
출처
- https://helm.sh/docs/topics/charts/
- https://helm.sh/docs/chart_best_practices/
- https://helm.sh/docs/topics/registries/
- https://helm.sh/docs/overview/
- https://github.com/yannh/kubeconform
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- https://kubernetes.io/docs/concepts/security/pod-security-standards/
- https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
- https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
- https://argo-cd.readthedocs.io/en/stable/user-guide/helm/
- https://cert-manager.io/docs/usage/ingress/
- https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry