Dev > Carvel > ytt
Aug 19, 2020
Aug 19, 2020
N/A Views
MD
warning
この記事は2年以上前に更新されたものです。情報が古くなっている可能性があります。

前の記事ではyttのTemplating機能を見てきました。本記事ではYAMLの柔軟な加工に便利なOverlay機能を見ます。

目次

Mapの操作

まずは最もよく使うMapの操作方法を見ます。

値の変更

次のKubernetesのmanifestファイルであるdeployment.ymlの中のDeploymentreplicas3に変更したいとします。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

次のoverlay file(scale.yml)を作成します。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Deployment", "metadata": {"name": "demo"}})
---
spec:
  replicas: 3

---の上の@overlay/matchアノテーションでマルチドキュメントなYAMLのうち、どのドキュメントを対象にするかを指定します。今回はKindDeploymentで、metadata.namedemoなドキュメントのみをoverlayの対象とします。
指定方法はこちらを参照してください。

次のコマンドを実行して、replicasが変わっていることを確認してください。

$ ytt -f deployment.yml -f scale.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

同じようにServicetypeLoadBalancerからNodePortに変更します。次のoverlay file(nodeport.yml)を作成します。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Service", "metadata": {"name": "demo"}})
---
spec:
  type: NodePort

次のコマンドを実行して、typeが変わっていることを確認してください。

$ ytt -f deployment.yml -f nodeport.yml             
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: NodePort

overlay filesは複数指定できるので、replicasの変更とtypeを同時に適用できます。

$ ytt -f deployment.yml -f scale.yml -f nodeport.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: NodePort

同じディクレトリ配下の全てのファイルを入力にしたい場合は

ytt -f .

で良いです。

値の追加

次は元のdeployment.ymlにはない要素を追加します。

次のprometheus-annotation.ymlDeploymenttemplatemetadataannotationsを追加します。
元のファイルにない要素には#@overlay/match missing_ok=Trueアノテーションを設定します。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Deployment", "metadata": {"name": "demo"}})
---
spec:
  template:
    metadata:
      #@overlay/match missing_ok=True
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"

次のコマンドを実行して、annotationsが追加されていることを確認してください。

$ ytt -f deployment.yml -f prometheus-annotation.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

