この記事は、富士通クラウドテクノロジーズ Advent Calendar 2021 5日目の記事です。
4日目は @o108minmin さんの 「今年はまったレシピとその試行錯誤」 でした。同じレシピを繰り返し作って改良していくところに職人魂を感じました!
TL;DR
- マイクロサービスのマニフェスト、deploymentが多くて管理が大変...
- ボイラープレートを減らすために、deploymentやserviceをまるごとテンプレート化!
- valuesを上書きできるのでchart内で環境差分の記述も不要!
はじめに
アプリケーションのKubernetesマニフェストは、何もしないとコピペまみれになってしまいがちです。特に、マイクロサービスの場合
- アプリごとの差分
- デプロイ環境ごとの差分
のダブルパンチでマニフェストが増えていってしまいます。
マニフェストの行数が多いと修正やレビューが大変になり、設定ミスも起きやすくなります...
そこで、本記事ではボイラープレートを極力減らすHelm Chartを作ってみました。
(Helm Chartを使うのは初めてなので、「こう書いた方が綺麗になる!」等ありましたらコメントいただけるとありがたいです )
バージョン
- Helm v3.7.1
- Dapr v1.5.0
- Skaffold v1.35.0
つくったもの
Daprの公式サンプル「Distributed Calculator」のHelm Chartです。
Distributed Calculatorは4つのバックエンドサービス(加減乗除サービス)と1つのフロントエンドサービスからなるマイクロサービスです。
サービスの個数分deploymentを書くのはつらいので、マニフェストをまるごとテンプレート化しています。
(このアイデアはGrafanaのHelm Chartを参考にしています)
{{/*
# NOTE: Values全体と対象アプリの情報量方が必要なので
# {{include "distributed-calculator.backend.deployment" (dict "Values" .Values "app" .Values.backends.hoge)}}
# のように呼び出す
*/}}
{{- define "distributed-calculator.backend.deployment" -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "distributed-calculator.deploymentName" .app }}
labels:
app: {{ .app.name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .app.name }}
template:
metadata:
labels:
app: {{ .app.name }}
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: {{ include "distributed-calculator.deploymentName" .app | quote}}
dapr.io/app-port: {{ .app.port | quote }}
dapr.io/config: "appconfig"
{{- with $.Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
containers:
- name: {{ .app.name }}
image: {{ include "distributed-calculator.deploymentImage" .app}}
ports:
- containerPort: {{ .app.port }}
imagePullPolicy: {{ .Values.imagePullPolicy }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- end }}
テンプレートさえできれば、各アプリの差分情報を流し込むだけでマニフェストの完成です。
# アプリごとの情報(差分が生じる最低限の部分だけ記述)
backends:
add:
name: add
image: dapriosamples/distributed-calculator-go
tag: latest
port: 6000
subtract:
name: subtract
image: dapriosamples/distributed-calculator-csharp
tag: latest
port: 80
multiply:
name: multiply
image: dapriosamples/distributed-calculator-python
tag: latest
port: 5000
divide:
name: divide
image: dapriosamples/distributed-calculator-node
tag: latest
port: 4000
# アプリ情報を入れるだけでマニフェスト完成
{{include "distributed-calculator.backend.deployment" (dict "Values" $.Values "app" $.Values.backends.add)}}
{{include "distributed-calculator.backend.deployment" (dict "Values" $.Values "app" $.Values.backends.subtract)}}
{{include "distributed-calculator.backend.deployment" (dict "Values" $.Values "app" $.Values.backends.multiply)}}
{{include "distributed-calculator.backend.deployment" (dict "Values" $.Values "app" $.Values.backends.divide)}}
「メタデータの名前とDaprのapp-idが対応しているか」や「コンテナのポートとDaprのポートが同じか」等、人手ではミスしやすい部分もテンプレートなら整合性を担保できます。
ちなみにテンプレートのファイル名 _backend.tpl.yaml
は以下の理由で決めました。
-
_
はじまりのファイルはHelmのレンダリング対象にならない(公式ページ) -
yaml
で終わるファイル名はVSCodeのYAMLシンタックスハイライトが効く
chartのつくり方
chartの骨組みは、 helm create
コマンドで自動生成されます。
$ helm create distributed-calculator
Creating distributed-calculator
デプロイされるリソースのマニフェスト一覧は helm template
で確認できます。テンプレートの修正→helm template
で確認を繰り返して、公式リポジトリのマニフェストと同じ出力になるまで編集しました。
$ helm template ./distributed-calculator/
また、サンプルリポジトリに含まれていなかったtracerのマニフェストについては、kubectlのdry-runで生成しました。
tracer(Zipkin)のデプロイ手順
# ↑をdry-runしてマニフェストだけ出力
$ kubectl create deployment zipkin --image openzipkin/zipkin --dry-run=client -o yaml
$ kubectl expose deployment zipkin --type ClusterIP --port 9411 --dry-run=client -o yaml
デプロイ
ローカルのKindクラスタにデプロイします。
注意点として、アプリのデプロイの前にdapr-systemやcomponent用ミドルウェア(今回はRedis)のデプロイが必要です。
# kindクラスター作成
$ kind create cluster
# 依存するリソースのデプロイ
# Dapr (https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/#add-and-install-dapr-helm-chart 参照)
$ helm upgrade --install dapr dapr/dapr \
--version=1.5 \
--namespace dapr-system \
--create-namespace \
--wait
# Redis (https://github.com/bitnami/charts/tree/master/bitnami/redis 参照)
$ helm install redis bitnami/redis
# アプリ本体(今回作ったチャート)をデプロイ
$ helm install distributed-calculator ./distributed-calculator/
serviceにポートフォワードすれば、localhost:8080
からフロントエンドの電卓アプリにアクセスできます。
$ kubectl port-forward svc/calculator-front-end 8080:80
Zipkinのtracerも動いているようです。
$ kubectl port-forward svc/zipkin 9411:9411
テスト
手動でアプリを叩かなくても、helm test
で想定通りデプロイされているかテストができます。
テストケースごとに専用のPodをデプロイし、コンテナが実行したコマンドの終了ステータスが0
ならテスト成功です。
このチャートでは、curlでフロントエンドからレスポンスを受け取れるかどうかをテストしています。
# Test if frontend app "calculator-front-end" is working
apiVersion: v1
kind: Pod
metadata:
name: "calculator-front-end-is-working"
labels:
{{- include "distributed-calculator.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: curl
# このイメージ色々なコマンドが入っていて便利
image: yauritux/busybox-curl
command: ['curl']
args: ['calculator-front-end.default.svc.cluster.local']
restartPolicy: Never
$ helm test distributed-calculator
NAME: distributed-calculator
LAST DEPLOYED: Tue Nov 23 16:53:44 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: calculator-front-end-is-working
Last Started: Tue Nov 23 16:57:59 2021
Last Completed: Tue Nov 23 16:58:02 2021
Phase: Succeeded
テスト用podはテストが終わるとCompleted
になるので、リソースを無駄遣いすることはありません。
$ kubectl get pod calculator-front-end-is-working
NAME READY STATUS RESTARTS AGE
calculator-front-end-is-working 0/1 Completed 0 92s
Skaffoldでデプロイ
マニフェストはシンプルになりましたがデプロイ手順が煩雑なので、skaffold run
の1コマンドでデプロイできるようにします。
apiVersion: skaffold/v2beta26
kind: Config
deploy:
helm:
releases:
- name: dapr
repo: https://dapr.github.io/helm-charts/
remoteChart: dapr
version: "1.5.0"
- name: redis
repo: https://charts.bitnami.com/bitnami
remoteChart: redis
- name: distributed-calculator
chartPath: distributed-calculator
overrides: # override Values.yaml
replicaCount: 2
マニフェスト中にもあるように、SkaffoldはHelm Chartの values.yaml
を上書きできます。リソースやパスワード等の環境差分をSkaffold側で吸収できるので、Chart自体をシンプルに保つことが出来ます。
SkaffoldだけでなくArgo CDでもValue書き換えの機能があるので、環境ごとの自動デプロイも実現できそうです。
おわりに
以上、Helm Chartでマイクロサービスのマニフェストを作ってみた紹介でした。
ChartではGo Template + sprigを使えるので、文字列処理の自由度が高くボイラープレート削減の手段が豊富だと感じました。
一方、テンプレートの挿入内容によってはSQLインジェクションのような構文の書き換えもできてしまうので、適切にクオーテーションを付けることを心掛けたいです(公式ではquote
関数が推奨されています)。
明日は @ntoofu さんの 「社内CTFを開催しました」です。お楽しみに!