GKE built-inなcAdvisorにtolerationsを設定したくなった

[PTAアドベントカレンダー2021](https://adventar.org/calendars/6450)の12/5の記事です。

# Background
株式会社AbemaTVでABEMAの広告プロダクト開発チームでSREをしているKosakaと言います。
元々はデザイナー・サーバサイドエンジニアをしていましたが、最近はクラウドネイティブな技術やパブリッククラウドと戯れています。
さて、ABEMAやアメーバをはじめ、サイバーエージェントが運営する各メディアをプラットフォームとした広告プロダクト・広告配信システムを開発しているチームが社内にはいくつかあります。
それらを横軸で束ねる、PTA（Publisher adTech Associations）というアライアンスが立ち上がり、所属エンジニアやビジネス職のメンバーを中心に、広告配信を支える技術や広告業界のドメイン知識に関する情報共有、勉強会、[公開イベント](https://cyberagent.connpass.com/event/227271/)などを定期的に開催しています。
今回はその[PTAのアドベントカレンダー2021](https://adventar.org/calendars/6450)のエントリとして記事を書きたいと思います。

# GKE built-in cAdvisor
運用中のやや古いGKEクラスタがあり、GKEビルトインのcAdvisorがデプロイされています。このクラスタにtaintsを設定したノードプールを追加したのですが、その追加したノードプールにもcAdvisorをデプロイするためにはcAdvisorのDaemonSetマニフェストにtolerationsを設定する必要があります。
しかしながら、GKEビルトインのcAdvisorは設定をカスタムできず、マニフェストに変更を反映しようとしても、GKEのレジリエンスにより変更が無効になってしまうため、次の方法でGKEビルトインと同じ設定のcAdvisorにtolerationsを追加したDaemonSetををデプロイしました。

# kustomize
方針としては、デプロイされているマニフェストをkubectlで取得し、kustomizeで変更を加えkubectl applyします。
最終的に以下のようなファイル構成になりました。

```
.
├── Makefile
├── README.md
├── base
│   ├── daemonset.yaml
│   └── kustomization.yaml
└── overlays
    ├── daemonset.yaml
    ├── daemonset_patch.yaml
    └── kustomization.yaml
```

## base
まず、kubectlでGKEビルトインのcAdvisorのマニフェストを取得します。yqやjqなどで不要な情報を取り除き、kustomizeのbaseとします。

```
kubectl get daemonset cadvisor -n kube-system -o yaml | yq eval 'del(.status)' - > ./base/daemonset.yaml
```

`.status`以外にもmetadataなど不要な情報がありますが、yqクエリの可読性が悪くなるため、今回はkustomizeのpatchを利用して除去することにしました。

`./base/daemonset.yaml`

として保存します。

`./base/kustomization.yaml`

```yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - daemonset.yaml
```

## overlays

overlaysとしては次の2ファイルを用意します。

`./overlays/daemonset.yaml`
tolerationに対してパッチを当てます。

```yaml
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: cadvisor
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: my-cadvisor
      territory: new-nodepools
  template:
    metadata:
      labels:
        name: my-cadvisor
        territory: new-nodepools
    spec: 
      tolerations:
        ## 必要なtolerations設定を記述
```

`./overlays/daemonset_patch.yaml`
不要となる情報を除去し、コンテナ名を変更したりしています。

```yaml
---
- op: replace
  path: /spec/template/spec/containers/0/name
  value: my-cadvisor
- op: remove
  path: /metadata/annotations
- op: remove
  path: /metadata/creationTimestamp
- op: remove
  path: /metadata/generation
- op: remove
  path: /metadata/resourceVersion
- op: remove
  path: /metadata/selfLink
- op: remove
  path: /metadata/uid
```

`./overlays/kustomization.yaml`

```yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../base
namePrefix: my-
nameSuffix: --new-nodepools # シングルハイフンだと意味上の区切りが不明瞭となることがあるため -- を代わりによく使っています
patchesStrategicMerge:
  - daemonset.yaml
patchesJson6902:
  - target:
      version: v1
      kind: DaemonSet
      name: cadvisor
    path: daemonset_patch.yaml
```

## Makefile
baseの更新、kustomizeの適用、kubectl applyをMakefile化しておきます。

```bash
.PHONY update-base:
update-base:
	rm -rf ./base/daemonset.yaml
	kubectl get daemonset cadvisor -n kube-system -o yaml | yq eval 'del(.status)' - > ./base/daemonset.yaml

.PHONY build:
build:
	@kustomize build ./overlays

.PHONY diff:
diff:
	@kustomize build ./overlays | kubectl diff -f -

.PHONY apply:
apply:
	@kustomize build ./overlays | kubectl apply -f -
```

これをCIなどで回し、差分があれば反映などとしておきましょう。

# Conclusion
まとめです。
あまりケースとしてはないかもしれませんが、クラスタ上にデプロイされているリソースのマニフェストを正として、kustomizeで変更を加えて別のリソースとしてデプロイすることができました。
これまでチーム内では、helmや他のテンプレートエンジンなどを利用していたため、kustomizeはあまり使われていませんでした。
比較的新しいプロジェクトではkptを採用していたりもしますが、大きい範囲の変更にあまり向いていないなど課題感があり、kustomizeとの併用を検討しています。