cloud-sre

GOOGLE_APPLICATION_CREDENTIALS を Kubernetes Pod に安全にマウントする手順

Google service account JSON を AWS Secrets Manager に保存し、Secrets Store CSI Driver で Pod にマウントし、Java から GOOGLE_APPLICATION_CREDENTIALS で読むための汎用手順。

Jun 26, 2026
KubernetesEKSAWS Secrets ManagerSecrets Store CSIFirebaseGitOps

この構成では、Google service account JSON を Git に入れない。外部の secret store に保存し、Pod には read-only のファイルとしてマウントする。アプリケーション側では GOOGLE_APPLICATION_CREDENTIALS をそのファイルに向ける。

Firebase Admin SDK、Google Cloud client library、または Java の GoogleCredentials.getApplicationDefault() を使う backend service なら、この形で扱える。

以下の例はすべて匿名化したもの。適用するときは placeholder を自分の環境に置き換える。

用語の説明

用語意味
GOOGLE_APPLICATION_CREDENTIALSGoogle client library が service account JSON の場所を見つけるために使う環境変数。
Secrets Store CSI DriverAWS Secrets Manager など外部 secret を Kubernetes Pod にファイルとしてマウントする CSI driver。
SecretProviderClassCSI driver に、どの外部 secret を取り、Pod 内でどのファイル名にするかを伝える Kubernetes custom resource。
GitOpsKubernetes manifest を Git に commit し、ArgoCD などの controller が cluster に同期する運用。

目標の形

コンテナの runtime では、この環境変数が入っている状態にする。

GOOGLE_APPLICATION_CREDENTIALS=/mnt/secrets-store/google-application-credentials.json

Pod 内には、このファイルが存在する。

/mnt/secrets-store/google-application-credentials.json

service account JSON 自体は外部 secret store にだけ置く。GitOps repository には入れない。

1. JSON を AWS Secrets Manager に保存する

環境と用途ごとに secret を分ける。名前から owner と用途が分かるようにしておく。

aws secretsmanager create-secret \
  --region <region> \
  --profile <profile> \
  --name <env>/shared/google_application_credentials_<app> \
  --description "Google service account credentials for <app>" \
  --secret-string file://./google-service-account.json

JSON を Kubernetes manifest に貼らない。JSON file も repository に commit しない。

secret がすでにある場合は、新しい version を入れる。

aws secretsmanager put-secret-value \
  --region <region> \
  --profile <profile> \
  --secret-id <env>/shared/google_application_credentials_<app> \
  --secret-string file://./google-service-account.json

2. Workload Role に Secret を読む権限を付ける

Pod が使う IAM role、または CSI driver が使う IAM role に、対象 secret の read 権限を付ける。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecretVersionIds"
      ],
      "Resource": "arn:aws:secretsmanager:<region>:<account-id>:secret:<env>/shared/google_application_credentials_<app>*"
    }
  ]
}

Resource はできるだけ狭くする。同じ role が少数の Google credential secret だけを扱うなら、<env>/shared/google_application_credentials* のような小さい wildcard でも運用しやすい。

3. SecretProviderClass を追加する

workload と同じ namespace に SecretProviderClass を作る。

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: <app>-google-application-credentials
  namespace: <namespace>
spec:
  provider: aws
  parameters:
    objects: |
      - objectName: "<env>/shared/google_application_credentials_<app>"
        objectType: "secretsmanager"
        objectAlias: "google-application-credentials.json"

objectName は AWS Secrets Manager の secret 名。objectAlias は Pod 内で見えるファイル名。

4. Deployment にマウントする

Deployment には、環境変数、read-only volumeMount、CSI volume を追加する。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <app>
  namespace: <namespace>
spec:
  template:
    spec:
      serviceAccountName: <workload-service-account>
      containers:
        - name: <app>
          image: <image>
          env:
            - name: GOOGLE_APPLICATION_CREDENTIALS
              value: /mnt/secrets-store/google-application-credentials.json
          volumeMounts:
            - name: secrets-store
              mountPath: /mnt/secrets-store
              readOnly: true
      volumes:
        - name: secrets-store
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: <app>-google-application-credentials

既存の config volume や application-secret volume があっても、Google JSON は別の secrets-store volume にする方が扱いやすい。これは application YAML ではなく credential file だから。

5. Kustomize に含める

GitOps directory が Kustomize の場合は、resource に追加する。

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: <namespace>

resources:
  - secret-provider-class.yaml
  - deployment.yaml
  - external-secret.yaml
  - pdb.yaml

commit するのは manifest だけ。JSON key は AWS Secrets Manager に残す。

6. Java から使う

Firebase Admin SDK や Google Cloud library では、Application Default Credentials を使う。

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .build();

FirebaseApp app = FirebaseApp.initializeApp(options);

GOOGLE_APPLICATION_CREDENTIALS が設定されていれば、Java process はマウントされた JSON file を自動で読む。

環境によって Firebase を無効にしたい場合は、設定値で bean を切り替える。

@Configuration
@ConditionalOnProperty(name = "firebase.enabled", havingValue = "true")
public class FirebaseConfig {
    // Firebase beans
}

環境設定ではこのように有効化する。

firebase:
  enabled: true

7. GitOps の render を確認する

push 前に render する。

kubectl kustomize <path-to-kustomize-dir>

出力に以下が含まれることを確認する。

GOOGLE_APPLICATION_CREDENTIALS
secretProviderClass: <app>-google-application-credentials

その後 GitOps 変更を push し、ArgoCD などの controller に同期させる。

8. Cluster で確認する

同期後、まず rollout を見る。

kubectl -n <namespace> rollout status deployment/<app> --timeout=120s

SecretProviderClass を確認する。

kubectl -n <namespace> get secretproviderclass <app>-google-application-credentials \
  -o jsonpath='{.spec.parameters.objects}{"\n"}'

環境変数を確認する。

kubectl -n <namespace> get deployment <app> \
  -o jsonpath='{range .spec.template.spec.containers[0].env[*]}{.name}{"="}{.value}{"\n"}{end}'

最後に、secret の中身を出さずにファイルの存在だけ確認する。

kubectl -n <namespace> exec deployment/<app> -- sh -c \
  'test -s "$GOOGLE_APPLICATION_CREDENTIALS" && echo credential_file_present'

この確認は、ファイルが存在して空ではないことだけを見る。JSON は表示しない。

よくある失敗

症状確認する点
Pod が volume を mount できないSecrets Store CSI Driver と AWS provider が install 済みか確認する。
Mount 時に AccessDenied になるIAM role が正しい Secrets Manager ARN を読めるか確認する。
ファイルはあるが Java が失敗するGOOGLE_APPLICATION_CREDENTIALS が実際の mounted filename を指しているか確認する。
Firebase bean が作られないfirebase.enabled=true など、アプリ側の有効化設定を確認する。
secret 更新後に新しい値を読まないcluster 側で secret rotation を明示的に有効化して確認していない限り、workload を restart または redeploy する。

この境界を保つと運用しやすい。インフラは credential file の path を提供し、アプリケーションは Google の default credential chain を使う。secret は codebase に入らず、Kubernetes 側から runtime の状態を確認できる。