今回の場合は、元のファイルにannotations自体の定義がなかったため、上記のoverlay fileで問題ありませんが、
annotationsの定義はあるけどもprometheus.io/****アノテーションの定義はないケースにも対応するには次のように修正します。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Deployment", "metadata": {"name": "demo"}})
---
spec:
  template:
    metadata:
      #@overlay/match missing_ok=True
      annotations:
        #@overlay/match missing_ok=True
        prometheus.io/scrape: "true"
        #@overlay/match missing_ok=True
        prometheus.io/port: "8080"

もう一つ例を見ます。次のnamespace.yml全てのYAMLドキュメントmetadatanamespace: demoを追加します。
この場合、#@overlay/matchbyに指定する関数はoverlay.allです。
デフォルトでは1ドキュメントだけがヒットすることを期待しているのですが、今回は2ドキュメントがヒットするので、
expects="1+"もつけます。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.all, expects="1+"
---
metadata:
  #@overlay/match missing_ok=True
  namespace: demo

次のコマンドを実行して、namespaceが追加されていることを確認してください。

$ ytt -f deployment.yml -f namespace.yml            
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
  namespace: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

namespace.ymlにYAMLドキュメントも追加できます。ここではNamespace自体の定義も追加します。

#@ load("@ytt:overlay", "overlay")
apiVersion: v1
kind: Namespace
metadata:
  name: demo
#@overlay/match by=overlay.all, expects="1+"
---
metadata:
  #@overlay/match missing_ok=True
  namespace: demo

次のコマンドで出力内容にNamespaceも追加されることを確認してください。

$ ytt -f deployment.yml -f namespace.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
  namespace: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer
---
apiVersion: v1
kind: Namespace
metadata:
  name: demo
  namespace: demo

Namespacemetadatanamespace: demoが含まれるのが変だと感じる場合は、次のようにoverlay.allではなく、overlay.not_opを使い、KindNamespaceの場合を除外します。

#@ load("@ytt:overlay", "overlay")
apiVersion: v1
kind: Namespace
metadata:
  name: demo
#@overlay/match by=overlay.not_op(overlay.subset({"kind": "Namespace"})), expects="1+"
---
metadata:
  #@overlay/match missing_ok=True
  namespace: demo

次のコマンドで、Namespaceの定義にnamespace: demoが含まれていないことを確認してください。

$ ytt -f deployment.yml -f namespace.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
  namespace: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer
---
apiVersion: v1
kind: Namespace
metadata:
  name: demo

値の削除

次は削除します。次のremove-service-type.ymlServicetypeを削除します。

削除したい要素には#@overlay/removeアノテーションをつけます。値はなんでも良いです。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Service", "metadata": {"name": "demo"}})
---
spec:
  #@overlay/remove
  type: 

次のコマンドで、Servicetypeが削除されていることを確認してください。

$ ytt -f deployment.yml -f remove-service-type.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo

List内のMapの操作

次にList内のMapを操作する例を見ます。

この場合Listの中のどの要素を操作するのかを#@overlay/matchアノテーションで指定する必要があります。

値の変更

次のchange-image.ymlではimageの値を変更します。
containersがListなので、このうちのどの要素をoverlayの対象かを指定する必要があります。

@overlay/match by="name"アノテーションをつけると、containersのうちnameが合致するものを対象とします。
次の例ではnamehello-cnbな要素のimageharbor.example.com/demo/hello-cnbに変更します。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Deployment", "metadata": {"name": "demo"}})
---
spec:
  template:
    spec:
      containers:
      #@overlay/match by="name"
      - name: hello-cnb
        image: harbor.example.com/demo/hello-cnb

次のコマンドを実行して、imageの変更を確認してください。

$ ytt -f deployment.yml -f change-image.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: harbor.example.com/demo/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

値の追加

次にList内のMapに値を追加する例を見ます。
次のdemo-env.ymlcontainersList中のnamehello-cnbなMapにenvを追加します。
元のファイルに存在しない場合は、前述の例と同様に#@overlay/match missing_ok=Trueをつけます。

なお、追加するenvもMapのListです。元のファイルにenvが定義されており、
かつその中にnameINFO_MESSAGEの要素がない場合にエラーにならないように、#@overlay/match by="name", missing_ok=Trueを付けます。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Deployment", "metadata": {"name": "demo"}})
---
spec:
  template:
    spec:
      containers:
      #@overlay/match by="name"
      - name: hello-cnb
        #@overlay/match missing_ok=True
        env:
        #@overlay/match by="name", missing_ok=True
        - name: INFO_MESSAGE
          value: "Hello World!"

次のコマンドを実行して、envが追加されていることを確認してください。

$ ytt -f deployment.yml -f demo-env.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
        env:
        - name: INFO_MESSAGE
          value: Hello World!
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

次のsidecar.ymlではcontainersListに値を追加します。これも同じく#@overlay/match by="name" missing_ok=Trueを付けています。

#! sidecar.yml

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Deployment", "metadata": {"name": "demo"}})
---
spec:
  template:
    spec:
      containers:
      #@overlay/match by="name" missing_ok=True
      - name: hello-sidecar
        image: busybox
        command: ['sh', '-c', 'echo Hello ytt! && sleep 3600']

次のコマンドを実行して、containersが更新されていることを確認してください。

$ ytt -f deployment.yml -f sidecar.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
      - name: hello-sidecar
        image: busybox
        command:
        - sh
        - -c
        - echo Hello ytt! && sleep 3600
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: demo
  type: LoadBalancer

値の削除

次はList内のMapの値を削除します。次のremove-service-targetport.ymlServiceportsList内のtargetPortを削除します。
前述の例と同じく、@overlay/removeアノテーションを付けます。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.subset({"kind":"Service", "metadata": {"name": "demo"}})
---
spec:
  ports:
  #@overlay/match by="name"
  - name: 80-8080
    #@overlay/remove
    targetPort:

次のコマンドを実行して、targetPortが削除されていることを確認してください。

$ ytt -f deployment.yml -f remove-service-targetport.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - image: making/hello-cnb
        name: hello-cnb
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - name: 80-8080
    port: 80
    protocol: TCP
  selector:
    app: demo
  type: LoadBalancer

Listの操作

最後に普通のListの操作を見ます。

値の変更

ここではCloud Foundryのmanifest fileである、次のmanifest.ymlを例に使います。

applications:
- name: hello
  memory: 1g
  path: hello.jar
  buildpacks:
  - java_buildpack

次のreplace-buildpack.ymlbuildpacks Listの中のjava_buildpackjava_buildpack_offlineに変えます。

MapのListの場合と異なり、普通のListを操作する場合は、どん要素が対象であるかを示すために、
@overlay/matchbyでListの値そのものと比較するか、Listのindexを指定する必要があります。

値そのものを差し替えるために@overlay/replaceを付けます。(デフォルトは@overlay/mergeです。)

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.all
---
applications:
#@overlay/match by=overlay.all
- buildpacks:
  #@overlay/match by=overlay.subset("java_buildpack")
  #@overlay/replace
  - java_buildpack_offline

次のコマンドを実行して、buildpacksの値が変更されていることを確認してください。

$ ytt -f manifest.yml -f replace-buildpack.yml
applications:
- name: hello
  memory: 1g
  path: hello.jar
  buildpacks:
  - java_buildpack_offline

値の追加

次のinsert-buildpack.ymlbuildpacks Listの中の先頭にdatadog_buildpackを追加します。
@overlay/insert before=Trueアノテーションを使います。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.all
---
applications:
#@overlay/match by=overlay.all
- buildpacks:
  #@overlay/match by=overlay.index(0)
  #@overlay/insert before=True
  - datadog_buildpack

次のコマンドを実行して、buildpacksに値が追加されていることを確認してください。

$ ytt -f manifest.yml -f insert-buildpack.yml
applications:
- name: hello
  memory: 1g
  path: hello.jar
  buildpacks:
  - datadog_buildpack
  - java_buildpack

値の削除

次のremove-buildpack.ymlbuildpacks Listの中のjava_buildpackを削除します。
@overlay/removeアノテーションを使います。

#@ load("@ytt:overlay", "overlay")

#@overlay/match by=overlay.all
---
applications:
#@overlay/match by=overlay.all
- buildpacks:
  #@overlay/match by=overlay.subset("java_buildpack")
  #@overlay/remove
  -

次のコマンドを実行して、buildpacksの値が削除されていることを確認してください。

$ ytt -f manifest.yml -f remove-buildpack.yml
applications:
- name: hello
  memory: 1g
  path: hello.jar
  buildpacks: []

yttのOverlayの色々なパターンを見てきました。

より実践的な利用例が見たい場合は、cf-for-k8s及びそのサブプロジェクトでyttが多用されているので、参考にすると良いです。

cf-for-k8sがなぜ広く採用されているHelmやKustomizeを使用しないかというissueがあります。
これに対して、Joe Beda氏がコメントしているので一読の価値があります。

Found a mistake? Update the entry.
Share this article: