Gremlin Certified Chaos Engineering Professional - GCCEPro を取得しました

カオスエンジニアリングを行うエンジニア向けに Gremlin 社が提供している認定プログラムに、プロフェッショナルレベルの資格として Gremlin Certified Chaos Engineering Professional - GCCEPro が新たに追加されました。
最近 Gremlin を使う機会があったので、受験して取得してみました。

f:id:polar3130:20211028182103p:plain

カオスエンジニアリング とは

大規模な分散システムにおいては、サービス間の相互作用が予測不可能な結果を招くことがあります。
そのような複雑な分散システムに対し、ふるまいの予測と検証を繰り返すことで、システムの信頼性を高めてゆく取り組みがカオスエンジニアリングです。

「カオスを注入する」という誤解を受けることがあるようですが、カオスな状態を抱えているのは検証対象のシステムです。
その検証手段のひとつが 障害の注入 (Fault Injection) になります。
対象システムに障害が発生した際のふるまいが事前の予測と一致するかを検証するために、システムに意図的な障害(ある程度ランダム性を持たせることがあります)を注入し、システムの脆弱性を見つけ出そうとします。
カオスエンジニアリングは、システムの脆弱性の発見と対処を繰り返してゆくことでシステムに回復力をもたらし、その結果としてシステムの信頼性を向上させよう、というアプローチと理解しています。

Gremlin は、障害の注入をシナリオに沿って計画・実行し、実行結果のログ確認やステータスチェックを行うことができるサービスとなっています。

カオスエンジニアリングの考え方やそれが求められるに至った背景については、カオスエンジニアリングの原則 を参照頂くと良いかと思います。

Gremlin の認定資格

現在 Gremlin の認定資格は 2つあります。

  • Gremlin Certified Chaos Engineering Practitioner - GCCEP

カオスエンジニアリング初学者向けに、2021年6月8日から開始された認定プログラムです。
公式のアナウンスは以下になります。

https://www.gremlin.com/blog/announcing-the-gremlin-chaos-engineering-practitioner-certificate-program/

  • Gremlin Certified Chaos Engineering Professional - GCCEPro

今回取得したのはこちらの資格になります。
2021年10月26日から開始された認定プログラムです。 公式のアナウンスは以下になります。

https://www.gremlin.com/blog/announcing-the-gremlin-chaos-engineering-professional-certificate-program/

試験概要

  • 時間    : 45 分
  • 問題数   : 30 問
  • 合否ライン : 80 %
  • 言語    : 英語
  • 出題形式  : 選択問題、自由記述(単語埋め)問題

受験までの流れと注意点

この資格の受験はトレーニングコースの受講とセットになっています。
こちら からサインアップし、Gremlin Certified Chaos Engineering Professional exam (GCCEPro) のコースを受講することで開始します。
前半はカオスエンジニアリングの攻撃手法、技術的・ビジネス的なメリット、Gremlin の基本的な操作方法などを学べるトレーニングとなっていました。

後半に GCCEPro の受験セクションがあり、そちらに遷移するとすぐに試験が始まるようになっています。
試験問題は一度回答すると受験中は前の問題に戻れないようでしたので、あらかじめ注意頂くと良いかと思います。
受験後はすぐに結果が表示され、その後メールでも受験結果が送付されてきました。

おわりに

シナリオに応じて適切な攻撃手法を選定したり、攻撃手法に応じた技術的なユースケースなどの概念的な知識のおさらいができました。
加えて、各攻撃手法が Gremlin ではどのように実装されているのか、チームで Gremlin のシナリオを管理するためにコンソールのどこから何が設定できるのか、といった Gremlin の使い方の詳細についても、普段自分が使っていなかったメニューの知識を得るきっかけになりました。

