こんにちは。研究開発部Architectグループ、ML Platformチームの神林です。ML Platformチームは主に研究開発部の成果を最大化するための基盤開発を行っているチームです。この記事では、Kubernetes(EKS)を導入することになった経緯や、周辺技術の選定、運用する上で必要な設計について書いていきます。
導入の経緯
研究開発部では、プロダクトのコアな技術を開発している特性上、プロダクト組織からの要求等で、絶え間なく新しい機能を提供していく必要があります。私達Architectグループのエンジニアは、新しい機能提供のためのインフラ構築を行っていますが、毎回0からアーキテクチャ設計を行っていたため、構築するためにそれなりに大きな工数をかけていました。また、デプロイ方法もCodeBuild、CodePipeline、GitHub Actions等様々なサービスを使っており、サービスそれぞれで異なるため、属人性が高く、一斉に修正パッチを適用していくようなケースでは運用負荷がかなり高くなっていました。サービスがどんどん生まれてくる中で、チームの業務がボトルネックになってしまう状況を解消するために、数ヶ月の技術検証を経てEKSを導入することにしました。
シングルクラスター、マルチテナント
Kubernetes導入にあたって、クラスターとテナントの関係をどうするかを検討しました。Kubernetesは年3回のリリースを行うことになっており*1、EKSでは直近4つのKubernetesバージョンをサポートしています。つまり最低でも1年4ヶ月程度の頻度でクラスターのアップデート作業を行う必要があります。クラスターアップデートにはかなりの労力を必要とするため、それほど知見を持っていない私達のチームでテナントごとに異なるクラスターを運用し、継続的にアップデートを行っていく体力は持ち合わせていません。できるだけ運用コストを少なくしたかったので、単一のクラスター、マルチテナント構成で運用することにしました。
Namespace
Kubernetesでは1つのクラスタの中にNamespaceと呼ばれる仮想クラスタを複数作成し、各Namespace環境を論理的に分離することができます。Namespaceの切り方は特に決まっているわけではなく、組織の事情やセキュリティ要件によって様々です。例えばチームごとに1つ用意する方法もあります。私達の運用では、あるチームが開発していたサービスを他のチームが触る可能性があるため、この方法は採用できませんでした。迷ったのですが、プロダクトの単位*2で1つずつ用意することにしました。サービス単位で作る運用も考えたのですが、Fargateを利用する場合に、Fargate Profileの作成、変更が頻繁に発生しそうだったのと、Fargate Profileデフォルトのサービスクォータを容易に突破してしまいそうだったのでやめました。Namespaceの切り方についてはGoogleが記事にしてますので参考になると思います。
マニフェスト管理
後述するようにCDとしてArgoCDを利用していますが、5 GitOps Best Practicesに従って、kubernetesマニフェストは単一リポジトリで管理することにしました。それぞれのサービスの各環境のマニフェストが1つのリポジトリに入ってくるため、数が増えてくると重複設定も増えますし管理が大変になってきます。このような重複をうまく管理するためにKustomizeやHelmを使うことを検討しました。HelmはGo Templateを使ってマニフェストの中で条件分岐やループを実現でき、表現力が非常に高いのですが、その分可読性が低くなり、宣言的に書けるシンプルなマニフェストの良さを打ち消しているように感じました。Kubernetesに慣れているメンバーが多ければ問題はなさそうですが、私達のチームは経験が浅いこともあり、表現力は劣りますが、ほぼpure yamlで書くことができるKustomizeを使うことにしました。
Kustomize - Kubernetes native configuration management
CD
KubernetesのCDについては、
- Kubernetesと親和性が高い(Kubernetes native)
- pull型のGitOps
- よくできたGUI
が実現されているArgoCDを採用しました。
Argo CD - Declarative GitOps CD for Kubernetes
ArgoCDの運用については後日公開予定です。
機密情報管理
機密情報の管理には、Kubernetesクラスター外で管理している機密情報をクラスター内で参照できるようにするExternal Secrets Operatorを使うことにしました。
AWSを使用しているため、AWS Secrets Managerを機密情報の入れ物として利用することにしましたが、サービスごとにSecrets Managerを作ると構築や維持するコストが嵩みます。これを避けるために、Namespaceごとに1つだけSecrets Managerを作ることにしました。これによってSecrets Managerへの機密情報追加、変更時の影響範囲を狭めつつ、構築コストも少なくできるようになりました。具体的な運用は下図のようになっています。
AWS Secrets ManagerとExternal Secrets Operatorのカスタムリソース SecretStore
は1:1で紐付いています。特定Namespace専用のSecrets Managerの中身のみを読み取ることができるIAM RoleをIRSAによってSecretStore
が使うServiceAccountと紐付けます。ExternalSecret
リソースは利用用途別に作っておき、AWS Secrets Managerの中に格納されている複数の機密情報のうち、使用するデータのみを設定しておきます。このように運用することで、ExternalSecret
の中身を小さくでき、依存するPodや更新時の影響範囲を狭めることができます。公式ドキュメントにある以下のケースに近いですね。
Multi Tenancy - External Secrets Operator
クラスターオートスケーラー
EKSのノードのAuto Scalingに関しては、選択肢が2つあります。一つはKubernetesが用意しているCluster Autoscaler、もう一つはAWSがOSSとしてリリースしているKarpenterです。EKSのCluster AutoscalerはノードのスケーリングにAuto Scaling Group(ASG)を使用しているため、ワークロードの要件に最適なインスタンスタイプ等を設定したASGを予め作成しておく必要があります。一方KarpenterはASGを通しておらず、Podが必要としているリソース量に応じたインスタンスを動的にプロビジョニングするので、より高速にノードが立ち上がり、コスト効率も上がります。Podに関しては、Cluster Autoscalerの場合、ノードがReadyになるのを待ってからkube-schedulerによってPodがノードに割り当てられます。Karpenterの場合、ノード作成のタイミングでPodがノードにバインドされるのでこちらもCluster Autoscalerに比べて速度が改善されています。Karpenterはまだ若いプロジェクトで実績は少ないですが、ワークロードに応じたインスタンスタイプの柔軟な選択や高速なAuto Scalingにメリットを感じ、採用しました。
Karpenter導入については後日詳細を書く予定です。
まとめ
Kubernetesの導入経緯、採用した周辺技術についていくつかピックアップ、クラスター運用設計周りについて触れました。このKuberntes基盤には Circuit
という名前を付けていて、様々なワークロードに対応できるように育てていく予定です。後日詳しい内容を公開できればと思います。
求人情報
求人情報です。私達Architectグループは北海道、東京、名古屋、大阪にメンバーが点在しています。
R&D MLOps/DevOpsエンジニア / Sansan株式会社
*1:Kubernetes Release Cadence Change: Here’s What You Need To Know | Kubernetes
*2:Sansan, Eight等。一つのプロダクトの中でサービス(APIやバッチ等)が複数動いているイメージです。