20180726

Kubernetes Helm chart 작성 팁

Helm 2.9 + K8s 1.10 클러스터 기준. 팀에서 차트 3개 관리하다 보니 패턴이 보여서 공유 메모. tiller 이슈까지 겸해서 정리.

1. values 기본값은 values.yaml에 주석으로. 주석 없으면 그 값이 뭘 의미하는지 아무도 모른다. README 써도 읽지 않는다. 사용자가 가장 먼저 여는 파일인 values.yaml 자체를 문서로 본다는 관점으로.

# -- 이미지 레지스트리 정보
image:
  repository: registry.local/app
  # -- 빈 문자열이면 Chart.AppVersion 사용. 배포 파이프라인에서 override 권장
  tag: ""
  pullPolicy: IfNotPresent

# -- 레플리카 수. HPA 쓰면 initial로만 의미
replicaCount: 2

# -- JVM/파이썬 등 런타임 힙 설정은 resources.limits.memory 기준으로 80% 잡기
resources:
  limits: { cpu: 500m, memory: 512Mi }
  requests: { cpu: 100m, memory: 256Mi }

2. _helpers.tpl에 라벨 정의. helm create가 뱉는 기본 템플릿을 그대로 쓰지 말고 직접 유지. 중요한 건 selector 라벨과 metadata 라벨의 분리. selector는 변경하면 기존 Deployment의 pod selector가 mismatch 나서 업그레이드가 깨진다. chart, version 같은 자주 변하는 값은 meta 라벨에만.

{{/* Selector — 절대 바꾸면 안 됨 */}}
{{- define "app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/* Metadata — 정보용. 바뀌어도 롤아웃 안 깨짐 */}}
{{- define "app.labels" -}}
{{ include "app.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
{{- end }}

Deployment의 spec.selector.matchLabels에는 selectorLabels만. metadata.labels와 template.metadata.labels엔 labels 전체. 이 두 줄 차이를 무시하고 합쳐 썼다가 다음 마이너 bump에서 "selector is immutable" 에러 만나 배포 막히는 케이스 흔함.

3. ConfigMap 변경 시 Pod 자동 재시작. 알아두기 전까진 "왜 설정 바꿨는데 반영이 안 되지" 하면서 pod delete 수동으로 하다가 사고 치기 쉽다. 해시를 파드 템플릿 어노테이션에 박아 두면 ConfigMap 변경 → 템플릿 해시 변경 → Deployment rolling update 자동 트리거.

# Deployment spec.template.metadata.annotations
annotations:
  checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
  checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}

원리: Pod 템플릿의 PodTemplateSpec 해시가 바뀌면 controller-manager의 deployment_controller가 신규 ReplicaSet을 생성하고 기존 것을 점진 축소(rolling update). configmap/secret 자체는 mount된 pod에 eventually 반영되지만 프로세스가 재시작되지 않으면 환경변수나 초기 로드만 쓰는 앱은 감지 못함. 이 해시 트릭이 rolling update를 강제하는 관용구.

4. 의존성 차트 관리. requirements.yaml로 redis/postgres subchart를 묶어 쓰는 경우, alias와 condition 적극 활용.

# requirements.yaml
dependencies:
  - name: postgresql
    version: 3.10.0
    repository: https://kubernetes-charts.storage.googleapis.com
    condition: postgresql.enabled
    alias: db

condition으로 외부 RDS를 쓸지 in-cluster PG를 쓸지 스위치. alias로 여러 벌 띄우기도 가능. 스테이징만 in-cluster, 프로덕션은 condition=false.

5. hook 순서 이해. pre-install, post-install, pre-upgrade, post-upgrade 훅과 helm.sh/hook-weight 조합. DB 마이그레이션 Job은 pre-upgrade로 두고, 차트가 실패하면 helm.sh/hook-delete-policy: before-hook-creation로 재시도 가능하게. 레시피가 몇 개 있다.

  • DB 마이그 Job: pre-upgrade, weight -5
  • 인덱스 생성 Job: post-upgrade, weight 0
  • 스모크 테스트 Job: post-upgrade, weight 10 (hook-succeeded면 삭제)

6. --force는 죄악. helm upgrade --force가 자주 필요하다면 차트 설계 오류다. immutable selector를 바꾸는 게 가장 흔한 원인. --force는 배포된 리소스를 kubectl replace 하는 거라 PVC 바인딩이나 외부 LB 엔드포인트가 뜯어질 수 있다.

7. 값 검증. Helm 2에는 values 스키마 기능이 아직 없다. 대신 required "x is required" .Values.foo나 필요하면 _helpers.tpl에 fail 헬퍼 써서 차트 렌더 단계에서 실패시킨다. Helm 3에는 JSON Schema 기반 validation이 들어온다는 루머라 기대 중.

tiller 고민

Helm 2의 가장 큰 통증. tiller가 클러스터 전역에 cluster-admin 권한으로 떠서 RBAC 정책이 엉성하면 보안상 문제. namespace 단위 tiller로 제한하거나 helm template | kubectl apply로 tiller를 우회하는 운영이 늘고 있다. Helm 3에서 tiller가 빠진다는 로드맵이 나와 있어서 2.10/2.11까지만 2.x 유지하고 3.0 RC 시점에 전환 계획.

운영 팁. helm history, helm rollback 자주 쓰게 되는데 기본적으로 deployment revision과 helm revision이 다름을 기억. kubectl rollout undo는 deployment 레벨 이전 ReplicaSet으로 돌리는 거고, helm rollback은 차트의 이전 릴리스로 돌리는 거. 둘이 엇갈리면 헷갈린다. 쉬운 규칙: "helm으로 올렸으면 helm으로 되돌리기".

RBAC 매번 뜯어고치는 게 지겨워서 chart-testing(ct) + 사내용 기본 RBAC 템플릿을 _helpers.tpl로 만들어 두는 중. 신규 차트 만들 때 이 기반에서 파생.

댓글 없음: