
こんにちは、2023年入社の片岡歩夢(ayumin)です。
今年の8月に新卒研修を終え、プラットフォーム事業本部マイクロサービスアーキテクトグループSREチームで働き始めて4カ月になります。
弊チームの業務内容については、チームの先輩である中井さんの記事から引用します。
SREチームは、DMMプラットフォームを開発・運用している120人規模の開発組織にマイクロサービスプラットフォームを提供しています。
マイクロサービスプラットフォームは k8s とその周辺のエコシステムにて構築されているアプリケーションプラットフォームで、弊チームが開発・運用を行い、プラットフォーム利用者はアプリケーション開発に集中できるようになっています。
今回は、チームに入ってから行った業務のうちの1つをご紹介します。
なぜGKEクラスタ認証情報のローテーションが必要だったのか
まず前提知識として、クラスタ認証情報とは何なのかについて説明します。
クラスタ認証情報とは、Kubernetesクラスタが持つKubernetes APIというREST APIエンドポイント(以下APIエンドポイント)にアクセスするための認証情報のことです。Kubernetes APIクライアント(以下APIクライアント)は、この認証情報をAPIエンドポイントにアクセスするために使用しています。
マネージドサービスであるGoogle Kubernetes Engine (以下GKE) でも同様に、この認証情報を使用してアクセスをする必要があります。
GKEにアクセスするためのGKEクラスタ認証情報は、クラスタルート認証局(以下CA)によって署名されています。そのためCAが期限切れになると認証情報の署名が無効になり、APIクライアントが持つクラスタ認証情報は効力を失い、APIエンドポイントにアクセスできなくなります。
これを避けるためGKEでは、新たなCA・新たなIPでAPIエンドポイントをクラスタに追加する仕組みが備わっています。この新たなAPIエンドポイントには、古い認証情報を持つAPIクライアントではアクセスできないため、APIクライアントの認証情報を更新する必要があります。
しかしGKEで新たなAPIエンドポイントを作成しても、APIクライアントが持つクラスタ認証情報が勝手に更新されるわけではありません。手動でAPIクライアントの認証情報の更新を行い、新たなAPIエンドポイントに対応させる必要があります。
もしここでAPIクライアントのクラスタ認証情報の更新漏れがあると、クラスタへの操作ができなくなり障害に直結する恐れもあります。
以上の理由から、CAの期限が来る前にGKEに新しいAPIエンドポイントを作成し、APIクライアントの認証情報を漏れなく更新する、「GKEクラスタ認証情報のローテーション」という作業を行う必要があります。
認証情報ローテーションの具体的な手順
ここからは、具体的なGKEクラスタ認証情報ローテーションの手順について説明します。
GKEのクラスタ認証情報を更新するには、以下のような手順を踏む必要があります。
- ローテーションを開始する : コントロール プレーンが、元の IP アドレスに加えて新しい IP アドレスでもサービスを開始します。新しい認証情報がワークロードとコントロール プレーンに発行されます。
- ノードを再作成する : GKE によりクラスタノードが再作成されます。これにより、ノードが新しい IP アドレスと認証情報を使用するようになります。
- API クライアントを更新する : ローテーションを開始した後、
kubectlを使用して開発マシンなどのクラスタ API クライアントを更新し、新しい IP アドレスを使用してコントロール プレーンと通信します。- ローテーションを完了する : コントロール プレーンが元の IP アドレスでのトラフィックの処理を停止します。古い認証情報は取り消されます。
ローテーションを開始すると、GKEクラスタには新しいIP・新しい認証情報でAPIエンドポイントを作成します。
また、ローテーション完了時に古いAPIエンドポイントと認証情報が削除されるため、ローテーション開始から完了までは古い認証情報を持つAPIクライアントもクラスタにアクセスすることが可能です。これを利用して、古い認証情報を持つAPIクライアントのダウンタイムを最小限にしながら、クラスタ認証情報を更新します。
ここで、手順1と手順4のローテーション開始・終了はgcloudコマンドを使ってクラスタ(厳密にはクラスタのコントロールプレーン)に行うもので、コマンドを実行するだけの作業になります。
また、手順2のノード再作成はローテーション開始コマンドを実行すると、GKE側で1週間かけて自動的に行われるため、これも特に対応の必要はありません。
今回主に詳細な調査や対応が必要だったのは、手順3のAPIクライアントの更新 です。ここに時間をかけた理由として、クラスタ認証情報更新は他チームが責任を持つAPIクライアントに影響を及ぼす可能性が高い業務であったことが挙げられます。
APIクライアントは、SRE以外でも様々な開発チームが使用しており、そこに障害など重大な影響を与える危険がありました。
DMMのプラットフォームには、前述の通り既に多くのマイクロサービスが動いており、そのマイクロサービスを開発する120人規模の開発者がいます。これだけの開発者が使用するプラットフォームを管理するSREは、各チームに影響を及ぼす範囲を正しく切り分け、対応が必要なチームに周知・対応を依頼する責任があります。その切り分けのために、慎重に調査を進めながら対応が必要なAPIクライアントの絞り込みを行いました。
対応が必要なAPIクライアントの絞り込み
APIクライアントの分類分け
APIクライアントは数多く存在しますが、認証方法で分類すると、ほとんどのAPIクライアントが4パターンのどれかの方法で認証を行っていると考えられます。
この4パターンのうち、どれに対して認証情報の更新対応が必要なのかを調査することで、具体的な影響範囲を絞り込むことにしました。
- クラスタ外部から認証を行うパターン
- 1a 開発者のローカルPC環境
- 1b GitHub Actions
- クラスタ内部から認証を行うパターン
- 2a Service Account が付与されたPod
- 2b Service Account が付与されていないPod

