CKS (Certified Kubernetes Security Specialist) を取得しました

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

過去に CKAD, CKA を取得した際の受験記録は こちら です。

試験概要

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

CKS (Certified Kubernetes Security Specialist)

Kubernetes のプラットフォームとコンテナベースのアプリケーションに関するセキュリティスペシャリストを対象とした試験です。
受験するためには CKA の認定を保持している必要があります。

  • 試験時間:2 時間
  • 問題数:15 ~ 20 問
  • 受験料:$375
  • 試験官との応対言語:英語
  • 試験問題の言語:英語・日本語・中国語を適宜切り替え可能
  • 受験したときの Kubernetes のバージョン:v1.22

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

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

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

試験概要

詳しくは公式サイトをご確認頂くのが良いかと思いますが、特に下記のドキュメントは目を通してから受験されることをおすすめします。
試験のレギュレーションをよく理解しておくことで、限られた時間で効率よく問題を解いてゆくことができると思います。

  • Candidate Handbook
  • カリキュラムの概要
  • Exam Tips
  • Frequently Asked Questions

再試験ポリシー

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

おすすめの学習リソース

Docker / Kubernetes 開発・運用のためのセキュリティ実践ガイド

book.mynavi.jp

Kubernetes やコンテナアプリケーションのセキュリティ対策に関するベストプラクティスがまとめられている書籍です。
試験対策を目的とした書籍ではありませんが、関連するトピックが多数含まれるほか、解説も丁寧で実践的な内容となっています。
CKS の受験にあたり何から手を付ければ良いか迷われている方は、まずはこの本を通読するところから始めるのがおすすめです。

Kubernetes 完全ガイド 第2版

book.impress.co.jp

言わずとしれた Kubernetes 関連の代表的な参考書です。
第 2 版が出版され、Kubernetes v1.18 をベースとした内容にアップデートされています。 Kubernetes の機能が網羅的に解説されており、今回 CKS の試験対策としては辞書的な使い方で活用しました。

Killer Shell (Killer.sh) CKS Simulator

killer.sh

CKS の本番の試験よりも難しいとされる試験シミュレータです。
CKS の試験登録を行うと、2 回分(1 回あたり 36 時間有効) の使用が可能で、有効な時間内であれば何度でも環境をリセットすることができるようになっています。
なお、このシミュレータは何度受験しても同じ内容・同じ出題順序となっています。

CKS Curated Resources

github.com

問題集やチュートリアルではないのですが、CKS の受験にあたって注意すべき事項と必要な基礎知識がまとめられていますので、一読しておくことをおすすめします。
私は利用しませんでしたが、Udemy で提供されている CKS 試験対策コースはこのサイトのリンクから割引で購入できます。

受験準備

 学習の流れ

まずは『Docker / Kubernetes 開発・運用のためのセキュリティ実践ガイド』をひととおり読みました。
ここで Kubernetes やコンテナアプリケーションのセキュリティに関する概要レベルの全体像が掴めたことで、後の学習がスムーズになったと思います。

その後、Killer Shell (Killer.sh) CKS Simulator を 2 周して試験に挑みました。
試験対策という観点では、Killer Shell の問題を解き、不明点を再学習するというサイクルを繰り返したのが効いたと思います。

受験場所

試験はオンラインで実施されるため、受験場所は受験者自身があらかじめ確保しておく必要があります。
私の場合、今回は自宅で受験しました。

受験するときのテーブル上には、許可されたもの(ディスプレイ、キーボード、マウスetc.)以外は一切置くことができません。
過去の CKAD, CKA の受験時もそうだったかは失念してしまったのですが、今回、ラベルを外した状態の水のペットボトルはテーブルに置くことが許可されていました。
また、私はキーボードとマウスにそれぞれアームレストを使っているのですが、こちらも利用可能でした。

Kubernetes.io, Falco, Trivy などの参照可能ドキュメントの活用

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

受験時の所感

試験の難易度

