はじめに
みなさんは、Kubernetesクラスタへアプリケーションやミドルウェアをインストールする際に、どのような方法で行っていますか?
Kubernetesには「kubectl」という公式のコマンドツールがあります。YAMLでKubernetesリソースの定義を書き、それをkubectl applyコマンドでインストールしている、という方も多いかと思います。
また、kubectlにも統合されている「Kustomize」や、Kubernetes向けパッケージマネージャの「Helm」を使っているという方もいらっしゃるでしょう。
今回は、これらのツールでは賄いきれないほどの複雑な環境へのデプロイに便利な「Helmfile」というツールを紹介します。
今回の対象読者
今回の内容は「著名なツールを選択したが、そのツールを実行するまでの手順やスクリプトが煩雑」という課題を抱えている方には、ぜひ本記事を通してHelmfileを試してみていただきたいと思います。
Helmfileは、公開情報からわかる範囲でも2018年4月~2020年12月現在までに16の企業・組織の本番環境で使われてきた実績のあるツールです。なお、ユーザー事例に関する詳細は、公式ドキュメントの「USERS」を参照してください。
特にツールについて課題や移行のモチベーションがない状態でも、他社の本番環境におけるKubernetesへのデプロイがどのように行われているかを参考にするきっかけとしても、本記事を活用いただけるとうれしいです。
Helmfile のインストール
それでは早速、Helmfileを試してみましょう。Helmfileを利用するにあたって、まずは最低限HelmfileとHelm 3をインストールする必要があります。
macOS の場合はHomebrewを利用すると、両方まとめてインストールすることができます。
$ brew install helmfile
...
(中略)
...
==> Installing dependencies for helmfile: helm
==> Installing helmfile dependency: helm
==> Pouring helm-3.4.1.catalina.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
/usr/local/etc/bash_completion.d
zsh completions have been installed to:
/usr/local/share/zsh/site-functions
==> Summary
🍺 /usr/local/Cellar/helm/3.4.1: 57 files, 42.3MB
==> Installing helmfile
==> Pouring helmfile-0.135.0.catalina.bottle.tar.gz
🍺 /usr/local/Cellar/helmfile/0.135.0: 5 files, 43.4MB
==> Caveats
==> helm
Bash completion has been installed to:
/usr/local/etc/bash_completion.d
zsh completions have been installed to:
/usr/local/share/zsh/site-functions
Helmfileの基本的な使い方
Helmfileは基本的にhelmfile.yamlというYAMLファイルにHelmfileが定める仕様に沿って「アプリケーション郡の期待状態(Desired State)」を記述し、目的に応じたHelmfileのコマンドを実行するという2ステップで利用します。
2020年12月時点で、Helmfileには17のコマンドが存在しますが、代表的なものは以下の2つです。
helmfile applyhelmfile template
applyはアプリケーションがまだクラスタに存在しない場合はインストールし、そうでなければアップデートします。一方、templateはapplyによりインストールされる全Kubernetesリソースのマニフェストを出力します。
helmfile applyの実行例
それでは、helmfile applyを試してみましょう。ここでは参考のため、helmを使ってpodinfoをfrontendおよびbackendの2つのReleaseとしてインストールする例で説明します。
Helmfileを利用しない場合は、以下のような手順になります。今回インストールしたいのはflaggerというChart RegistryからダウンロードできるpodinfoというChartで、それぞれfrontendとbackendという名前を持つReleaseとしてインストールしています。
helm repo add flagger https://flagger.app
helm upgrade --install --wait frontend \
--namespace test \
--set replicaCount=2 \
--set backend=http://backend-podinfo:9898/echo \
flagger/podinfo
helm upgrade --install --wait backend \
--namespace test \
--set hpa.enabled=true \
flagger/podinfo
Helmfileを利用する場合は、まず以下のようなhelmfile.yamlを作成します。
repositories:
- name: flagger
url: https://flagger.app
releases:
- name: frontend
namespace: test
chart: flagger/podinfo
values:
- replicaCount: 2
backend: http://backend-podinfo:9898/echo
- name: backend
namespace: test
chart: flagger/podinfo
values:
- hpa:
enabled: true
helmfile applyを実行すると、helmfile.yamlの内容に従って2つのReleaseが作成されます。
helmfile apply
applyを実行すると、以下のようなログが出力されます。
Adding repo flagger https://flagger.app
"flagger" has been added to your repositories
Comparing release=frontend, chart=flagger/podinfo
Comparing release=backend, chart=flagger/podinfo
********************
Release was not present in Helm. Diff will show entire contents as new.
********************
test, backend-podinfo, ConfigMap (v1) has been added:
-
+ # Source: podinfo/templates/configmap.yaml
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: backend-podinfo
+ labels:
+ app: podinfo
+ chart: podinfo-5.0.0
+ release: backend
+ heritage: Helm
+ data:
+ config.yaml: |-
+ # http settings
+ http-client-timeout: 1m
+ http-server-timeout: 30s
+ http-server-shutdown-timeout: 5s
test, backend-podinfo, Deployment (apps) has been added:
-
+ # Source: podinfo/templates/deployment.yaml
+ apiVersion: apps/v1
+ kind: Deployment
...
中略
...
+ name: http
+ selector:
+ app: frontend-podinfo
Upgrading release=frontend, chart=flagger/podinfo
Upgrading release=backend, chart=flagger/podinfo
Release "backend" does not exist. Installing it now.
NAME: backend
LAST DEPLOYED: Sun Nov 29 08:39:42 2020
NAMESPACE: test
STATUS: deployed
REVISION: 1
NOTES:
podinfo backend deployed!
Listing releases matching ^backend$
Release "frontend" does not exist. Installing it now.
NAME: frontend
LAST DEPLOYED: Sun Nov 29 08:39:42 2020
NAMESPACE: test
STATUS: deployed
REVISION: 1
NOTES:
podinfo frontend deployed!
Listing releases matching ^frontend$
frontend test 1 2020-11-29 08:39:42.663356 +0900 JST deployed podinfo-5.0.0 5.0.0
backend test 1 2020-11-29 08:39:42.668443 +0900 JST deployed podinfo-5.0.0 5.0.0
UPDATED RELEASES:
NAME CHART VERSION
backend flagger/podinfo 5.0.0
frontend flagger/podinfo 5.0.0
最初にAdding repo flagger $URLで利用するChart Repositoryを登録し、次にComparing release=$NAME、chart=$CHARTで対象のReleaseへの更新内容を決定するために差分チェックを行っています。
最後にUpgrading release=$NAME、chart=$CHARTが出力されているタイミングでhelm upgrade --installコマンドが実行され、対象Releaseが作成・更新されています。
helmfile templateの実行例
helmfile applyを実行すると、ミドルウェアやアプリケーション一式をまとめてインストール・更新可能なことがわかりました。このコマンドは筆者としても便利だと感じていて、本番環境へのデプロイをこのコマンドで行っている組織もまた多いようです。
しかし、要件によってはhelmfile applyが適さないこともあります。例えば「デプロイ前にkubeevalなどでマニフェストの正しさを検証したい」といった場合、kubeevalの入力はhelmfile.yamlではなくKubernetesマニフェストであるため困ります。
またArgoCDでGitOpsを実装したいという場合、ArgoCDがサポートする入力はKubernetesマニフェスト、kustomize、Helm Chartの3つしかなく、こちらもhelmfile.yamlはサポートしていないため、一見すると対応できないように思えます。
これらの理由でKubernetesマニフェストが必要な場合はhelmfile templateを利用すると良いでしょう。helmfile templateは、インストール対象の全マニフェストを標準出力等に書き出すコマンドです。
kubeevalと組み合わせる場合は、helmfile templateの結果を以下のようにkubeevalの入力とすることができます。
$ helmfile template --include-crds > all.yaml
$ kubeval all.yaml
ArgoCDと組み合わせる場合は、ArgoCDのConfig Management Pluginという機能からhelmfile templateを呼び出すと良いでしょう。
例として、ArgoCD自体もHelmfileでインストールする場合は、以下のようなhelmfile.yamlで必要なConfig Management Pluginの設定が可能です。
- name: argocd
chart: argo/argo-cd
values:
- global:
# See https://hub.docker.com/r/chatwork/argocd-helmfile
image:
repository: chatwork/argocd-helmfile
tag: latest
# See https://github.com/argoproj/argo-helm/blob/master/charts/argo-cd/values.yaml#L521-L523
config:
# See https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-cm.yaml#L214
configManagementPlugins: |
- name: helmfile
init:
command: ["/bin/sh", "-c"]
# ARGOCD_APP_NAMESPACE is one of the standard envvars
# See https://argoproj.github.io/argo-cd/user-guide/build-environment/
args: ["helmfile --state-values-set ns=$ARGOCD_APP_NAMESPACE -f helmfile.yaml template --include-crds | sed -e '1,/---/d' | sed -e '/WARNING: This chart is deprecated/d' | sed -e 's|apiregistration.k8s.io/v1beta1|apiregistration.k8s.io/v1|g' > manifests.yaml"]
generate:
command: ["/bin/sh", "-c"]
args: ["cat manifests.yaml"]
Config Management Pluginについての詳細は、ArgoCDの公式ドキュメントを参照してください。
Helmfileの便利機能
Helmfileには、シェルスクリプト等で気軽に実装することが難しいような便利な機能もいくつか用意されています。
kubectl、helm、kustomizeのいずれを使うにしても、単一のツールではできないことをカバーするために、頑張ってシェルスクリプトやCIの設定ファイルでラップする…ということは現実的によくあることだと思います。
基本的に、Helmfileはこれらのツールを補完することをゴールにしているため、他のツールには見られない便利な機能が用意されていることがあります。
筆者の独断と偏見でピックアップすると、以下の機能がおススメです。
- Kustomize統合(#Kustomize-統合)
- Terraformサポート(#Terraform-サポート)
Kustomize統合
Kustomize統合には大きく2つの機能があります。ひとつは「Helmfileを使っ Helm Chart以外のものをデプロイする」ものです。kustomizeでは難しいような動的な設定を記述したり、マニフェストをChartに書き直したり、その逆を行ったりする必要はなく、すべてをHelmfileでひとまとめにデプロイできます。
例えば、特定のCRDのみHelmを使わず、ただのマニフェストで管理し、ミドルウェア類はHelm Chart、自社アプリケーションはKustomizeで管理したい、というような複雑な(しかし経験上はよくある)要件があったとしても、Helmfileであれば以下のような設定を記述するだけで済みます。
releases:
#
# CRDの例: ./crds/*.yaml にCRDのマニフェストが保存されている場合
#
- name: crds
chart: ./crds
#
# ミドルウェアの例: fluentd は Chart でインストール
#
- name: fluentd
chart: stable/fluentd
#
# アプリケーションの例: web アプリは kustomize でインストール
#
- name: myapp
# path/to/kustomiation/kustomization.yaml の親ディレクトリを chart に指定すると、
# それを Chart に自動変換する…という仕様になっています
chart: ./path/to/kustomization
もうひとつの機能は「Chartをインストール前に修正する」ものです。kustomizeには入力となったマニフェストに一定の変更を加える(例えば、特定のラベルを全リソースに付与する)機能がありますが、helmにはその機能はありません(Chart側でそのような設定をサポートする必要があります)。
Helmfileを利用すると、Chartをkustomizeで修正後にインストールできるようになります。
仮に、先ほど紹介したpodinfoのインストール前にteam=myteamというラベルとargocd.argoproj.io/sync-wave=5というアノテーションを付与する場合は、以下のように記述してhelmfile applyやhelmfile templateを実行するだけです。
releases:
- name: podinfo
chart: flagger/podinfo
transformers:
- apiVersion: builtin
kind: AnnotationsTransformer
metadata:
name: notImportantHere
annotations:
argocd.argoproj.io/sync-wave: "5"
fieldSpecs:
- path: metadata/annotations
create: true
- apiVersion: builtin
kind: LabelTransformer
metadata:
name: notImportantHere
labels:
team: "myteam"
fieldSpecs:
- path: metadata/labels
create: true
Helm Chart側で「--set labels[0]="foo=bar"のChart Value経由でラベルを付与する」といった機能がサポートされていれば良いのですが、このような機能はHelm / Chartのどちらでも標準化されていないためあまり期待はできず、経験上もそのような機能がないChartは多く存在します。
Helm Chartをフォーク、機能追加した上で本家に取り込んでもらったり、もしくは独自に保守していくという選択肢もありますが、そこまで手間をかけたくないという場合は、このKustomize連携機能が役立つと思います。
Terraformサポート
AWSリソースなどを含むインフラの管理にTerraformを使っており、Terraformで構築したKubernetesクラスタに別途kubectl等でアプリケーションをインストールしている、という方はいますか?
HelmfileのTerraformサポートを利用すると、その2ステップを集約できるかもしれません。具体的には、terraform-provider-helmfileというTerraformカスタムプロバイダを利用するとTerraformからHelmfileを実行できます。
例えば、以下のようなtfファイルを記述すると、Kubernetesクラスタを構築後にHelmfileでアプリケーション一式をインストール、という一連の手順やスクリプトはterraform applyコマンド一発に集約できます。
resource "helmfile_release_set" "mystack" {
content = file("./helmfile.yaml")
}
terraform-provider-helmfileの便利な機能のひとつに「バイナリのバージョン管理」があります。CI上でHelmfileやHelmなどを実行する場合、CIパイプラインで使う仮想マシンイメージやコンテナイメージに含まれるHelmfileやHelmを定期的にアップデートする運用がどうしても発生してしまいますが、本機能を使うとTerraformの設定ファイルを数行変更するだけで済みます。
例えば、Helmfile 0.135.0とHelm 3.4.1を使って前述のmystackをデプロイする場合は、以下のようにversionとhelm_versionをTerraformの設定ファイルに記述します。
resource "helmfile_release_set" "mystack" {
content = file("./helmfile.yaml")
version = "0.135.0"
helm_version = "3.4.1"
}
おわりに
今回は、Helmfileを使ってKubernetesマニフェスト、Kustomization、Helm Chartなどから構成されるアプリケーション一式を効率的に管理する方法を紹介しました。
皆さんの環境でもKubernetesへのデプロイパイプラインが複雑化していたり、スクリプトの保守が難しくなってきたり、といった問題が起きていたら、ぜひHelmfileを試してみてください。
- この記事のキーワード