近年ではkubernetesクラスタ上で動作させるアプリケーションが増えてきて、クラスタ上で稼働するアプリケーションの数も増えてきていますが、kubernetesが提供するyamlによる宣言的ファイルだけでは、この複雑なアプリケーションの構築等において、対処しずらい状況になってきています。kubernetesにおいて、事実上の標準ツールとなっているHelmについて、ポイントとなりそうな部分について紹介したいと思います。

Helmについて

公式サイトでは、Helm は、Kubernetes アプリケーションの管理を支援するとあります。
パッケージ管理を作成したり、更新したり、削除したり、依存関係定義を追加するような機能を持っています。簡単にまとめると、下記のようなことができます。

  • Chartと呼ばれる設定ファイルに基づいた各種リソースの自動作成
  • デプロイされたアプリケーションの削除、更新の管理
  • リポジトリの検索、ダウンロード、インストール
  • パッケージ化、リポジトリへのアップロード

アプリケーション開発と紐付けて考えた場合は、独自チャートを作成する必要があります。チャート作成時には、下記のコマンドを実行するて雛形が作成されます。

$ helm create demo

作成されたものは、下記のようなものが作成されます。

Chart.yaml
charts
templates  ・・・テンプレート置き場
values.yaml ・・・設定値

values.yamlやChart.yamlが必須項目となっています。実際に書いていく際には、Go言語のテンプレート機能が利用されているので、Go言語のテンプレート機能の知識があると独自チャート作成は、しやすいと思います。詳しくは、Chart Guidsを見たり、公開されているサンプル等を見ながら開発していくと良いかと思います。

Helm導入で変わること

メンテナンスの観点でみると 

yamlファイルで管理している場合、本番環境、ステージ環境、開発(ローカル)環境のように複数の環境に分かれている場合には、環境ごとにyamlファイルを分けるという運用がよく取られるかと思います。環境ごとに若干な差異は、あるかもしれませんが、ほぼ同じようなファイルが複数作成された状態になり、メンテナンスが大変になる傾向があります。Helmでは、Goのテンプレート機能を使うことで、環境ごとの分岐の記載も増えるので、例えば、クラウド上に構築した環境だけ、行うような特殊なanotationを利用する等の記載も同一テンプレートファイルに記載することができます。このような記載でファイルの重複が排除できます。

デプロイ戦略の観点でみると

CI/CDプロセスを自動化するということを考えた場合、CIプロセスやGitのコミットログなどの一意な値をimageのタグとしてpushすることになるかと思います。 下記例は、最新コミットのSHAを取得する例です。

$ git rev-parse HEAD

純粋なyamlファイルの場合、環境変数等を埋め込んだりする機能はないので、手や自作ツール、envsubst等のツールを使って、値を埋め込んだyamlファイルを作成することを行ってから、更新したyamlを下記のようなコマンドで適用します。

$ kubectl apply -f demo.yaml

Helmを使うと、イメージのビルド、Pushまでは同じですが、予め、下記のようなテンプレートを作成しておくと、

apiVersion: v1
kind: Service
metadata:
  name: demo-api
  labels:
    app: demo-api
spec:
  ports:
  - port: 80
    name: http
  selector:
    app: demo-api
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
spec:
  selector:
    matchLabels:
      app: demo-api
  template:
    metadata:
      labels:
        app: demo-api
    spec:
      {{- if .Values.imagePullSecrets }}
      imagePullSecrets:
        - name : {{ .Values.imagePullSecrets.name }}
      {{- end }}
      containers:
        - name: {{ .Values.api.container.name }}
          image: {{ .Values.api.container.image }}:{{ .Values.container.tag }}
          imagePullPolicy: Always

helmで管理しているパッケージに新しいイメージを使うように更新するには、以下のように行います。パッケージ更新コマンド(helm upgrade)時に、ビルドしたイメージのtagの情報を渡して、パッケージ全体で参照している{{ .Values.container.tag }}の値に設定してから、パッケージを作成します。

HOSTNAME:=yyyy
PROJECT_ID:=xxxx
REPOSITORY_URL:=${HOSTNAME}/${PROJECT_ID}

# GITからHASHを取得する
GIT_HASH:=$(shell git rev-parse HEAD)

api-deploy-stage:
	cd backend; docker build -t demo-api -f cmd/api/Dockerfile .
	docker tag demo-api ${REPOSITORY_URL}/demo-api:$(GIT_HASH)
	docker push ${REPOSITORY_URL}/demo-api:$(GIT_HASH)

helm-lint:
	helm lint --set container.tag=$(GIT_HASH) ./demo-helm

install-local:
	helm install demo-helm-local --set container.tag=$(GIT_HASH) ./demo-helm


install-stage:
	helm install demo-helm-staging --set container.tag=$(GIT_HASH) --values=./demo-helm/staging-values.yaml ./demo-helm 

upgrade-stage:
	helm upgrade demo-helm-staging --set container.tag=$(GIT_HASH) --values=./demo-helm/staging-values.yaml ./demo-helm 

まとめ

今回は、kubernetsを使ったアプリケーション開発でポイントになりそうな点を中心に見ていきました。Helmは、Goのテンプレート機能をうまくyamlに組み込んで利用するパッケージマネージャであることがわかります。kubernetsを触り始めたメンバーでもyamlベースであることから入りやすいと思います。