延續前一日對 Helm values 的介紹,今天我們來探討 如何讓 values 的設計更為優雅。
在最初的例子中,我們只設定了 replica、image 與 resource,畫面簡潔清爽。然而,一旦加入 親和度(affinity),整份 values.yaml
就會立刻變得臃腫。
replicaCount: 2
image:
repository: nginx
tag: "1.25"
resources:
limits:
cpu: "500m"
memory: "256Mi"
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "kubernetes.io/arch"
operator: "In"
values:
- "amd64"
- "arm64"
這樣的寫法在服務單一時還能接受,但如果 多個服務共用一份 values.yaml,光是想像就讓人窒息。
造成這種冗長的根本原因在於:資訊被綁死在過多的階層中。
如果我們願意把縮排轉化為「拼接訊息」,整體結構可以大幅簡化。
nginx-1.25:
limit:cpu-1.mem-512Mi
request:cpu-500m.mem-256Mi
nodeAffinity:
- kubernetes.io/arch=amd64
- kubernetes.io/arch=arm64
mysql-9.4.0:
limit:cpu-1.mem-512Mi
request:cpu-500m.mem-256Mi
nodeAffinity:
- kubernetes.io/arch=amd64
- kubernetes.io/arch=arm64
這樣寫的好處:
_help.tpl
邏輯解析還原。_help.tpl
解析Helm values 是團隊的介面,並沒有唯一的設計風格。
如果專注於 資源配置,我們甚至可以將資訊集中在一個區塊:
resource:
nginx: lcpu-1-lmem-512Mi.rcpu-500m-rmem-256Mi
mysql: lcpu-1-lmem-512Mi.rcpu-500m-rmem-256Mi
這種 values 乍看怪異,但可以搭配 tpl
解析回傳統格式。
_help.tpl
:解析邏輯{{- define "parse.resource" -}}
{{- $svc := .svc -}}
{{- $raw := index .Values.resource $svc -}}
{{- $items := splitList "." $raw -}}
limits:
cpu: {{ index (splitList "-" (index $items 0)) 1 }}
memory: {{ index (splitList "-" (index $items 0)) 3 }}
requests:
cpu: {{ index (splitList "-" (index $items 1)) 1 }}
memory: {{ index (splitList "-" (index $items 1)) 3 }}
{{- end -}}
resources:
{{ include "parse.resource" (dict "svc" "nginx" "Values" .Values) | nindent 2 }}
resources:
limits:
cpu: 1
memory: 512Mi
requests:
cpu: 500m
memory: 256Mi
tpl就像javascript之於html,可以協助我們進行邏輯的判斷與重組,盡可能的活用tpl可以讓我們設計出符合團隊需求的內容。
我們再分享另一個範例。
真實場景中,常會遇到 資料來源不對稱 的需求,例如服務A需要以DB為主體,服務B需要以dataset為主體,但為了保證服務的穩定,我們需要使用同一份value,此時,我們一樣可以透過使用 _help.tpl
編寫邏輯處理。
舉例來說,我們有一份 values:
DB-1:
dataset: dataset-A
table:
- table-1
- table-2
DB-2:
dataset: dataset-A
table:
- table-x
- table-y
DB-3:
dataset: dataset-B
table:
- table-n
- table-h
現在希望按照 dataset 重新 group,可以寫一個 _groupby.tpl
:
{{- define "groupby.dataset" -}}
{{- $out := dict -}}
{{- range $dbName, $conf := .Values -}}
{{- if hasKey $conf "dataset" -}}
{{- $ds := $conf.dataset -}}
{{- $tables := $conf.table | default (list) -}}
{{- $exist := get $out $ds | default (list) -}}
{{- $_ := set $out $ds (concat $exist $tables) -}}
{{- end -}}
{{- end -}}
{{- toYaml $out -}}
{{- end -}}
呼叫方式:
datasets:
{{ include "groupby.dataset" . | nindent 2 }}
輸出結果:
datasets:
dataset-A:
- table-1
- table-2
- table-x
- table-y
dataset-B:
- table-n
- table-h
這種設計思路就是:
邏輯交給
_help.tpl
,介面留給操作者。
values 像一份表單,使用者只需填寫欄位。
經過上面兩個範例,相信讀者對於tpl的功用已經有所了解,接下來我們稍稍討論關於參數命名的問題。
在部屬 Kafka 資源時,設定中常見多種服務同時在一份value中進行多階層的規劃,這種規劃不但冗長,還充滿重複的 key:
kafka:
nodepool:
broker:
kafkaCluster:
name:
controller:
kafkaCluster:
name:
connect:
kafkaCluster:
name:
若將重複的資訊提取出來,例如:kafkaCluster.name
;並透過拼接組合壓縮階層:
kafka_cluster_name: my-cluster
broker-pool: {...}
controller-pool: {...}
connect-pool: {...}
透過重新的拼接,我們將資訊抽象化並加以壓縮(例如將 nodepool
的設定整合為 broker-pool
、controller-pool
...)。同時,將 kafkaCluster.name
提取出來,不僅能 避免重複輸入造成的錯誤,也讓 nodepool
的訊息從結構中 解耦。
這正是「減少階層」的核心:
資訊的傳遞,不必依附在縮排,而是透過拼接與解耦來完成。
程式的本質,不在於結構,而在於訊息的流動。
今天的分享圍繞在主題:設計 Helm values,減少階層可以更優雅。
我們透過將 層級壓縮,以 字串拼接 來承載資訊,讓結構更直覺;同時避免重複填寫,減少閱讀時的心智消耗。這樣的設計,讓 values 不只是設定檔,而是一個 供團隊操作的介面。
另一方面,將複雜的解析邏輯封裝進 _help.tpl
,把細節交給 go 語言 去處理,使得 values 更專注於「表達」而非「計算」。
相信透過這樣的思路,讀者能夠設計出 更彈性、更易維護 的 Helm values.yaml。
感謝各位閱讀,我們明天見!