試験監督によるモニタリングなどのないオンライン試験で、公式ドキュメントの参照も可能、Gremlin のコンソールを見て回答を確認することも可能(むしろ推奨らしい)、となっていますので、英語での受験が問題なければ難易度はそれほど高くない試験かと思います。
ただし、Gremlin を使ったことがある前提の試験となっている(特定コマンドのパラメータレベルの知識を問う問題もありました)ため、未使用の方はトライアルなどで Gremlin の機能をひととおり試してから受験されることをおすすめします。

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 との大きな違いです。

f:id:polar3130:20211026132503p:plain

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 を示しています。

f:id:polar3130:20211026132738p:plain

上記の 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 でのセットアップが必要になりますね。

f:id:polar3130:20211026133822p:plain

Google Cloud Deploy を触ってみた

GCP の継続的デリバリのための新しいサービスとして、Google Cloud Deploy (以下、Cloud Deploy) が登場しました。

どんなものか触ってみました。

Google Cloud Deploy とは

デリバリパイプラインからマニフェストレンダリング (YAML の生成) を分離することで、よりシンプルに継続的デリバリを実現するためのマネージドサービス、と理解しています。

マニフェストレンダリングとデプロイには Skaffold が使用されています。

この記事の執筆時点 (2021年9月27日) では Preview のサービスとなっています。

Google Cloud の中の人の紹介ブログは こちらこちら

Cloud Deploy によるデプロイの流れ

詳しくは公式ドキュメントを参照頂ければと思いますが、ざっと書くと以下のような流れになります。

  1. デリバリパイプラインとターゲットを YAML で記述する
  2. デリバリパイプラインとターゲットを Cloud Deploy に登録する
  3. Cloud Deploy に登録されたパイプラインを指定してリリースを作成する
  4. リリース作成時にマニフェストレンダリングが行われ、ターゲット毎のマニフェストとソースファイルが GCS に格納される
  5. リリース作成後、パイプラインの最初のターゲットに自動でロールアウトが開始される (Skaffold を使ってターゲットにマニフェストが適用される)
  6. ロールアウトに成功すると、次のターゲットへの promote (ロールアウトの作成) が行える

マニフェストレンダリングはリリース作成時のみに行われ、以後の各ターゲットへのロールアウトと分離されているところがポイントですね。
ターゲット毎のマニフェストの差分を Skaffold の Profile で定義できるため、環境毎のイメージタグの使い分けなどが簡単に管理できそうです。

補足

  • ターゲットランタイム (リリースのロールアウト先) は現在のところ GKE のみ対応
  • パイプラインは直列な定義のみをサポートしており、複数のターゲットに同時並行でロールアウトすることはできない
  • マニフェストレンダリング、およびターゲットへのデプロイを行う Skaffold の実行環境には Cloud Build を用いる
  • 実行環境の Cloud Build にはプライベートワーカープールも利用可能
  • ターゲット毎に手動承認プロセスの要否を設定できる
  • 現状は GCP コンソールからリリースやターゲットを設定することができない (リリースの promote やロールアウト失敗時の再デプロイはコンソールから実行可能)
  • リリース作成時に、ソースファイル (Skaffold の Config、レンダリング元となる Kubernetes マニフェスト、パイプライン定義) がまとめてスナップショットされるため、途中でパイプラインが変更されても「このリリースがどのパイプライン定義に基づいているか」が追跡できる

気になったポイント

実際の開発に Cloud Deploy を導入することを想定したとき、気になりそうなポイントをいくつか挙げてみます。

限定公開クラスタでの利用

ターゲットランタイムが限定公開クラスタの場合、 (パブリックエンドポイントの有無に関わらず) Cloud Build はプライベートワーカープールを使うほうが良さそうだという印象を受けました。

Cloud Deploy からターゲットランタイムへのデプロイは、Cloud Build からの skaffold apply として実行されるため、実際には Cloud Build からクラスタへのアクセスを考える必要があります。

Cloud Deploy は特に指定しなければデフォルトの Cloud Build ワーカープールで実行されますが、パブリックエンドポイントを持つ限定公開クラスタの場合は、アクセス元 (Cloud Build) の IP アドレスが動的に変化するためマスター承認済みネットワークの設定が難しくなります (マスター承認済みネットワークを設定しない、あるいは動的に書き換えるといった対応が必要になりそうですが、いずれも容易な選択肢ではないように思います) 。
そのため、パブリックエンドポイントがある限定公開クラスタであっても、Cloud Build はプライベートワーカープールを設けてプライベート IP アドレスからコントロールプレーンにアクセスするほうがアクセス制御がやりやすそうです。

プライベートエンドポイントのみの限定公開クラスタを利用する場合、今後は Cloud Build にプライベートワーカープールを選択することが多いと思いますので、その場合は問題にならないですね。

構成方法はこちらが参考になります。                                                                                                                                                                                                                                                                                                                                                                                 

Skaffold のバージョン管理

Cloud Deploy がサポートする Skaffold のバージョンは、現在のところ 1.24.0 となっています。
90日毎に新しいバージョンを追加して、以後のリリースのデフォルトバージョンとなります。
今のところ、Cloud Deploy がレンダリングやデプロイで使用する Skaffold のバージョンを指定することはできません。

サポートのリストに追加された Skaffold バージョンは 12 か月間+α のあいだサポートされるようですが、マニフェストレンダリングなどでバージョン間の不整合が起きる可能性を回避したいと考えるとローカルの開発環境と Skaffold のバージョンを合わせておくほうが望ましいと思いますので、Cloud Deploy の Skaffold バージョンの更新に合わせて 3か月毎に開発環境の Skaffold のバージョンを更新する運用が必要になりそうです。

今後期待したいアップデート

国内リージョン対応

東京、大阪リージョンではまだ使えませんでした。

Cloud Deploy (のデリバリパイプライン)とターゲットランタイムが同一のリージョンである必要は無いようですが、デリバリパイプラインを配置したリージョンに応じて Cloud Deploy インスタンスが決まるため、ターゲットランタイムと同じリージョンにデリバリパイプラインを配置したいというケースはありそうです。

VPC Service Controls 対応

現在は未対応となっています

ビルドアーティファクトを格納している Artifact Registry や GCS が VPC SC で保護されているというケースはあると思いますし、Cloud Build をプライベートワーカープールで利用する場合はこちらも VPC SC で保護したいケースがあると思いますので、今後の対応を期待したいですね。

API メソッドの一覧を見ると、promote や承認も個別の権限として定義されているため、VPC SC に対応すれば Ingress/Egress Policy と併せて柔軟な権限制御が行えそうです。

デプロイ戦略の選択肢の拡充

現状はレンダリングされたマニフェストクラスタへ単純に展開するため、Cloud Deploy 単体によるデプロイは Inplace(Rolling Update)になります。

一方で、本番環境では Blue/Green や Canary を使って稼働中のバージョンのアプリケーションを変更することなくリリースしたいというケースも少なくないと思いますので、そうしたデプロイ戦略を Cloud Deploy で簡単に扱えると嬉しいなと思います。

また、発展形として Progressive Delivery のサポートにも期待したいですね。
デプロイ後のアプリケーションのメトリクスに基づいてリリースの良し悪しを判定し、必要に応じて自動でロールバックするような仕組みがあると便利です。

Netskope を使って、SaaS の利用をユーザベースで制御する

Netskope を使って、特定の SaaS の利用をユーザベースで制御する

 

最近 Netskope を使う機会があり、設定を色々試しています。

Netskope は CASB(Cloud Access Security Broker)と呼ばれるセキュリティソリューションのひとつです。

https://www.netskope.com/products/casb

 

今回は、特定のクラウドサービスに対して、指定したドメインのユーザだけを利用許可するための設定を紹介します。

