2020年でKubernetesクラスターを3回再構築した話

こんにちは、Tier IVでMLOpsを担当している澁井(しぶい)です。
Tier IVには2020年9月に入社して、現在社歴2ヶ月です。Tier IVでは自動運転のための機械学習システムとオペレーションを開発しています。MLOpsでは機械学習のパイプライン開発から基盤構築、運用までをエンドツーエンドに行うのですが、MLOpsエンジニアは現状私一人で、寂しく気ままに開発しています。
前職ではECの会社でMLOpsとEdge AIを担当していました。基盤の一部にAWSKubernetesマネージドサービスであるEKS (Elastic Kubernetes Service) を使っていたのですが、退職直前の2020年7月にそのKubernetesクラスターを再構築しました。2年越しで使っていたKubernetesクラスターが2クラスターあり、Kubernetesコンポーネントがいろいろと古くなっていてアップデートができない状態だったため、再構築した次第です。結果として前職最後の仕事がKubernetesクラスターを2個作り直すというものになりました。作り直したKubernetesクラスターが元気に動いているか気になるところです。

f:id:cvusk:20201104110828p:plain
さよならk8s

Kubernetesクラスター再構築はもう当面やらないだろうと思っていたのですが、つい最近またKubernetesクラスター(EKS Fargateクラスター)再構築をおこないました。何故Kubernetesクラスターを作り直さなければならなかったのか、AWS EKSやKubernetesの歩みとともに、振り返ってみたいと思います。

用語

Kubernetesを差す用語について、以下のように使い分けます。

Kubernetesと私

私はKubernetesを2016年頃から触り始めており、もうかれこれ4年もKubernetesとともに歩んできています。その間に私は転職を3回経験しましたが、仕事を辞めてもKubernetesだけは辞めずに使い続けています。検証目的を含めれば、私が作ってきたKubernetesクラスターの数は100を下らないと思います。

f:id:cvusk:20201104110853p:plain
k8sと私

Kubernetesはコンテナをマルチサーバやクラウド環境で効率良く運用するためのコンテナオーケストレータです。Dockerに代表されるコンテナをサーバで動かし、複数のコンテナを複数のサーバで連携させるためのインフラや管理層を抽象化したミドルウェアKubernetesになります。KubernetesGoogle社内のBorgというコンテナオーケストレータプロジェクトが原点となっており、ここ数年で急速に発展、浸透してきているインフラOSSになります。私がKubernetesを使い始めた頃は構築スクリプトも用意されておらず、自分でネットワーク設計含めて手動でインストールする必要があったのですが、最近ではAWSGCP、MS Azure等の主要クラウドでマネージドサービスが提供され、簡単に使い始められるようになっています。コンテナでシステムを作る用途ではとても便利で拡張性も高いミドルウェアなので、未経験の方はぜひ一度使ってみてください。

f:id:cvusk:20201104110920p:plain
k8sの全体像

Tier IVではメインのインフラにAWSを使っています。その一部でKubernetesをマネージドサービスのAWS EKSで稼働させています。前職ではGCPAWSで開発しており、私はAWS EKSの経験もあります。実は前職の最後に作り直した2クラスターはEKSクラスターでした。作り直しの際にAWS EKSの使い方をいろいろと調べていたこともあり、Tier IVでもAWS EKSを使うことができて、とても馴染みやすかったです。再構築するまでは・・・。

ここ数年でKubernetesはどう変わったか

OSSとしてKubernetesの開発が始まってから6年ほど経っていますが、その間に多くの変化がありました。構築方法としてkubeadmが導入されたり、セキュリティとしてPod Security PolicyやNetwork Policyが導入されたり、LinuxだけでなくWindows Nodeがサポートされたりしています。Kubernetesの周辺機能でも、PrometheusとGrafanaによるログ監視機能、Istioによるサービスメッシュ、Knativeによるサーバレス基盤が開発され、KubernetesがProduction Readyなシステム基盤に成長しています。
Kubernetesの機能と同じくらい重要なのは、Kubernetesの実行環境としてマネージドサービスが主流になったことだと思います。インフラの主流がクラウドになっていったことと同様に、Kubernetesの主流もクラウド上のマネージドサービスになりました。クラウド上のマネージドサービスとして具体的なものを挙げると、AWSであればEKSやEKS Fargate、GCPであればGKE (Google Kubernetes Engine)、MS AzureであればAKS (Azure Kubernetes Service) です。それぞれのクラウドベンダーが自社クラウドに適したインテグレーションを進めており、Kubernetesのうまい使い方はマネージドサービスのうまい使い方になっていってます。
特にAWSではKubernetesのマネージドサービスとしてAWS EKSとEKS Fargateという2通りを提供しており、用途に合わせた選択が可能になっています。AWS EKSはKubernetesのコントロールプレーンのみをAWSが管理する方式で、Worker NodeやPod等のリソースはユーザが操作します。対してEKS Fargateではコントロールプレーンに加えてWorker NodeまでAWSが管理し、ユーザはノードレベルを気にせず純粋にKubernetesリソースの管理だけに集中できるサービスになっています。個人的にはノードインスタンス含めて管理したいので、AWS EKSのほうが好きですが、インスタンス管理が面倒であればEKS Fargateも良い選択肢です。

