はじめに
この記事は DMMグループAdvent Calender 2022 の10日目の記事です。 10日目はプラットフォーム事業本部マイクロサービスアーキテクトグループSREチームの岩崎惣が担当します。
DMMプラットフォームではマイクロサービスアーキテクチャを採用しておりマイクロサービスを稼働させる環境としてKubernetes(EKS/GKE)を利用しています。Kubernetesには約120名のエンジニアが、様々なマイクロサービスを稼働させておりマイクロサービスをNamespaceで分離してマルチテナント化を行なっています。
今回の投稿では、マルチテナント環境で稼働しているKubernetes External Secrets(KES)をExternal Secrets Operator(ESO)へ移行をご紹介します。
マイクロサービスアーキテクチャやマルチテナント環境に関しては、pospomeが紹介していますので興味のある方はそちらの記事をご確認ください。
Kubernetes External Secrets(KES)の非推奨
2021年11月頃からKESは非推奨になりNode.jsで書かれていたKESをGoで書き直したESOへの移行が公式からアナウンスされました。 移行用のツールなども整備が行われ、遂に2022年7月にはKESのリポジト自体もArchiveされメンテナンス自体も行われなくなりました。 github.com
KESからESOの変更点
ESOに書き直され、新たにSecretStoreリソースが追加されました。SecretStoreはExternalSecret Podの環境変数として渡されていたAWS/GCP/Azureなどへの接続情報をKubernetesリソースとして定義出来るようになりました。 SecretStoreが追加されたことでGCPの複数プロジェクトへの接続を別々のSecretStoreとして表現できるようになりました。
■KESでのGCPへの接続設定
--- apiVersion: v1 kind: Secret metadata: name: mysecret namespace: external-secrets type: Opaque stringData: gcp-creds.json: |- ${{JSON_KEYFILE}} --- apiVersion: apps/v1 kind: Deployment metadata: name: external-secrets-kubernetes-external-secrets namespace: external-secrets spec: spec: containers: name: kubernetes-external-secrets image: 'ghcr.io/external-secrets/kubernetes-external-secrets:8.4.0' - env: - name: GOOGLE_APPLICATION_CREDENTIALS value: /app/gcp-creds/gcp-creds.json
■ESOでのGCPへの接続設定
--- apiVersion: v1 kind: Secret metadata: name: gcpsm-secret namespace: external-secrets-operator labels: type: gcpsm type: Opaque stringData: secret-access-credentials: |- ${{JSON_KEYFILE}} --- apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: # Set the same value as the projectID. name: pf-msa-test spec: provider: gcpsm: # Specify the GCP project where the Secret Manager is located. projectID: pf-msa-test auth: secretRef: secretAccessKeySecretRef: namespace: external-secrets-operator name: gcpsm-secret key: secret-access-credentials
また、Secretリソースを作成するExternalSecretと外部への接続を管理するSecretStoreはクラスター全体で共有する、「ClusterExternalSecret」「ClusterSecretStore」とNamespace内で利用する「ExternalSecret」「SecretStore」の4つに分かれたことでRBAC機能での細やかなアクセス管理が可能になりました。