例えば、企業の開発端末などに適用するセキュリティポリシとして、企業のドメインのユーザでのみ Gmail の利用を許可し、その他のドメイン(例えば個人利用)のユーザでは Gmail の利用を制限したい、といったユースケースに利用できます。

宛先 URL や送信元 IP アドレスに基づく単純なフィルタリングでは、利用するユーザに応じてクラウドサービスの利用を制限することは困難な場合が多く、こうしたクラウドサービスへのきめ細かいアクセス制御が求められる状況において Netskope が活躍します。

 

以下では、前述の例をとり、指定したドメイン(@hoge.com)の Google アカウントにのみ Gmail の利用を許可するための設定手順を示します。

動作を確認した Netskope のバージョンは 74.0.0.347 です。

 

設定手順

  1. Home > Policies > Profiles > Constraints のメニューを開き、”New User Constraint Profile” をクリック(新しい Constraint を作成)します。

  2. User Constraint Profile の新規作成画面が表示されたら、適当なプロファイル名を入力し、”EMAILS” を次の通り設定します。
    - プルダウンメニューで “DOES NOT MATCH” を選択する
    - アドレス入力欄に “*.@hoge.com” を入力する

    User Constraint Profile では上記のように正規表現を利用したマッチングが可能となっています。
    入力が完了したら “SAVE CONSTRAINT PROFILE” をクリックすると「hoge.com 以外の Email アドレス にマッチするプロファイル」が作成されます。

  3. 新規プロファイルの作成に成功していれば、Constraints メニューの “APPLY CHANGES” ボタンが活性化しているはずです。これをクリックして変更理由を記入し、”APPLY” ボタンをクリックして変更を適用します。

  4. 次に、作成したプロファイルを Gmail へのアクセス制御設定と関連付けしてゆきます。
    Home > Policies > Real-time Protection のメニューを開き、”NEW POLICY” をクリックして次のとおりポリシを設定します。
    - Cloud Apps + Web: Google Gmail
    - Select Activities:
         Activities: Select All
         Constraint (From User): 1. で作成した Profile を指定
         Constraint (To User): No constraints
         Action: Block
    その他の項目は適当なものを設定し、 “SAVE”  をクリックすると新規ポリシが作成されます。

  5. 新規ポリシの作成に成功していれば、Real-time Protection メニューの “APPLY CHANGES” ボタンが活性化しているはずです。これをクリックして変更理由を記入し、”APPLY” ボタンをクリックして変更を適用します。

  6. 以上で設定は完了です。エージェントへのポリシ配信に数分程度時間を要する場合があるため、一定時間待ってから、設定が想定どおりのものとなっているか確認してみてください。ポリシが正しく適用されていれば、hoge.com ドメインGoogle アカウントでのみ Gmail が利用できるようになっているはずです。


今回は、Gmail を例に設定方法を紹介しましたが、ユーザのドメインを限定したクラウドサービスへのアクセスはいずれも上記とほぼ同じ手順で設定可能です。

 

補足

  • 手順 6. で hoge.com以外の Google アカウントが Gmail へのログインを試みると、ログインプロセスまではブロックされずに進めることができます(ログインに成功した後、Gmail の受信トレイは表示できずにブロックされるはずです)。これは Netskope の Real-time Protection において、Gmail の Activities にログインを特定するメニューがないためです。ログインに成功しても何も見れないので実害はありませんが、ログインそのものは上記設定では禁止できません。

  • プロファイル名やポリシ名に.(ドット) が使用できないため、ドメインをプロファイル名やポリシ名に記載したい場合は別の特殊文字で代替する必要があります(ドットの代わりにアンダーバーを使う、など)

CKA / CKAD を取得しました

CNCF (Cloud Native Computing Foundation) が提供している、ふたつの Kubernetes の認定資格を取得しました。
受験準備や参照したリソースについて書き残しておきたいと思います。

試験概要

Linux Foundation 傘下で Kubernetes をはじめ様々な Cloud Native 関連の OSS プロジェクトをホストしている CNCF (Cloud Native Computing Foundation) という組織があり、そちらから提供されている監督付きのオンライン試験となっています。

