Kustomizeで環境ごとに異なるマニフェストを作る

執筆日:

更新日:

ブログの更新頻度が落ちていますが、サボっているわけではないです。。 はい。mosuke5です。

やはりKubernetesマニフェストのテンプレートエンジンは必要だ、と気づいてしまったので、マニフェストを生成できるツールを探していました。 そこで、Kustomizeを試しみたのでアウトプットとして残しておきます。使ってみた上でハマったポイントや注意すべき点なども含めて書きたいと思います。

モチベーション

kubernetes初心者として、マニフェストもかけてきたし、kubectl apply -f xxxxですぐにデプロイできるし、最高だなって思ってました。 しかし、いざ、開発環境と本番環境の2つのNamespaceで利用したくなったり、運用のことを考えるといろいろと不都合が発生してきました。

  1. 開発環境と本番環境でパラメータが変えたいことがある
  2. 本番環境だけ入れたいリソースがある(例えばLoadBalancerは本番だけほしいなど)
  3. イメージをLatestで使いたくない。タグ指定したいが、都度手動で変更したくない
  4. configMapやsecretの変更したときのアプリ側の再起動どうしよう、など

こうなってきたときに、「あっ。やっぱりマニフェストのテンプレートエンジンが必要だ。。」と強く感じました。 マニフェストの生成にはいくつか方法があるようなのですが、kubernetes v1.14からkubectlに統合されたKustomizeが便利そうだったので、そちらでまずは使い勝手を試してみようと思い立ったわけです。

KubernetesにおけるCI/CD重要なポイントを下記にまとめました。その1つとしてKustomize(マニフェストのテンプレート化)の重要性を書いています。参考にどうぞ。

Kustomizeとは

名前から少し推察できるかもしれませんが、CustomizeのCをKubernetesのKにもじったのが、Kustomizeであり、configuration(manifest) managementのツールです。(公式Github)

基本的な構成としてはkustomization.yml + resources(manifest files)になります。 kustomization.ymlにはどのマニフェストファイルを対象にするか、そしてそれらに対してどのような変更や修正、変数を割り当てるかを記述するかたちです。最終的なアウトプットとしては、変更・修正が施されたマニフェストファイルの中身がアウトプットされます。 公式ドキュメントにも書いてありますがマニフェストに対するsedみたいな一面があります。

ドキュメント

Kustomizeを実際に触っていく上でハマったポイントもあったのでご紹介します。
Kustomizeはkubernetes v1.14からkubectlに組み込まれることになりました。実際に、kubectl -hでヘルプを見てみてもわかります。

Advanced Commands:
  kustomize      Build a kustomization target from a directory or a remote url.

kubectlに統合されているのはバージョンが最新のものではありません。また、Kustomizeも進化が早いので機能差分がでてくることもめずらくありません。

自分も使っていく中で何度か下記のようなエラーメッセージに出くわしました。下記の例だと、envsというフィールドがありませんというエラーです。公式ドキュメントを見て記述していたのですが、kubectlに見込まれているバージョンではそのフィールドがなかったということです。

$ kubectl kustomize .
Error: couldn't make target for ../../base: json: unknown field "envs"

そのため、ドキュメントをみる場合は使っている方法に合わせて使い分けるといいです。

とにもかくにも使ってみる

Hello World

Kustomizeはサンプルもいくつか用意してくれているので始めるのは簡単です。Hello World的にはこちらが良いと思います。

kustomization.ymlが下記の通り書いてありますが、resourcesに記載の3ファイルを対象に共通のlabelを付与するという意味になります。

commonLabels:
  app: hello

resources:
- deployment.yaml
- service.yaml
- configMap.yaml

configMapGenerator

上のHello Worldの例ではマニフェストにconfigMap.yamlが含まれていましたが、configMapを生成するには、もうひとつconfigMapGeneratorという機能を使うこともできます。(公式ドキュメント)

こちらの機能を使うと、configMapを作成時に名前にハッシュ値が付きます。そして、configMapに変更があった場合に、そのハッシュ値も変更され別のconfigMapとして保存されます。また、そのconfigMapを参照しているDeploymentなどのconfigMapの名前も変更してくれます。 それによって、configMapを変更時にアプリ側のPodsの再作成もされます。運用面を考えると非常に便利な機能です。

以下に自分のいいたいことが全て書かれていたので、詳細についてはこちら参照してみてください。
kustomizeでconfigMapを取り扱うときの注意

overlay

自分がKustomizeを触ろうとした主なモチベーションの部分である、環境ごとにパラメータを変えたい、一部のリソースを変えたいというニーズにはoverlayという機能で対応することができます。
overlayは名前の通り「覆う」的な意味なので、Baseのマニュフェストを用意して、環境ごとにパッチ当てするマニフェストを用意する、という形になります。

今回こんなサンプルを作りました。(コードはこちら)

  • Nginxをデプロイする
  • 開発環境用はnamespaceをdevelopmentに、本番環境用はnamespaceをproductionにする
  • 本番環境用のNginxのServiceはType=LoadBalancerにする

ファイル構造はこのような形です。

.
├── base
│   ├── kustomization.yml
│   ├── nginx-deployment.yml
│   └── nginx-service.yml
└── overlays
    ├── development
    │   └── kustomization.yml
    └── production
        ├── change-nginx-service-to-loadbalancer.yml
        └── kustomization.yml

baseには共通のマニフェストです。出力すると下記のとおりです。 見ておいてほしいのはnginx-servceのTypeです。baseマニフェストではClusterIPを指定しています。

$ kubectl kustomize base
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - name: http-port
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: ClusterIP    # baseではClusterIPを指定
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.17.0
        name: nginx
        ports:
        - containerPort: 80

しかし、本番環境ではNamespaceを指定して、TypeをLoadBalancerで公開したかったとします。 以下は本番環境用にパッチ当てするためのkustomization.ymlなのですが、baseのマニフェストを指定し、namespaceとパッチを当てるマニフェストを指定しました。

$ cat overlays/production/kustomization.yml
bases:
- ../../base
namespace: "production"
patchesStrategicMerge:
- change-nginx-service-to-loadbalancer.yml   # serviceのtypeを変えるパッチを当てる

$ cat overlays/production/change-nginx-service-to-loadbalancer.yml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: LoadBalancer   # typeをLoadBalancerへ
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 80
      targetPort: 80

このようにして、本番環境用のパッチを当てて出力すると、typeがLoadBalancerに変わっていることがわかります。これで、1つのマニフェストを使って環境ごとにパラメータや一部のリソースを変更してデプロイできそうです。

$ kubectl kustomize overlays/production
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: production
spec:
  ports:
  - name: http-port
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer   # typleがLoadBalancerに変更されている
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.17.0
        name: nginx
        ports:
        - containerPort: 80

まとめ

Kubernetesの初歩の勉強をしているうちはあまり必要性を感じないのですが、それなりに「運用」をみすえると、マニフェストを自動で生成する必要性がでてきます。 その1つの方法としてKustomizeというツールを触りました。kubectlにも統合されているので比較的簡単にはじめることができますし、自分がやりたいと思っていたことは問題なく実現できることがわかりました。

今後使っていくなかで、また知見がたまればおしらせします。

記事の内容に関連した相談、仕事依頼したい New

記事の内容やクラウドネイティブ技術に関する相談、仕事依頼を開始しました。
仕事依頼、相談をしてみる

フィードバック

本記事に対して、フィードバックあればこちらのフォームからご記入ください。
記事の内容にフィードバックしてみる

このエントリーをはてなブックマークに追加