kashinoki38 blog

something like tech blog

Helm入門

2021年3月追記(本当はもっと後に書いたけど、そういうことにする)

本記事は Helm v2 に関して記載しており、情報が相当古いです。
今から読むならこちらの記事を参照ください 🙇‍♂️🙇‍♂️

atmarkit.itmedia.co.jp

atmarkit.itmedia.co.jp


Prometheus、Grafanaあたりをコード管理していくにはHelmが良さそうなので勉強。 f:id:kashionki38:20200607210200p:plain

コンセプト

  • yamlの生成は、ChartとReleaseで実現
    • Chartはyamlファイルの雛形を固めて圧縮したもの
    • ReleaseはChartに基づいてコピー生成されたyamlファイル

コマンド

リポジトリ

$ helm repo add loki https://grafana.github.io/loki/charts #リポジトリの追加
$ helm repo list
$ helm inspect chart loki/loki-stack
$ helm inspect values loki/loki-stack
$ helm install -n monitoring loki/loki-stack --generate-name #リポジトリで公開されているChartを使ってアプリケーションをインストール(デプロイ)する
$ helm list -n monitoring #デプロイされたReleaseの一覧
NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
loki-stack-1591529810   monitoring      1               2020-06-07 20:36:59.2916867 +0900 JST   deployed        loki-stack-0.37.3       v1.5.0

デプロイ時にパラメータ設定を行う

以下2通り。

helm install --set <パラメータ名>=<値>, <パラメータ名>=<値>
helm install -f <YAMLファイル>

インストールしたHelmのアップグレード

$ helm upgrade loki-stack-1591529810 -n monitoring loki/loki-stack --set wordpressBlogName=foobar#Releaseのアップグレード
$ helm history loki-stack-1591529810 -n monitoring #「helm upgrade」コマンドでアップグレードを行った履歴

既存Chartのダウンロード

$ helm pull stable/wordpress #既存Chartのダウンロード
$ helm install wordpress ./wordpress #ファイルでのインストール

リリース系

  • helm install : チャートからリリースを作成
# v2
$ helm install <チャート名> -n <リリース名>
# v3
$ helm install <リリース名> <チャート名>
# デバッグ
$ helm install . --debug --dry-run
  • helm upgrade : チャート更新があったら、作成済みリリースに更新適用
  • helm delete : リリースを削除
  • helm ls : リリースをリストアップ
  • helm get [values, manifest] : リリースの情報表示

チャート系

  • helm repository [list, add, delete] : チャートのレポジトリを追加/削除
  • helm create : 自作チャートの雛形作成
  • helm dependency : チャート内の依存関係解決
  • helm search : チャートを検索

チャート作成

$ helm create mychart
Creating mychart
$ tree mychart/
mychart/
├── Chart.yaml                     # チャートの概要
├── charts                         # このチャートが依存するチャート(Subchart)を格納するディレクトリー
├── templates                      # マニフェストのテンプレートを格納するディレクトリー
│   ├── NOTES.txt                  # OPTIONAL: チャートの使用方法を記載したプレーンテキスト
│   ├── _helpers.tpl               # includeやtemplateアクションでの参照先 
│   ├── deployment.yaml       
│   ├── ingress.yaml               
│   ├── service.yaml               
│   ├── serviceaccount.yaml        
│   └── tests
│       └── test-connection.yaml
└── values.yaml                    # このチャートのデフォルト値を記載したyaml。install/update時に環境に応じたファイルを渡すことで定義し値を差し替え可能

3 directories, 9 files

構文

  • 構文は{{}}に囲まれて表記。それ以外の部分はk8sマニフェスト
  • {{- -}}のように-をつけることで、それより前の半角スペース、それより後の改行コードを除外できる
  • {{- /**/ -}}のように/**/でコメント

