- Kubernetes처럼 설정 대상이 늘어나는 환경에서는 YAML 파일을 직접 늘리는 방식이 곧 한계에 닿으며, YAML 템플릿보다 설정 데이터를 생성하는 접근이 더 적합해짐
- Helm chart는
values.yaml과 Go 템플릿으로 값을 주입하지만, 선택 필드, 배열, 맵이 들어가는 순간 조건문과 들여쓰기 부담이 커짐
- YAML은 공백 규칙이 엄격한데 Helm 템플릿 파서는 YAML 구조를 이해하지 못해,
toYaml과 indent 조합이 쉽게 취약한 설정 생성으로 이어짐
- YAML은 JSON의 상위 집합이라 상호 변환이 단순하며, Jsonnet은 외부 변수, 조건부 필드, 맵 조합, 객체 병합으로 설정 객체 생성을 코드처럼 다룸
- kr8은 Jsonnet 기반 흐름으로 여러 Kubernetes 클러스터 설정을 만들고 조작하며, 복잡한 YAML 문자열 조립 대신 객체를 직접 생성·변형하는 쪽을 택함
설정 복잡성은 YAML 파일 수가 늘어날 때 시작됨
- 애플리케이션과 인프라가 일정 규모를 넘어서면 설정 복잡성이 빠르게 커짐
- 배포 대상이 1~2개라면 YAML 설정 파일을 직접 작성해도 충분하지만, 그 이상으로 늘어나면 설정을 체계적으로 관리해야 함
- 여러 설정 파일이 필요한 이유는 대개 같은 대상이라도 일부 값이 서로 다르기 때문임
dev, stg, prod 같은 환경별 배포
- Europe, North America 같은 지역별 배포
- 모든 설정이 다른 것은 아니지만, 차이가 충분히 크면 공통 부분과 다른 부분을 분리해 관리해야 함
- 설정 관리 분야는 이런 문제를 오래 다뤄왔고, 여러 도구가 각자의 방식으로 YAML을 활용해 왔음
- Puppet에 포함된 hiera는 계층적으로 변수를 조회할 수 있어 강력하고 유연하며, YAML 자체를 템플릿할 필요를 크게 줄임
Helm chart에서 드러나는 YAML 템플릿의 문제
- 클라우드 컴퓨팅과 Kubernetes로 설정 대상이 운영체제 위 계층까지 확장되면서 CloudFormation과 Helm 같은 도구가 등장함
- Helm chart는
values.yaml에 정의한 외부 파라미터를 받아 렌더링할 수 있음
- 단순 문자열 값은 비교적 간단함
image: "{{ .Values.image }}"
values.yaml에 image 값을 지정하면 해당 값이 템플릿에 들어감
- 선택 필드처럼 더 복잡한 설정을 다루기 시작하면 문제가 커짐
{{- with .resourceGroup }}
resourceGroup: {{ . }}
{{- end }}
- 선택 값은 비워둘 수 없기 때문에 조건문과 루프가 필요하고, 템플릿이 쉽게 지저분해짐
- 배열이나 맵을 넣을 때는
toYaml과 indent를 조합해야 함
{{- with .Values.podAnnotations }}
annotations:
{{ toYaml . | indent 8 }}
{{- end }}
toYaml로 YAML을 다시 YAML로 바꾸는 함수 호출도 어색하지만, 더 큰 문제는 공백 처리임
YAML 공백 규칙과 템플릿 엔진의 충돌
- YAML은 들여쓰기와 공백 규칙이 엄격함
- 다음 예시는 유효하거나 완전한 YAML이 아님
something: nothing
hello: goodbye
- 사람이 직접 작성한다면 백스페이스를 몇 번 눌러 고칠 수 있지만, 템플릿 시스템으로 YAML을 생성할 때는 단순하지 않음
- 설정 파일이 5~10개를 넘는 수준이라면 직접 작성보다 설정 생성이 필요해짐
.Values.podAnnotations 값을 이미 들여쓰기된 annotations 아래에 넣으려면, 값 자체도 정확한 수준으로 들여쓰기해야 함
- Go 템플릿 파서는 YAML을 이해하지 않기 때문에, 템플릿 문법을 보기 좋게 들여쓰기하려 해도 문제가 생김
{{- with .Values.podAnnotations }}
annotations:
{{ toYaml . | indent 6 }}
{{- end }}
- 템플릿 시스템이 YAML 구조를 모르는 상태에서 공백과 조건문을 함께 다루면, 복잡한 설정 생성이 점점 어려워짐
- JSON을 직접 작성하는 방식도 주석 부재와 쉼표 누락 문제 때문에 적합하지 않으며, 이런 불편 때문에 YAML이 쓰이게 됨
Jsonnet은 JSON 설정을 생성하는 데이터 템플릿 언어임
- YAML은 JSON의 상위 집합이므로 JSON과 YAML 사이 변환은 단순함
- 여러 애플리케이션과 프로그래밍 언어는 JSON과 YAML을 기본적으로 파싱하거나 변환할 수 있음
- Python에서도 YAML을 읽어 JSON으로 출력할 수 있음
python -c 'import json, sys, yaml ; y=yaml.safe_load(sys.stdin.read()) ; print(json.dumps(y))'
외부 변수와 선택 필드 처리
- Jsonnet은 외부 변수를 사용해 설정 값을 주입할 수 있음
{
image: std.extVar('image'),
}
- CLI에서 외부 변수를 넘기면 JSON 결과가 생성됨
jsonnet image.jsonnet -V image="my-image"
{
"image": "my-image"
}
- 선택 필드는 템플릿 조건문을 문자열 안에 끼워 넣지 않고, 코드의 조건식으로 표현할 수 있음
// define a variable - yes, jsonnet also has comments
local rg = null;
{
image: std.extVar('image'),
// if the variable is null, this will be blank
[if rg != null then 'resourceGroup']: rg,
}
rg가 null이면 resourceGroup 필드는 결과에 포함되지 않음
- 값을 지정하면 해당 필드가 출력됨
맵과 객체 조작이 YAML 들여쓰기보다 단순함
- Kubernetes pod annotation처럼 맵을 설정에 넣는 경우, Jsonnet에서는 값을 변수로 정의한 뒤 객체에 배치할 수 있음
local annotations = {
'nginx.ingress.kubernetes.io/app-root': '/',
'nginx.ingress.kubernetes.io/enable-cors': true,
};
{
metadata: { // annotations are nested under the metadata of a pod
annotations: annotations,
},
}
- 이 방식은 YAML 템플릿에서 들여쓰기를 맞추는 것보다 훨씬 단순함
- 생성 결과는
metadata.annotations 아래에 annotation 맵이 들어간 JSON 객체임
{
"metadata": {
"annotations": {
"nginx.ingress.kubernetes.io/app-root": "/",
"nginx.ingress.kubernetes.io/enable-cors": true
}
}
}
- 기존 객체에 annotation을 추가하는 작업도 Jsonnet에서는
+ 연산자로 처리할 수 있음
local annotations = {
'nginx.ingress.kubernetes.io/app-root': '/',
'nginx.ingress.kubernetes.io/enable-cors': true,
};
{
metadata: {
annotations: annotations,
},
} + { // this adds another JSON object
metadata+: { // I'm using the + operator, so we'll append to the existing metadata
annotations+: { // same as above
something: 'nothing',
},
},
}
- 결과 객체에는 기존 annotation에
something: "nothing"이 추가됨
{
"metadata": {
"annotations": {
"nginx.ingress.kubernetes.io/app-root": "/",
"nginx.ingress.kubernetes.io/enable-cors": true,
"something": "nothing"
}
}
}
- 단순 예시에서는 코드가 더 길어 보일 수 있지만, 설정이 복잡해질수록 객체를 이런 방식으로 조작하는 기능이 유용해짐
kr8은 Jsonnet 방식으로 Kubernetes 설정을 다룸
- kr8은 여러 Kubernetes 클러스터의 설정을 쉽고 단순하게 만들고 조작하기 위해 이런 방법들을 사용함
- 핵심 흐름은 YAML 템플릿을 공백과 조건문으로 조립하는 대신, JSON 설정 객체를 생성하고 필요한 방식으로 변형하는 것임