(Qiita Advent Calendar - GCP(Google Cloud Platform) Advent Calendar 2022 の 5 日目の記事です)
Google Cloud が提供している Managed Anthos Service Mesh(以下、Managed ASM)は、Istio をベースとしたサービスメッシュのマネージドサービスです。
最近は、Managed Data Plane という Istio-proxy の自動アップグレードオプションも登場してきています。
今回は、Managed ASM や Managed Data Plane がどのようにバージョンをコントロールしているのか、GKE に展開されているカスタムリソースから分かる情報や、環境を構築してみて得られた知見をいくつかご紹介します。
当記事内の情報は公式ドキュメントに仕様として明記されているものではなく、あくまで実機のカスタムリソースの定義などからいちユーザが推測したものとなりますのでご承知おきください。
そもそもマネージドであるがゆえ、できる限りユーザがその挙動を知らなくても良いようにしてくれているものではあるのですが、裏で動いている仕組みを推測・理解しておくことでトラブルシューティングなどの際に役立つこともあるかと思います。
GKE Autopilot + Managed ASM + Managed Data Plane による Kubernetes, Istio 環境をセットアップし、アプリケーションの Pod 起動時に ASM の Data Plane として Istio-proxy が自動インジェクションされるところまでを 前回記事 でご紹介していますので、ご興味のある方はこちらもご参照ください。
おさらい
前回記事の最後では、Istio-proxy の自動インジェクションを Namespace へのラベル付与で有効化し、サンプルアプリケーションとして httpd の Pod を起動した結果、ASM の Data Plane である Istio-proxy がサイドカーとして Pod に挿入されていることを確認しました。
以下のとおり、コンテナイメージの情報を取得してみると、インジェクションされた Istio-proxy のバージョンは 1.14.5-asm.3 だとわかります。
$ kubectl get pod httpd -o jsonpath='{.spec.containers[*].image}' | tr ' ' '\n'
httpd
gcr.io/gke-release/asm/proxyv2:1.14.5-asm.3
Istio-proxy のバージョンの決定
Managed ASM の場合、公式ドキュメントによれば、Istio-proxy のバージョンは ASM のリリースチャネルに沿って決定されるとあります。
Managed ASM のリリースチャネルは、GKE のリリースチャネルとは独立していますが、似たようなコンセプトで Rapid, Regular, Stable の 3 種類のチャネルが提供されています。
記事執筆時点では、Regular Channel のバージョンは ASM 1.14 となっていました。
なお、ASM のセマンティックバージョニング部分の表記は基本的に Istio のバージョンと一致しています。
Istio のバージョンに -asm** のサフィックスを付与したものが ASM の完全なバージョン表記となります。
上記リンクで紹介したように、各リリースチャネルに対応した ASM のバージョンはドキュメントから確認することができますが、挿入されるはずのバージョンを実機から確認したいというケースもあるでしょう。
各チャネルに対応する現在のバージョンは、以下のコマンドを実行することで実機から確認することができます。
$ kubectl get dataplanecontrols.mesh.cloud.google.com -n istio-system
NAME REVISION VERSION TARGETBASISPOINTS STATUS AGE
asm-managed-9kxsf asm-managed 1.14.5-asm.6 10000 Ready 3d1h
asm-managed-rapid-7zclm asm-managed-rapid 1.15.3-asm.1 10000 Ready 3d1h
asm-managed-stable-9wss4 asm-managed-stable 1.13.9-asm.2 10000 Ready 3d1h
$ kubectl describe dataplanecontrols.mesh.cloud.google.com asm-managed-9kxsf
Name: asm-managed-9kxsf
Namespace:
Labels: <none>
Annotations: <none>
API Version: mesh.cloud.google.com/v1alpha1
Kind: DataPlaneControl
Metadata:
Creation Timestamp: 2022-11-26T12:29:07Z
Generate Name: asm-managed-
Generation: 1
Managed Fields:
API Version: mesh.cloud.google.com/v1alpha1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:generateName:
f:spec:
.:
f:proxyTargetBasisPoints:
f:proxyVersion:
f:revision:
Manager: Google-GKEHub-Controllers-Servicemesh
Operation: Update
Time: 2022-11-26T12:29:07Z
API Version: mesh.cloud.google.com/v1alpha1
Fields Type: FieldsV1
fieldsV1:
f:status:
.:
f:observedGeneration:
f:proxyMetrics:
f:proxyTargetBasisPoints:
f:state:
Manager: mdp
Operation: Update
Subresource: status
Time: 2022-11-26T12:32:15Z
Resource Version: 28883
UID: ed9323f2-a420-4bc3-976a-13c70fd0d906
Spec:
Proxy Target Basis Points: 10000
Proxy Version: 1.14.5-asm.6
Revision: asm-managed
Status:
Observed Generation: 1
Proxy Metrics:
Proxy Target Basis Points: 10000
State: Ready
Events: <none>
dataplanecontrols は、ASM のために Google が提供しているカスタムリソースです。
metadata.managedFields を見る限り、各リリースチャネルの ASM バージョンは Google-GKEHub-Controllers-Servicemesh というサービスが更新しているようですね。
他にも、mdp(Managed Data Plane の略でしょうか)というサービスがこのリソースを共同所有していることがわかります。
デフォルトでは、Regular Channel を示す asm-managed のバージョンが適用されるため、1.14.5 の istio-proxy が挿入されたことがわかります。
ASM 固有のリビジョンまで細かく見ていくと、httpd に挿入されている Istio-proxy のバージョンが 1.14.5-asm.3 であるのに対し、dataplanecontrols の asm-managed は 1.14.5-asm.6 となっています。
httpd の Pod をデプロイしてから asm-managed のリビジョンに更新が入ったのかとも考えましたが、新たに別の Pod を展開しても 1.14.5-asm.3 の Istio-proxy が挿入されました。
$ kubectl get dataplanecontrols.mesh.cloud.google.com -n istio-system
NAME REVISION VERSION TARGETBASISPOINTS STATUS AGE
asm-managed-9kxsf asm-managed 1.14.5-asm.6 10000 Ready 3d1h
asm-managed-rapid-7zclm asm-managed-rapid 1.15.3-asm.1 10000 Ready 3d1h
asm-managed-stable-9wss4 asm-managed-stable 1.13.9-asm.2 10000 Ready 3d1h
$ kubectl run httpd-alt --image httpd
Warning: Autopilot set default resource requests on Pod default/httpd-alt for container httpd-alt, as resource requests were not specified, and adjusted resource requests to meet requirements. See http://g.co/gke/autopilot-defaults and http://g.co/gke/autopilot-resources.
pod/httpd-alt created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
httpd 2/2 Running 0 12h
httpd-alt 2/2 Running 0 2m27s
$ kubectl get pod httpd-alt -o jsonpath='{.spec.containers[*].image}' | tr ' ' '\n'
httpd
gcr.io/gke-release/asm/proxyv2:1.14.5-asm.3
インジェクション後にマネージドコントロールプレーンのバージョンが上がった場合は、「マネージド コントロール プレーンがアップグレードされてから 1〜2 週間後に完了します」と公式ドキュメントに記載があるため、一時的にバージョンが一致しない期間ができるとは想定していました。
一方で、新規にインジェクションする場合は基本的に dataplanecontrols の Proxy Version が適用されるものと想像していたため、これは意外な結果となりました。
上記の結果からすると、どうやら dataplanecontrols のリソースに定義されているバージョンは、実際に自動でインジェクションされる Istio-proxy のバージョンとは完全には連動していないようです。
あるいは、ASM 固有のリビジョンでは更新がかからず、Istio のマイナーバージョンやパッチバージョンに変更が発生した場合にのみ更新がかかる、という条件になっているのかもしれません。
ASM における Proxy Injection の実装先
では、実際に Istio-procy をインジェクションする際のバージョンを決定する仕組みはどこに実装されているのでしょうか。
自動インジェクションのメカニズムは Mutating Webhook によって実装されているため、Webhook の定義を確認してみましょう。
Webhook の名前からして、istio-revision-tag-default というリソースがそれらしいと推測できます。
$ kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io
NAME WEBHOOKS AGE
admissionwebhookcontroller.config.common-webhooks.networking.gke.io 1 3d2h
filestorecsi-mutation-webhook.storage.k8s.io 1 3d2h
gke-vpa-webhook-config 1 3d2h
istio-revision-tag-default 4 3d1h
istiod-asm-managed 2 3d1h
mutate-scheduler-profile.config.common-webhooks.networking.gke.io 1 3d2h
neg-annotation.config.common-webhooks.networking.gke.io 1 3d2h
pod-ready.config.common-webhooks.networking.gke.io 1 3d2h
sasecret-redacter.config.common-webhooks.networking.gke.io 1 3d2h
workload-defaulter.config.common-webhooks.networking.gke.io 1 3d2h
$ kubectl describe mutatingwebhookconfigurations.admissionregistration.k8s.io istio-revision-tag-default
Name: istio-revision-tag-default
Namespace:
Labels: app=sidecar-injector
istio.io/owned-by=mesh.googleapis.com
istio.io/rev=asm-managed
istio.io/tag=default
Annotations: <none>
API Version: admissionregistration.k8s.io/v1
Kind: MutatingWebhookConfiguration
Metadata:
Creation Timestamp: 2022-11-26T12:31:43Z
Generation: 1
Managed Fields:
API Version: admissionregistration.k8s.io/v1
Fields Type: FieldsV1
fieldsV1:
... (略) ...
Manager: Google-GKEHub-Controllers-Servicemesh
Operation: Update
Time: 2022-11-26T12:31:43Z
Resource Version: 28506
UID: ae84eef6-0371-4375-8979-2c268a513feb
Webhooks:
Name: rev.namespace.sidecar-injector.istio.io
... (略) ...
Name: rev.object.sidecar-injector.istio.io
... (略) ...
Admission Review Versions:
v1beta1
v1
Client Config:
URL: https://asm-autopilot-cluster-asm-managedtiwu6ybzqbglr-n354hllh2a-uc.a.run.app:443/inject/ISTIO_META_CLOUDRUN_ADDR/asm-autopilot-cluster-asm-managedtiwu6ybzqbglr-n354hllh2a-uc.a.run.app:443
Failure Policy: Fail
Match Policy: Equivalent
Name: namespace.sidecar-injector.istio.io
Namespace Selector:
Match Expressions:
Key: istio-injection
Operator: In
Values:
enabled
Object Selector:
Match Expressions:
Key: sidecar.istio.io/inject
Operator: NotIn
Values:
false
Reinvocation Policy: Never
Rules:
API Groups:
API Versions:
v1
Operations:
CREATE
Resources:
pods
Scope: *
Side Effects: None
Timeout Seconds: 10
... (略) ...
Name: object.sidecar-injector.istio.io
... (略) ...
Events: <none>
(今回はリビジョン指定なしの istio-injection ラベルを Namespace に付与して自動インジェクションを利用しているため、kubectl describe の結果は対応する Webhook の namespace.sidecar-injector.istio.io だけを抜粋しています)
各 webhook の Client Config の URL を見るに、Managed ASM の Istio-proxy の自動インジェクションは Cloud Run で実装されていることがわかります。
したがって、ASM の自動インジェクションはクラスタに展開された CRD と Webhook によって、Cloud Run を呼び出すことで Istio-proxy のバージョンを決定し、Pod にサイドカーとしてインジェクションしている、といった実装になっているものと考えられます。
dataplanecontrols の ASM のバージョン定義が更新されると、恐らく Cloud Run 側にも反映され、いずれ更新後のバージョンの Istio-proxy がインジェクションされるようになると思われますが、どうやら dataplanecontrols の更新の Cloud Run への反映はリアルタイムでは行われない実装になっていると思われます。
自動インジェクションの実装からわかる Managed ASM 利用時の注意点
上記の調査結果からわかる、ユーザが気をつけるべき点としては、自動インジェクション発生時に Cloud Run のコールドスタートの影響を受ける可能性がある、ということが挙げられます。
Webhook の呼び出し先の Cloud Run は一定時間呼び出しがなければインスタンスの数がゼロにスケールインすることができるため、下記のように自動インジェクションが失敗することで Pod の起動に失敗する可能性があります。
CI / CD パイプラインなど自動化の仕組みで Pod を展開する場合には、この事象が発生する可能性を考慮してリカバリの仕組みを備えておく必要がありそうです。
$ kubectl run httpd --image httpd Error from server (InternalError): Internal error occurred: failed calling webhook "namespace.sidecar-injector.istio.io": failed to call webhook: Post "https://asm-autopilot-cluster-asm-managedtiwu6ybzqbglr-n354hllh2a-uc.a.run.app:443/inject/ISTIO_META_CLOUDRUN_ADDR/asm-autopilot-cluster-asm-managedtiwu6ybzqbglr-n354hllh2a-uc.a.run.app:443?timeout=10s": context deadline exceeded
なお、最近の Cloud Run には、startup CPU boost や Always on CPU など、コールドスタートの影響を低減あるいは解消するためのアップデートも出てきていますので、今後は無視できるレベルになる可能性も期待できそうですね。
GKE Autopilot 利用時の追加の注意点
今回の検証には GKE Autopilot を利用しました。
GKE Autopilot では、ゼロにスケールした(クラスタに割り当てられたノードが存在しない)状態でも Pod の作成リクエストが成功するため、リクエストを受け付けてから実際にノードが割り当てられるまでの時間が一定程度かかった場合、結果的に Pod のスケジューリング先がないものとして失敗してしまうケースがあります。
Managed ASM と併せて GKE Autopilot を利用する場合はこちらも注意が必要ですね。
$ kubectl run httpd --image httpd Warning: Autopilot set default resource requests on Pod default/httpd for container httpd, as resource requests were not specified, and adjusted resource requests to meet requirements. See http://g.co/gke/autopilot-defaults and http://g.co/gke/autopilot-resources. pod/httpd created $ kubectl get po -w NAME READY STATUS RESTARTS AGE httpd 0/2 Init:0/1 0 11s httpd 0/2 Init:0/1 0 25s httpd 0/2 Init:Error 0 33s httpd 0/2 Init:0/1 1 (6s ago) 35s httpd 0/2 Terminating 1 (6s ago) 35s $ kubectl get po No resources found in default namespace.
まとめ
前回記事の補完的な位置付けで、Managed ASM の自動インジェクションの仕組みを調べました。
クラスタに展開されたカスタムリソースや Webhook の定義から仕組みを推測し、実機の調査を通じて、利用時にユーザが注意すべきいくつかのポイントをご紹介しました。
普段ユーザが考慮すべき設計・運用の要素を低減してくれるという点でとても便利なマネージドサービスですが、その仕様を実機の設定や挙動から把握しておくことで、より上手に使っていくことができるのではと思います。