f:id:cvusk:20201104110940p:plain
AWS EKS

AWS EKSではKubernetesクラスターの構築にはeksctlというツールを使います。ノードはAWS EC2を基盤とするため、オンデマンドインスタンスとスポットインスタンスをワークロードに応じて使い分けることでコスト削減ができます。メインのノードにはオンデマンドインスタンスによるマネージドノードを使いつつ、ジョブ等の一時的なタスクにはスポットインスタンスで立ち上げるというものになります。EKSクラスターのpodからAWSの他サービス(S3やRDS等々)を参照、操作するには、AWSのIAM (Identity and Access Management) ポリシーと連携するためにOIDC (Open ID Connect) を使用します。AWSでは各サービスへのアクセス許可をIAMで管理しますが、Kubernetesクラスターではリソースへのアクセス許可をRBAC (Role-based Access Control) で管理します。AWS IAMとKubernetes RBACを連動させる仕組みがOIDCになります。
これらのツールや構成は最近1,2年で整備されていったものです。それ以前に構築されたEKSクラスターでは導入できないツールもあります。たとえばeksctlというEKSクラスターの構築、管理ツールは、最初からeksctlでEKSクラスターを構築していないと使えないツールです。eksctlの登場以前に構築したクラスターにeksctlによるクラスター管理を追加導入することはできません。eksctlはyamlマニフェストベースでEKSクラスターの構成を管理できるため、Infrastructure as Codeでクラスターを管理しようとすると必須のツールになります。EKSクラスターのIAM設定やノード管理もeksctlで実施できるため、EKSクラスター運用には欠かせないツールと言えます。
Kubernetesクラスターにデプロイされたリソースの管理にはWeave Fluxを使います。FluxはAWS EKS のKubernetesクラスターでGitopsを実現するツールで、Kubernetesクラスターのflux namespaceでリソースとして稼働します。FluxのDaemonsetがレポジトリをウォッチして、レポジトリにKubernetesマニフェストの更新があると自動的にKubernetesリソースに反映する仕組みです。Fluxは途中導入可能ですが、マニフェストがGit管理されていない状態から追加していくには手間がかかるのが実情です。
EKSクラスターを使うにはeksctlやFluxだけでなく、EKSクラスターと連携するAWSサービスを管理することも重要です。S3やRDSにデータを格納する構成や、外部からのアクセスを制限するセキュリティグループの適用等々、クラウドKubernetesを使うためには周辺サービスとの連携が不可欠です。Tier IVではAWSの利用サービスはCloudFormationで構成管理し、Githubマニフェストを保管する運用にしています。マニュアルで作ったAWSリソースをCloudFormation管理に置き換えることは困難であるため、やはりリソースを作る時点からCloudFormationを使うほうが便利です。
EKS クラスターをInfrastructure as Codeで管理するためにはeksctlによるKubernetesクラスター管理、FluxによるKubernetesリソース管理、CloudFormationによる周辺機能の管理が必要になるのです。

f:id:cvusk:20201104110955p:plain
EKSとIaC

Kubernetesクラスター再構築

前置きが長くなりましたが、AWS EKSを効率的に安定運用するためには、eksctlやFluxを導入した運用設計を行う必要があります。既存のEKSクラスターがそれらのツールに対応していない場合、クラスター自体を作り直すことになります。
Tier IVでもどうやって作ったのかドキュメンテーションされていない(=Infrastructure as Codeになっていない)EKS Fargateのクラスターがありました。社内向けのツールが稼働していることはわかっていました。EKSクラスターを運用管理していくために、私はクラスターを作りなおすことにしました。作り直したEKSクラスターをそのまま、今後私がTier IVで作る機械学習基盤に流用してしまおうという下心もありました。
私個人としては2020年に入って3回目(全部AWS EKS)のKubernetesクラスター再構築になります。前2回までのEKSクラスター再構築によって、AWS EKSを構築するために必要なツールは認識していました。会社やシステムの違いを吸収すれば簡単に再構築できると思っていました。しかしこの認識は裏切られました。

どう動いているのかわからないソフトウェア

最初に直面した課題は、既存のEKS Fargateクラスターで動いているソフトウェアがどう動いているのかわからないというものでした。EKS Fargateクラスター自体は半年くらい前に作られたものだったのですが、運用担当者がいない状態でした。急成長するテック・スタートアップでは稀によくあることですが、まずは作ることが優先で、運用は後回しになっている状態でした。
現状のKubernetesクラスターで動いているリソースや構成を整理する必要がありました。各namespaceで kubectl get **kubectl describe ** を叩きまわり、Kubernetesクラスター上で動いているリソースをyamlファイルにマニフェストとして出力することからはじめました。結果として以下のような構成であることが判明しました。