Kubernetes やコンテナベースのアプリケーションのセキュリティに関して幅広いトピックが出題され、全体の難しさとしては CKA と同じくらいのレベルではないかと感じました。
セキュリティに関して幅広い出題範囲が設けられていますが、基本をよく理解しておけば回答可能な問題が多く、重箱の隅をつつくような問題はなかったという印象でした。
問題ごとに配点の目安となるパーセンテージが明記されていますので、これを参考に回答順序を考えても良いでしょう。

ブラウザの制限

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

おわりに

幅広い出題範囲のおかげ(?)で、Kubernetes やコンテナアプリケーションのセキュリティに関する様々な知識を学習・再認識する機会になりました。
CKA 認定保持者を対象とした試験のため、受験プロセスには慣れている方が多いかと思いますが、コンソールや細かなレギュレーションのアップデートなどもあるため、受験要項などの公式ドキュメントはよく読んだうえで受験されることをおすすめします。

GKE Autopilot の Spot Pod とプリエンプションの関係

Google Cloud Platform Advent Calendar 2021 の 9 日目の投稿です。
(執筆を予定されていた方が投稿されなかったため代わりに投稿させて頂きます)

先日、GKE Autopilot で Spot Pod が利用可能になりました(執筆時点ではプレビュー)。
Google Cloud の Blog に投稿されたアナウンスの記事は こちら

中断可能なワークロードに対して大幅なコスト削減が期待できるオプションですが、活用にあたっていくつかの制約を理解しておく必要があり、この記事ではそのひとつとして Spot Pod のシャットダウン時の仕様について触れたいと思います。

Spot Pod とプリエンプション

Spot Pod は、Spot Pod の利用をリクエストするための nodeSelector または affinity を Pod のテンプレートに付与することで、GKE Autopilot が GCE の Spot VM を自動でプロビジョニングして Spot Pod をデプロイする、というかたちで使用されます。

GKE 1.20 以降の Preemptible VM / Spot VM では、Graceful Node Shutdown が有効化&設定されています。
利用している Spot VM がプリエンプションの対象となった場合は、Pod を安全に終了させるための猶予期間がリクエストできるようになっており、最長 25 秒 ※ の猶予期間をリクエスト可能です。

※ Autopilot ワークロードの Spot Pod をリクエストする
https://cloud.google.com/kubernetes-engine/docs/how-to/autopilot-spot-pods#request-spot-pods

プリエンプションでの Spot Pod の最大猶予期間は 25 秒です。

このリクエストできる期間の長さは Graceful Node Shutdown の設定によって上限が決まる仕組みになっています。
実際の設定を見てみましょう。

apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: /etc/srv/kubernetes/pki/ca-certificates.crt
authorization:
  mode: Webhook
cgroupRoot: /
clusterDNS:
- 10.12.0.10
clusterDomain: cluster.local
enableDebuggingHandlers: true
evictionHard:
  memory.available: 100Mi
  nodefs.available: 10%
  nodefs.inodesFree: 5%
  pid.available: 10%
featureGates:
  DynamicKubeletConfig: false
  ExecProbeTimeout: false
  GracefulNodeShutdown: true
  InTreePluginAWSUnregister: true
  InTreePluginAzureDiskUnregister: true
  InTreePluginOpenStackUnregister: true
  InTreePluginvSphereUnregister: true
  RotateKubeletServerCertificate: true
kernelMemcgNotification: true
kind: KubeletConfiguration
kubeReserved:
  cpu: 1060m
  ephemeral-storage: 41Gi
  memory: 1019Mi
readOnlyPort: 10255
serverTLSBootstrap: true
shutdownGracePeriod: 30s
shutdownGracePeriodCriticalPods: 5s
staticPodPath: /etc/kubernetes/manifests

Autopilot ではノードの設定が参照できないため、上記では GKE Standard で Spot VM としてプロビジョニングしたノードの kubelet の設定を参照しています(kubelet v1.21.5-gke.1302, cos-containerd, Spot VM の設定で GKE Standard にプロビジョニングしたノードの kubelet-config )。
Autopilot がプロビジョニングする Spot VM と全く同じ設定かどうかは確認する術がありませんが、ドキュメントを読む限り Graceful Node Shutdown まわりの設定は同じような設定になっていると考えても良さそうです。

設定内容を見てみると、kubelet の featureGates で Graceful Node Shutdown が有効化されています。
Graceful Node Shutdown に関連するパラメータは以下のふたつです。

  • shutdownGracePeriod: 30s
  • shutdownGracePeriodCriticalPods: 5s

Kubernetes の Graceful Node Shutdown のドキュメント ※ を読むと、ノードのプリエンプション時に Pod がリクエスト可能な terminationGracePeriodSeconds の最長期間は、CriticalPods と呼ばれるシステム系の Pod(Pod Priority が system-cluster-critical または system-node-critical)と、それ以外の Pod を区別して設定できるようになっています。
通常の(システム系以外の)Pod については、 shutdownGracePeriod の値からshutdownGracePeriodCriticalPods の値を引いた残り時間が、リクエスト可能な terminationGracePeriodSeconds の最長期間となります。

※ Graceful node shutdown
https://kubernetes.io/docs/concepts/architecture/nodes/#graceful-node-shutdown

GKE 1.20 以降の Preemptible VM / Spot VM では、上記の設定により (shutdownGracePeriod) - (shutdownGracePeriodCriticalPods) = 25 秒 が terminationGracePeriodSeconds でリクエスト可能な最長期間であることがわかります。
これが Spot Pod や、Spot VM で実行する Pod の terminationGracePeriodSeconds の上限としてドキュメントに記載されているということですね。

まとめ

Spot Pod のシャットダウン時の仕様と、(GKE Standard の Spot VM の設定から得られる推測を含みながらではありますが)それを実装している Graceful Node Shutdown の設定を確認しました。

Spot Pod のシャットダウンについては以下を認識しておくと良いでしょう。

  • Spot Pod は載っているノードのプリエンプションが発生した際に Pod を安全に終了させるための猶予期間が指定できる(terminationGracePeriodSeconds)
  • Spot Pod の terminationGracePeriodSeconds は最大 25 秒がリクエストでき、それ以上の値を指定しても無視される
  • Spot Pod の terminationGracePeriodSeconds はあくまでもリクエストであり、指定しても必ずその猶予期間が確保されるとは限らない

以上です。

あなたにぴったりな Anthos Service Mesh を見つけよう

Google Cloud Platform Advent Calendar 2021 の 1 日目の記事です。

Anthos Service Mesh (以下、ASM) は Google Cloud が提供しているマネージドサービスメッシュです。
ASM には今年1年で多くのアップデートがありました。
GCP 以外の環境 (他のパブリッククラウドやオンプレミス) でも利用できるという Anthos のコンセプトにも関係しているのか、今年の ASM のアップデートでは実行環境やプロジェクトの要件に応じた複数の利用形態が登場しています。

選択肢が増えたことでドキュメントも拡充され、分量が増えているため、どのユースケースにどの利用形態が向いているのか、またどのオプションがどの利用形態で利用できるのか、といった情報を端的に掴むのがやや難しい面もあるのではないかと思います。
そこで今回は、ASM の利用形態と想定されるユースケースの違いについてざっくりと概要をご紹介し、これから ASM の導入を検討されている方に向けてヒントとなる情報を記せればと考えました。
なお、この記事では、GCP 上の GKE (公式ドキュメントでいうところの GKE clusters on Google Cloud) で利用する ASM の話にスコープを絞ります。

各構成の概要

記事執筆時点 (2021年11月29日) では、各構成の概要は以下のようになっています。
表に続いて、構成間の主な違いや共通点を説明します。

f:id:polar3130:20211130230021p:plain

構成間の主な違い

ASM In-cluster Control Plane (以下、ASM In-cluster CP) はコントロールプレーン (Istiod) をユーザのクラスタ内にデプロイする利用形態となっています。
従来から提供されてきた ASM の構成であり、Istiod のアップグレードタイミングやキャパシティを自分たちで制御したい場合に適しています。

f:id:polar3130:20211125144504p:plain

Managed ASM はコントロールプレーン (Istiod) を Google が管理する新しい利用形態です。
以下のイメージ図に示すように、コントロールプレーン (Istiod) がユーザのクラスタにデプロイされずとも、ASM を利用することができます。
Managed ASM では Istiod のアップグレードやキャパシティ管理をユーザが考慮する必要がなくなります。

f:id:polar3130:20211026132503p:plain

加えて、Managed ASM では Managed Data Plane の機能を利用することで、データプレーンにあたる Istio-proxy (Envoy) サイドカーのアップグレードを自動化することができます。
この構成では、以下のイメージ図に示すように、Data Plane Controller というコンポーネントがユーザのクラスタにデプロイされます。
Managed ASM のコントロールプレーンがアップグレードされる際に、併せてデータプレーンもアップグレードされます。

f:id:polar3130:20211125144453p:plain

関連する要素

GKE のタイプ

GKE には現在、ノードの管理をユーザが行う Standard と、ノードの管理が不要になる Autopilot の 2 つの利用形態があります。
ASM In-cluster CP を利用する場合は、GKE が Standard クラスタである必要があります。
Managed ASM を利用する場合は、GKE クラスタの構成に制限がありません。つい先日までは、Autopilot では利用できない、Private Endpoint のみの Private Cluster (限定公開クラスタ) では利用できない、といった制限がありましたが、最新の 1.11.x では Managed ASM が全ての GKE クラスタの構成をサポートするようになりました。
https://cloud.google.com/service-mesh/docs/release-notes#November_19_2021

 セキュリティ

セキュリティ関連では、CA の構成と VPC Service Controls を取り上げます。

CA はオープンソースの Istio の場合 Istio CA (以前の Citadel) が利用されますが、ASM では Mesh CA というマネージドの CA が提供されています。
ASM In-cluster CP では、デフォルトで Mesh CA を利用する構成となりますが、ASM をインストールする際のオプションで Certificate Authority Service (以下、CAS) を利用することも可能です。
CAS はプライベート CA を Google マネージドなインフラストラクチャで管理できるようにするためのサービスです。
Managed ASM はこれまで Mesh CA のみがサポートされていましたが、最新の 1.11.x では CAS もサポートされるようになりました。
(最新のリリースによって構成間の差異が無くなってしまった部分ではあるのですが、まだあまり知られていない変更点かと思いましたのでここで取り上げさせて頂きました)

VPC Service Controls (以下、VPC SC) は、リソースアクセスの論理的な境界を設けることで、境界内の GCP リソースのデータを保護する機能です。
機微情報を扱うアプリケーションなどで利用されることが多い機能のひとつとなっています。
ASM を構成した GKE や、Mesh CA もまた、GCP リソースのひとつであるため、(サポートされていれば) VPC SC の境界内に含めることができます。
ASM In-cluster CP がインストールされた GKE は VPC SC のサポート対象であるため、VPC SC でリソースを保護することが可能です。
一方、Managed ASM はまだサポートされていないため、VPC SC が利用できません。

なお、VPC SC のサポート対象リソースは以下で確認できます。

https://cloud.google.com/vpc-service-controls/docs/supported-products

課金

課金については ASM In-cluster CP と Managed ASM で利用できる選択肢に違いがないのですが、ASM を知るひとつの材料になると思いますのでここでご紹介したいと思います。

ASM は Anthos スイートの機能のひとつであり、以前は Anthos としての契約(Anthos API の有効化)を行うことでしか利用できませんでした。
Anthos の契約の一部として ASM を利用する場合には、Anthos 全体の利用料金の中に ASM の課金も含まれるようになっていましたが、今年のアップデートで Standalone ASM という ASM だけを利用する課金形態が追加されました。
これにより、Anthos のその他の機能は不要だがマネージドサービスメッシュだけ利用したい、といったユーザでも、Anthos の契約をせず従量課金で気軽に ASM を利用できるようになっています。
ASM In-cluster CP と Managed ASM のどちらであっても、Anthos API を有効化しての課金と、Standalone ASM での従量課金 の両方に対応しています。

構成を問わず共通して利用できる機能

mTLS の強制や、Ingress/Egress Gateway など、Istio としての基本的な機能は ASM In-cluster CP と Managed ASM のどちらでも利用可能です。
また、Cloud Operations (Cloud Monitoring, Cloud Logging, Cloud Trace, etc.) との統合や、Multi Cluster Service / Multi Cluster Ingress の利用といった Google Cloud の他の機能との統合も、多くの場合は ASM In-cluster CP と Managed ASM のどちらでも対応されています。

ユースケース

上記に示したように、現在の ASM には (Google Cloud の GKE で利用する場合だけでも) 複数の構成の選択肢があります。
以下では、いくつかのユースケースを提示しながら、各要件に合った ASM の構成をご紹介したいと思います。

ケース1. 世の中でより実績のある構成を求める場合

→ GKE Standard + ASM In-cluster CP + MeshCA

ASM のローンチ当初からサポートされてきた構成であり、GKE Autopilot や Managed ASM の登場以前は ASM と言えばこの構成を指していました。
Web で公開されている情報も ASM の利用形態の中で最も多く、 (私見ですが) 最も標準的な ASM の利用形態と言えるのではないかと思います。
構成上の制約が少なく、Private Cluster や VPC SC を使ったエンタープライズ向けの利用にもおすすめです。

この構成では GKE, ASM それぞれにアップグレードやキャパシティをユーザが管理してゆく必要があります。
GKE, ASM のアップグレードについては CloudNative Days Tokyo 2021 に登壇した際の資料で詳しくご紹介していますので、ご興味あればご参照頂ければと思います。

ケース2. とにかく一番手軽に使える ASM が欲しい場合

→ GKE Autopilot + Managed ASM + managed Data Plane + MeshCA + Standalone ASM

GKE, ASM の運用負荷が最小となる構成です。
GKE は Autopolot の場合リリースチャネルの利用が必須となるため、ユーザが GKE のコントロールプレーンおよびデータプレーン(ノード)のアップグレードを検討する必要はありません。
ASM もコントロールプレーンが Google によって管理され、コントロールプレーンの構成に伴ってデータプレーンも自動アップグレードされるため、ユーザによるアップグレードの検討は不要です。
Istio の各種機能を、最もインフラを意識せずに利用できる構成とも言えるのではないでしょうか。

CA は Mesh CA を利用することで証明書の管理も Google に任せることができ、Standalone ASM であれば Anthos API の有効化も不要です。
最近、Managed ASM はコンソールの GKE クラスタ作成フォームからも有効化できるようになりましたので、セットアップも非常に簡単化されています。
まずは ASM を試してみたい、という方におすすめです。

f:id:polar3130:20211026133822p:plain

ケース3. 可能な限りマネージドな領域を減らしたい場合

→ GKE Standard (+ Private Cluster) + ASM In-cluster CP + CAS

一見マネージドサービスの考え方とは反するように思えるかもしれませんが、GKE + ASM という構成を使いつつも可能な限り自分たちでコンポーネントのバージョンを制御したいという場合はあり得ます。

例えば以下のような要件が考えられます:

  • 運用ルール上、システムコンポーネントのバージョンを記録・管理しており、自動でバージョンが上がってしまうことは極力避けたい ※
  • 障害発生時に再現試験などを行うため、システムコンポーネントのバージョンを固定したい
  • 業界標準や法規制等の対応で、サービスメッシュ内の通信の暗号化 (mTLS) にプライベート CA を用いる必要がある

(※ GKEのコントロールプレーンは、リリースチャネルを利用していない場合でもパッチレベルでは自動アップグレードが走ります)

マネージドな領域を減らしてでも、その他のメリット(Istio の設計や運用にあたって Google のサポートを受けられる、オープンソースの Istio のコミュニティサポートよりも長い期間ひとつの ASM バージョンを運用できる等)を理由に GKE + ASM の構成を採用したい、というケースはあると思いますので、ASM がエンタープライズで利用される場合にはこういった構成も選択肢のひとつになると考えられます。

 まとめ

オプションを含めて様々な選択肢が採用できるになった ASM の現状と、いくつかのユースケースにおいて候補となる GKE, ASM の構成をご紹介しました。
これから ASM の利用を検討されている方々の参考になれば幸いです。