ESOでのマルチテナント構成
ESOはマルチテナントでの利用が想定されておりKubernetes管理者とアプリケーション開発者の責任境界をどうするかで3パターンの例が用意されています。
選択肢は3つありますが、セキュリティの最小限の原則から言えばNamespaceリソースとしてSecretStoreを作成しアクセスを絞れる「ESO as a Service」の構成をとるのがベストと言えます。 ですがプラットフォーム事業本部では、1つのKESサービスを複数のマイクロサービスで共有して利用していたため、元々複数のGCPプロジェクトへのアクセス可能なサービスアカウントが用意されていました。 ESOの移行作業とリソースの責任境界変更を同時に実施すると大変なので、まずは移行作業に集中するためにKESと同じ方針を踏襲することとしました。 既存のKESの方針を踏襲する形で移行を検討すると「ESO as a Service」より「Shared ClusterSecretStore」構成はKubernetes管理者が管理し、ExternalSecretをアプリケーション開発者が管理するという責任境界がKESに酷似しておりExternalSecretを作成するまでのフローに変更の必要がほとんどなく低コストで移行が可能だったので今回はこちらを選択することにしました。
■Shared ClusterSecretStoreの構成
KESからESOへの移行
KESからESOへの移行方法として公式は、kes-to-esoの利用を推奨しています。 ですが、kes-to-esoを検証していくうちに変更する影響範囲がかなり大きいため別の方法を模索することにしました。
kes-to-esoの挙動
kes-to-esoを実行すると下記の流れでKubernetes ClusterのExternalSecretの変更が行われます。
- ESOのreplicasを0にしてESOを停止させる
- KES設定を参照しESOリソースのSecretStoreとExternalSecretのマニフェストを生成する
- 生成したESOのSecretStoreとExternalSecretをデプロイ
- KESのreplicasを0にしてKESを停止させる
- KESリソースのExternalSecretで生成されたSecretリソースにあるmetadata.ownerReferencesをESOリソースのExternalSecretへ書き換える
- ESOのreplicasを1にしてESOを起動させる
ArgoCDの自動同期とkes-to-esoが競合してしまう
プラットフォーム事業本部では、全てのマイクロサービスのマニフェストを管理するGitHubリポジトリとKubernetes Clusterへデプロイを行うArgoCDをSREチームで提供しています。
マイクロサービスは、GitHubリポジトリに保存されているマニフェストと自動同期を行なっておりkes-to-esoを実行してしまうとKubernetes ClusterとGitHubリポジトリのマニフェストに差分が発生してしまいArgoCD側が差分を埋めるためにGitHubリポジトリ側のマニフェストをデプロイしてしまいます。
また、ArgoCDでの自動同期のタイミング次第でSecretに設定されているmetadata.ownerReferencesをKES ExternalSecretの設定に巻き戻してしまいESOを利用したSecret管理が行われなくなってしまいます。
これを回避する方法は、下記の手順で進める必要があります。
- ArgoCDの全アプリケーションの自動同期をOFFにする。(ArgoCDのデプロイを停止する)
- kes-to-eso を実行し、クラスター上のリソースをESOに差し替える。
- GitHubリポジトリ上のマニフェストファイルをESOのものに差し替える。
- ArgoCDの自動同期をONにする。
■KES管理のSecretをESOが作成しようとした場合のエラー
# kubectl describe externalsecret.external-secrets.io -n iwasaki-so-test test-secret-eso Name: test-secret-eso Namespace: iwasaki-so-test Labels: <none> Annotations: <none> API Version: external-secrets.io/v1beta1 Kind: ExternalSecret Status: Conditions: Last Transition Time: 2022-05-27T03:25:02Z Message: could not update Secret Reason: SecretSyncedError Status: False Type: Ready Refresh Time: 2022-05-27T03:24:51Z Synced Resource Version: 1-a2ac4aecf101004be36ec1b3fae77c21 ~~~~~~~~~ Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Updated 3m26s (x9 over 7m28s) external-secrets Updated Secret Warning UpdateFailed 3m14s (x17 over 10m) external-secrets could not set ExternalSecret controller reference: Object iwasaki-so-test/test-secret-eso is already owned by another ExternalSecret controller test-secret-kes
マイクロサービス単位でExternalSecretを作り直す
ダウンタイムと影響範囲を可能な限り最小で移行する方法を検討した結果、kes-to-esoを利用せずKESとESOを並行に稼働しそれぞれのマイクロサービス単位でExternalSecretを再作成していくのがベストだと判断しました。
KESとESOはapiVersionが別になっており並行稼働していても問題はないことがわかっていたため、ExternalSecretで生成するSecret名を別々にすればmetadata.ownerReferencesの書き換えも不要になりdeploymentリソースなどのSecretを参照しているマニフェストを機械的に置き換えるのみで移行が可能で問題発生時も参照を戻すだけのためロールバックが容易というメリットがあります。 ArgoCDはデプロイを行なったDeploymentなどのリソースから生成されるPodなどのリソースに対して変更を加える機能がないため、直接Kubernetes Clusterに生成されたSecretのmetadata.ownerReferencesを書き換える必要があったのですが、ExternalSecretを作り直すことでその作業が不要になったのも今回の手法を選択した理由の一つになりました。
また、この手法を取るとESO構築とClusterSecretStore追加をKubernetes管理者が行い、ExternalSecretをアプリケーション開発者が再作成し参照を切り替えるという責任境界で作業を分けられたのも大きなメリットになりました。
kubectlコマンドでのShortname重複挙動
ExternalSecretリソースをkubectlで操作する場合、shortnameはes
になります。
ExternalSecretリソースを取得する場合は以下です。
■ExternalSecretが取得できた場合の結果
# kubectl get es -A NAMESPACE NAME STORE REFRESH INTERVAL STATUS READY pf-test secret-eso pf-msa-test 30s SecretSynced True
しかし、このコマンドで取得できたリソースはArgo EventsのEventSourceという全く別のリソースでした。 ■実際に取得された結果
# kubectl get es -A NAMESPACE NAME AGE argo-events calendar 100d argo-events gcp-pubsub 232d
ソースを確認したところKubernetesにあるAPIリソースをShortnameで呼び出す際に、APIリソースを導入した順番の早いものから呼び出されるという仕様によるものであるとわかりました。 APIリソースのマッピングを変更するには対象のCRD入れ直す、今回で言えばArgo EventsのEventSourceのCRDを一度削除して再度インストールするしか方法がありませんでした。 Argo Events入れ替えが大変であり、Shortname自体で呼び出しているユーザが少なかったのでユーザにShortnameが使えなくなった旨を周知して対応しないこととしました。
■KES削除後のapi-resourcesの状況
# kubectl api-resources NAME SHORTNAMES APIVERSION NAMESPACED KIND eventsources es argoproj.io/v1alpha1 true EventSource externalsecrets es external-secrets.io/v1beta1 true ExternalSecret
最後に
今回はExternal Secrets移行を書かせていただきましたが、マイクロサービスアーキテクトグループ SREチームではPlatform SREとしてマイクロサービス開発の開発速度や利用体験向上を目指しプラットフォームの開発・運用を行なっており一緒に取り組んでくれる方の募集もしております、ご興味ありましたらカジュアル面談もやっておりますのでぜひご検討ください。
引き続きDMMアドベントカレンダー2022もお楽しみください。