AKS 以外の Kubernetes(kind / minikube / k3s など)でも、Azure Key Vault のシークレットを Pod に安全に注入したい場面は少なくありません。本記事では「Azure Key Vault Provider for Secrets Store CSI Driver はローカル Kubernetes で使えるのか?」という疑問に結論から答え、導入に必要な前提条件、インストールと設定の具体例、そして“Key Vault に接続できない状態で Pod を再起動したらどうなるか”まで、実運用目線で整理します。
ローカル Kubernetes でも Azure Key Vault CSI ドライバーは利用できる
結論から言うと、ローカル環境の Kubernetes クラスターでも利用可能です。Secrets Store CSI Driver と、そのプロバイダー実装である Azure Key Vault Provider は、AKS 専用コンポーネントではなく、CSI を利用できる Kubernetes クラスターであれば動作します。
ただし、AKS で「何となく動いていた」部分がローカルでは自動化されないため、主に次の差分が出ます。
| 観点 | AKS でやりやすいこと | ローカル Kubernetes で必要になること |
|---|---|---|
| 認証 | マネージド ID / Workload Identity などが使いやすい | サービス プリンシパル(クライアント ID・シークレット)などを自前で用意 |
| ネットワーク | Azure 内で閉じた経路を作りやすい | ローカル→Azure への疎通(DNS/Proxy/VPN/Private Endpoint)を自分で整備 |
| 運用 | 周辺設定(RBAC/アドオン)も含めてドキュメントが豊富 | クラスタ種類(kind/minikube/k3s)に応じた差分吸収が必要 |
そもそも Secrets Store CSI Driver と Azure Key Vault Provider は何をするものか
構成は大きく 2 つです。
- Secrets Store CSI Driver:Kubernetes の CSI を使って、シークレットを「ボリューム」として Pod にマウントする仕組み(本体)
- Azure Key Vault Provider:Key Vault へ接続し、指定したシークレット/キー/証明書を取得するプラグイン(プロバイダー)
動作イメージは次のとおりです。
- Pod が起動する
- ボリュームのマウント処理で CSI ドライバーが動く
- SecretProviderClass の設定に従い、Azure Key Vault Provider が Key Vault から値を取得する
- 取得した値が Pod のマウント先(例:/mnt/secrets-store)にファイルとして配置される
重要なポイントは、標準の Kubernetes Secret のように「etcd に値が保存される」ことを前提としない設計であることです。つまり、“必要なときに取得して Pod に渡す”ことに主眼があります(もちろん後述の「同期」機能を使えば Kubernetes Secret として保持もできます)。
導入前の前提条件チェック
ローカル Kubernetes で失敗しやすいのは、機能そのものではなく「前提条件の抜け」です。最低限、次を満たしているかを先に確認すると手戻りが減ります。
| 前提条件 | なぜ必要か | チェック方法の例 |
|---|---|---|
| ローカル Kubernetes クラスター(kind / minikube / k3s 等) | CSI ドライバーを DaemonSet で配布してノード側でマウント処理を行う | kubectl get nodes / kubectl get pods -A |
| Helm | Secrets Store CSI Driver と Provider を素早く導入できる | helm version |
| Azure Key Vault | 取得元となるシークレット・キー・証明書を格納する | Azure Portal / az keyvault show |
| 認証方式(サービス プリンシパル等) | AKS のようなマネージド ID に頼れないケースが多い | az ad sp create-for-rbac など |
| ローカル→Key Vault への疎通(DNS/Proxy/VPN) | マウント時に Key Vault へ到達できないと Pod が起動できない | クラスタ内 Pod から curl / nslookup で確認 |
ローカル特有のネットワーク注意点
- 企業ネットワークのプロキシ配下では、CSI ドライバー/Provider の Pod が外部へ出られず詰まることがあります(HTTP_PROXY/HTTPS_PROXY/NO_PROXY の要否を確認)。
- Key Vault をPrivate Endpointで閉域化している場合、ローカルからは VPN/ExpressRoute 相当の経路とPrivate DNS 解決が必要です。DNS が Public に向くと到達できません。
- kind/minikube は「コンテナ/VM の中に Kubernetes がある」ため、ホスト OS の DNS やルーティングがそのまま使えるとは限りません。
認証方式の選び方(ローカルで現実的なのはどれか)
「ローカルではマネージド ID が使えない」という理解は概ね正しい一方、実務的には次の選択肢があります。
| 方式 | ローカルでの使いやすさ | メリット | 注意点 |
|---|---|---|---|
| サービス プリンシパル(クライアント シークレット) | 高い | 設定が単純で始めやすい | 長期秘密情報(client secret)を管理する必要がある |
| 証明書ベースのサービス プリンシパル | 中 | シークレットより運用ポリシーに合う場合がある | 証明書ローテーション設計が必要 |
| Workload Identity(OIDC + Federated Credential) | 中〜低(構築できれば強い) | 「クライアントシークレット無し」を狙える | OIDC Issuer の公開・Azure AD 側の構成が必要で、ローカルは難易度が上がる |
本記事では、最も導入しやすいサービス プリンシパル(クライアント シークレット)を前提に手順を示します。
Azure 側の準備(Key Vault と権限)
Key Vault に格納する値を用意する
例として、Key Vault に「DB 接続文字列」をシークレットとして保存している想定にします。
サービス プリンシパルを作成する(例)
Azure CLI を使う場合の一例です(値は環境に合わせて置き換えてください)。
# サービス プリンシパル作成(出力される appId と password を控える)
az ad sp create-for-rbac -n "sp-kv-csi-local"
Key Vault に対して必要な権限は、運用形態で 2 パターンあります。
- Key Vault のアクセス ポリシーで許可する(従来からのやり方)
- Azure RBACで許可する(統一したい組織で採用されがち)
最小権限の考え方
「参照だけ」なら基本は Get(必要に応じて List)で十分です。運用上、Key Vault 内の対象オブジェクトを厳密に制限できるか、監査ログと合わせて検討してください。
Kubernetes 側の導入手順(Helm での基本構成)
ここからがローカル Kubernetes へのインストールです。大枠は次の順番です。
- Secrets Store CSI Driver(本体)をインストール
- Azure Key Vault Provider(プロバイダー)をインストール
- サービス プリンシパル資格情報を Kubernetes Secret として登録
- SecretProviderClass を作成
- Pod で CSI ボリュームとしてマウント
Secrets Store CSI Driver のインストール例
一般的には kube-system 名前空間に入れるケースが多いです。
# Helm リポジトリ追加(名称は任意)
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm repo update
# インストール(例)
kubectl create namespace kube-system --dry-run=client -o yaml | kubectl apply -f -
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver
--namespace kube-system
実運用では、次の機能を目的に応じて有効化することが多いです。
- Secret のローテーション(Key Vault 側で値が更新されたら、Pod マウント内も更新)
- Kubernetes Secret への同期(後述:ネットワーク障害時の耐性を上げたい場合)
Azure Key Vault Provider のインストール例
# Provider の Helm リポジトリ追加(名称は任意)
helm repo add csi-secrets-store-provider-azure https://azure.github.io/secrets-store-csi-driver-provider-azure/charts
helm repo update
# インストール
helm install csi-secrets-store-provider-azure csi-secrets-store-provider-azure/csi-secrets-store-provider-azure
--namespace kube-system
インストール後、次が揃っているか確認します。
kubectl get pods -n kube-system | grep -E "secrets-store|provider-azure"
サービス プリンシパルの資格情報を Kubernetes Secret に保存する
ローカルクラスターでは、Key Vault へ接続するための資格情報を Kubernetes Secret として用意し、Pod の CSI ボリューム設定で参照するのがシンプルです。
# アプリを動かす名前空間に作成(例:default)
kubectl create secret generic secrets-store-creds \
--from-literal=clientid="YOUR_CLIENT_ID" \
--from-literal=clientsecret="YOUR_CLIENT_SECRET" \
-n default
ここでのポイントは次のとおりです。
- Secret のキー名(
clientid/clientsecret)は、Provider 側が期待する形式に合わせます。 - Secret はPod と同じ名前空間に作っておくと、マニフェストが読みやすくトラブルも少ないです。
- Git 管理に含めない(CI/CD で投入する、Sealed Secrets 等を併用する)など、漏えい対策を前提にします。
SecretProviderClass の作成例(Key Vault から取得する定義)
SecretProviderClass は「どの Key Vault から」「何を」「どういう形式で」取るかを宣言するリソースです。以下は最小構成の例です(名前は分かりやすさ優先の例)。
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-keyvault-spc
namespace: default
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "false"
keyvaultName: "YOUR_KEYVAULT_NAME"
tenantId: "YOUR_TENANT_ID"
objects: |
array:
- |
objectName: "YOUR_SECRET_NAME"
objectType: secret
設定の意図を整理するとこうなります。
| パラメータ | 意味 | ローカルでの典型値 |
|---|---|---|
| usePodIdentity | Pod Identity を使うか | false |
| useVMManagedIdentity | VM/ノードのマネージド ID を使うか | false |
| keyvaultName | 参照する Key Vault 名 | 自分の環境に合わせる |
| tenantId | Azure AD テナント ID | 自分の環境に合わせる |
| objects | 取得するオブジェクト定義(secret/key/cert 等) | 必要な分だけ列挙 |
Pod からシークレットをマウントする(最小の Pod マニフェスト例)
次の例では、Key Vault のシークレットを /mnt/secrets-store にファイルとしてマウントします。ローカル環境のサービス プリンシパル方式では、nodePublishSecretRef で先ほど作成した Secret を渡す点が重要です。
apiVersion: v1
kind: Pod
metadata:
name: demo-keyvault-csi
namespace: default
spec:
containers:
- name: app
image: busybox:1.36
command: ["/bin/sh", "-c"]
args:
- |
echo "Mounted files:";
ls -la /mnt/secrets-store;
echo "Show secret content (for demo only):";
cat /mnt/secrets-store/YOUR_SECRET_NAME;
sleep 3600
volumeMounts:
- name: secrets-store-inline
mountPath: /mnt/secrets-store
readOnly: true
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: azure-keyvault-spc
nodePublishSecretRef:
name: secrets-store-creds
適用して動作確認します。
kubectl apply -f pod.yaml
kubectl logs -f demo-keyvault-csi -n default
よくある確認ポイント
- Pod が
ContainerCreatingから進まない場合、まずkubectl describe podの Events を見ます(マウント失敗がほぼここに出ます)。 secrets-store-csi-driverとprovider-azureの Pod が kube-system で正常稼働しているか確認します。- DNS 解決の問題は頻出です。クラスタ内の一時 Pod で
nslookupやcurlを実行し、Key Vault の FQDN に到達できるか確認します。
ネットワーク障害時の挙動:Key Vault に接続できない状態で Pod を再起動するとどうなるか
ここが本題です。結論を先に言うと、「Pod の再作成(削除→作成、別ノードへの再スケジュール)を伴う再起動」では、Key Vault に到達できないと通常は起動できません。理由は、CSI ボリュームのマウントがコンテナ起動前に必須であり、マウント時点で Key Vault から値を取得できないとセットアップが失敗するためです。
「キャッシュが残って起動できるか?」に対する実務的な答え
Secrets Store CSI Driver の基本思想は、シークレットを Kubernetes のデータストア(etcd)へ恒久保存せず、Pod に必要な形で提供することです。そのため、一般的な運用の見え方としては次のようになります。
- 永続的なローカルキャッシュに頼って Pod を立ち上げる設計ではない
- Pod が消える(再作成される)と、次回マウントでは再度 Key Vault へ取得に行く
- Key Vault が到達不能なら、マウントに失敗し Pod は起動完了しない(ContainerCreating のまま、またはイベントで失敗)
一方で、「再起動」の言葉が指す範囲によって結果が変わる点は押さえておくと安全です。
| ケース | 何が起きるか | Key Vault 断でも起動できる可能性 | 補足 |
|---|---|---|---|
| コンテナだけ再起動(CrashLoop などで同一 Pod 内) | Pod のボリュームは維持されたままコンテナが再起動 | 高い | すでにマウント済みなら、コンテナは同じファイルを読める |
| Pod を削除して再作成(Deployment のローリング等) | 新しい Pod としてマウント処理がやり直し | 低い | マウント時に Key Vault へ到達できないと失敗しやすい |
| ノード再起動・kubelet 再起動により再マウントが必要 | マウントが張り直される可能性 | 低い | 環境依存だが「再マウント=再取得」のリスクとして扱うのが安全 |
| Pod が別ノードへ再スケジュール | 別ノードで新規マウント | 低い | ローカルの“以前の状態”は引き継がれない |
| Key Vault 断だが Pod は動き続けている | 既存のマウント済みファイルは残る | (起動済みなら)影響が小さいことも | ただしローテーション/更新は止まり、値が古くなる |
実際に Pod が止まるときの見え方
Key Vault に到達できない状態で「新しい Pod が起動しようとした」場合、典型的には次のようになります。
- Pod が
ContainerCreatingのまま進まない kubectl describe podの Events に「MountVolume.SetUp failed」「rpc error」「provider error」などが出る- 結果として、アプリケーションは起動以前で止まる(コンテナのログがそもそも出ない)
この挙動は厳しく見えますが、意図としては明快で、“取得に失敗したのに古い値で勝手に起動してしまう”事故を防ぎやすい(フェイルクローズ)という面があります。セキュリティ要件が強い環境では、この方が扱いやすいことも多いです。
「Key Vault に繋がらないなら起動だけはさせたい」場合の現実的な設計
要件によっては「一時的に古い値でも良いからサービスを継続させたい」ことがあります。その場合、素の CSI マウントだけに頼らず、次の設計を検討すると現実的です。
Kubernetes Secret への同期を併用する
Secrets Store CSI Driver には、Key Vault から取得した値を Kubernetes Secret として同期(同期先は etcd)するオプションがあります。これを使うと、次のメリット・注意点が出ます。
| 観点 | メリット | 注意点 |
|---|---|---|
| 可用性 | Key Vault が一時断でも、既に作られた Kubernetes Secret を使って起動できる構成を作れる | “最新ではない”値で動く可能性を許容する設計が必要 |
| セキュリティ | Pod 起動の依存先を減らせる | シークレットが etcd に保存されるため、RBAC・暗号化・バックアップ範囲の再設計が必要 |
| 運用 | アプリ側が通常の Secret マウントや envFrom を使える | ローテーション/同期失敗時の監視が必須 |
同期を行う場合は、SecretProviderClass に同期対象を定義します(例)。
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: azure-keyvault-spc
namespace: default
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "false"
keyvaultName: "YOUR_KEYVAULT_NAME"
tenantId: "YOUR_TENANT_ID"
objects: |
array:
- |
objectName: "YOUR_SECRET_NAME"
objectType: secret
secretObjects:
- secretName: kv-synced-secret
type: Opaque
data:
- objectName: "YOUR_SECRET_NAME"
key: "DB_CONNECTION_STRING"
この後、アプリ側は次のように通常の Secret として使えます(例:環境変数)。
env:
- name: DB_CONNECTION_STRING
valueFrom:
secretKeyRef:
name: kv-synced-secret
key: DB_CONNECTION_STRING
この構成なら、Key Vault が断の状態でも Kubernetes Secret が残っている限りは起動できます。ただし、同期を維持するには「CSI での取得が動く時間帯」が必要なので、ネットワーク断が長期化する場合は値の鮮度が落ちることを前提に、アプリ側で失敗時のリトライや段階的な縮退運転を設計するのが現実的です。
“起動時だけ必要”か、“常に最新が必要”かで方針を分ける
同じ「シークレット」でも性質が異なります。設計判断を誤ると、どちらの要件も満たせません。
| シークレットの性質 | おすすめ方針 | 理由 |
|---|---|---|
| 起動時だけ読めればよい(例:外部 API の固定キー) | 同期(Kubernetes Secret)も併用し、短時間の断に耐える | 古い値でも致命的になりにくい |
| 必ず最新であるべき(例:短命トークン、頻繁にローテーションされるパスワード) | CSI マウントを主とし、Key Vault 到達を強制(フェイルクローズ) | 古い値で動く方が危険(監査やセキュリティ要件) |
ローカル Kubernetes での運用を安定させるコツ
監視すべき “失敗の入口” は 3 つ
- DNS:Key Vault の名前解決ができない(Private Endpoint 環境で特に多い)
- 経路:プロキシや FW で 443 が閉じている、VPN が落ちている
- 権限:403(アクセス許可不足)や、テナント/アプリ ID の取り違え
トラブルシューティング早見表
| 症状 | よくある原因 | 確認・対処の方向性 |
|---|---|---|
| Pod が ContainerCreating のまま | Key Vault へ到達できずマウント失敗 | kubectl describe pod の Events を確認し、DNS/経路/プロキシを切り分け |
| 403 Forbidden | Key Vault のアクセス権不足 | アクセス ポリシー / RBAC の割り当てと、対象 vault/サブスクの確認 |
| 401 / unauthorized_client | クライアント ID・シークレット・テナント ID の不整合 | Secret の値・テナント・アプリ登録を再確認。期限切れの client secret に注意 |
| dial tcp / i/o timeout | ネットワーク経路断、FW、プロキシ設定不備 | クラスタ内 Pod から Key Vault FQDN へ疎通確認。必要なら NO_PROXY 調整 |
| 取得できるが更新されない | ローテーション未設定、更新間隔が長い | ローテーション機能を有効化し、要件に合う間隔に設定。更新失敗ログも監視 |
セキュリティ面の最低ライン
- サービス プリンシパルは最小権限(Get を基本)で運用する
- Kubernetes Secret(資格情報)へアクセスできる ServiceAccount を絞り、Namespace を分離する
- 同期(Kubernetes Secret)を使うなら、etcd 暗号化やバックアップ範囲、閲覧権限の棚卸しを必ず行う
- 監査観点では、Key Vault のログ(誰が何を取得したか)と Kubernetes のイベント/ログを突合できる形が望ましい
まとめ:ローカルで使えるが「起動時の依存関係」を設計に織り込む
- ローカル Kubernetes でも Azure Key Vault Provider for Secrets Store CSI Driver は利用可能。AKS 専用ではない。
- ローカルでは特に認証(サービス プリンシパル等)とネットワーク疎通を自分で整える必要がある。
- Pod の再作成を伴う再起動時は、マウント時に Key Vault に到達できないと起動できないケースが多い。永続キャッシュで自動復旧する前提にはしない。
- 「断でも起動させたい」要件があるなら、Kubernetes Secret 同期などを併用し、可用性とセキュリティのバランスを明確にする。

コメント