cloud-sre
GOOGLE_APPLICATION_CREDENTIALS を Kubernetes Pod に安全にマウントする手順
Google service account JSON を AWS Secrets Manager に保存し、Secrets Store CSI Driver で Pod にマウントし、Java から GOOGLE_APPLICATION_CREDENTIALS で読むための汎用手順。
この構成では、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_CREDENTIALS | Google client library が service account JSON の場所を見つけるために使う環境変数。 |
| Secrets Store CSI Driver | AWS Secrets Manager など外部 secret を Kubernetes Pod にファイルとしてマウントする CSI driver。 |
SecretProviderClass | CSI driver に、どの外部 secret を取り、Pod 内でどのファイル名にするかを伝える Kubernetes custom resource。 |
| GitOps | Kubernetes 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 の状態を確認できる。