LabHub
배우기 러닝패스 코스

Helm 차트 제작과 배포 · --set 의 문법과 타입 · 이론

--set 은 편의 옵션이 아니라 작은 언어다

LabHub 에서 이어서 보기

한 줄 요약

--set 은 값을 넘기는 지름길이 아니라 문법과 타입 규칙을 가진 작은 언어이고, 같은 값을 values 파일로 줄 때와 결과가 달라질 수 있다.

왜 이게 문제가 되나

배포 스크립트에 이런 줄이 있다고 하자.

helm upgrade api ./api --set image.tag=8

렌더 결과는 image: registry.local/api:8 이고 아무 문제가 없어 보인다. 그런데 차트의 템플릿이 이렇게 생겼다면 이야기가 달라진다.

image: {{ .Values.image.repository }}:{{ .Values.image.tag }}

--set image.tag=8 은 값을 숫자 8 로 넣는다. 위 템플릿은 문자열 이어붙이기라 결과가 같아 보이지만, 태그를 따로 내보내는 자리(어노테이션, 라벨, ConfigMap 의 data)에서는 숫자가 그대로 나가 API 서버가 거절한다. 반대로 values 파일에 tag: "8" 이라고 적으면 문자열이 들어간다. 같은 값을 적는 두 방법이 서로 다른 타입을 만든다는 것이 이 주제의 핵심이다.

--set 이 타입을 정하는 규칙

helm v3.16 에서 직접 확인한 결과는 이렇다.

| 적은 값 | 들어가는 타입 |
| --- | --- |
| 8 | 숫자 |
| 1.10 | 문자열 (소수점이 있으면 정수로 읽히지 않는다) |
| 0755 | 문자열 (앞의 0 이 살아 있어야 하므로) |
| true | 불리언 |
| null | 그 키를 삭제 |

여기서 자주 오해가 생긴다. "버전처럼 생긴 값은 위험하다" 고 뭉뚱그려 외우면 1.108 을 같은 것으로 취급하게 되는데, 실제로 문제가 되는 것은 정수로 읽히는 값불리언으로 읽히는 값뿐이다. 그래서 --set-string 이 필요한 자리도 거기다. 규칙을 외우기보다 값 덤프를 한 번 렌더해 타입을 눈으로 확인하는 편이 빠르다.

null 은 특히 조심해야 한다. 빈 문자열을 넣는 --set key= 와 달리 --set key=null 은 키 자체를 없앤다. 차트가 if .Values.x 로만 분기한다면 둘이 같게 동작하지만, 키의 존재로 판단하는 곳에서는 갈린다.

문법 — 점·쉼표·중괄호·대괄호·역슬래시

--set image.repository=registry.local/web,replicas=5   # 점은 깊이, 쉼표는 구분--set 'args={alpha,beta,gamma}'                        # 중괄호는 리스트 통째로--set 'args[0].name=first,args[0].value=1'             # 대괄호는 원소 자리--set 'nodeSelector.kubernetes\.io/os=linux'           # 역슬래시는 점의 의미를 끈다

키 이름 안에 점이 들어가는 일은 쿠버네티스에서 아주 흔하다. kubernetes.io/os, app.kubernetes.io/name, prometheus.io/scrape 가 모두 그렇다. 탈출하지 않으면 kubernetes 아래 io/os 라는 중첩 맵이 조용히 만들어지고, 렌더는 성공하지만 원하는 라벨은 붙지 않는다.

복잡한 값에는 전용 옵션이 따로 있다. --set-json 은 값을 JSON 그대로 받아 타입까지 의도한 대로 넣고, --set-file 은 파일의 내용을 값으로 넣는다. 인증서 본문이나 설정 파일을 통째로 넘길 때는 --set-file 이 유일하게 현실적인 방법이다. --set config.ca=/path/ca.pem 으로 쓰면 경로 문자열이 값이 되어 버린다.

무엇으로 배포했는지 나중에 알 수 있는가

--set 의 진짜 비용은 문법도 타입도 아니고 기록이 남지 않는다는 데 있다. 배포가 끝난 뒤 "지금 운영은 무슨 값으로 돌고 있나" 를 물으면, -f values-prod.yaml 로 배포한 팀은 저장소의 파일 하나를 열어 답한다. --set 으로 배포한 팀은 릴리스 안에서 꺼내야 한다.

helm get values api            # 사용자가 준 값만helm get values api --all      # 차트 기본값까지 합쳐진 최종 값

꺼낼 수 있으니 괜찮다고 생각하기 쉽지만, 이 값은 코드 검토를 거치지 않았고, 누가 왜 그렇게 정했는지가 어디에도 없고, 클러스터가 사라지면 함께 사라진다. 그래서 값이 두세 개를 넘어가면 파일로 옮기는 편이 낫다. 남겨 둘 만한 --set배포마다 반드시 달라지는 것 하나둘 — 대개 이미지 태그다.

그리고 바로 그 태그가 타입 사고가 나는 자리다. --set-string image.tag=$TAG 로 못 박아 두거나, 차트 쪽 템플릿에서 {{ .Values.image.tag | quote }} 로 감싸 두면 값을 넘기는 쪽이 무엇을 하든 안전해진다. 값을 넘기는 사람은 여럿이고 차트는 한 곳이므로, 방어는 차트 쪽에서 하는 편이 비용이 적다.

현장에서 만나는 모습

배포 스크립트가 --set 으로 길게 늘어나기 시작하면 두 가지 문제가 함께 온다. 첫째, 무엇을 넘겼는지 기록이 남지 않는다. helm get values 로 꺼낼 수는 있지만 저장소에는 없어서 코드 검토를 거치지 않는다. 둘째, 셸 따옴표와 헬름 문법이 겹쳐서 읽기가 어려워진다. 중괄호와 대괄호는 셸도 특별하게 다루므로 작은따옴표로 감싸야 하는데, 이걸 빠뜨리면 셸이 먼저 펼친 결과가 헬름에 넘어간다.

그래서 실무의 경계는 대체로 이렇다 — 구조가 있는 값은 values 파일로, 배포마다 달라지는 한두 값만 --set 으로. 이미지 태그는 배포마다 달라지는 대표적인 값이라 --set 에 남는 경우가 많은데, 그 자리가 정확히 타입 사고가 나는 자리다. 태그에는 --set-string 을 쓰거나, 차트 쪽에서 | quote 를 걸어 두면 어느 쪽으로 넘어와도 안전해진다. 차트를 만드는 사람이 할 수 있는 방어가 있다면 그쪽을 먼저 하는 편이 낫다 — 값을 넘기는 사람은 여러 명이고, 차트는 한 곳이기 때문이다.

다음 실습에서 할 것

넘어온 값을 JSON 으로 그대로 내보내는 차트를 만들어 두고, --set 의 문법을 하나씩 걸어 본다. 키 안의 점을 탈출하고, 정수와 소수점 값의 타입이 어떻게 갈리는지 확인하고, --set-json--set-file 을 쓰고, null 로 키를 지운다. 마지막에는 같은 두 값을 values 파일과 --set--set-string 세 가지로 넘겨 렌더 결과를 나란히 비교한다.