Kubernetes as a Serviceを支えるテスト環境のContinuous Delivery

[Z Lab Advent Calendar 2021](https://qiita.com/advent-calendar/2021/zlab) 一日目の記事になります。
今年も全記事が埋められるかわかりませんが、社内で行っている多くの技術に関する記事を公開しようと思いますので最後までお楽しみください。

# はじめに

この記事ではKubernetesクラスタを提供するプラットフォーム(Kubernetes as a Service)を支えるためにそのプラットフォームの開発・検証を行っているチームのクラスタのContinuous Deliveryの事例について紹介します。

# Kubernetes as a Service(KaaS)とは

本題とは関係ありませんがどういうシステムの開発・検証環境かということを理解しておいたほうが、後半の内容がわかりやすいと思いますので軽く紹介したいと思います。
Kubernetes as a Serviceというのは、Kubernetesクラスタを提供するプラットフォームのことをこの記事では指していて、例えばGKE[^GKE]やEKS[^EKS], AKS[^AKS]のようなサービスのことです。ゼットラボ株式会社ではオンプレ環境向けにKaaSを開発し、Yahoo! JAPANに提供しています。Yahoo! JAPANでは現在このKaaSを使って数百のKubernetesクラスタの構築・運用を行っています。

[^GKE]: GKE: https://cloud.google.com/kubernetes-engine
[^EKS]: EKS: https://aws.amazon.com/jp/eks/
[^AKS]: AKS: https://azure.microsoft.com/ja-jp/services/kubernetes-service/#overview

KaaSは大規模に利用されることを前提にその状態でも適切にメンテナンスされている状態を保つ体制を作るために以下のことが実現できることを目標に開発されました。

- デプロイの再現性
    - 全く同一の構成のクラスタを再現可能であること
    - Immutable Infrastructureを採用し、作成したノードはいわゆる家畜[^1]のように管理できること
- ゼロダウンタイムなクラスタのアップグレード
    - サービスを停止することなくクラスタ自体のアップグレードが行えること
        - サービスの停止が必要になるとサービスごとに日程調整が必要となり、運用負荷が高くなることを防ぐために必須
- 低コストでできるクラスタのアップグレード
    - アップグレードコストを下げ、技術的負債を継続的に解消できる助けに
- Infrastructure as Codeでの管理
    - マニフェストファイルなどのファイルで構成管理ができ、構成状態のレビューが容易に行えること
- クラスタの数やノードの数に運用負荷が大きく依存しない運用体制が組めること
    - クラスタやノードの管理が自動化されるなどのスケールできる体制になっていること

上記を実現するためにKubernetesの [CustomResourceDefinition(CRD)](https://kubernetes.io/ja/docs/concepts/extend-kubernetes/api-extension/custom-resources/) とそのCRDを扱うカスタムコントローラを用いてクラスタの構築・管理を行うように実現されています。

[^1]: ペットと家畜: http://ec2-54-248-216-128.ap-northeast-1.compute.amazonaws.com/blog/2014/pets-vs-cattle/

詳しくはいくつかの別の発表[^2]などで紹介したのでよければそちらも見てみてください。

[^2]: https://speakerdeck.com/shmurata/thirdpartyresource-woshi-tuta-kubernetes-as-a-service-falseshi-zhuang, https://speakerdeck.com/shmurata/180715-line-meetup, https://speakerdeck.com/shmurata/how-we-accomplish-noops-by-kubernetes-operator

# 今回対象とする環境

上記PFを開発しているゼットラボ株式会社では提供機能に応じ、様々な検証方法を用意しています。その中で、今回は、クラスタの正常動作、アドオンの正常動作を確認するための検証環境の管理方法について紹介します。[^3]。
この環境はKubernetesクラスタを提供するサービスのテストを行う環境になるので、主に作成したKubernetesクラスタがきちんと意図通り動作するか、そのクラスタで同梱するアドオンが正常に動作するか、正常なアプリケーションをデプロイして正常に機能するかという部分を確認するために検証環境を構築していく必要があります。またKubernetesはアップグレードサイクルが3から4ヶ月程度で公式サポートは最新から2マイナーバージョン前までとなっており、KaaSでも同様の3つまたは4つのサポートマイナーバージョンとし、それらすべてのバージョンを検証対象としています。
そのため、テスト環境は以下のクラスタを必要とします。

[^3]: 今回記載の内容はゼットラボ株式会社のいくつかあるチームのうちの一つのチームが実施している内容であり、全てのチームがこの管理を行っているわけではありません。

- サポートしているすべてのマイナーバージョンの最新パッチバージョンのクラスタ
- クラスタの作成、アップグレードなどのe2eテスト用クラスタ

これらのクラスタを用意し、日々conformanceテストだったり、e2eテストを実行して動作検証をしたり、常時稼働させて長期間動作させた場合には問題が発生しないかといったことを常にチェックできるようにしています。

# 検証環境のContinuous Delivery

検証環境は以前はConcource CIを使って構築していましたが、数年前からArgoCDで管理するようにしています。これはクラスタ作成のような時間のかかるデプロイだったり、社内の検証環境という周辺システムの安定性がそこまで高くない環境だと自動的にリトライをしてくれるGitOps型のほうが合っていると判断したためです。

## ArgoCD

ArgoCDはGitOps型のKubernetesへのCDツールとして広く利用されています。詳しくは公式ドキュメント[^4]を参照ください。
[^4]: ArgoCD Docs: https://argo-cd.readthedocs.io/en/stable/

通常はArgoCDを使ってKubernetes上で動作するアプリケーションをGitOpsで管理しますが、KaaSの場合はKubernetesクラスタも一つのKubernetesリソースとして管理できるためKubernetesクラスタ自体の管理もArgoCDを使って管理することが出来ます。
そして当然それらのクラスタの上で動作するアドオンやアプリケーションの管理もArgoCDを用いて管理しています。
イメージとしては以下のようにArgoCD用のクラスタからKaaSを管理するコントローラがデプロイされているクラスタへKubernetesクラスタを表すリソースをデプロイし、各クラスタが作られ、そのクラスタにもArgoCDからアドオン[^6]やアプリケーションをデプロイします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100565/019e4b07-e27d-9ed6-f639-83260585af1b.png)

[^6]: CoreDNSやIngress Controller, metrics-serverなどのクラスタの動作に必要なアドオンです。図だと省いていますが実際にはアドオンをまとめて管理するコンポーネントがいて、それをデプロイする形で実現しています。

この環境の実現には最初はArgoCDとCI+自作のシェルスクリプトだけ実現していましたが、自作のシェルスクリプトのメンテナンスが辛くなってきたこともあり以下の拡張プラグインやツールを導入しています。それぞれ紹介します。

### ApplicationSet

上記のように非常に多くのクラスタがあり、それらに同じようなアプリケーションをデプロイすることが多いためテンプレート化してApplicationリソース[^7]を作っていましたが、ApplicationSetという複数のAplicationリソースを管理するツールを利用することでこのテンプレートとシェルスクリプトを削除でき、管理を楽にできるようにしています。
ApplicationSet自体は別の記事[^8]で紹介しているのでよければそちらを参照ください。

[^7]: ArgoCDのデプロイする単位をまとめたリソースのこと
[^8]: https://qiita.com/shmurata/items/a6ee4193e2d2c8a2d34c

このツールを使って、各クラスタへ同じApplicationを作成するケースや、Kubernetesクラスタのリソースを管理するApplicationなどをまとめています。デプロイする内容で一部異なる部分はHelmを使って差分を吸収しています。
他にも色んな部分で利用していますが、今回はそのあたりは省略して次回機会があれば自分がコントリビュートした部分も含めて紹介したいと思います。

### Argo CD Image Updater

こういった検証環境だと開発の最新版を適応したいことがよくあると思います。我々も同じように最新のアドオンだったり、e2eテストを適応したいということがあり、これもCI上でリソースのアノテーションなどにcommit hashを入れるなどの対応をしていましたが、Argo CD Image Updaterを使うことでイメージの更新を自動検知して、最新版に差し替えることが出来ます。
使い方や詳細な動作は公式ドキュメント[^9]を参照してください。

[^9]: Argo CD Image Updater: https://argocd-image-updater.readthedocs.io/en/stable/

### argocd-vault-plugin

SecretについてはGitOpsで管理すると例のごとくGit上に保存され秘密情報が公開されてしまいますので、別の仕組みで管理する必要があります。
一般的によく聞く方法としては以下の方法があるかと思います。

- Secretリソースの中身を暗号化してGitにコミットする方法
    - sealed-secrets: https://github.com/bitnami-labs/sealed-secrets
- 別のCRDなどで管理し、別のシークレットストアから取得してSecretリソースを生成する方法
    - kubernetes-external-secrets: https://github.com/external-secrets/kubernetes-external-secrets
- デプロイ時にシークレットの値を取得して差し替える方法
    - argocd-vault-plugin: https://github.com/IBM/argocd-vault-plugin

どの方法も一長一短ありますが、我々は最後のargocd-vault-pluginを使った方法を利用しています。
これは暗号化された状態であってもシークレット情報を公開したくなかったこと、シークレットストアへのアクセス管理が面倒なのでArgoCDという一つのところにまとめたかったという部分が理由になります。

# 運用して見た感想と今後

Concource CIなどのいわゆるCIOpsからGitOpsに移行して感じた点は以下の点です。

- デプロイパイプラインが単純化された
  - できることが少なくなったので良くも悪くも複雑なことができなくなった
- 一本のデプロイパイプラインだけを把握したい場合複雑になった
- 周辺システムの障害などで失敗時した際の復旧が簡単になった
  - 今までは原因を特定して、パイプライン全体を把握した人が影響範囲などを判断しながら手動でリトライが必要だった
  - リトライされることが前提になっているので、全体を把握していないくてもとりあえずリトライすることで復旧できるようになった
- レビュー周りは元々マニフェストファイル自体はGitにコミットして管理していたので大きな変更点はなかった
- 一時的にテストなどで変更を加えてしまったことを咎められるようになった

CD周りは今回紹介したように整理されてきて管理しやすい状態になってきたと感じていますが、マニフェストの自動生成などを含むCI部分にはまだ複雑さが残っている部分があり、そちらにCDのしわ寄せがいってしまったように感じています。来年はCI周りも宣言的に管理できないかなどを検討してみたいと思っています。