CKA (Certified Kubernetes Administrator)

Kubernetes クラスタの構築・運用を行う管理者を対象とした試験です。

  • 試験時間:3 時間
  • 問題数:24 問
  • 受験料:$300
  • 試験官との応対言語:英語
  • 試験問題の言語:英語・日本語・中国語を適宜切り替え可能

CKAD (Certified Kubernetes Application Developer)

Kubermetes でアプリケーションを実行する開発者を対象とした試験です。

  • 試験時間:2 時間
  • 問題数:19 問
  • 受験料:$300
  • 試験官との応対言語:英語
  • 試験問題の言語:英語・日本語・中国語を適宜切り替え可能

試験官との応対、および試験の言語

私は通常の CKA / CKAD で受験したため試験官との応対は英語で行いました。
日本語話者の試験官を希望する場合は、CKA / CKAD ではなく CKA-JP / CKAD-JP に申し込む必要があるようです。

CKA / CKAD で受験した場合でも、試験問題は日本語を選択可能です。
したがって英語を使う必要があるのは試験前の環境確認や注意事項の案内など簡単なやりとりに限られますが、不安があれば CKA-JP / CKAD-JP での申し込みを検討されても良いかもしれません。

試験内容のアップデート

Kubernetes のリリースに合わせて、試験は四半期ごとに更新される計画となっています。
最新の試験環境バージョンは各試験のFAQのリンクから確認できます。

https://training.linuxfoundation.org/wp-content/uploads/2020/02/CKA-CKAD-FAQ-02.2020.pdf

私が受験した際はちょうど更新がかかったばかりのところで、CKA / CKAD どちらも試験環境の Kubernetes は最新の v1.17 となっていました。
当然ですが、試験問題も Kubernetes のバージョンに合わせてアップデートされていました。

再試験ポリシー

試験に落ちた場合、1 回まで再受験が可能となっているようです。
そのため、ある程度準備ができたところで試験内容のレベル感を把握するために一度受験してみる、といったアプローチも良いかと思います。
私は CKA / CKAD いずれも 1 回目の受験で合格したため利用することはありませんでした。

おすすめの学習リソース

Kubernetes 完全ガイド

book.impress.co.jp

言わずとしれた Kubernetes 関連の代表的な参考書です。
Kubernetes v1.11 をベースに書かれており、最新の Kubernetes のバージョン(v1.17)とは少し離れてきていますが、今でも Kubernetes の全体像や各リソースの基礎を抑えるには最適な一冊です。

Kubernetes The Hard Way

github.com

現在は kubeadm や kubespray といったクラスタ構築をサポートするソフトウェアがありますが、こちらのチュートリアルではそういったサポートなしに Kubernetes クラスタを構築する流れを学ぶことができます。
CKAD だけの受験を考えている方は必要ありませんが、CKA を受験する方は何周か実施して流れを掴んでおくと良いでしょう。

CKAD Exercises

github.com

有志の方が公開している CKAD 向けの問題集です。
CKA でも同じような知識は求められるため、CKA のみの受験を考えている方にもおすすめの学習リソースです。

ckad-prep-notes

github.com

問題集やチュートリアルではないのですが、CKADの受験にあたって注意すべき事項と必要な基礎知識がまとめられていますので、一読しておくことをおすすめします。

その他の学習リソース

kodekloud.com

コミュニティでこちらのチュートリアルも試験対策に好評と聞き、数問だけ手を付けてみました。
が、私が利用した際はバーチャルコンソールの反応が遅く、結局はほとんど利用しませんでした。
問題の内容は良さそうでしたので、レスポンスが問題なければこちらも活用できるのではと思います。

学習の流れ