まず公式情報として、GKEのクラスタ認証情報の更新の公式ドキュメントを見ると以下のような記述があります。
クラスタ外のすべての API クライアント(デベロッパー マシンの
kubectlなど)を更新する必要があります
(参考:https://cloud.google.com/kubernetes-engine/docs/how-to/credential-rotation?hl=ja#updating_api_clients)
しかし、この表記は少々不正確で、厳密にはクラスタ内外問わず自動的に新しい認証情報に追従できない全てのAPIクライアント のクラスタ認証情報を更新する必要があります。
逆に、自動的に新しい認証情報に追従できるAPIクライアントは、対応が不要なAPIクライアントと言えます。対応不要なAPIクライアントは調査の必要がなくなるため、先に対応が不要なAPIクライアントを洗い出すことにしました。
対応が不要なクライアント
クラスタ内のPodをService Accountを用いて認証しているパターン(2a)
ここで自動的に新しい認証情報に追従できるパターンとして、GKEのService Accountを用いて認証しているパターン が挙げられます。
Service Accountを使用した認証では、コントロールプレーンがService Account用の認証トークンをSecretリソースとして作成し、それをPodのボリュームにマウントすることで認証を実現しています。
APIエンドポイントの情報を持つコントロールプレーンが認証トークン作成していることからもわかる通り、Service Account用の認証情報トークンの内容は、CAの更新に合わせて自動的に更新されます。
以上から、当パターンの場合は対応不要であると判断し対応範囲から除外しました。
クラスタ外からGitHub Actionsを用いて認証しているパターン(1b)
実はもう1つ、自動的に新しい認証情報に一部追従できるパターンがあります。GitHub Actionsを用いて認証をしているパターン です。
このパターンがなぜ自動的に新しい認証情報に追従できるのかを説明する前に、前提知識となるクラスタ外からGKEのクラスタに接続する方法について説明します。
クラスタ外からGKEクラスタに接続するためには、gcloudコマンドを使用し、Google Cloud Platform (以下GCP) の権限を用いてGKEのクラスタ認証情報を取得する必要があります。例えば、ローカルPCからGKEにアクセスする場合は、初めてGKEクラスタにkubectlでアクセスする前に一度gcloudコマンドを使用して、クラスタ認証情報を取得することになります。
一方、GitHub Actionは毎回新たなインスタンスで実行されるため、ローカルPCと同じように認証情報を保存できません。そのため多くの場合、GKEクラスタにアクセスするためのcredentialを取得するget-gke-credentialsというactionをワークフロー内に記述し、ワークフローが実行されるたびにこのactionを実行します。
すなわち、GitHubのSecretsなどインスタンス外部の永続的に保存ができる場所に認証情報を保存しない限り、実行のたびクラスタ認証情報を新たに取得し直していることになります。
このactionの存在と開発者のユースケースから、GitHub Actionsを使用するパターンでも自動的に新しい認証情報に追従できると判断し、対応範囲から除外しました。
対応が必要なクライアントと対応方法
ここまでで、対応が必要な可能性がある(=自動的に新しい認証情報に追従できない)パターンは1a, 2bの2つに絞り込めました。
- クラスタ外部から認証を行うパターン
- 開発者のローカルPC環境
GitHub Actions(対応不要)
- クラスタ内部から認証を行うパターン
Service Account が付与されたPod(対応不要)- Service Account が付与されていないPod
ここからは、対応が必要な可能性があるパターンをより正確に調査し、どのような対応が必要かを決めます。
クラスタ外から開発者のローカルPCの認証情報を用いて認証するパターン(1a)
ローカルPCの認証情報を用いて認証するパターン に関しては以下の理由から更新対応が必須であることが判断できていました。
- GKEのクラスタ認証情報の更新の公式ドキュメントで、明示的に対応が必要なケースとして紹介されていること
- ローカルPCからAPIクライアントを使うユースケースが存在していること
ローカルPCの認証情報更新は、gcloudコマンドでクラスタ認証情報を再取得することで実行可能なため、以下のコマンドの実行を開発者に依頼することにしました。
gcloud container clusters get-credentials <クラスタ名> \ --region <リージョン名> \ --project <プロジェクト名>
クラスタ内のPodをService Accountを付与せずに認証を行うパターン(2b)
最後に、クラスタ内のPodをService Accountを付与せずに認証を行うパターン について考えます。このパターンは自動的に新しい認証情報に追従ができないため、もしマイクロサービスプラットフォーム内に存在すれば対応が必要です。
しかし、クラスタ内からあえてService Accountを使用せずにAPIエンドポイントにアクセスする、という稀なユースケースでしか存在しないパターンであり、プラットフォーム内には存在しない可能性があり調査をしました。
結論から言うと、DMMのプラットフォームにおいてこのパターンは1件だけ存在しました。それが、Kubernetes用のCI/CDツールであるArgoCDの認証です。
本来、ArgoCDはService Accountを用いてクラスタ内部から認証を行うため、クラスタ認証情報の更新対応は不要です。しかし、DMMのマイクロサービスプラットフォームでは特殊なArgoCDのユースケースがあり、そのユースケースを満たすためにArgoCDに直接Secretとして認証情報を保存していました。
ArgoCDはSREが管理しているツールだったため、ArgoCD CLIを用いて今までと同じクラスタ名で新しい認証情報を追加し、古い認証情報はクラスタごと削除する方針で対応することにしました。
まとめ
DMMのマイクロサービスプラットフォームを支える技術であるGKEのクラスタ認証情報のローテーションを行いました。その際、本作業の影響範囲の把握が必要になったため、以下のようなフローで調査を行いました。
- APIクライアントを認証方法で分類分け
- パターンごとに対応の要不要の調査
- 対応が必要な場合どのような方法で対応するかを調査
配属されてから初めての大きなタスクであり、難しい部分も多くありましたが、チームの方々の助けもあり結果的にトラブルや障害なく終えられ一安心しています。
Kubernetesの認証の仕組みやService Accountがどのように認証を実現しているかなど、ハードスキル面でももちろん学びがありましたが、マイクロサービスアーキテクトのSREチームとしての立ち回り、チーム内でどのように調査結果を伝えるか、などソフトスキル面でも非常に学びの多い業務となりました。
まだ配属から4カ月程度ですが、これからも両方のスキルを伸ばしながら頑張っていきたいと思います。ご精読ありがとうございました。
宣伝
マイクロサービスアーキテクトグループSREチームでは、DMMを支えるプラットフォームを開発・運用できる方を募集しております。