変数

  • {{ . }}による参照と{{ $ }}による参照があり、前者はvalues.yamlなどでの定義、後者は自ファイル内での定義
  • 事前定義変数
    • .Release.Name : helm installコマンドの引数で指定されるリリース名
    • .Release.Namespace : デプロイ先のk8sネームスペース
    • .Chart : Chart.yamlファイルの中身
    • Files : Chart内に含まれるファイルの一覧
    • Capabilities : k8s環境の情報
# template/deployment.yaml
  replicas: {{ .Values.replicaCount }}
---
# values.yaml
replicaCount: 1
# https://qiita.com/keiSunagawa/items/db0db26579d918c81457
  {{- $volumeName := "myvolume" -}}
    volumeMounts:
      - mountPath: /etc/{{ $volumeName }}/
        name: {{ $volumeName }}
    volumes:
      - name: {{ $volumeName }}
# template/deployment.yaml
      containers:
        - name: {{ .Chart.Name }}
---
# template/deployment.yaml
      app.kubernetes.io/instance: {{ .Release.Name }}
---

関数

nindent

  • パイプで渡すことで、引数に渡した数の半角インデントを埋め込む

toYaml

  • 変数をyaml形式で展開
# template/deployment.yaml
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
---
# values.yaml
podSecurityContext: {}

include

  • templates/_helpers.tplに定義した名前付きtemplateの展開
  • include <定義名> パス。基本パスは.(ルート)を使うぽい
# template/deployment.yaml
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
{{ include "mychart.labels" . | indent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "mychart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
---
# templates/_helpers.tpl
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "mychart.labels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
helm.sh/chart: {{ include "mychart.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}

# .Values.nameOverrideがなければ$name=.Chart.Name
# .Chart.Nameに.Release.Nameが含まれなければ、.Release.Name-.Chart.Nameを返す(なるほどだから、sample-mychartとなったわけだ
{{- define "mychart.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

アクション

  • if/for/withなどをアクションと呼ぶらしい

if

  • -付きが推奨
# templates/_helpers.tpl
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}

with

  • 引数に渡したオブジェクトをルートオブジェクトとして展開
# template/deployment.yaml
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }} #この.(ルート)は.Vlues.imagePullSecretsを指す
    {{- end }}
---
# values.yaml
imagePullSecrets: []
  • withブロック内では事前定義オブジェクトは使えないため、事前にファイル定義しておく
# https://helm.sh/docs/chart_template_guide/variables/
  {{- $relname := .Release.Name -}}
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ $relname }}
  {{- end }}

range

  • 引数に渡した配列でループを回す
# https://qiita.com/keiSunagawa/items/db0db26579d918c81457#range
# values.yaml
configPaths:
  - key: nginx.conf
    path: "files/nginx.conf"
  - key: postfix.main.cf
    path: "files/main.cf"
---
# comfigmap.yaml
{{- $files = .Files -}}

data:
{{- range .Values.configPaths -}}
  {{ .key }}: |
{{ $files.get .path | nindent 4 }}
{{- end -}}

template

  • _helpers.tplに定義してある名前付きtemplateを展開
  • ほぼincludeと一緒?
# template/deployment.yaml
      serviceAccountName: {{ template "mychart.serviceAccountName" . }}
---
# templates/_helpers.tpl
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
    {{ default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
    {{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}

yamlサンプル

deployment.yaml

  • 通常のk8sのdeploymentのyamlベース
# mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
{{ include "mychart.labels" . | indent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "mychart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "mychart.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
    {{- end }}
      serviceAccountName: {{ template "mychart.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}

values.yaml

# mychart/values.yaml
# Default values for mychart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name:

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: chart-example.local
      paths: []

  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

nodeSelector: {}

tolerations: []

affinity: {}

templates/_helpers.tpl

# templates/_helpers.tpl
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "mychart.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Common labels
*/}}
{{- define "mychart.labels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
helm.sh/chart: {{ include "mychart.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}

{{/*
Create the name of the service account to use
*/}}
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
    {{ default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
    {{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}

参考

qiita.com qiita.com knowledge.sakura.ad.jp