試験準備として Kubernetes The Hard Way を1周、CKAD-exercises を 2 周実施し、ckad-prep-notes と 公式の受験要項をひととおり読みました。
試験とは無関係に以前から Kubernetes The Hard Way は何度か通したことがあったため、今回の受験準備としては流れを確認する程度にとどめています。
Kubernetes 完全ガイド』も既に何度か読み直していましたので今回の受験向けには改めて通し読みはしていませんが、必要に応じてかいつまんで読むようなかたちで復習に活用しました。

(おまけ)Kubernetes ポケットリファレンス

gihyo.jp

昨年出版されたこちらの書籍の制作に参加しておりまして、おまけの参考書籍としてご紹介させて頂きます。
書名のとおりコマンドリファレンスにフォーカスした書籍となっており、各コマンドでよくあるトラブルなどにも触れていますので、kubectl に習熟する過程でお役に立つのではと思います。
制作にあたっては Kubernetes v1.16 をベースに各コマンドの実行確認やドキュメントチェックなどを繰り返していましたので、私の場合はこの書籍の制作過程でも Kubernetes に対するスキルアップが図れました。

受験準備

受験場所

試験はオンラインで実施されるため、受験場所は受験者自身があらかじめ確保しておく必要があります。
私の場合はコワーキングスペースで提供されている1名用個室を予約して受験しました。
受験前には試験官との受験環境確認があり、受験後にはアンケート回答があるため、試験の予定時間+前後30分を目安に場所を確保しておくと良いと思います。

受験するときのテーブル上には、許可されたもの(ディスプレイ、キーボード、マウスetc.)以外は一切置くことができません。
私の場合はレンタルした個室のテーブルにシェードランプが置かれており、受験前の環境確認の際に取り外すよう試験官から指示を受けました。
幸い取り外し可能な備品でしたので対応できましたが、レンタルしたスペースに備え付けの備品があるような場合は注意が必要かと思います。

Kubernetes.io の活用

試験中には公式ドキュメントを参照することができますが、試験時間を考えるとその場で新しい知識を仕入れている余裕はないと思います。
私の場合はよく参照するいくつかのページはすぐにアクセスできるよう、ブラウザのブックマークに登録しておくようにしました。

受験時の所感

試験の難易度

個々の問題の難易度としては、管理者向けを想定している CKA のほうがより高度な知識を求められたと思います。
一方、CKAD のほうが 1 問あたりに使える時間が少ないため、こちらはスピーディに問題を解いてゆかなければならないところに難しさがありました。
問題ごとに配点の目安となるパーセンテージが明記されていますので、これを参考に回答順序を考えても良いでしょう。

バーチャルコンソールの操作性

試験時に利用するバーチャルコンソールはレスポンスよく利用でき、特に不自由は感じませんでした。
コピーは Ctrl+Insert、ペーストは Shift+Insert で行えます(Windowsの場合)。
tmux も使えるようですが私は利用しませんでした。

ブラウザの制限

ブラウザのタブは、試験問題+バーチャルコンソールのタブがひとつと、これに加えて Kubernetes.io の公式ドキュメントを参照するためのタブをひとつのみ開くことができます。
公式の受験要項にも記載されていますが、Kubernetes.io の公式ドキュメント以外の Web ページにアクセスしないことや、ドキュメント参照のために複数のタブを開かないことは受験者自身の努力で遵守する必要があります。
特に試験で利用する端末にアクセス制限や開くタブ数の制限が設定されるわけではないため、ブックマークなどを利用する際はうっかり許可されていないページにアクセスしないよう注意が必要です。

おわりに

普段 Kubernetes での開発や運用を経験している方であれば、基本を押さえておけば問題の難易度はそれほど高くないと思います。
CKA / CKAD はすべての問題をコマンドラインから回答する形式となっているため、kubectl や Linux の基本的なコマンドはとにかく手を動かして慣れておくことが重要です。
加えて、よく参照するドキュメントにすぐにアクセスできるような準備や、配点に応じた時間配分を意識しておくと良いかと思います。