Managed Anthos Service Mesh を Private Cluster で利用する
Google Cloud でマイクロサービスを実現する際、サービスメッシュが必要であれば Anthos Service Mesh (以下、ASM) はその有力な選択肢のひとつです。
今回は、Managed ASM を GKE の Private Cluster で利用する際の注意点についてご紹介したいと思います。
Managed Anthos Service Mesh
Managed ASM は、Istio サービスメッシュの Control Plane に Google が管理するマネージド Istiod を用いる、ASM の利用方法のひとつです。
従来から提供されてきた In-cluster Cotrol Plane の ASM と比べて、Istiod の運用や管理の必要がなくなります。
最近 ASM は Anthos API を有効化しなくても利用できるようになったため、Managed ASM も Standalone ASM として利用可能です。
関連する Google Cloud の中の人のブログは こちら 。
Managed ASM の概要は以下の図のようになります。
図の左下部の Managed Istiod の部分がユーザのクラスタに展開されない点が In-cluster との大きな違いです。
ASM のインストールには従来 install_asm というインストール補助スクリプトが提供されてきましたが、後継として asmcli というコマンドラインツールが最近 GA になりました。
今回はインストールに asmcli を利用します。
Private Cluster
Private Cluster (限定公開クラスタとも呼ばれます) のクラスタエンドポイントへのアクセスは以下の 3 通りの構成方法 があります。
- Private Endpoint のみ (Public Endpoint が無効)
- Public Endpoint が有効、かつ Master Authorized Networks が無効
- Public Endpoint が有効、かつ Master Authorized Networks が有効
Master Authorized Networks (マスター承認済みネットワークとも呼ばれます) は Public Endpoint を有効化した場合に利用できる、エンドポイントへのアクセス元を限定するための機能です。
Master Authorized Networks を設定することで、外部 IP アドレスとノードおよび Pod 以外の内部 IP アドレスからクラスタエンドポイントへの (クライアントからの) アクセスを限定することができます。
以下の図は Public Endpoint と Master Authorized Networks を有効化した Private Cluster を示しています。
上記の 3 つの構成方法のうち、Managed ASM で利用可能な Private Cluster の選択肢は Public Endpoint が有効な構成に限られます。
パブリック エンドポイント アクセスを備えた GKE 限定公開クラスタ。Master Authorized Network (MAN) が有効かどうかは関係ありません ... (中略) ... Google が管理するコントロール プレーンでは、パブリック エンドポイント アクセスを有効にする必要があります。これは、パブリック エンドポイントに外部からアクセスできることを意味するわけではありません。
引用元:https://cloud.google.com/service-mesh/docs/supported-features-mcp#environments
Private Cluster における Managed ASM のセットアップと注意点
以降では、Managed ASM を Private Cluster で利用するためのセットアップの流れを示しながら、注意すべきポイントをいくつかご紹介します。
検証環境のバージョン
以降の動作検証には下記バージョンの GKE, ASM を利用しました。
- GKE : 1.21.4-gke.2300 (cos_containerd)
- Managed ASM : Regular Channel (1.10.4-asm.14)
- asmcli : 1.11.2-asm.17+config2
クラスタエンドポイントとアクセス経路
まず、Public Endpoint と Master Authorized Networks を有効にした Private Cluster を用意しました。
クラスタが Managed ASM を利用できる状態にセットアップするためには、asmcli を実行するインスタンス/端末がどこかに必要です。
今回は asmcli の挙動を説明する目的から、Private Cluster と同じ VPC 上に立ち上げた GCE を Bastion サーバとして利用します。
また、設定の簡略化のため、ノードと同じ内部 IP アドレスレンジ内にインスタンスを作成しています。
(Public Endpoint が有効なクラスタであっても、クライアントアクセス元が外部 IP アドレスになるとは限りません。プロジェクトのセキュリティ要件などによりますが、Public Endpoint は別の目的で有効化するものの、ASM のセットアップは同一 VPC 内から行いたい、というケースはあると思います)
$ gcloud container clusters describe temp-cluster --region us-central1 addonsConfig: dnsCacheConfig: {} gcePersistentDiskCsiDriverConfig: enabled: true horizontalPodAutoscaling: {} httpLoadBalancing: {} kubernetesDashboard: disabled: true networkPolicyConfig: {} authenticatorGroupsConfig: {} autoscaling: autoscalingProfile: BALANCED clusterIpv4Cidr: 10.60.0.0/14 createTime: '2021-10-25T09:18:51+00:00' currentMasterVersion: 1.21.4-gke.2300 currentNodeCount: 4 currentNodeVersion: 1.21.4-gke.2300 databaseEncryption: state: DECRYPTED defaultMaxPodsConstraint: maxPodsPerNode: '110' endpoint: *************** id: ec9882ed*************** initialClusterVersion: 1.21.4-gke.2300 instanceGroupUrls: - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-a/instanceGroupManagers/*************** - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-b/instanceGroupManagers/*************** - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-c/instanceGroupManagers/*************** - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-f/instanceGroupManagers/*************** ipAllocationPolicy: clusterIpv4Cidr: 10.60.0.0/14 clusterIpv4CidrBlock: 10.60.0.0/14 clusterSecondaryRangeName: gke-temp-cluster-pods-ec9882ed servicesIpv4Cidr: 10.64.0.0/20 servicesIpv4CidrBlock: 10.64.0.0/20 servicesSecondaryRangeName: gke-temp-cluster-services-ec9882ed useIpAliases: true labelFingerprint: *************** legacyAbac: {} location: us-central1 locations: - us-central1-a - us-central1-b - us-central1-c - us-central1-f loggingConfig: componentConfig: enableComponents: - SYSTEM_COMPONENTS - WORKLOADS loggingService: logging.googleapis.com/kubernetes maintenancePolicy: resourceVersion: e3b0c442 masterAuth: clusterCaCertificate: *************** masterAuthorizedNetworksConfig: enabled: true monitoringConfig: componentConfig: enableComponents: - SYSTEM_COMPONENTS monitoringService: monitoring.googleapis.com/kubernetes name: temp-cluster network: default networkConfig: datapathProvider: LEGACY_DATAPATH defaultSnatStatus: {} network: projects/***************/global/networks/default subnetwork: projects/***************/regions/us-central1/subnetworks/default networkPolicy: enabled: true provider: CALICO nodeConfig: diskSizeGb: 100 diskType: pd-standard imageType: COS_CONTAINERD machineType: e2-standard-2 metadata: disable-legacy-endpoints: 'true' oauthScopes: - https://www.googleapis.com/auth/devstorage.read_only - https://www.googleapis.com/auth/logging.write - https://www.googleapis.com/auth/monitoring - https://www.googleapis.com/auth/servicecontrol - https://www.googleapis.com/auth/service.management.readonly - https://www.googleapis.com/auth/trace.append serviceAccount: default shieldedInstanceConfig: enableIntegrityMonitoring: true workloadMetadataConfig: mode: GKE_METADATA nodePools: - autoscaling: {} config: diskSizeGb: 100 diskType: pd-standard imageType: COS_CONTAINERD machineType: e2-standard-2 metadata: disable-legacy-endpoints: 'true' oauthScopes: - https://www.googleapis.com/auth/devstorage.read_only - https://www.googleapis.com/auth/logging.write - https://www.googleapis.com/auth/monitoring - https://www.googleapis.com/auth/servicecontrol - https://www.googleapis.com/auth/service.management.readonly - https://www.googleapis.com/auth/trace.append serviceAccount: default shieldedInstanceConfig: enableIntegrityMonitoring: true workloadMetadataConfig: mode: GKE_METADATA initialNodeCount: 1 instanceGroupUrls: - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-a/instanceGroupManagers/*************** - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-b/instanceGroupManagers/*************** - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-c/instanceGroupManagers/*************** - https://www.googleapis.com/compute/v1/projects/***************/zones/us-central1-f/instanceGroupManagers/*************** locations: - us-central1-a - us-central1-b - us-central1-c - us-central1-f management: autoRepair: true maxPodsConstraint: maxPodsPerNode: '110' name: default-pool networkConfig: podIpv4CidrBlock: 10.60.0.0/14 podRange: gke-temp-cluster-pods-ec9882ed podIpv4CidrSize: 24 selfLink: https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster/nodePools/default-pool status: RUNNING upgradeSettings: maxSurge: 1 version: 1.21.4-gke.2300 notificationConfig: pubsub: {} privateClusterConfig: enablePrivateNodes: true masterIpv4CidrBlock: 192.168.1.0/28 peeringName: gke-***************-peer privateEndpoint: 192.168.1.2 publicEndpoint: *************** releaseChannel: {} selfLink: https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster servicesIpv4Cidr: 10.64.0.0/20 shieldedNodes: enabled: true status: RUNNING subnetwork: default workloadIdentityConfig: workloadPool: ***************.svc.id.goog zone: us-central1
今のところ、このクラスタは Master Authorized Networks を有効化したものの、何のネットワークも登録していない (許可していない) 状態です。
したがって、Public Endpoint は有効化されているものの、Public Endpoint へのクライアントアクセスは一切許可していません。
一方、Private Endpoint はクラスタの Control Plane と VPC Peering しているノードのネットワーク上から引き続きアクセス可能な状態です (Bastion サーバから Private Endpoint に対してはネットワークの到達性が確保されているものとします) 。
ここで、クラスタへのクレデンシャルを取得し、kubectl
を投げてみるとどうなるでしょうか。
$ gcloud container clusters get-credentials temp-cluster Fetching cluster endpoint and auth data. kubeconfig entry generated for temp-cluster. $ kubectl get nodes (応答なし)
タイムアウトしました。
これは、対象のクラスタが Public Endpoint の有効な Private Cluster である場合に、gcloud container clusters get-credentials
コマンドが kubeconfig に書き込むエントリのクラスタエンドポイントは Public Endpoint となるためです。
ドキュメントには下記のように記載されています。
パブリック IP アドレスを持つマシンは、パブリック IP アドレスが承認済みネットワークのリストに含まれている場合にのみ、kubectl を使用してパブリック エンドポイントと通信できます。これには、Google Cloud の外のマシンと外部 IP アドレスを持つ Google Cloud VM が含まれます。
引用元:https://cloud.google.com/kubernetes-engine/docs/concepts/private-cluster-concept#overview
実際に kubeconfig を確認してみると、以下のようになっています。
$ cat ./.kube/config apiVersion: v1 clusters: - cluster: certificate-authority-data: *************** server: https://(クラスタの Public Endpoint の IP アドレス) name: gke_***************_us-central1_temp-cluster contexts: - context: cluster: gke_***************_us-central1_temp-cluster user: gke_***************_us-central1_temp-cluster name: gke_***************_us-central1_temp-cluster current-context: gke_***************_us-central1_temp-cluster kind: Config preferences: {} users: - name: gke_***************_us-central1_temp-cluster user: auth-provider: config: access-token: *************** cmd-args: config config-helper --format=json cmd-path: /usr/lib/google-cloud-sdk/bin/gcloud expiry: "2021-10-26T01:34:39Z" expiry-key: '{.credential.token_expiry}' token-key: '{.credential.access_token}' name: gcp
したがって、Private Endpoint 経由でクラスタにアクセスする場合は、kubeconfig のエントリを Private Cluster の内部 IP アドレスを使用したものに書き換える必要があります。
これは gcloud container clusters get-credentials
に internal-ip オプションを付与することで実現できます。
$ gcloud container clusters get-credentials --internal-ip temp-cluster Fetching cluster endpoint and auth data. kubeconfig entry generated for temp-cluster. $ cat ./.kube/config apiVersion: v1 clusters: - cluster: certificate-authority-data: *************** server: https://192.168.1.2 name: gke_***************_us-central1_temp-cluster contexts: - context: cluster: gke_***************_us-central1_temp-cluster user: gke_***************_us-central1_temp-cluster name: gke_***************_us-central1_temp-cluster current-context: gke_***************_us-central1_temp-cluster kind: Config preferences: {} users: - name: gke_***************_us-central1_temp-cluster user: auth-provider: config: cmd-args: config config-helper --format=json cmd-path: /usr/lib/google-cloud-sdk/bin/gcloud expiry-key: '{.credential.token_expiry}' token-key: '{.credential.access_token}' name: gcp $ kubectl get nodes NAME STATUS ROLES AGE VERSION gke-temp-cluster-default-pool-1d81231b-tsck Ready <none> 15h v1.21.4-gke.2300 gke-temp-cluster-default-pool-b5dc41dd-xb6n Ready <none> 15h v1.21.4-gke.2300 gke-temp-cluster-default-pool-ccc654f7-b0qj Ready <none> 15h v1.21.4-gke.2300 gke-temp-cluster-default-pool-d4cae408-2sfz Ready <none> 15h v1.21.4-gke.2300
これで VPC 内の Bastion からクラスタエンドポイントに対して kubectl でアクセスすることができました。
asmcli によるセットアップ
ではいよいよ asmcli install
コマンドで Managed ASM を構成してみます。
$ ./asmcli install \ > -p *************** \ > -l us-central1 \ > -n temp-cluster \ > --managed \ > --output_dir temp-cluster \ > --enable-all asmcli: Setting up necessary files... asmcli: Using /root/temp-cluster/asm_kubeconfig as the kubeconfig... asmcli: Checking installation tool dependencies... asmcli: Fetching/writing GCP credentials to kubeconfig file... asmcli: [WARNING]: nc not found, skipping k8s connection verification asmcli: [WARNING]: (Installation will continue normally.) asmcli: Getting account information... asmcli: Verifying cluster registration. asmcli: Enabling required APIs... asmcli: Enabling the service mesh feature... asmcli: Verifying cluster registration. asmcli: Registering the cluster as temp-cluster... asmcli: [WARNING]: Failed, retrying...(1 of 2) asmcli: [WARNING]: Failed, retrying...(2 of 2) asmcli: [WARNING]: Command 'run_command gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' failed.
Verifying cluster registration. の途中で asmcli によるセットアップが中断されてしまいました。
この様子から、先程の kubeconfig の設定とアクセス経路の関係に鑑み、「asmcli コマンドもまた、クラスタの Public Endpoint にアクセスしようとしているのではないか」という仮説が立てられます。
この答え合わせをするには asmcli install
コマンドに verbose オプションを付与します。
$ ./asmcli install \ > -p *************** \ > -l us-central1 \ > -n temp-cluster \ > --managed \ > --verbose \ > --output_dir temp-cluster \ > --enable-all asmcli: Setting up necessary files... asmcli: Using /root/temp-cluster/asm_kubeconfig as the kubeconfig... asmcli: Checking installation tool dependencies... asmcli: Fetching/writing GCP credentials to kubeconfig file... asmcli: Running: '/usr/bin/gcloud container clusters get-credentials temp-cluster --project=*************** --zone=us-central1' asmcli: ------------- Fetching cluster endpoint and auth data. kubeconfig entry generated for temp-cluster. asmcli: Running: '/usr/bin/kubectl --kubeconfig /root/temp-cluster/asm_kubeconfig config current-context' asmcli: ------------- asmcli: [WARNING]: nc not found, skipping k8s connection verification asmcli: [WARNING]: (Installation will continue normally.) asmcli: Getting account information... asmcli: Running: '/usr/bin/gcloud auth list --project=*************** --filter=status:ACTIVE --format=value(account)' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud config get-value auth/impersonate_service_account' asmcli: ------------- (unset) asmcli: Running: '/usr/bin/gcloud container clusters list --project=*************** --filter=name = temp-cluster AND location = us-central1 --format=value(name)' asmcli: ------------- asmcli: Running: '/root/temp-cluster/kpt version' asmcli: ------------- asmcli: Verifying cluster registration. asmcli: Enabling required APIs... asmcli: Running: '/usr/bin/gcloud services enable --project=*************** container.googleapis.com monitoring.googleapis.com logging.googleapis.com cloudtrace.googleapis.com meshconfig.googleapis.com iamcredentials.googleapis.com gkeconnect.googleapis.com gkehub.googleapis.com cloudresourcemanager.googleapis.com stackdriver.googleapis.com meshca.googleapis.com' asmcli: ------------- Operation "operations/acf.p2-***************-9134cd80-9677-479a-90ab-f315e7df61f5" finished successfully. asmcli: Running: '/usr/bin/gcloud container clusters describe --project=*************** --region us-central1 temp-cluster --format=json' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud container clusters describe --project=*************** --region us-central1 temp-cluster --format=json' asmcli: ------------- asmcli: Enabling the service mesh feature... asmcli: Running: 'gcloud beta container hub mesh enable --project=***************' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud beta container hub mesh enable --project=***************' asmcli: ------------- Service Mesh Feature for project [***************] is already enabled asmcli: Verifying cluster registration. asmcli: Running: '/usr/bin/gcloud container clusters describe temp-cluster --zone=us-central1 --project=*************** --format=value(selfLink, network)' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud container hub memberships list --format=value(name) --project ***************' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud container hub memberships list --format=value(name) --project ***************' asmcli: ------------- asmcli: Registering the cluster as temp-cluster... asmcli: Running: 'gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' asmcli: ------------- kubeconfig entry generated for temp-cluster. ERROR: (gcloud.container.hub.memberships.register) Failed to check if the user is a cluster-admin: W1026 01:27:41.354630 27359 transport.go:288] Unable to cancel request for *genericclioptions.CommandHeaderRoundTripper Unable to connect to the server: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) asmcli: [WARNING]: Failed, retrying...(1 of 2) asmcli: Running: 'gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' asmcli: ------------- kubeconfig entry generated for temp-cluster. ERROR: (gcloud.container.hub.memberships.register) Failed to check if the user is a cluster-admin: W1026 01:28:04.744751 27413 transport.go:288] Unable to cancel request for *genericclioptions.CommandHeaderRoundTripper Unable to connect to the server: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) asmcli: [WARNING]: Failed, retrying...(2 of 2) asmcli: [WARNING]: Command 'run_command gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' failed.
asmcli install
コマンドは、その処理中に temp-cluster/asm_kubeconfig という kubeconfig を別途作成し、このファイルにクラスタの Public Endpoint が宛先となるエントリを作成していることがわかります。
gcloud コマンドで作成した kubeconfig のエントリは引き続き内部 IP アドレスを向いていますが、asmcli がクラスタのエンドポイントを選択する際には新しい kubeconfig を作成するため、asmcli は Public Endpoint に向けて通信を試み、Master Authorized Networks で許可されていないためにタイムアウトを起こしてしまう、というわけです。
このとき、asm_kubeconfig は以下のようになっていました。
$ cat /root/temp-cluster/asm_kubeconfig apiVersion: v1 clusters: - cluster: certificate-authority-data: *************** server: https://(クラスタの Public Endpoint の IP アドレス) name: gke_***************_us-central1_temp-cluster contexts: - context: cluster: gke_***************_us-central1_temp-cluster user: gke_***************_us-central1_temp-cluster name: gke_***************_us-central1_temp-cluster current-context: gke_***************_us-central1_temp-cluster kind: Config preferences: {} users: - name: gke_***************_us-central1_temp-cluster user: auth-provider: config: access-token: *************** cmd-args: config config-helper --format=json cmd-path: /usr/lib/google-cloud-sdk/bin/gcloud expiry: "2021-10-26T02:16:55Z" expiry-key: '{.credential.token_expiry}' token-key: '{.credential.access_token}' name: gcp
この問題は、asmcli install
コマンド実行時に --kc | --kubeconfig オプションを付与し、gcloud コマンドで作成した内部 IP アドレス宛のエントリが含まれる kubeconfig を指定することで解決できます。
--kc | --kubeconfig オプションは本記事の執筆時点 (2021/10/26) では asmcli リファレンスのドキュメントに記載されていないコマンドオプションですが、実際にインストールした asmcli のコマンドオプションの一覧を表示させるとその存在を確認できます。
$ ./asmcli asmcli 1.11.2-asm.17+config2 usage: asmcli [SUBCOMMAND] [OPTION]... Set up, validate, and install ASM in a Google Cloud environment. Use -h|--help with -v|--verbose to show detailed descriptions. SUBCOMMANDS: install validate print-config create-mesh OPTIONS: -l|--cluster_location <LOCATION> -n|--cluster_name <NAME> -p|--project_id <ID> --kc|--kubeconfig <KUBECONFIG_FILE> --ctx|--context <CONTEXT> --fleet_id <FLEET ID> --network_id <NETWORK ID> -c|--ca <CA> -o|--option <FILE NAME> -s|--service_account <ACCOUNT> -k|--key_file <FILE PATH> -D|--output_dir <DIR PATH> --co|--custom_overlay <FILE NAME> --ca_cert <FILE PATH> --ca_key <FILE PATH> --root_cert <FILE PATH> --cert_chain <FILE PATH> --ca_pool <CA POOL> -r|--revision_name <REVISION NAME> --platform <PLATFORM> --channel <CHANNEL> FLAGS: -e|--enable_all --enable_cluster_roles --enable_cluster_labels --enable_gcp_apis --enable_gcp_iam_roles --enable_gcp_components --enable_registration --enable_namespace_creation --managed --print_config --disable_canonical_service -v|--verbose --dry_run --only_validate --only_enable -h|--help --version
では kubeconfig オプションを付与して asmcli install
コマンドを実行してみましょう。
$ ./asmcli install \ --managed \ --kubeconfig /root/.kube/config \ --output_dir temp-cluster \ --enable-all asmcli: Reading cluster information for gke_***************_us-central1_temp-cluster asmcli: Setting up necessary files... asmcli: Using /root/.kube/config as the kubeconfig... asmcli: Checking installation tool dependencies... asmcli: [WARNING]: nc not found, skipping k8s connection verification asmcli: [WARNING]: (Installation will continue normally.) asmcli: Getting account information... asmcli: Verifying cluster registration. asmcli: Enabling required APIs... asmcli: Enabling the service mesh feature... asmcli: Verifying cluster registration. asmcli: Registering the cluster as temp-cluster... asmcli: [WARNING]: Failed, retrying...(1 of 2) asmcli: [WARNING]: Failed, retrying...(2 of 2) asmcli: [WARNING]: Command 'run_command gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' failed.
kubeconfig は指定した内部 IP アドレス向けのエントリを持つものを参照したようですが、依然として asmcli install
コマンドは失敗しています。
ここで、外形的な確認方法にはなりますが asmcli install
コマンド実行中に tshark でパケットを拾ってみると、以下のような結果が得られました。
$ tshark -i ens4 -f "host (クラスタの Public Endpoint の IP アドレス)" Capturing on 'ens4' 1 0.000000000 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 48032 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903931182 TSecr=0 WS=128 2 1.007583122 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48032 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903932189 TSecr=0 WS=128 3 3.023571340 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48032 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903934205 TSecr=0 WS=128 4 7.247576382 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48032 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903938428 TSecr=0 WS=128 5 15.439576255 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48032 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903946619 TSecr=0 WS=128 6 23.381462419 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 48056 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903954560 TSecr=0 WS=128 7 24.399586137 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48056 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903955578 TSecr=0 WS=128 8 26.415605468 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48056 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903957593 TSecr=0 WS=128 9 30.543617550 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48056 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903961721 TSecr=0 WS=128 10 38.735588521 10.128.0.17 → (クラスタの Public Endpoint の IP アドレス) TCP 74 [TCP Retransmission] 48056 → 443 [SYN] Seq=0 Win=65320 Len=0 MSS=1420 SACK_PERM=1 TSval=2903969911 TSecr=0 WS=128 ^C10 packets captured
Registering the cluster as temp-cluster. の処理中に、Public Endpoint へのアクセスが行われており、これによって asmcli がタイムアウトしていることがわかります。
この原因は先程とは別のところにあり、verbose オプションを付けて実行するとわかりますが Registering the cluster as temp-cluster. の処理中に asmcli は内部で gcloud container hub memberships register
コマンドを実行しており、このときクラスタへのアクセスに Public Endpoint を用いていることがわかります。
... (略) ... asmcli: ------------- asmcli: Registering the cluster as temp-cluster... asmcli: Running: 'gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' asmcli: ------------- asmcli: Running: '/usr/bin/gcloud container hub memberships register temp-cluster --project=*************** --enable-workload-identity --gke-uri=https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster' asmcli: ------------- kubeconfig entry generated for temp-cluster. ERROR: (gcloud.container.hub.memberships.register) Failed to check if the user is a cluster-admin: W1026 02:15:40.839850 32642 transport.go:288] Unable to cancel request for *genericclioptions.CommandHeaderRoundTripper Unable to connect to the server: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) asmcli: [WARNING]: Failed, retrying...(1 of 2) ... (略) ...
私が確認した限りでは、asmcli が内部で実行している gcloud container hub memberships register
コマンドの引数をオプション等で変更することはできないように見受けられました。
対象が Public Endpoint の有効な Private Cluster である場合、asmcli は実行元インスタンス/端末がクラスタノードと同一の VPC 上にあったとしても Public Endpoint 経由でアクセスしようとする処理が含まれていることから、現状「Private Cluster に Private Endpoint 経由で Managed ASM をセットアップする」ことはできなさそうです。
なお、クラスタの Master Authorized Networks で Bastion の外部 IP アドレスを許可すれば、asmcli install
コマンドの実行は問題なく成功します。
$ ./asmcli install \ --managed \ --kubeconfig /root/.kube/config \ --output_dir temp-cluster \ --enable-all asmcli: Reading cluster information for gke_***************_us-central1_temp-cluster asmcli: Setting up necessary files... asmcli: Using /root/.kube/config as the kubeconfig... asmcli: Checking installation tool dependencies... asmcli: [WARNING]: nc not found, skipping k8s connection verification asmcli: [WARNING]: (Installation will continue normally.) asmcli: Getting account information... asmcli: Verifying cluster registration. asmcli: Enabling required APIs... asmcli: Enabling the service mesh feature... asmcli: Verifying cluster registration. asmcli: Registering the cluster as temp-cluster... asmcli: Checking for project ***************... asmcli: Reading labels for us-central1/temp-cluster... asmcli: Adding labels to us-central1/temp-cluster... asmcli: Querying for core/account... asmcli: Binding ***************@*************** to cluster admin role... clusterrolebinding.rbac.authorization.k8s.io/***************-cluster-admin-binding created asmcli: Creating istio-system namespace... namespace/istio-system created asmcli: Binding user:***************@*************** to required IAM roles... asmcli: Initializing meshconfig managed API... asmcli: Configuring kpt package... asm/ set 16 field(s) of setter "gcloud.container.cluster" to value "temp-cluster" asm/ set 20 field(s) of setter "gcloud.core.project" to value "***************" asm/ set 2 field(s) of setter "gcloud.project.projectNumber" to value "***************" asm/ set 16 field(s) of setter "gcloud.compute.location" to value "us-central1" asm/ set 1 field(s) of setter "gcloud.compute.network" to value "***************-default" asm/ set 3 field(s) of setter "gcloud.project.environProjectNumber" to value "***************" asm/ set 2 field(s) of setter "anthos.servicemesh.rev" to value "asm-managed" asm/ set 11 field(s) of setter "anthos.servicemesh.tag" to value "1.11.2-asm.17" asm/ set 3 field(s) of setter "anthos.servicemesh.trustDomain" to value "***************.svc.id.goog" asm/ set 1 field(s) of setter "anthos.servicemesh.tokenAudiences" to value "istio-ca,***************.svc.id.goog" asm/ set 1 field(s) of setter "anthos.servicemesh.spiffeBundleEndpoints" to value "***************.svc.id.goog|https://storage.googleapis.com/mesh-ca-resources/spiffe_bundle.json" asm/ set 2 field(s) of setter "anthos.servicemesh.idp-url" to value "https://container.googleapis.com/v1/projects/***************/locations/us-central1/clusters/temp-cluster" asm/ set 3 field(s) of setter "anthos.servicemesh.trustDomainAliases" to value "***************.svc.id.goog" namespace/istio-system labeled asmcli: Provisioning control plane... {} asmcli: Using the following managed revision for validating webhook: asm-managed-rapid asm/ set 1 field(s) of setter "anthos.servicemesh.controlplane.validation-url" to value "https://asm-temp-cluster-asm-managed-rapid-***************-uc.a.run.app:443/validate" asm/ set 1 field(s) of setter "anthos.servicemesh.managed-controlplane.cloudrun-addr" to value "asm-temp-cluster-asm-managed-rapid-***************-uc.a.run.app:443" asmcli: Configuring ASM managed control plane revision CRD... asmcli: Configuring base installation for managed control plane... asmcli: Configuring ASM managed control plane validating webhook config... asmcli: Configuring CNI... asmcli: Applying asm/control-plane-revision/crd.yaml... customresourcedefinition.apiextensions.k8s.io/controlplanerevisions.mesh.cloud.google.com configured asmcli: Applying istio-1.11.2-asm.17/manifests/charts/base/files/gen-istio-cluster.yaml... customresourcedefinition.apiextensions.k8s.io/destinationrules.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/envoyfilters.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/gateways.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/serviceentries.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/sidecars.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/virtualservices.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/workloadentries.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/workloadgroups.networking.istio.io configured customresourcedefinition.apiextensions.k8s.io/authorizationpolicies.security.istio.io configured customresourcedefinition.apiextensions.k8s.io/peerauthentications.security.istio.io configured customresourcedefinition.apiextensions.k8s.io/requestauthentications.security.istio.io configured customresourcedefinition.apiextensions.k8s.io/telemetries.telemetry.istio.io configured customresourcedefinition.apiextensions.k8s.io/dataplanecontrols.mesh.cloud.google.com configured customresourcedefinition.apiextensions.k8s.io/istiooperators.install.istio.io configured serviceaccount/istio-reader-service-account configured serviceaccount/istiod-service-account configured clusterrole.rbac.authorization.k8s.io/istiod-istio-system configured clusterrole.rbac.authorization.k8s.io/istio-reader-istio-system configured clusterrolebinding.rbac.authorization.k8s.io/istio-reader-istio-system configured clusterrolebinding.rbac.authorization.k8s.io/istiod-istio-system configured role.rbac.authorization.k8s.io/istiod-istio-system configured rolebinding.rbac.authorization.k8s.io/istiod-istio-system configured asmcli: Applying asm/istio/options/managed-control-plane-webhooks.yaml... validatingwebhookconfiguration.admissionregistration.k8s.io/istiod-istio-system-mcp created asmcli: Applying mcp_configmap.yaml... configmap/asm-options created asmcli: Applying asm/istio/options/cni-managed.yaml... customresourcedefinition.apiextensions.k8s.io/dataplanecontrols.mesh.cloud.google.com configured serviceaccount/istio-cni created clusterrole.rbac.authorization.k8s.io/istio-cni created clusterrole.rbac.authorization.k8s.io/istio-cni-repair-role created clusterrolebinding.rbac.authorization.k8s.io/istio-cni created clusterrolebinding.rbac.authorization.k8s.io/istio-cni-repair-rolebinding created clusterrole.rbac.authorization.k8s.io/mdp-controller created clusterrolebinding.rbac.authorization.k8s.io/mdp-controller created configmap/istio-cni-config created daemonset.apps/istio-cni-node created asmcli: Configuring ASM managed control plane revision CR for channels... asmcli: Installing ASM Control Plane Revision CR with asm-managed-rapid channel in istio-system namespace... controlplanerevision.mesh.cloud.google.com/asm-managed-rapid created asmcli: Installing ASM Control Plane Revision CR with asm-managed channel in istio-system namespace... controlplanerevision.mesh.cloud.google.com/asm-managed created asmcli: Installing ASM CanonicalService controller in asm-system namespace... namespace/asm-system created customresourcedefinition.apiextensions.k8s.io/canonicalservices.anthos.cloud.google.com configured role.rbac.authorization.k8s.io/canonical-service-leader-election-role created clusterrole.rbac.authorization.k8s.io/canonical-service-manager-role created clusterrole.rbac.authorization.k8s.io/canonical-service-metrics-reader created serviceaccount/canonical-service-account created rolebinding.rbac.authorization.k8s.io/canonical-service-leader-election-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/canonical-service-manager-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/canonical-service-proxy-rolebinding created service/canonical-service-controller-manager-metrics-service created deployment.apps/canonical-service-controller-manager created asmcli: Waiting for deployment... deployment.apps/canonical-service-controller-manager condition met asmcli: ...done! asmcli: asmcli: ***************************** 1.11.2-asm.17 asmcli: ***************************** asmcli: The ASM control plane installation is now complete. asmcli: To enable automatic sidecar injection on a namespace, you can use the following command: asmcli: kubectl label namespace <NAMESPACE> istio-injection- istio.io/rev=asm-managed --overwrite asmcli: If you use 'istioctl install' afterwards to modify this installation, you will need asmcli: to specify the option '--set revision=asm-managed' to target this control plane asmcli: instead of installing a new one. asmcli: To finish the installation, enable Istio sidecar injection and restart your workloads. asmcli: For more information, see: asmcli: https://cloud.google.com/service-mesh/docs/proxy-injection asmcli: The ASM package used for installation can be found at: asmcli: /root/temp-cluster/asm asmcli: The version of istioctl that matches the installation can be found at: asmcli: /root/temp-cluster/istio-1.11.2-asm.17/bin/istioctl asmcli: A symlink to the istioctl binary can be found at: asmcli: /root/temp-cluster/istioctl asmcli: ***************************** asmcli: Successfully installed ASM.
追加の確認として、(Managed ASM の場合はサポート外ですが) Public Endpoint が無効な Private Cluster に asmcli で In-cluster Control Plane の ASM をインストールした際は問題なく Private Endpoint からクラスタにアクセスし、ASM のインストールに成功していました。
したがって、この事象は Public Endpoint の有効な Private Cluster に限られそうです。
アプリケーションのデプロイ
Managed ASM が利用できるようになったので、Release channel を設定して ASM を利用してみます。
ここでは default namespace で Regular channel を利用し、nginx を起動してみます。
# kubectl get namespaces --show-labels NAME STATUS AGE LABELS asm-system Active 5m34s control-plane=controller-manager,kubernetes.io/metadata.name=asm-system default Active 17h kubernetes.io/metadata.name=default gke-connect Active 7m28s hub.gke.io/project=***************,kubernetes.io/metadata.name=gke-connect,version=20211015-01-00 istio-system Active 7m14s kubernetes.io/metadata.name=istio-system,topology.istio.io/network=***************-default kube-node-lease Active 17h kubernetes.io/metadata.name=kube-node-lease kube-public Active 17h kubernetes.io/metadata.name=kube-public kube-system Active 17h kubernetes.io/metadata.name=kube-system # kubectl label namespace default istio-injection- istio.io/rev=asm-managed --overwrite label "istio-injection" not found. namespace/default labeled # kubectl get namespaces --show-labels NAME STATUS AGE LABELS asm-system Active 5m49s control-plane=controller-manager,kubernetes.io/metadata.name=asm-system default Active 17h istio.io/rev=asm-managed,kubernetes.io/metadata.name=default gke-connect Active 7m43s hub.gke.io/project=***************,kubernetes.io/metadata.name=gke-connect,version=20211015-01-00 istio-system Active 7m29s kubernetes.io/metadata.name=istio-system,topology.istio.io/network=***************-default kube-node-lease Active 17h kubernetes.io/metadata.name=kube-node-lease kube-public Active 17h kubernetes.io/metadata.name=kube-public kube-system Active 17h kubernetes.io/metadata.name=kube-system # kubectl run nginx --namespace default --image nginx pod/nginx created # kubectl get pods --namespace default -o jsonpath="{.items[*].spec.containers[*]['name', 'image']}" nginx istio-proxy nginx gcr.io/gke-release/asm/proxyv2:1.10.4-asm.14 (見やすさのため改行しています)
バージョン 1.10.4-asm.14 の Istio-proxy がインジェクションされました。
ドキュメントでは こちら に各 Release channel で利用される ASM のバージョンが記載されていますが、本記事の執筆時点 (2021/10/26) では実機とドキュメントに乖離がありました。
ドキュメントでは Regular channel が 1.10.4-asm.9 となっていますが、実際に Regular channel を利用してインジェクションされた Istio-proxy は上記のとおり 1.10.4-asm.14 となっています。
今回は紹介しませんでしたが、Managed ASM を利用する場合は Data Plane の自動更新を行う Managed Data Plane も併せて利用できるため、アップグレード運用をマネージドにするなら気にする必要がないといえばそうなのですが、現在の ASM バージョンを確認したい場合は実機を参照したほうが確実と言えそうです。
このとき、Istio-proxy は Google の管理する Managed Istiod と通信をしていますが、Managed ASM の Managed Istiod から Istio-proxy への通信はクライアントアクセスではないため、Master Authorized Networks を有効にする場合でも明示的に許可する必要はありません (送信元がわからないため設定のしようもないはずです) 。
Private Cluster のドキュメントには下記のような記載があり、Managed Istiod も同様にユーザには見えないかたちで Public Endpoint を利用しているものと思われます。
パブリック エンドポイントへのアクセスを無効にしている場合でも、Google は、定期メンテナンスやコントロール プレーンの自動アップグレードなどのクラスタ管理目的で、コントロール プレーンのパブリック エンドポイントにアクセスできます。
引用元:https://cloud.google.com/kubernetes-engine/docs/concepts/private-cluster-concept#overview
ドキュメントとの整合性
前述のように、Private Cluster における Managed ASM のサポートされる環境のドキュメントには下記のような記載がありました。
パブリック エンドポイント アクセスを備えた GKE 限定公開クラスタ。Master Authorized Network (MAN) が有効かどうかは関係ありません
引用元:https://cloud.google.com/kubernetes-engine/docs/concepts/private-cluster-concept#overview
ドキュメントには Master Authorized Networks が有効かどうかは関係ないとありますが、これはあくまで Managed ASM をセットアップした後の状況を指しているものと思われます。
今回検証したようなシチュエーションであれば、運用上は Master Authorzed Networks が有効な Private Cluster への Managed ASM のインストールやその他操作を行うために asmcli を実行するインスタンス/端末の外部 IP アドレスを Master Authorzed Networks で許可する必要があるでしょう。
まとめ
今回の検証を通じて確認したことをまとめます。
- 対象が Public Endpoint の有効な Private Cluster である場合、asmcli は実行元インスタンス/端末がクラスタノードと同一の VPC 上にあったとしても Public Endpoint 経由でアクセスしようとする処理が一部に含まれている
- 上記の asmcli のふるまいから、Master Authorzed Networks が有効な Private Cluster への Managed ASM のセットアップやその他操作を行うためには、asmcli の実行元の外部 IP アドレスを Master Authorzed Networks で許可する必要がある
- Managed ASM における Managed Istiod から Istio-proxy への通信はクライアントアクセスではないため、Master Authorized Networks を有効にする場合でも明示的に許可する必要はない
検証を通じて、Managed ASM と Private Cluster の関係性がより理解できました。
この記事が Managed ASM の利用を検討されている方のお役に立てば幸いです。
補足
コンソールの GKE クラスタ作成フォームから Anthos Service Mesh が有効化できるようになりました。
特に明記されていませんがチェックボックスを入れてクラスタを作成すると Managed ASM が有効化されます。
インストールのためだけであれば asmcli の実行元を Master Authorized Networks に登録する必要なく ASM を使い始められそうにみえますが、まだ Private Cluster + Managed ASM の組み合わせに対応していないようで、フォームから ASM を有効化しようとすると Private Cluster の設定が外されてしまいます。
Private Cluster + Managed ASM の構成をお求めの場合は引き続き asmcli でのセットアップが必要になりますね。