f:id:cvusk:20201104111014p:plain
謎のEKS

Kubernetesクラスターで動くWebシステムとしてはオーソドックスなアーキテクチャーです。DjangoベースのWebフロントとRDS MySQLで構成されており、Django PodからPython KubernetesクライアントでJobを起動するワークフローになっています。マニフェストさえあれば再構築したEKSクラスターでも動かすことができそうに見えます。

謎の課金

インフラ再構築の基本として、ノードインスタンスとコンテナを整理していきました。既存のEKS Fargateクラスターで稼働しているPod数は3で、resource requestもCPUで合計5gに届かないくらいでした。EC2インスタンス換算でm5.largeタイプが3台程度で済むはずで、オンデマンドインスタンスでフル稼働しても$80以内に収まるものです。しかしなぜかAWSの課金コンソールでは月間$6,000程度発生する状態になっていました。
課金原因を調査すると、EKS FargateクラスターでKubernetes Nodeが100台ほど配備されていました。kubectl describe nodes でノード内のリソースを見てもPodは配備されておらず、不要なノードが延々と稼働している状態でした。EKS Fargateでは1ノードに1Podが稼働する構成になっていますが、Kubernetes Jobを起動すると、Jobを手動で消さない限り、CompleteしたPodがノードを専有し続ける仕様のようでした。Jobを起動すればするほど課金が膨らむ状態になっていたのです。前職でも似たようなKubernetes無駄課金がありましたが、転職先でも同じものを見るとは!?
この仕様はEKS Fargateクラスター特有のもので、EKSクラスターであれば発生しないものです。今回のEKSクラスター移行で不要ノードの問題は解決しました。

でもいろいろ書き換える必要があった

ソフトウェアのコードやKubernetesマニフェストを調べていってわかったことは、EKSクラスターだけでなくシステムの構成も見直したほうが良いということでした。具体的には以下のような課題が挙げられます。

  1. インターネットからHTTPでアクセスされている。
    → Route53でドメインを取ってACMで証明書を作って、HTTPSに変更する。
  2. RDSのマスターユーザパスワードがGithubのプライベートレポジトリに入っている。
    → AWS Secret Managerに移管する。
  3. 監視がない。
    → Datadog監視を追加する。
  4. default namespaceにデプロイされている。
    → プロダクト専用のnamespaceを切る。

これらをそれぞれで作成されるリソース単位でブレークダウンし、eksctlで管理するもの、Fluxで管理するもの、CloudFormationで管理するものに分類しました。EKSクラスター自体はeksctlのyamlマニフェストKubernetesリソースはFlux管理するKubernetesマニフェストになります。それ以外はすべてCloudFormationマニフェストにします。eksctlもKubernetesも、部分的にホストクラウドを操作する機能が備わっています。たとえばIngress ControllerはKubernetesからAWSのALBを配備します。このALBはCloudFormationの管理外になります。しかしALBへのアクセス制御は必要なため、CloudFormationでセキュリティグループのみ追加する必要があります。
新EKS クラスターの構築は以下の手順で実行しました。

  1. CloudFormationでAWS VPCとサブネットを作成する。
  2. eksctlでEKSクラスターを構築する。
  3. KubernetesクラスターにFluxを導入する。
  4. CloudFormationでRDSをスナップショットから作成する。
  5. Fluxで必要なKubernetesリソースをデプロイする。
  6. CloudFormationでRoute53、ACMからドメインと証明書を作成する。
  7. CloudFormationでEKSクラスター、ALB、RDSへのアクセスをセキュリティグループで制限する。

eksctl、Flux、CloudFormationで管理するリソースとコンフィグレーションが一部重複するため、ツールを行ったり来たりしました。大変でしたが、新EKSクラスターを無事本番システムとして安定運用できる状態にしました。

まとめ

長くなりましたが、AWS EKSクラスターを年間で3回作り直した経験談でした。Kubernetesはここ数年で急速に発展したインフラMWであり、マネージドサービス含めてアーキテクチャーの変更が度々発生してきました。最近では仕様も安定しており、破壊的な変更も少なってきています。そのため、Kubernetes黎明期にチャレンジ精神で作ったKubernetesクラスターは再構築のタイミングにきているものがあると思います。ちょうど私はそのタイミングに3回遭遇しました。システム移行プロジェクトは生産性がなく、好きでないエンジニアも多いと思います。しかし私としては、地図のないダンジョンを探検しながら攻略していくゲームっぽさがあり、嫌いではなかったりします。
長くなりましたが、読んでいただきありがとうございました。


Tier IVでは、様々なエンジニアを募集しています。もしご興味があればカジュアル面談も可能ですので以下のページからコンタクトいただければ幸いです!