y-ohgi's blog

TODO: ここになにかかく

GKE-autopilot とCloud SQL を使用したGCP 環境を構築する

TL;DR

  • GKE-autopilot で環境構築をする例
  • 一般的なAPI サーバーを構築することを目指し、GKE-autopilot、GCP のマネージド証明書を使ったhttps 化、Cloud SQL との疎通をさせる
  • kustomize で複数環境の構築

About

GCP リソース/API は基本的に手作業でやりつつ、GKE 周りは基本的にkustomize でやります。
作業はCloud Shell 上で行います。

version

  • gcloud
    • 347.0.0
  • GKE
    • 1.19.9-gke.1900

Repository

サンプルコードは以下
https://github.com/y-ohgi/example-gke-autopilot

構築

GKE Autopilot クラスタの作成

gcloud beta clusters create-auto コマンドで作成を行えるのですが、GUI からポチポチでクラスタ名とリージョンの変更を行います。
ネットワークはデフォルトのままにしていますが、メンテナンスウィンドウはデフォルトだといつでも実行されてしまうため、適宜変更をオススメします。

設定項目が少ないので、本番でもGUI からでも良いかなと個人的に思っています。

まずはKubernetes Engine API の有効化を行い、クラスタの作成からAutopilot を選択

f:id:y-ohgi:20210816233418p:plain

クラスタの名前は適宜変更してもらい、リージョンは asia-northeast1 (日本) に設定。

今回はリリースチャンネルはRegular に。
ここは本番環境はStable にして、開発環境はRegular にすると良いのかなと。

本番環境の場合は、自動化の「メンテナンスの時間枠」を有効化にチェックを入れ、メンテナンスウィンドウを運用するサービスに応じて変更。

クラスタの作成は5分ほどかかるので待つ。

nginx を立ててhttps で疎通する

f:id:y-ohgi:20210816233435p:plain

Cloud SQL と疎通させる前に、まずはGKE 上に nginx を立ててhttps で受け付けます

IP の取得とドメインへ登録

マネージド証明書を取得する前準備です。
IP の取得と、そのIP をCloud DNS へ登録していきます。

ここでは、Cloud DNS にドメインを登録済みの前提で進めます。

まずは静的IP の取得を行います。
ここで静的IP へ付与した名前はmanifest 上で使用するので、控えておきます。
静的IP は環境毎に必要になるので、プレフィックスに prd-dev- などを付けておくと良いでしょう。

$ gcloud compute addresses create ${ADDRESS_NAME} --global
$ gcloud compute addresses describe ${ADDRESS_NAME} --global
address: 35.190.xxx.xxxx
addressType: EXTERNAL
creationTimestamp: '2021-07-16T00:59:17.408-07:00'
 :

取得した静的IP をCloud DNS のA レコードへ登録します。
Cloud DNS もAPI を有効化する必要があったり移譲したりがあると思うので、GUI でポチポチA レコードを登録します。

kustomize の構成

Pull Request を作ったので、実際のコードは以下を参照ください。

https://github.com/y-ohgi/example-gke-autopilot/pull/1