なお、この記事では主な機能の違いを示し、現在の ASM の概要をお伝えすることを目的としました。
選択可能な全機能のサポート状況を網羅的に把握されたい場合は下記の公式ドキュメントをご参照ください。

https://cloud.google.com/service-mesh/docs/supported-features
https://cloud.google.com/service-mesh/docs/supported-features-mcp

Microsoft Certified: Azure Solutions Architect Expert を更新しました

以前取得した Azure Solutions Architect Expert の更新プログラムが受けられるようになっていたので、資格の有効期限を延長してきました。
更新の流れや資格のアップデート情報をお伝えしたいと思います。

f:id:polar3130:20211105135523p:plain

Azure Solutions Architect Expert などの Microsoft のロールベース認定資格は、専用の更新プログラムを受験することで有効期限の延長を行う方式となっています。 テストセンター等で取得時と同じ試験の再受験をする必要はありません。
私は Azure Solutions Architect Expert を 2019 年 5 月に取得したのですが、今年に入って有効期限が半年ほど延長される措置があり、例外的に取得から 2 年半での更新となりました。

当時の受験記録は こちら

更新の流れ

資格の有効期限まで 6 ヶ月を切ると、認定資格のダッシュボードに「更新」のボタンが表示されます。
ボタンをクリックすると更新プログラムのページが表示され、「更新評価を受ける」のボタンを押すとオンライン試験の受験を開始できます(ボタンを押すと確認などなくいきなり試験が始まりますのでご注意ください)。
更新前にスクリーンショットを撮り忘れてしまったため、別の資格で恐縮ですが、更新プログラムのページは以下のようになっています。

f:id:polar3130:20211105135547p:plain

試験が終わるとすぐに結果が表示され、ほどなくしてダッシュボード上の有効期限の表示も更新されます。
有効期限の更新は元の期限から 1 年間延長される形式となっているようなので、早めに更新の評価を受けても損はなさそうです。

私は何の準備もなく更新評価を受け始めてしまったのですが、ページ下部には更新評価の試験に関係する Microsoft Learn のモジュールがリストされていますので、スキルのアップデートに不安がある領域があれば事前に予習することも可能となっているようです。

試験概要

私が受験した際には以下のような試験となっていました。

  • 試験時間: 45 分
  • 問題数 : 26 問
  • 合格基準: 65 %

落ちた場合は再受験が何度でも可能と記載されていました。
ただし、前の受験から 24 時間以上の間隔を空ける必要があるそうです。

試験内容のリニューアル

以下は更新プログラムの話ではなく、新規に Azure Solutions Architect Expert の取得を目指している方向けの情報になります。

資格更新後にダッシュボードを見て気付いたのですが、資格の取得履歴・有効期限が 2 つのレコードで記載されています。

f:id:polar3130:20211105135530p:plain

Azure Solutions Architect Expert はちょうど試験内容のリニューアルを行っている時期だったようで、資格の概要ページにアクセスしたところ、以下のように記載されていました。

f:id:polar3130:20211105135600p:plain

資格の概要ページにもリンクがありますが、Microsoft Learn の Blog に詳しいスケジュールが載っていました。

https://techcommunity.microsoft.com/t5/microsoft-learn-blog/reimagining-the-azure-solutions-architect-expert-certification/ba-p/2813695

2021 年 10 月 7 日に投稿されたこのブログ記事では、以下のスケジュールで認定試験がリニューアルされるとアナウンスされています。

  • 2021 年 11 月 16 日から新試験の AZ-305 がベータ版で受験可能となる
  • 2022 年 3 月 31 日をもって AZ-303, AZ-304 が廃止される

現在の試験要項に沿って AZ-303, AZ-304 向けの試験対策をされている方、すでにどちらかの試験をパスしている方などは、既存試験の受験時期に注意して早めに受験されることをおすすめします。

なお、リニューアル版の Azure Solutions Architect Expert の資格概要ページは以下の URL となっています。

https://docs.microsoft.com/en-us/learn/certifications/azure-solutions-architect/renew/

おわりに

これから更新プログラムの受験を予定されている方や、新たに Azure Solutions Architect Expert の取得を目指している方のお役に立てば幸いです。

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