Posted at

helm Chartを書くときに先に知っておきたかった良い8つのこと

Kubernetes3 Advent Calendar 201811日目の記事です。

自分がhelm Chartのテンプレートを書く前に知っておけば良かったなーというポイントを紹介します。

大体、The Chart Template Developer’s Guideに書いてあるので、ここをちゃんと読んでから始めれば良いのですが...


1. 複数環境ある場合は--valuesオプションで変数を環境別に指定するのがよさそう

このスライドのように環境別に変数ファイルを使うのがよさそうな感じです。(同社なので、マネしてます...)


2. --setオプションは--valuesで指定したファイルを上書きする

tagの指定だけ上書きする時などに使っています。


3. - は難しい

ifの-は前の空白と改行を削除してくれるのですが、elseだけ表示したい場合にコントロールが難しいです...

下のように書くと、

  {{- if .Values.hpa.enabled -}}

{{- else -}}
replicas: {{ .Values.replicas | default "2" }}
{{- end -}}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "my-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

下のようになってしまいますが、yamlとして正しくないので、エラーになります...

spec:replicas: 3selector:

matchLabels:
app.kubernetes.io/name: my-chart
app.kubernetes.io/instance: my-chart

ドキュメントには indent function ({{indent 2 "mug:true"}})と書けとあるのですが、この方法だとdefaultが使えない(?)ので、結局、下のようにしました。

spec:

{{- if .Values.hpa.enabled -}}
{{- else }}
replicas: {{ .Values.replicas | default "2" }}
{{- end }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "my-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

.Values.hpa.enabled trueの時、

spec:

selector:
matchLabels:
app.kubernetes.io/name: my-chart
app.kubernetes.io/instance: my-chart

.Values.hpa.enabled falseの時、

spec:

replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: my-chart
app.kubernetes.io/instance: my-chart

下のようにしてみると、

  {{- if .Values.hpa.enabled -}}

{{- else }}
replicas: {{ .Values.replicas | default "2" }}
{{ end -}}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "my-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

trueの時ダメでした...

spec:selector:

matchLabels:
app.kubernetes.io/name: my-chart
app.kubernetes.io/instance: my-chart
template:
metadata:
labels:
app.kubernetes.io/name: my-chart
app.kubernetes.io/instance: my-chart


4. templateよりもincludeとnindentを使った方がよさそう

templateはインデントが制御されないので、 {{- include xxx . | nindent n }} と書いた方が良いようです。

複数containerあるときなど便利です。

apiVersion: apps/v1beta2

kind: Deployment
metadata:
name: {{ include "my-chart.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "my-chart.name" . }}
helm.sh/chart: {{ include "my-chart.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
{{- if .Values.hpa.enabled -}}
{{- else }}
replicas: {{ .Values.replicas | default "2" }}
{{- end }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "my-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "my-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
# ここ
{{- include "my-chart.app" . | nindent 6 }}
{{- include "my-chart.sidecar" . | nindent 6 }}


5. withは書く量が減るが諸刃の剣

withを使った場合、endまでそのスコープの変数しか使えなくなるので、その階層とは別の変数を使いたい場合、endするか、再度定義するしかないです。

なので、さっき複数containerでテンプレートを分けると良いかもといいましたが、withを使うとスコープ毎に変数を定義しないといけない場合もあります。

{{- define "my-chart.sidecar" -}}

{{- with .Values.sidecar -}}
- image: "{{ .image.repository }}:{{ .image.tag | default "v1.0.2" }}"
name: sidecar
resources:
requests:
cpu: {{ .resources.requests.cpu | default "50m" }}
memory: {{ .resources.requests.memory | default "50Mi" }}
limits:
cpu: {{ .resources.limits.cpu | default "100m" }}
memory: {{ .resources.limits.memory | default "100Mi" }}
ports:
- containerPort: 24224
name: sidecar
protocol: "TCP"
env:
- name: SIDECAR_WEB_HOST
value: "localhost:9990"
{{- end -}}
{{- end -}}


6. テンプレートのネストとwithのネスト

テンプレートのネストは普通に出来ます。インデントは継承されるので、下記の例だと6 + 2になります。

(上で例示したdeployment内の{{- include "my-chart.app" . | nindent 6 }}のため)

{{- define "my-chart.app" -}}

{{- with .Values.app -}}
- name: app
image: "{{ .image.repository }}:{{ .image.tag }}"
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
requests:
cpu: {{ .resources.requests.cpu | default "100m" }}
memory: {{ .resources.requests.memory | default "500Mi" }}
limits:
cpu: {{ .resources.limits.cpu | default "100m" }}
memory: {{ .resources.limits.memory | default "500Mi" }}
env:
{{- include "my-chart.app.env" . | nindent 2 }}
{{- end -}}
{{- end -}}

ただし、with のスコープは親のままなので、.Values.appの中のでテンプレートをincludeすると .Values.app スコープとなってしまいます。

{{- define "my-chart.app.env" -}}

# .Values.app.envではない
{{- with .env -}}
env:
- name: APP_ENVIRONMENT
value: {{ .APP_ENVIRONMENT | quote }}
- name: WRK_WARMUP_DURATION
value: {{ .WRK_WARMUP_DURATION | default "10" | quote }}
{{- end -}}
{{- end -}}


8. Valuesのキーは必須

cpu: {{ .resources.requests.cpu | default "100m" }}とdefaultを設定しても、

変数ファイルには、必ず、下のようにrequestsまで書かないといけません。

resources:

requests: {}
limits: {}

なので、上のようにcontainersごとに分けて、構造化すると、デフォルト値で良いものでもキーを多めに書かないといけなくなります。

environment: dev

replicas: 3

app:
image:
repository: nginx
tag: stable
resources:
requests: {}
limits: {}
env:
APP_ENVIRONMENT: dev

sidecar:
image:
repository: fluentd
tag: stable
resources:
requests: {}
limits: {}

hpa:
enabled: false


最後に

変数とテンプレートの設計をどうするかが頭を悩ませる所です。まだ、模索中です...