$ tree kustomize/
kustomize/
|-- base
|   |-- certificate.yaml
|   |-- deployment.yaml
|   |-- ingress.yaml
|   |-- kustomization.yaml
|   |-- namespace.yaml
|   `-- service.yaml
`-- overlays
    |-- dev
    |   |-- certificate.yaml
    |   |-- deployment.yaml
    |   |-- ingress.yaml
    |   |-- kustomization.yaml
    |   `-- service.yaml
    `-- prd
        `-- kustomization.yaml

GKE マネージド証明書を使ったhttps の設定

GKE マネージド証明書を使用したhttps の構成を構築するには2つポイントがあります。

  1. ヘルスチェックパスの変更
  2. Ingress のannotation でマネージド証明書の指定

1. ヘルスチェックパスの変更

GCE のヘルスチェックを使用してTLS 証明書を発行するため、GCE のデフォルトのヘルスチェックである /healthz を変更する必要があります。
デフォルトのnginx だと /healthz というエンドポイントにアクセスると404 が返ってきてヘルスチェックが落ちるため、ヘルスチェックのエンドポイントを / へ変更する必要があります。

GKE のCRD の BackendConfig でヘルスチェックエンドポイントを設定し、Service のannotation ( beta.cloud.google.com/backend-config )でそのBackendConfig を参照するようにします。

kustomize/base/service.yaml

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: config-default
spec:
  healthCheck:
    checkIntervalSec: 10
    timeoutSec: 3
    requestPath: /

---

apiVersion: v1
kind: Service
metadata:
  name: sandbox
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
    beta.cloud.google.com/backend-config: '{"default": "config-default"}'

spec:
  type: NodePort
  selector:
    app: sandbox
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

annotations はkustomize は変更しないため、以下のように環境舞に明示的にkustomize で書き換える必要があります。

kustomize/overlays/dev/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: sandbox
  annotations:
    beta.cloud.google.com/backend-config: '{"default": "dev-config-default"}'

2. Ingress のannotation でマネージド証明書の指定

Ingress の annotations に3つ設定を追加します。

kustomize/base/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sandbox
  annotations:
    kubernetes.io/ingress.global-static-ip-name: sandbox # gcloud コマンドで取得した静的IP
    networking.gke.io/managed-certificates: sandbox # certificate.yaml で設定したname
    kubernetes.io/ingress.class: gce

spec:
  defaultBackend:
    service:
      name: sandbox
      port:
        number: 80

前項のService と同じくannotations はkustomize は書き換えないため、Ingress でも明示的に書き換えます。

kustomize/overlays/dev/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sandbox
  annotations:
    kubernetes.io/ingress.global-static-ip-name: dev-sandbox
    networking.gke.io/managed-certificates: dev-sandbox
    kubernetes.io/ingress.class: gce

apply する

GKE のクレデンシャルを取得し、apply します。

$ gcloud container clusters get-credentials autopilot-cluster-1 --region asia-northeast1 --project gke-autopilot-blog
$ kubectl apply -k kustomize/overlays/dev

証明書の発行に時間がかかるため、15分ほど待つと対象のドメインにhttp/https で接続するとnginx が表示されます。
次はcloudsql-proxy を使用してCloud SQL と疎通させます。

CloudSQL と疎通させる

f:id:y-ohgi:20210816233454p:plain

一気に登場人物が多くなります。
それぞれ概要だけざっくり説明します。

  1. Cloud IAM
    • 後述するcloudsql-proxy とCloud SQL を疎通させるための権限を付与するためのIAM です
  2. Cloud SQL
    • 疎通先のCloud SQL です。今回はPostgreSQL を使用しますが、MySQL でも同じ構成になります。
  3. Pod
    • GCP が提供しているcloudsql-proxy と、pgcli 用にpostgres をpod に入れます

Pull Request は以下です。
https://github.com/y-ohgi/example-gke-autopilot/pull/2

Cloud IAM の設定

Cloud IAM (Service Account。以降GSA) とKubernetes Service Account (以降KSA) の2種類を使用します。
役割としてはGSA にCloud SQL への疎通権限を付与し、KSA からGSA を使用するような形です。

GSA の作成

まずはGSA から作成します。
GCP のプロジェクトid が必要になるので、はじめに取得し、環境変数に入れておきます。

$ gcloud projects list
PROJECT_ID                      NAME                PROJECT_NUMBER
gke-autopilot-blog              gke-autopilot-blog  5555555555555
  :
$ export PROJECT_ID=gke-autopilot-blog

GSA の作成をします。

$ gcloud iam service-accounts create ${GSA_NAME}

GSA にCloud SQL への接続権限を付与します。

$ gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com \
    --role roles/cloudsql.client

roles/cloudsql.client のAPI を有効化します。

$ gcloud services enable sqladmin.googleapis.com

GSA とKSA の紐付け。
KSA はまだ作成していませんが、先に紐付けだけしてしまいます。
${K8S_NAMESPACE} はnginx の構築をした際のnamespace で、 ${KSA_NAME} は今から作成するKSA の名前になります。
個人的に、特にこだわりがなければ GSA_NAMEKSA_NAME は同一にして問題ないと思います。

$ gcloud iam service-accounts add-iam-policy-binding \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:${PROJECT_ID}.svc.id.goog[${K8S_NAMESPACE}/${KSA_NAME}]" ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

KSA の作成とPod へ付与

manifest からKSA を作成します。
deployment へKSA の定義と、Pod へKSA の付与を行います。
<GSA_NAME><PROJECT_ID> は先程使用した値と同じものです。

kustomize/base/deployment.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sandbox
  annotations:
    iam.gke.io/gcp-service-account: <GSA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
 :
    spec:
      serviceAccountName: sandbox
 :

Service/Ingress と同じくannotations は上書きされないこと、 Deploymentの serviceAccountName は明示的に宣言する必要があるためkustomize で上書きします。
個人的に、GSA は環境毎にそれぞれ作ると良いと考えています。

kustomize/overlays/dev/deployment.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sandbox
  annotations:
    iam.gke.io/gcp-service-account: <GSA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
 :
    spec:
      serviceAccountName: dev-sandbox
 :

Cloud SQL (PostgreSQL) の立ち上げ

GUI からPostgreSQL 13 をtokyo リージョンに立ち上げます。
このブログでは疎通させることを目的にしているため説明しませんが、インスタンスの設定は適宜行ってください。

ここで生成したパスワードは接続時に使用するので、控えて置いてください(デフォルトユーザーで疎通確認します)。

Pod へpgcli とcloud-sql-proxy の追加

GCP が提供するcloud-sql-proxy と、postgres イメージをPod へ追加します。

cloud-sql-proxy はその名の通りCloud SQL とのプロキシを行ってくれます。
command: の2つ目の - "-instances=<PROJECT ID>:asia-northeast1:<INSTANCE NAME>=tcp:5432" で疎通させたいインスタンスの接続名を指定します。

postgres イメージは後述する $ kubectl exec でコンテナに入り、 $ psql コマンドで疎通確認をしたいため使用します。起動後は何もさせないよう $ tail -f /dev/null を実行させておきます。

kustomize/base/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sandbox

 :

      - name: cloud-sql-proxy
        image: gcr.io/cloudsql-docker/gce-proxy:1.24.0
        command:
        - "/cloud_sql_proxy"
        - "-instances=<PROJECT ID>:asia-northeast1:<INSTANCE NAME>=tcp:5432"
        ports:
        - containerPort: 5432
      - name: pgcli
        image: postgres:13
        command:
        - "tail"
        - "-f"
        - "/dev/null"

Pod からCloud SQL へ疎通確認

Pod に立てたpgcli コンテナに入り、Cloud SQL へ接続できるかを確認します。

Pod NAME を取得し、 $ kubectl exec でpgcli コンテナに入り、 $ psql -h localhost -p 5432 -U postgres コマンドでCloud SQL へ接続できることを確認します。

$ kubectl get pod
NAME                           READY   STATUS    RESTARTS   AGE
dev-sandbox-844855db4f-6zknf   3/3     Running   0          7m56s

$ kubectl exec -it dev-sandbox-844855db4f-6zknf -c pgcli -- bash
root@dev-sandbox-844855db4f-6zknf:/# psql -h localhost -p 5432 -U postgres
Password for user postgres:
psql (13.4 (Debian 13.4-1.pgdg100+1), server 13.3)
Type "help" for help.

postgres-> \l
                                                List of databases
     Name      |       Owner       | Encoding |  Collate   |   Ctype    |            Access privileges
---------------+-------------------+----------+------------+------------+-----------------------------------------
 cloudsqladmin | cloudsqladmin     | UTF8     | en_US.UTF8 | en_US.UTF8 |
 postgres      | cloudsqlsuperuser | UTF8     | en_US.UTF8 | en_US.UTF8 |
 template0     | cloudsqladmin     | UTF8     | en_US.UTF8 | en_US.UTF8 | =c/cloudsqladmin                       +
               |                   |          |            |            | cloudsqladmin=CTc/cloudsqladmin
 template1     | cloudsqlsuperuser | UTF8     | en_US.UTF8 | en_US.UTF8 | =c/cloudsqlsuperuser                   +
               |                   |          |            |            | cloudsqlsuperuser=CTc/cloudsqlsuperuser
(5 rows)

postgres->

片付け

kubectl でk8s Object を削除します。

$ kubectl delete -k kustomize/overlays/dev

取得した静的IP を削除します。

$ gcloud compute addresses delete ${ADDRESS_NAME} --global
The following global addresses will be deleted:
 - [dev-sandbox]

Do you want to continue (Y/n)?  Y

GSA を削除します。

$ gcloud iam service-accounts delete ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
You are about to delete service account
[dev-sandbox@gke-autopilot-blog.iam.gserviceaccount.com].

Do you want to continue (Y/n)?  Y

Cloud SQL とCloud DNS をGUI からポチポチ削除します。

所感

GKE Standard のノリで使える制約の少なく運用が楽になる素晴らしいGKE のクラスタータイプだと感じました。
また、GKE-autopilot でもマネージド証明書やWorkload Identity をお手軽に使える点に感動です。

基本的にGKE のノリで使えたのですが、唯一古いNode に当たるとWorkload Identity が動かないことがあったことがハマった点でした。その時(2021/08 前半)はPod のリソースをある程度大きめに設定することで解決できましたが、ブログを書いているときはリソースを設定せずに問題なく動きました。
GKE のクラスターを選定する際は第一の選択肢になりえるのかなとおもいます(GKE-autopilot を使うならCloud Run でも良い気はしますが、Cron Job とか使いたいときとか?)。

とにかく、考えることが減って(CA/NPA etc)、GKE の運用が楽になるのは良いですね。使っていきたいと思います。

Ref