技術開発チームの基盤システム担当の小倉です。
最近、社内システム用のコンテナ基盤をDockerからマネージドKubernetesサービスであるAmazon EKSとAzure Kubernetes Serviceに移設しました。その際に、Kubernetesのマニフェストの管理にオープンソースのKustomizeを利用しました。
私自身は実際に動かすことでKustomizeについての理解が進みました。そのため、本ブログはハンズオン形式でKustomizeを紹介します。ぜひ、手元で動かしていただけましたら幸いです。
Kustomizeとは
公式ドキュメントから引用すると以下のとおりです。
KustomizeはKubernetesマニフェストを横断し、フォークすることなく設定オプションを追加、削除、更新します。スタンドアロンバイナリとしてもkubectlのネイティブ機能としても利用可能です。
本ブログ内ではkustomizeコマンド(スタンドアロンバイナリ)を利用します。インストール手順は以下をご参照ください。
https://kubectl.docs.kubernetes.io/installation/kustomize/
kustomizeを使うには、kustomization.yamlというファイルが必要です。Kubernetesに適用する際には、kustomize buildコマンドでkustomization.yamlを基にマニフェストをビルドし、パイプでkubectlコマンドに渡して利用します。
$ kustomize build {kustomization.yamlがあるディレクトリ} |kubectl apply -
例の紹介
この例で利用しているサンプルは以下で公開しています。
https://github.com/heartbeatsjp/kustomize_samples
実例を紹介します。このセクションでは、./firstディレクトリのサンプルを利用しています。
まずはresourcesセクションに注目します。resourcesは他のkustomizationもしくはマニフェストファイルを読み込むセクションです。この例では、./overlays/productionと./overlays/stagingがそれぞれ、./baseを指定しています。この両者の関係において、./overlays/productionと./overlays/stagingはオーバーレイ、baseはベースと呼ばれます。 参照する側と参照される側の関係を示しており、以下のように数珠つなぎで参照できます。
Overlay → Base(Overlay) → Base(Overlay) → Base
この例では、baseを指定してビルドすると、./base/deployment.yamlの内容がそのまま出力されます。 これは./base/kustomization.ymlが./base/deployment.yamlを読み込んでいるだけだからです。diffコマンドで差分がないことを確認できます。
$ diff base/deployment.yaml <(kustomize build base/)
次に、./overlays/productionを指定してビルドした場合との差分を確認します。
$ diff -u <(kustomize build base/) <(kustomize build overlays/production/) ...省略... name: hello-world - name: hello-world + name: production-hello-world spec: - replicas: 1 + replicas: 4 selector:
nameとreplicasに差分がでました。図で示すと以下のような流れになります。
また、./overlays/productionと./overlays/stagingを比較すると以下のようになります。
$ diff -u <(kustomize build overlays/staging/) <(kustomize build overlays/production/) ...省略... name: hello-world - name: staging-hello-world + name: production-hello-world spec: - replicas: 2 + replicas: 4 selector:
このようにベースのパラメータを環境ごとに変更できます。続いて、パラメータを変更した機能であるパッチやトランスフォーマー、コンポーネントといった機能を紹介していきます。
パッチ(JSON6902 patches)
replicasの値を上書きした機能のことをパッチと呼びます。 パッチには、JSON6902 patchesとstrategic merge patchの2つの記法が利用できます。 本ブログ内では、より簡潔な構文をもつJSON6902 patchesの記法を利用します。
JSON6902の6902はRFCの番号です。構文や機能の詳細はRFCをご参照ください。
https://datatracker.ietf.org/doc/html/rfc6902
パッチはkustomization.ymlファイルにインラインで書くこともできます。前述のreplicasの上書きをインラインで記載すると以下のようになります。pathでファイルを指定していた部分をpatchに置き換え、その後にファイルの中身を指定しています。インデントに注意してください。
patches: - patch: |- - op: replace path: /spec/replicas value: 4 target: kind: Deployment
実際に書き換えの処理を記載しているのは、op、path、valueブロックです。opに操作の種類を、pathにどの部分を操作するのかを、valueに適用する値を指定します。
pathは「操作対象の値のキー」からトップレベルまで遡って/
で区切ります。配列にアクセスする場合は以下のようにインデックスを指定します。
- op: replace path: /spec/template/spec/containers/0/image value: hello-world-staging
また、上の例でtargetにkind: Deploymentと指定していますが、この場合は全てのDeploymentリソースに適用されます。特定のリソースにのみ適用したい場合は、name: hello-worldなどの固有のパラメータも追加して指定する必要があります。
配列への追加(add)
このセクションでは、./patch_addディレクトリのサンプルを利用しています。
op(Operations)にはreplaceの他にもadd、removeなどが指定できます。addは配列に要素を追加する場合に利用します。環境変数のように複数パラメータを配列で指定する際に利用します。以下が利用例です。
差分を確認すると配列に要素を追加できていることがわかります。
$ diff -u <(kustomize build base/) <(kustomize build overlays/production/) ...省略... - name: DB_USER value: dbuser + - name: DB_PASSWORD + value: production-password image: hello-world name: hello-world
配列に追加する際は、path: /spec/template/spec/containers/0/env/-のように最後にハイフンを指定する点がポイントです。
コンポーネント(Components)
このセクションでは、./components_kustomizeディレクトリのサンプルを利用しています。
コンポーネントとはv3.7.0から利用できる機能です。 https://kubectl.docs.kubernetes.io/guides/config_management/components/では以下のように説明されています。
コンポーネントは複数のオプション機能をサポートするアプリケーションを扱うときに便利で、異なるオーバーレイでそれらのサブセットのみを有効にしたい場合、つまり、異なる環境やオーディエンスに対して異なる機能を有効にしたい場合に使用します。
先ほどまでの例でコンポーネントは利用していませんが、環境(オーバーレイ)ごとにパラメータを調整できました。最小限の例でしたが、機能単位での追加も可能に見えます。Kustomizationとの違いは以下に説明があります。
コンポーネントとしてマークされたKustomizationは基本的に通常のKustomizationと同じ機能を持ちます。 主な違いは、親Kustomization(オーバーレイまたはコンポーネント)のリソースが蓄積された後、その上で評価されることです。
例を基にKustomizeとコンポーネントの違いを紹介します。
まず、コンポーネントはapiVersionとkindが異なります。(Kustomizeは省略できるため記載していません)そして呼び出すときも、resourcesではなく、componentsに指定します。
./overlays/componentsは、baseとcomponentsを読み込み、./overlays/kustomizeは、baseとkustomizeを読み込みます。 コンポーネント以外は同じ内容でビルドし、両者の違いを確認します。
$ diff -u <(kustomize build overlays/kustomize/) <(kustomize build overlays/components/) ...省略... name: hello-world spec: - replicas: 1 + replicas: 4 selector: matchLabels:
kustomizeの方がreplicasの値を変更できていません。これはkustomizeの影響範囲は親Kustomizationには及ばないためです。先ほど引用したコンポーネントの違いのように説明すると、「2つのベースのリソースが蓄積されずに評価が完了し、別のベースへのパッチが反映されなかった」ということになります。
コンポーネントは親のKustomization(オーバーレイ)に影響を与えられるため、親Kustomizationでpatchを書いたときと同じ効果が得られます。この特性はコンポーネントを使ううえでの注意点にもなります。コンポーネントでNamespaceやnamePrefixなどを定義すると親Kustomizationまで影響してしまうためです。親Kustomizationに影響するということは、そのKustomizeの配下の全てのリソースに影響し、全てのリソースのNamespaceやリソース名を変えてしまうことになります。
影響範囲のイメージを図に示します。
コンポーネントの利用例
コンポーネントを利用して、Ingressにパッチを適用する例を紹介します。このセクションでは、./componentsディレクトリのサンプルを利用しています。
./base/ingress.yamlにてrules: []のようにすると、配列を空の状態で定義できます。productionは2つのコンポーネントを参照し、stagingは1つのコンポーネントを参照します。
productionをターゲットにビルドした際のIngressリソースは以下のとおりとなります。
$ kustomize build overlays/production/ ...省略... apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kustomize-ingress spec: rules: - host: hello-world.net http: paths: - backend: service: name: hello-world port: number: 8000 path: / pathType: Prefix - host: hello-kustomize.net http: paths: - backend: service: name: hello-kustomize port: number: 8000 path: / pathType: Prefix
もとは空の配列であったIngressのrulesに2つ追加されていることがわかります。stagingは./components/hello-worldしか参照していないためその分のルールだけ追加されます。./components/hello-worldと./components/hello-kustomizeはそれぞれDeploymentとServiceのマニフェストファイルを読み込んでいます。コンポーネントを使うことでDeployment、Service、Ingressのルールを1つのサービス・機能としてまとめて管理できます。
これをコンポーネントなしで実現しようとすると環境ごとにIngressを整備するか、親Kustomizationでパッチをそれぞれ定義する必要があります。追加削除の変更が大きくなりますし、共通リソースに手を加えなければなりません。 コンポーネントを利用すればオーバーレイから参照するか否かだけでデプロイを制御し、機能のセットを追加できます。
トランスフォーマー(Transformer)
トランスフォーマーについて詳しい説明は以下のリンクにあります。
https://github.com/kubernetes-sigs/kustomize/blob/master/examples/transformerconfigs/README.md
以下、概要の引用です。
Kustomize は元のリソース セットに一連の変換を適用することで新しいリソースを作成します。 Kustomize は以下のデフォルトのトランスフォーマーを提供します:
- annotations
- images
- labels
- name reference
- namespace
- prefix/suffix
- variable reference
上記のように便利なものがあらかじめいくつか用意されています。すでに本ブログではprefixはnamePrefixとして利用しました。引用にもあるとおり、元の値の変換を行えることがパッチとの違いになります。またトランスフォーマーの中にはkustomizeコマンドにサブコマンドが用意されているものもあり、手順やCIの作成に便利です。
その他活用例
CIでのイメージタグの更新
ビルドしたアプリケーションのコンテナのタグをマニフェストへ反映するためにKustomizeの機能を活用できます。Gitlab CIによる例を以下に紹介します。
...省略... script: - git clone git@gitlab.xxxxx.jp:kube/manifest.git --single-branch --branch main manifest && cd cd-manifest - git checkout -b update_image_tag - cd overlays/production - kustomize edit set image ${IMAGE_URL}=:${IMAGE_TAG} - git commit -am "update image tag" - git push origin update_image_tag -o merge_request.target=main -o merge_request.create
環境ごとのパッチを適用する
パスワードやバックエンドのアクセス先名など環境ごとに変更したい場合があります。その場合は、以下のようにコンポーネントをさらにもう1階層作成すると環境ごとのパッチを適用できます。
./app ├── common │ ├── deployment.yml │ ├── ingress-patch-domain.yml │ ├── kustomization.yml │ └── service.yml ├── production │ ├── deployment-env-patch.yml │ └── kustomization.yml └── staging ├── deployment-env-patch.yml └── kustomization.yml
productionとstagingはそれぞれcommonを参照し、オーバーレイからproductionとstagingをコンポーネントとして参照します。
おわりに
これまで紹介したようにKustomizeを利用することで、複数環境のマニフェストを共通化して管理できます。本ブログで紹介した以外にも機能をもっており、またHelmやArgoCDとの連携なども可能で使い勝手の良いものです。
Kustomizeを利用している方や利用を検討している方に本ブログが参考となれば幸いです。