Kubernetesのマニフェストをリポジトリ管理しつつ、リソースの削除も反映したい件
はじめに
最近、Kubenetesの運用管理に携わるようになりました。
マニフェスト(*1)はYAMLで管理して、Gitリポジトリに入れています。
複数の環境を持つシステムにおいては、Kustomizeを使って、マニフェストをDRYに保つように努力しています。
そんなある日のこと、あるワークロードからExternalNameで参照しているServiceオブジェクトの定義を変えることになりました。
下のような変更です:
apiVersion: v1 kind: Service metadata: - name: my-api1 + name: my-api2 namespace: myapp spec: type: ExternalName - externalName: my-api1.example.com + externalName: my-api2.example.com
問題
……で、このYAMLを kubectl apply
で適用するわけですが、次の事実に気づきました:
- 上のYAMLを
kubectl apply
で適用すると、新しくmy-api2 Serviceが作られるが、 my-api1 Serviceは消えない
metadata.name
を変えなければそうはならないのでしょうが、名前が実体を表していないのは気持ちが悪いので、なるべくなら変えたいところです。
そうでなくとも、並行稼働期間にmy-api2 Serviceを追加し、後からmy-api1 Serviceを削除したいと思った場合も、同じ問題が発生します。*2
つまり、マニフェストから削除しても、単に kubectl apply
するだけでは削除が反映されない、ということです。
この場合の解決策は簡単で、 kubectl delete service my-api1
を叩けば話は終わりです。
実際、このときはそうしました。
しかし、例えばマニフェストをkustomizeで適用する自動化のパイプラインを組んだとき、差分に応じてこういうアドホックな処理を入れるのはつらそうだなと思いました。(まだそこまで自動化できてはいませんが。)
こういった問題の対処については、本などにもあまり載ってなさそうで、どうするのが良いのかと疑問に思いました。
そこで、自分でも少し調べつつ、とあるKubernetesのコミュニティで識者に聞いてみました。
本稿では、その回答も含めて、現在私が把握している方法を紹介します。
※公開コミュニティでの質疑内容ではありますが、どなたの回答かというのは本稿では伏せておきます。
方法
①labelsでバージョン管理する
次のようなやり方です。
- 管理対象のリソースに
version
というキーでラベルをつけておく - リソース設定を更新する場合は、
version
も更新する - 古いリソースは
version
を指定してまとめて削除する
先の例だと、次のようにラベルを付ける形になります。
apiVersion: v1 kind: Service metadata: name: my-api1 namespace: myapp labels: version: 1 spec: type: ExternalName externalName: my-api1.example.com
apiVersion: v1 kind: Service metadata: name: my-api2 namespace: myapp labels: version: 2 spec: type: ExternalName externalName: my-api2.example.com
古いリソースを削除する際は、例えば次のようなコマンドを実行します:
kubectl delete services,... -l 'version!=2'
ラベルによって処理が統一されるので、自動化もしやすそうです。
ただし、毎回再作成していいのかなど、実運用に落とす際は考慮しなければならないこともありそうだと思いました。
②kubectl apply --pruneを使う
現時点ではalpha機能のようですが、 kubectl apply
には --prune
というオプションがあり、指定すると引数として渡したマニフェスト以外のリソースを削除するという挙動になります。*3
例:
# app=nginxラベルのリソースにマニフェストを適用し、マニフェスト外のリソースを削除する kubectl apply --prune -f manifest.yaml -l app=nginx # マニフェストに書かれていないConfigMapを全て削除 kubectl apply --prune -f manifest.yaml --all --prune-whitelist=core/v1/ConfigMap
なんとなく実行するのが怖いオプションですが、商用環境で使われている方もいるそうです。 というか、KubernetesのAddon Managerの中で使われており、Addon Managerを利用しているので、結果的に使っている……ということのようです。
③Argo CDのAutomatic Pruningを使う
Kubernetes向け継続的デリバリーツールのArgo CDの自動同期(Automated Sync)機能を使うと、Gitリポジトリにマニフェストの変更をpushすると、Kubernetesクラスタに継続的に反映されるようになります。(まだ使ったことはありませんが、そういう理解です。)
この際、Automatic Pruningオプションによってマニフェストから定義が消えたオブジェクトを、自動で削除するようにできます。
このオプションはデフォルトではOFFになっているそうです。
④Terraformで管理する
各種クラウドサービスに対応した構成管理ツールのTerraformですが、KubernetesのProviderもあります。
ので、管理対象のリソースをTerraformの設定に落として、tfstateで状態を管理すれば削除も同期できそうです。
(追記)5/10 次の記事の通り、試してみました:
終わりに
Kubernetesのマニフェストをリポジトリ管理しつつ、リソースの削除も反映したい場合に使えそうな方法をいくつか紹介しました。
実際にKubernetesの運用管理をされている方は、なんらかの方法でこの問題を解決していると思います。
「自分はこうしているよ」など他の方々からも知見が得られれば嬉しいです。
余談
マニフェストをexportして管理する
kubectl get
には --export
というオプションがあり、これを付けると status
などクラスタ特有の情報を取り除いた形でオブジェクトの情報を取得することができます。*4
これを利用すれば、export/importの形で変更を適用できるのではないかと思いました。
ただし、exportされたマニフェストを kubectl apply
で適用するだけでは、削除が同期されないという問題は解消できません。
また、 --export
オプションはv1.14で非推奨とされています。
v1.18ではまだ残っていますが、今後どうなるのかはわかりません。
脚注
*1:「A manifest specifies the desired state of an object that Kubernetes will maintain when you apply the manifest」 cf. https://kubernetes.io/ja/docs/reference/glossary/#term-manifest
*2:今回はどちらかといえばこれに該当していました。
*3:https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#apply
*4:https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#get