🦾

GKE Autopilot + ASM + Gateway API + Certificate Manager で nginx を動かす

2023/02/17に公開

はじめに

タイトルの通り、

  • GKE Autopilot
  • ASM(Anthos Service Mesh)
  • Gateway API
  • Certificate Manager

を組み合わせた環境を作り、その上で nginx コンテナを動かしてみます。

作業は glcoud を使用してコマンドラインベースで行います。Google Cloud コンソールCloud Shell(右上のターミナルアイコンから起動) を使用すると初期設定不要でブラウザだけで作業が完結するので、お勧めです。

コマンドによっては完了までにかなり時間がかかるものがありますが、気長に待ちましょう!

環境変数セット

色々な環境で試せるように、以降出てくるコマンドで環境変数を参照するようにしました。

実際の環境に合わせて、好きな値を入れてください。

# プロジェクトID
export PROJECT_ID=some-project-1234
# クラスタ名
export CLUSTER_NAME=test-cluster
# クラスタのリージョン
export CLUSTER_REGION=us-central1
# クラスタを操作するサービスアカウント名
export SERVICE_ACCOUNT_NAME=gke-cluster-operator
# デプロイ先の名前空間
export NAMESPACE=default
# 予約するIPにつける名前
export IP_NAME=test-cluster-ip
# 証明書につける名前
export CERT_NAME=test-cert
# 紐づけるドメイン
export DOMAIN=test.example.com

サービスアカウント作成

クラスタを作成する前に、クラスタ操作用のサービスアカウントを作っておきます。

クラスタ作成時に何も指定しなければ Compute Engine のデフォルトサービスアカウントが使用されるのですが、公式ドキュメントによると必要最小限な権限を持つサービスアカウントを使用する事を推奨との事。

https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster?hl=ja#use_least_privilege_sa

以下の通りアカウントを作成します。

gcloud iam service-accounts create ${SERVICE_ACCOUNT_NAME} \
    --display-name="GKEクラスタ実行用SA"

作成後、必要なロールを付与します。

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/container.clusterAdmin

クラスタ作成

GKE Autopilot のクラスタを作成します。

https://cloud.google.com/kubernetes-engine/docs/how-to/creating-an-autopilot-cluster?hl=ja#gcloud

まずは Kubernetes Engine API を有効にします。

gcloud services enable container.googleapis.com

完了後、先程作成したサービスアカウントを指定して、クラスタを作成します。

Autopilot でなければこのタイミングでオプションで Gateway API を有効にできるのですが、Autopilot 用の create-auto にはそのオプションがありません。ASM もついでにここで有効に出来ればいいのですが…こちらも現時点では出来ないようです。

gcloud container clusters create-auto ${CLUSTER_NAME} \
    --region ${CLUSTER_REGION} \
    --project=${PROJECT_ID} \
    --service-account=${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

ASMを有効に

Google Cloud の(web)GUIコンソール画面からならクラスタを作成する時に同時に ASM を有効に出来て楽なのですが、gcloud から ASM を有効にする場合は asmcli というツールを使う必要があります。

https://cloud.google.com/service-mesh/docs/unified-install/quickstart-asm?hl=ja#download_asmcli

記事の通りですが、DLして

curl https://storage.googleapis.com/csm-artifacts/asm/asmcli_1.15 > asmcli

実行権限をつけて

chmod +x asmcli

実行します。

./asmcli install \
  --project_id ${PROJECT_ID} \
  --cluster_name ${CLUSTER_NAME} \
  --cluster_location ${CLUSTER_REGION} \
  --verbose \
  --managed \
  --use-managed-cni \
  --enable_all \
  --enable_registration \
  --ca mesh_ca

有効化したら、istio の自動インジェクションを有効にしておきます。インストール完了のメッセージにも記載がありますね。

今回はデフォルトではなく特定の名前空間に対して有効にするので、対象の名前空間を作ります。

kubectl create namespace ${NAMESPACE}
kubectl label namespace ${NAMESPACE} istio-injection- istio.io/rev=asm-managed --overwrite

GatewayAPIを有効に

こちらも有効にしていきます。

https://cloud.google.com/kubernetes-engine/docs/how-to/deploying-gateways?hl=ja#enable-gateway-existing-cluster

前述の通り作成時には有効に出来ないので、 update コマンドを使用してクラスタを更新します。

gcloud container clusters update ${CLUSTER_NAME} \
    --gateway-api=standard \
    --region=${CLUSTER_REGION}

IPアドレスを予約

公開用のIPアドレスを予約します。

予約しなくても自動的に割り振られるようなのですが、ドメインとの紐づけを考えて固定しておきます。

gcloud compute addresses create ${IP_NAME} \
    --project=${PROJECT_ID} \
    --global

予約されたIPは以下のコマンドで確認できます。

gcloud compute addresses describe ${IP_NAME} --global | grep address:

ドメインを用意

ドメインを用意し、前項で予約したIPアドレスをDNSのAレコードに登録します。この作業はドメイン取得やDNSレコード設定に使用しているサービスによるので、適宜行ってください。

証明書を用意

Certificate Manager を使用して証明書を用意します。

まずは Certificate Manager API を有効にする必要があります。

gcloud services enable certificatemanager.googleapis.com

有効になった後、以下のコマンドで証明書を作成します。

gcloud certificate-manager maps create ${CERT_NAME}-map
gcloud certificate-manager certificates create ${CERT_NAME} \
    --domains=${DOMAIN}
gcloud certificate-manager maps entries create ${CERT_NAME}-entry \
    --map=${CERT_NAME}-map \
    --hostname=${DOMAIN} \
    --certificates=${CERT_NAME}

nginxをデプロイ

以下のコマンドで nginx をデプロイします。

Gateway.spec.addresses で予約したIPアドレスを、 Gateway.metadata.annotations で証明書を、HTTPRoute.spec.hostnamesで公開用ドメインをそれぞれ指定しています。

kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx-test
    service: nginx-test
  name: nginx-test
  namespace: ${NAMESPACE}
spec:
  ports:
    - name: http
      port: 80
  selector:
    app: nginx-test
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-test
  name: nginx-test
  namespace: ${NAMESPACE}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
        - image: nginx
          imagePullPolicy: IfNotPresent
          name: nginx-test
          ports:
            - containerPort: 80
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: nginx-test-gateway
  namespace: ${NAMESPACE}
  annotations:
    networking.gke.io/certmap: ${CERT_NAME}-map
spec:
  gatewayClassName: gke-l7-gxlb
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
  addresses:
    - type: NamedAddress
      value: ${IP_NAME}
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
  labels:
    app: nginx-test
  name: nginx-test-route
  namespace: ${NAMESPACE}
spec:
  hostnames:
    - ${DOMAIN}
  parentRefs:
    - name: nginx-test-gateway
  rules:
    - backendRefs:
        - name: nginx-test
          port: 80
      matches:
        - path:
            type: PathPrefix
            value: /
EOF

導通確認する

webブラウザで設定したドメインにアクセスして、初期画面が表示されるかどうか確認します。

コマンドライン上で確認する場合はこちら。

curl https://${DOMAIN}

おわりに

個人的に動作させるまでに結構試行錯誤したり苦労したので、メモとして残しておきます。

この構成だと、今回挙げたサービスだけではなく裏で色々な機能がオンになったりするので、冒頭に書いた通り確認用のプロジェクトを作って、不要になったらそのプロジェクトごと削除すると楽かなと思います。

Discussion