kustomize で secrets を作成する方法

# 概要
最近、GKEの導入を進めており、GKEへのデプロイは、Cloud Buildから行うようにしています。
Manifestの管理は、Kustomizeを活用しています。

よく課題となるが、GCPのサービスアカウントといった秘匿性のある情報の管理です。
例えば、GKE上のコンテナからCloud SQL Proxy経由でCloud SQLに接続する場合、
Cloud SQL ProxyのコンテナにGCPのサービスアカウントを食わせる必要があると思います。

参考：https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/blob/master/cloudsql/mysql_wordpress_deployment.yaml#L49

このサービスアカウントは、KMSで暗号化することでGitHubでも管理できるようにしていますが、
この場合、デプロイする際に暗号化をほどいてからGKEにデプロイする必要があります。

今回は、Cloud Buildによるサービスアカウントの復号とKustomizeの `secretGenerator` を活用した例をご紹介できればと思います。

# 環境
* GKE 1.14.10-gke.27
* Kustomize 3.4.0
* Cloud Build

# 詳細
## ディレクト構成
以下のようなディレクトリ構成を前提にしています。

```
.gcp
├── secrets
│   └── service-account.$PROJECT_ID.json.enc #サービスアカウントをKMSで暗号化していたもの
k8s
└── ops
    ├── base
    │   ├── envoy
    │   │   ├── configmap.yaml
    │   │   ├── deployment.yaml
    │   │   └── service.yaml
    │   ├── grpc-gateway
    │   │   ├── ingress.yaml
    │   │   └── service.yaml
    │   ├── grpc-server
    │   │   └── service.yaml
    │   └── kustomization.yaml
    └── overlays
        └── development
            ├── grpc-gateway
            │   ├── deployment.yaml
            │   ├── ingress.yaml
            │   └── ssl.yaml
            ├── grpc-server
            │   └── deployment.yaml
            └── kustomization.yaml # secretGeneratorの設定を入れる
```

## 前提条件
* Cloud KMSで暗号化したサービウアカウントが`.gcp/secrets/service-account.$PROJECT_ID.json.enc`にあるものとします。
* Cloud KMSのキーリングは、`secrets`、キーは`service-account`とします。事前に作成していただくか、適宜読み替えてください。

## 事前準備
Cloud Build上で起動させるkustomizeの公式イメージがなかったので、
[こちらのリポジトリ](https://github.com/GoogleCloudPlatform/cloud-builders-community/tree/master/kustomize)を参考に、Kustomizeイメージをビルドしました。
ビルドしたkustomizeのDockerイメージは、GKEをデプロイするGCPと同じプロジェクト内のGCRにPushします。
このリポジトリには、`cloudbuild.yaml`があるので、READMEを参考にすればCloud Build経由でイメージをビルドできるかと思います。

## Cloud Build
Cloud Build側で`gcloud kms decrypt`を実行し、特定のディレクトリ(今回ですとk8s/ops/overlays/development/grpc-server)に配置します。
`$PROJECT_ID`には、指定せずともCloud Buildを実行しているGCPのプロジェクト名が入ります。
環境(devとかstg等)ごとにGCPプロジェクトが分かれているので、このようなサービスアカウント名にすることで各環境にも転用できるようにしています。

次に上述の手順でBuildしたKustomizeイメージを指定し、kustomize経由でkubernetesにdeployします。
このkustomizeイメージでは、[kustomize.bash](
https://github.com/GoogleCloudPlatform/cloud-builders-community/blob/master/kustomize/kustomize.bash)がENTRYPOINTで指定されています。
引数に`build`を指定することで、`kustomize $@ | kubectl apply -f -`を実行しているようです。

```yaml

  - id: decrypt service accout
    name: gcr.io/cloud-builders/gcloud
    args:
      - kms
      - decrypt
      - --ciphertext-file=.gcp/secrets/service-account.$PROJECT_ID.json.enc
      - --plaintext-file=${_DIR}/grpc-server/service-account.$PROJECT_ID.json
      - --location=global
      - --keyring=secrets
      - --key=service-account

  - id: deploy
    name: 'gcr.io/$PROJECT_ID/kustomize'　# 自分でBuildしたKustomizeイメージを指定
    dir: '${_DIR}'
    args:
      - 'build'
    env:
      - 'APPLY=true'
      - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}'
      - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}'

substitutions:
  _CLOUDSDK_COMPUTE_ZONE: asia-northeast1-a
  _CLOUDSDK_CONTAINER_CLUSTER: <your GKE cluster name>
  _DIR: k8s/ops/overlays/development
```

## Kustomize
Cloud Buildで`k8s/ops/overlays/development/grpc-server/`にサービスアカウントをdecryptしましたが
そのサービスアカウントを `secretGenerator` を使用してリソースを作成します。

```yaml
bases:
  - ../../base

patchesStrategicMerge:
- grpc-gateway/ingress.yaml

resources:
- grpc-gateway/deployment.yaml
- grpc-gateway/ssl.yaml
- grpc-server/deployment.yaml

secretGenerator: # secretGeneratorを使用し、復号したサービスアカウントを基にsecretsを作成
- name: service-account
  files:
    - grpc-server/service-account.<your project name>.json
```

## 実行結果
secretGeneratorで生成されたsecretsは以下のようにSuffixがつきます。

```
$ kubectl get secrets
NAME                                           TYPE                                  DATA   AGE
service-account-f25f88gf6d                 Opaque                                1      24d
```

## Secretsのmount
セキュリティ的にあまり良くないようですが、Deploymentのvolumesにmountする形で使用しました。要改善。
さきほど確認したsecretsにはSuffixがついていましたが、そのたありはよろしくやってくれるようです。

```yaml

        volumeMounts:
        - name: service-account-file
          mountPath: /mnt
          readOnly: true
        env:
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: "/mnt/service-account.visits-for-innovators-dev.json"
      volumes:
      - name: service-account-file
        secret:
          secretName: service-account
```

# まとめ
GCPのサービスアカウントといった秘匿性のあるファイルを、kustomize構成で管理、デプロイする方法についてご紹介でした。
他にいい方法があれば教えてください。

# 参考
以下の文献を参考にさせていただきました。
[Cloud Build + Cloud KMS + kustomizeでGKEへデプロイする](https://qiita.com/Yarimizu14/items/7e08c6487ac2b7f18ea3)