GKE
Knative
CloudRun

Cloud Run on GKEに覗くKnative


概要


  • Cloud Run on GKEでKnativeがどう使われているのかを調べたい

  • Cloud Run on GKEの挙動と仕様をなんとなく理解した気になりたい


環境構築

公式ドキュメントにCloud Run on GKE関連のチュートリアルベースにGoogle Cloud Shellで準備していきます。

この記事では一からKnative自体の説明はしないので、公式ドキュメントや拙著『Knativeの歩き方 KubernetesからServerlessを訪ねて』、登壇資料などを参考にしてください。

ここからGoogle Cloud Shellを使って準備していきます。ローカル環境のターミナルは利用しません。


Google Cloud Shell準備

GKEでクラスタを準備し、Cloud Runを実行するGCPプロジェクトに移動してください。

Cloud Shellは別ウィンドウで開くと便利です。

newwindow_color.png


Kubernetesクラスタ準備

利用するAPIを有効化し、c-runという名前のクラスタを作成します。

# APIの有効化

$ gcloud services enable container.googleapis.com containerregistry.googleapis.com cloudbuild.googleapis.com

# 環境変数設定
$ export VERSION=latest
$ export ZONE=us-central1-a
$ export CLUSTER_NAME=c-run

# 環境変数を利用してクラスタ作成
$ gcloud beta container clusters create ${CLUSTER_NAME} \
--addons=HorizontalPodAutoscaling,HttpLoadBalancing,Istio,CloudRun \
--machine-type=n1-standard-4 \
--cluster-version=${VERSION} --zone=${ZONE} \
--enable-stackdriver-kubernetes --enable-ip-alias \
--scopes cloud-platform

ここではc-runという名前で作成します。

作成には少し時間がかかります。つぎのURLから作成状況が確認できます。

https://console.cloud.google.com/kubernetes/list

つぎのコマンドを実行してノードの準備ができていることを確認してください。

$ kubectl get nodes

NAME STATUS ROLES AGE VERSION
gke-c-run-default-pool-8d7008c0-0vz9 Ready <none> 2d5h v1.12.7-gke.10
gke-c-run-default-pool-8d7008c0-j05x Ready <none> 2d5h v1.12.7-gke.10
gke-c-run-default-pool-8d7008c0-j15b Ready <none> 2d5h v1.12.7-gke.10

正常に作成が完了していれば3のノードのステータスがReadyになっているはずです。


Knative関連のコンポーネント確認

ここまでの準備でどのNamespaceに何が準備されたのでしょうか。kubectlで確認してみましょう。

$ kubectl get namespaces

NAME STATUS AGE
default Active 6m54s
istio-system Active 6m13s
knative-serving Active 6m19s
kube-public Active 6m54s
kube-system Active 6m54s

defaultkube-publickube-systemに加えてknative-servingistio-systemのネームスペースが存在します。

KnativeのコンポーネントのServing、Build、Eventingの中でServingだけが利用されている雰囲気です。

この記事では後ほどKnativeが依存しているIstioのistio-ingressgatewayからエンドポイントを取得します。

$ kubectl get pod -n istio-system

NAME READY STATUS RESTARTS AGE
istio-citadel-7cbd68dc58-65kg5 1/1 Running 0 6m47s
istio-cleanup-secrets-msjn6 0/1 Completed 0 6m51s
istio-egressgateway-76bd65699f-fmrd2 1/1 Running 0 6m48s
istio-galley-848d6b698c-tv5nz 1/1 Running 0 6m48s
istio-ingressgateway-64d6cfc6cb-729cd 1/1 Running 0 6m48s
istio-pilot-57c9ff7496-jlhk5 2/2 Running 0 6m47s
istio-policy-57bf7c4984-dd7tw 2/2 Running 0 6m48s
istio-sidecar-injector-9db9b45f5-62cmr 1/1 Running 0 6m46s
istio-telemetry-bf847c494-pklss 2/2 Running 0 6m47s
promsd-8cc5d455b-xtvw8 2/2 Running 1 6m45s

knative-servingのPodを確認してみると、cloudrun-controllerという見慣れないコントローラーが存在しています。

$ kubectl get pod -n knative-serving

NAME READY STATUS RESTARTS AGE
activator-7d7b59bd6c-xctcg 1/1 Running 1 5m28s
autoscaler-84cc7b78c4-bqvg6 1/1 Running 0 5m27s
cloudrun-controller-5859fbcc44-2cxht 1/1 Running 0 5m24s
controller-db5bbf4b9-sfb9n 1/1 Running 0 5m26s
webhook-85ddccf9c6-dmgg8 1/1 Running 0 5m25s

後ほどどういう役割を果たしていそうか仕様に照らし合わせて確認してみましょう。

その他はServingをインストールするときに含まれるものと変わらないですね。

Knative Serving関連のリソースはこの段階ではまだ存在していません。

$ kubectl get ksvc,configuration,route,revision

No resources found.


Cloud Run on GKEのデプロイ

# デフォルトの実行環境設定

$ export CLUSTER_NAME=c-run
$ export ZONE=us-central1-a
$ gcloud config set run/cluster ${CLUSTER_NAME}
$ gcloud config set run/cluster_location ${ZONE}

# Cloud Runのデプロイ
# デフォルト設定なしでデプロイする場合、GKE上にデプロイしたい場合は
# --cluster--cluster-location オプションが必要です。
# 省いた場合はon GKEでない方にデプロイされます。
$ export SERVICE_NAME=autoscale-go
$ gcloud beta run deploy ${SERVICE_NAME} --image gcr.io/knative-samples/autoscale-go:0.1

# デプロイに成功するとつぎのようにサービス名、Namespace、クラスター名、Revision名、エンドポイントが表示されます。
# Namespaceも--namespaceで指定できます。
# その他オプションはgcloud beta run deploy --help で確認してみてください。
# あとは[このドキュメント https://cloud.google.com/sdk/gcloud/reference/beta/run/
Deploying container to Cloud Run on GKE service [autoscale-go] in namespace [default] of cluster [c-run]
✓ Deploying new service... Done.
✓ Creating Revision...
- Routing traffic...
Done.
Service [autoscale-go] revision [autoscale-go-h425w] has been deployed and is serving traffic at autoscale-go.default.example.com

Knativeのリソースが登録された雰囲気があるので確認してみましょう。

$ kubectl get ksvc,configuration,route,revision

NAME DOMAIN LATESTCREATED LATESTREADY READY REASON
service.serving.knative.dev/autoscale-go autoscale-go.default.example.com autoscale-go-h425w autoscale-go-h425w True

NAME LATESTCREATED LATESTREADY READY REASON
configuration.serving.knative.dev/autoscale-go autoscale-go-h425w autoscale-go-h425w True

NAME DOMAIN READY REASON
route.serving.knative.dev/autoscale-go autoscale-go.default.example.com True

NAME SERVICE NAME GENERATION READY REASON
revision.serving.knative.dev/autoscale-go-h425w autoscale-go-h425w-service 1 True

Knative Serving関連のリソースが一通り登録されていることが確認できました。

それぞれ図のような関係にあります。

serving.jpg


オートスケールアウト・インを観察する

Cloud Runではコンテナあたりのリクエスト数を設定できます。

Cloud Run on GKE上のコンテナにリクエストするにあたってはrakyll/heyを使うことで並列リクエスト数を指定します。

$ go get github.com/rakyll/hey

istio-ingressgatewayからエンドポイントを取得し、リクエストします。

リクエストを処理するサーバーの実装はこれで、リクエストパラメタで各リクエストでsleepする時間を指定できます。今回は1秒です。

$ IP_ADDRESS="$(kubectl get service istio-ingressgateway --namespace istio-system --output jsonpath="{.status.loadBalancer.ingress[*].ip}")"


# 500並列で150000リクエストする
$ hey -host autoscale-go.default.example.com -c 500 -n 150000 \
"http://${IP_ADDRESS?}?sleep=1000"

別セッションでPodの様子を見てみます。

$ kubectl get pods -w

NAME READY STATUS RESTARTS AGE
autoscale-go-k7nxb-deployment-7cc8f74b4-5snjn 3/3 Running 0 9s
autoscale-go-k7nxb-deployment-7cc8f74b4-hm2zz 3/3 Running 0 22s
autoscale-go-k7nxb-deployment-7cc8f74b4-hvh9j 3/3 Running 0 19s
autoscale-go-k7nxb-deployment-7cc8f74b4-j6s4p 3/3 Running 0 19s
autoscale-go-k7nxb-deployment-7cc8f74b4-r6kd6 3/3 Running 0 17s
autoscale-go-k7nxb-deployment-7cc8f74b4-rv29q 3/3 Running 0 17s
autoscale-go-k7nxb-deployment-7cc8f74b4-rv29q 3/3 Terminating 0 68s
autoscale-go-k7nxb-deployment-7cc8f74b4-r6kd6 3/3 Terminating 0 68s
autoscale-go-k7nxb-deployment-7cc8f74b4-hvh9j 3/3 Terminating 0 70s
autoscale-go-k7nxb-deployment-7cc8f74b4-5snjn 3/3 Terminating 0 60s
autoscale-go-k7nxb-deployment-7cc8f74b4-j6s4p 3/3 Terminating 0 72s
autoscale-go-k7nxb-deployment-7cc8f74b4-5snjn 2/3 Terminating 0 63s
autoscale-go-k7nxb-deployment-7cc8f74b4-rv29q 2/3 Terminating 0 71s
autoscale-go-k7nxb-deployment-7cc8f74b4-hvh9j 2/3 Terminating 0 73s
autoscale-go-k7nxb-deployment-7cc8f74b4-r6kd6 2/3 Terminating 0 72s
autoscale-go-k7nxb-deployment-7cc8f74b4-j6s4p 2/3 Terminating 0 74s
autoscale-go-k7nxb-deployment-7cc8f74b4-98db2 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-98db2 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-bpdkg 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-98db2 0/3 Init:0/1 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-bpdkg 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-bpdkg 0/3 Init:0/1 0 1s
autoscale-go-k7nxb-deployment-7cc8f74b4-98db2 0/3 PodInitializing 0 2s
autoscale-go-k7nxb-deployment-7cc8f74b4-bpdkg 0/3 PodInitializing 0 2s
autoscale-go-k7nxb-deployment-7cc8f74b4-98db2 2/3 Running 0 3s
autoscale-go-k7nxb-deployment-7cc8f74b4-bpdkg 2/3 Running 0 3s
autoscale-go-k7nxb-deployment-7cc8f74b4-bpdkg 3/3 Running 0 4s
autoscale-go-k7nxb-deployment-7cc8f74b4-z9h9l 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-z9h9l 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-z9h9l 0/3 Init:0/1 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-98db2 3/3 Running 0 4s
autoscale-go-k7nxb-deployment-7cc8f74b4-bq7wl 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-bq7wl 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-f682w 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-bq7wl 0/3 Init:0/1 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-f682w 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-8s4hg 0/3 Pending 0 0s
autoscale-go-k7nxb-deployment-7cc8f74b4-z9h9l 0/3 PodInitializing 0 2s
autoscale-go-k7nxb-deployment-7cc8f74b4-8s4hg 0/3 Pending 0 0s
...

Deploymentも見てましょう。

$ kubectl get deployment -w

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
autoscale-go-h425w-deployment 0 0 0 0 7d6h
autoscale-go-k7nxb-deployment 5 5 5 5 16h
autoscale-go-k7nxb-deployment 4 5 5 5 16h
autoscale-go-k7nxb-deployment 4 5 5 5 16h
autoscale-go-k7nxb-deployment 4 4 4 4 16h
autoscale-go-k7nxb-deployment 3 4 4 4 16h
autoscale-go-k7nxb-deployment 3 4 4 4 16h
autoscale-go-k7nxb-deployment 3 3 3 3 16h
autoscale-go-k7nxb-deployment 2 3 3 3 16h
autoscale-go-k7nxb-deployment 2 3 3 3 16h
autoscale-go-k7nxb-deployment 2 2 2 2 16h
autoscale-go-k7nxb-deployment 1 2 2 2 16h
autoscale-go-k7nxb-deployment 1 2 2 2 16h
autoscale-go-k7nxb-deployment 1 1 1 1 16h
autoscale-go-k7nxb-deployment 0 1 1 1 17h
autoscale-go-k7nxb-deployment 0 1 1 1 17h
autoscale-go-k7nxb-deployment 0 0 0 0 17h

heyでリクエストしたセッションに戻ると、統計情報が表示されています。

$ hey -host autoscale-go.default.example.com -c 500 -n 150000   "http://${IP_ADDRESS?}?sleep=1000"

Summary:
Total: 361.7094 secs
Slowest: 6.9157 secs
Fastest: 1.1585 secs
Average: 1.1942 secs
Requests/sec: 414.6975
Total data: 4800000 bytes
Size/request: 32 bytes

Response time histogram:
1.159 [1] |
1.734 [149499] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
2.310 [0] |
2.886 [0] |
3.461 [0] |
4.037 [0] |
4.613 [0] |
5.189 [19] |
5.764 [47] |
6.340 [117] |
6.916 [317] |

Latency distribution:
10% in 1.1599 secs
25% in 1.1609 secs
50% in 1.1639 secs
75% in 1.1746 secs
90% in 1.2061 secs
95% in 1.2439 secs
99% in 1.3797 secs

Details (average, fastest, slowest):
DNS+dialup: 0.0006 secs, 1.1585 secs, 6.9157 secs
DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs
req write: 0.0001 secs, 0.0000 secs, 0.0369 secs
resp wait: 1.1935 secs, 1.1584 secs, 6.7449 secs
resp read: 0.0000 secs, 0.0000 secs, 0.0125 secs

Status code distribution:
[200] 150000 responses

可視化した方がわかりやすいですが、リクエスト数見合いでDeployment(ReplicaSet)のdesiredが増え、Podが増えていく様子が確認できます。リクエストが止んだらPodは全て無くなります。

レイテンシーはほぼ1.159〜1.734の階級に集中しており、単にリクエストを返す以上に時間がかかるもの(コールドスタートを待っているもの)もちょうど500(0.3%)ほどあります。

今度はコンテナあたりの並列リクエスト数をデフォルトの半分の40にして試してみます。

# 並列実行数設定の更新

$ gcloud beta run services update ${SERVICE_NAME} --concurrency=40
✓ Deploying... Done.
✓ Creating Revision...
- Routing traffic...
Done.
Service [autoscale-go] revision [autoscale-go-vks24] is active and serving traffic at autoscale-go.default.example.com

$ hey -host autoscale-go.default.example.com -c 500 -n 150000   "http://${IP_ADDRESS?}?sleep=1000"


Summary:
Total: 506.9715 secs
Slowest: 19.9889 secs
Fastest: 0.1576 secs
Average: 1.4615 secs
Requests/sec: 295.8746

Total data: 4678992 bytes
Size/request: 31 bytes

Response time histogram:
0.158 [1] |
2.141 [146098] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
4.124 [756] |
6.107 [313] |
8.090 [776] |
10.073 [605] |
12.056 [121] |
14.039 [98] |
16.023 [112] |
18.006 [127] |
19.989 [121] |

Latency distribution:
10% in 1.1602 secs
25% in 1.1673 secs
50% in 1.3392 secs
75% in 1.5084 secs
90% in 1.6149 secs
95% in 1.7162 secs
99% in 7.4334 secs

Details (average, fastest, slowest):
DNS+dialup: 0.0006 secs, 0.1576 secs, 19.9889 secs
DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs
req write: 0.0000 secs, 0.0000 secs, 0.0193 secs
resp wait: 1.4607 secs, 0.1575 secs, 19.9888 secs
resp read: 0.0001 secs, 0.0000 secs, 0.0189 secs

Status code distribution:
[200] 145080 responses
[503] 4048 responses

Error distribution:
[872] Get http://xx.xx.xx.xx?sleep=1000: net/http: request canceled (Client.Timeout exceeded while awaiting headers)

それなりにエラー出てますね…

$ kubectl get deployment -w

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
autoscale-go-h425w-deployment 0 0 0 0 7d7h
autoscale-go-k7nxb-deployment 0 0 0 0 17h
autoscale-go-vks24-deployment 0 0 0 0 9m23s
autoscale-go-vks24-deployment 1 0 0 0 9m33s
autoscale-go-vks24-deployment 1 0 0 0 9m33s
autoscale-go-vks24-deployment 1 0 0 0 9m33s
autoscale-go-vks24-deployment 1 1 1 0 9m34s
autoscale-go-vks24-deployment 7 1 1 0 9m34s
autoscale-go-vks24-deployment 7 1 1 0 9m34s
autoscale-go-vks24-deployment 7 1 1 0 9m34s
autoscale-go-vks24-deployment 7 7 7 0 9m35s
autoscale-go-vks24-deployment 10 7 7 0 9m36s
autoscale-go-vks24-deployment 10 7 7 0 9m36s
autoscale-go-vks24-deployment 10 7 7 0 9m36s
autoscale-go-vks24-deployment 10 10 10 0 9m37s
autoscale-go-vks24-deployment 10 10 10 1 9m38s
autoscale-go-vks24-deployment 10 10 10 2 9m39s
autoscale-go-vks24-deployment 13 10 10 2 9m40s
autoscale-go-vks24-deployment 13 10 10 2 9m40s
autoscale-go-vks24-deployment 13 10 10 2 9m40s
autoscale-go-vks24-deployment 13 13 13 2 9m41s
autoscale-go-vks24-deployment 13 13 13 3 9m44s
autoscale-go-vks24-deployment 13 13 13 4 9m44s
autoscale-go-vks24-deployment 13 13 13 5 9m44s
autoscale-go-vks24-deployment 13 13 13 6 9m45s
autoscale-go-vks24-deployment 13 13 13 7 9m46s
autoscale-go-vks24-deployment 13 13 13 8 9m46s
autoscale-go-vks24-deployment 13 13 13 9 9m47s
autoscale-go-vks24-deployment 12 13 13 9 10m
autoscale-go-vks24-deployment 12 13 13 9 10m
autoscale-go-vks24-deployment 12 13 13 9 10m
autoscale-go-vks24-deployment 12 12 12 9 10m
autoscale-go-vks24-deployment 11 12 12 9 10m
autoscale-go-vks24-deployment 11 12 12 9 10m
autoscale-go-vks24-deployment 11 12 12 9 10m
autoscale-go-vks24-deployment 11 11 11 9 10m
autoscale-go-vks24-deployment 12 11 11 9 11m
autoscale-go-vks24-deployment 12 11 11 9 11m
autoscale-go-vks24-deployment 12 11 11 9 11m
autoscale-go-vks24-deployment 12 12 12 9 11m
autoscale-go-vks24-deployment 12 12 12 10 11m
autoscale-go-vks24-deployment 11 12 12 10 11m
autoscale-go-vks24-deployment 11 12 12 10 11m
autoscale-go-vks24-deployment 11 12 12 10 11m
autoscale-go-vks24-deployment 11 11 11 10 11m
autoscale-go-vks24-deployment 12 11 11 10 11m
autoscale-go-vks24-deployment 12 11 11 10 11m
autoscale-go-vks24-deployment 12 11 11 10 11m
autoscale-go-vks24-deployment 12 12 12 10 11m
autoscale-go-vks24-deployment 11 12 12 10 11m
autoscale-go-vks24-deployment 11 12 12 10 11m
autoscale-go-vks24-deployment 11 12 12 10 11m
autoscale-go-vks24-deployment 11 11 11 10 11m
autoscale-go-vks24-deployment 10 11 11 10 12m
autoscale-go-vks24-deployment 10 11 11 10 12m
autoscale-go-vks24-deployment 10 11 11 10 12m
autoscale-go-vks24-deployment 10 10 10 10 12m
autoscale-go-vks24-deployment 9 10 10 10 12m
autoscale-go-vks24-deployment 9 10 10 10 12m
autoscale-go-vks24-deployment 9 9 9 9 12m
autoscale-go-vks24-deployment 10 9 9 9 13m
autoscale-go-vks24-deployment 10 9 9 9 13m
autoscale-go-vks24-deployment 10 9 9 9 13m
autoscale-go-vks24-deployment 10 10 10 9 13m
autoscale-go-vks24-deployment 10 10 10 10 13m
autoscale-go-vks24-deployment 9 10 10 10 13m
autoscale-go-vks24-deployment 9 10 10 10 13m
autoscale-go-vks24-deployment 9 9 9 9 13m
autoscale-go-vks24-deployment 8 9 9 9 13m
autoscale-go-vks24-deployment 8 9 9 9 13m
autoscale-go-vks24-deployment 8 8 8 8 13m
autoscale-go-vks24-deployment 9 8 8 8 14m
autoscale-go-vks24-deployment 9 8 8 8 14m
autoscale-go-vks24-deployment 9 8 8 8 14m
autoscale-go-vks24-deployment 9 9 9 8 14m
autoscale-go-vks24-deployment 9 9 9 9 14m
autoscale-go-vks24-deployment 10 9 9 9 14m
autoscale-go-vks24-deployment 10 9 9 9 14m
autoscale-go-vks24-deployment 10 9 9 9 14m
autoscale-go-vks24-deployment 10 10 10 9 14m
autoscale-go-vks24-deployment 10 10 10 10 14m
autoscale-go-vks24-deployment 11 10 10 10 14m
autoscale-go-vks24-deployment 11 10 10 10 14m
autoscale-go-vks24-deployment 11 10 10 10 14m
autoscale-go-vks24-deployment 11 11 11 10 14m
autoscale-go-vks24-deployment 10 11 11 10 14m
autoscale-go-vks24-deployment 10 11 11 10 14m
autoscale-go-vks24-deployment 10 11 11 10 14m
autoscale-go-vks24-deployment 10 10 10 10 14m
autoscale-go-vks24-deployment 9 10 10 10 15m
autoscale-go-vks24-deployment 9 10 10 10 15m
autoscale-go-vks24-deployment 9 9 9 9 15m
autoscale-go-vks24-deployment 8 9 9 9 15m
autoscale-go-vks24-deployment 8 9 9 9 15m
autoscale-go-vks24-deployment 8 8 8 8 15m
autoscale-go-vks24-deployment 9 8 8 8 15m
autoscale-go-vks24-deployment 9 8 8 8 15m
autoscale-go-vks24-deployment 9 8 8 8 15m
autoscale-go-vks24-deployment 9 9 9 8 15m
autoscale-go-vks24-deployment 9 9 9 9 15m
autoscale-go-vks24-deployment 10 9 9 9 15m
autoscale-go-vks24-deployment 10 9 9 9 15m
autoscale-go-vks24-deployment 10 9 9 9 15m
autoscale-go-vks24-deployment 10 10 10 9 15m
autoscale-go-vks24-deployment 10 10 10 9 15m
autoscale-go-vks24-deployment 10 10 10 10 15m
autoscale-go-vks24-deployment 11 10 10 10 16m
autoscale-go-vks24-deployment 11 10 10 10 16m
autoscale-go-vks24-deployment 11 10 10 10 16m
autoscale-go-vks24-deployment 11 11 11 10 16m
autoscale-go-vks24-deployment 11 11 11 11 16m
autoscale-go-vks24-deployment 10 11 11 11 16m
autoscale-go-vks24-deployment 10 11 11 11 16m
autoscale-go-vks24-deployment 10 10 10 10 16m
autoscale-go-vks24-deployment 9 10 10 10 16m
autoscale-go-vks24-deployment 9 10 10 10 16m
autoscale-go-vks24-deployment 9 9 9 9 16m
autoscale-go-vks24-deployment 8 9 9 9 16m
autoscale-go-vks24-deployment 8 9 9 9 16m
autoscale-go-vks24-deployment 8 8 8 8 16m
autoscale-go-vks24-deployment 9 8 8 8 17m
autoscale-go-vks24-deployment 9 8 8 8 17m
autoscale-go-vks24-deployment 9 8 8 8 17m
autoscale-go-vks24-deployment 9 9 9 8 17m
autoscale-go-vks24-deployment 9 9 9 9 17m
autoscale-go-vks24-deployment 10 9 9 9 17m
autoscale-go-vks24-deployment 10 9 9 9 17m
autoscale-go-vks24-deployment 10 9 9 9 17m
autoscale-go-vks24-deployment 10 10 10 9 17m
autoscale-go-vks24-deployment 10 10 10 10 17m
autoscale-go-vks24-deployment 9 10 10 10 17m
autoscale-go-vks24-deployment 9 10 10 10 17m
autoscale-go-vks24-deployment 9 9 9 9 17m
autoscale-go-vks24-deployment 8 9 9 9 17m
autoscale-go-vks24-deployment 8 9 9 9 17m
autoscale-go-vks24-deployment 8 8 8 8 17m
autoscale-go-vks24-deployment 7 8 8 8 17m
autoscale-go-vks24-deployment 7 8 8 8 17m
autoscale-go-vks24-deployment 7 7 7 7 17m
autoscale-go-vks24-deployment 6 7 7 7 17m
autoscale-go-vks24-deployment 6 7 7 7 17m
autoscale-go-vks24-deployment 6 6 6 6 17m
autoscale-go-vks24-deployment 5 6 6 6 17m
autoscale-go-vks24-deployment 5 6 6 6 17m
autoscale-go-vks24-deployment 5 5 5 5 17m
autoscale-go-vks24-deployment 4 5 5 5 18m
autoscale-go-vks24-deployment 4 5 5 5 18m
autoscale-go-vks24-deployment 4 4 4 4 18m
autoscale-go-vks24-deployment 3 4 4 4 18m
autoscale-go-vks24-deployment 3 4 4 4 18m
autoscale-go-vks24-deployment 3 3 3 3 18m
autoscale-go-vks24-deployment 2 3 3 3 18m
autoscale-go-vks24-deployment 2 3 3 3 18m
autoscale-go-vks24-deployment 2 2 2 2 18m
autoscale-go-vks24-deployment 1 2 2 2 18m
autoscale-go-vks24-deployment 1 2 2 2 18m
autoscale-go-vks24-deployment 1 1 1 1 18m

DESIREDがMAX13になり、AVAILABLEもMAX11と、--concurrency=80(デフォルト)のときのほぼ倍です。(503何でしょう気になります。)

Knative的にトラフィック制御してるなら、先ほど確認したknative-servingNamespace内にあるactivatorとautoscalerを使ってリクエスト数を計測し、Deploymentを調整しているはずですね。

Knativeではリクエストを処理するPod内のqueue-proxyコンテナでRevision毎の同時リクエスト数を計測します。queue-proxyAutoscalerに1秒毎に計測データを送信し、Autoscalerは2秒毎に計測データを評価します。その評価に基づき、AutoscalerはPod毎のリクエストが100回/秒になるように調整します。(デフォルトで)

Podの中身を見てみます。

$ kubectl get pod -oyaml autoscale-go-xxxxx-deployment-yyyyyyyyyyyyyy

Cloud Run on GKEで実行するPodにおいてもPod内のqueue-proxyコンテナが入っています。

また、queue-proxyはつぎのイメージを利用しており、Knative Servingの仕組みがそのまま当てはまりそうな雰囲気があります。

image: gcr.io/knative-releases/github.com/knative/serving/cmd/queue@sha256:b5c759e4ea6f36ae4498c1ec794653920345b9ad7492731fb1d6087e3b95dc43

さらに、環境変数SERVING_AUTOSCALERにはautoslacerがセットされています。

CONTAINER_CONCURRENCY40となっていて、gcloudコマンド経由で設定した同時リクエスト数が反映されていることがわかります。

つぎの図そのままに動いていそうです。

autoscale.png

knative-serving NamespaceのPodはKnativeの仕様通りの使われ方がしているとすると、cloudrun-controllerって何者なんでしょうか?

$ kubectl get pod cloudrun-controller-5859fbcc44-2cxht  -n knative-serving -oyaml

apiVersion: v1
kind: Pod
metadata:
annotations:
sidecar.istio.io/inject: "false"
creationTimestamp: 2019-05-04T09:00:34Z
generateName: cloudrun-controller-5859fbcc44-
labels:
app: cloudrun-controller
pod-template-hash: 5859fbcc44
name: cloudrun-controller-5859fbcc44-2cxht
namespace: knative-serving
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: cloudrun-controller-5859fbcc44
uid: 138094bd-6e4b-11e9-98c9-42010a80013f
resourceVersion: "1165"
selfLink: /api/v1/namespaces/knative-serving/pods/cloudrun-controller-5859fbcc44-2cxht
uid: 13c4b9c5-6e4b-11e9-98c9-42010a80013f
spec:
containers:
- args:
- -logtostderr=true
- -stderrthreshold=INFO
image: gcr.io/gke-release/knative/gke-internal/knative/cloudrun/cmd/controller@sha256:c0a1c84ee39176a0ecefb16e058de25e5168eea7e4927e9e558c59c2c5f1bfd7
imagePullPolicy: IfNotPresent
name: cloudrun-controller
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: cloudrun-controller-token-hpzk9
readOnly: true
dnsPolicy: ClusterFirst
nodeName: gke-c-run-default-pool-8d7008c0-0vz9
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: cloudrun-controller
serviceAccountName: cloudrun-controller
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: cloudrun-controller-token-hpzk9
secret:
defaultMode: 420
secretName: cloudrun-controller-token-hpzk9
status:
conditions:
(略)

中身を見てみると、使っているイメージはgcr.io/gke-release/knative/gke-internal/knative/cloudrun/cmd/controller@sha256:xxxxです。

Quotas and Limitsを見る限り、Knativeのスコープ外の項目も多いので、そういうのを管理してたりするんでしょうか。


Revisionの確認

これまで何度か(記事に書いてないのも含め)設定を変更してきましたが、Revisionは残っているのでしょうか?みてみましょう。

$ kubectl get revision

NAME SERVICE NAME GENERATION READY REASON
autoscale-go-h425w autoscale-go-h425w-service 1 True
autoscale-go-k7nxb autoscale-go-k7nxb-service 2 True
autoscale-go-vks24 autoscale-go-vks24-service 3 True

それぞれの管理下にあるDeploymentも確認できます。

$ kubectl get deployment

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
autoscale-go-h425w-deployment 0 0 0 0 7d9h
autoscale-go-k7nxb-deployment 0 0 0 0 19h
autoscale-go-vks24-deployment 0 0 0 0 158m

こんなイメージです。

background.png

Knativeの方のServiceを見てみると、リクエストはすべてGENERATION 3で受けるものとして保存されていることがわかります。

$ kubectl get ksvc autoscale-go

NAME DOMAIN LATESTCREATED LATESTREADY READY REASON
autoscale-go autoscale-go.default.example.com autoscale-go-vks24 autoscale-go-vks24 True

$ kubectl get ksvc autoscale-go -oyaml
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
annotations:
client.knative.dev/user-image: gcr.io/knative-samples/autoscale-go:0.1
serving.knative.dev/creator: toshi0607@mercari.com
serving.knative.dev/lastModifier: toshi0607@mercari.com
creationTimestamp: 2019-05-05T02:36:29Z
generation: 3
name: autoscale-go
namespace: default
resourceVersion: "2222578"
selfLink: /apis/serving.knative.dev/v1alpha1/namespaces/default/services/autoscale-go
uid: 9615e4f5-6ede-11e9-98c9-42010a80013f
spec:
runLatest:
configuration:
revisionTemplate:
metadata:
creationTimestamp: null
initializers:
pending: null
labels:
client.knative.dev/nonce: ifwrwzlhkh
spec:
container:
image: gcr.io/knative-samples/autoscale-go@sha256:e5e89c5fd57c717b49d41be89faebc526bdcda017e898ae86c2bf20f5cd339b5
name: ""
resources:
requests:
cpu: 400m
containerConcurrency: 40
timeoutSeconds: 300
status:
address:
hostname: autoscale-go.default.svc.cluster.local
conditions:
- lastTransitionTime: 2019-05-12T09:27:32Z
status: "True"
type: ConfigurationsReady
- lastTransitionTime: 2019-05-12T09:27:32Z
status: "True"
type: Ready
- lastTransitionTime: 2019-05-12T09:27:32Z
status: "True"
type: RoutesReady
domain: autoscale-go.default.example.com
domainInternal: autoscale-go.default.svc.cluster.local
latestCreatedRevisionName: autoscale-go-vks24
latestReadyRevisionName: autoscale-go-vks24
observedGeneration: 3
traffic:
- percent: 100
revisionName: autoscale-go-vks24

GKE上にデプロイしたCloud Runであっても、そうでないものと同様にコンソール上でステータスを確認できます。

console.png

autoscale-goを見てみると、現在指しているRevisionのYAMLファイルも確認できます。kubectl describe revisiongcloud beta revisions describeで表示されるものとほぼ同じです。

run_detail.png

コマンドの方はそれぞれLog URL分が多く、gcloudはさらにannotationとしてcreatorlastModifierのアカウント情報が付加されます。


今後追加されるかもしれない機能

ここまで見てきたとおり、Cloud Run(少なくともon GKE)ではKnative Servingの機能がほとんど利用できます。

gcloudコマンドやCloud RunのUI上ではトラフィックの分割(によるブルーグリーンデプロイメントやカナリアリリース)はできませんが、直接Knativeのリソースをいじれば利用できるのかもしれません。

コンソールではRevisionへ流すトラフィック比率の表示については実装されています。

コマンドやUIから利用できる日もそう遠くはないのではないかなと思っています。

BuildはGCRとより密な感じで組み込めるかもしれません。

今回コンテナのイメージはすでに登録されているものを使いましたが、コードからそんなに体験を損なわずにシュッとデプロイされた状態にできる日はこれまた遠くないと思っています。

Eventingについてはv0.5でまた実装が激しく変化したり、Knativeを利用したriffというプロダクトでEventingへの依存を切ったり、Eventing自体がどういう方向に進むかわからないですね。


参考資料


余談


  • Servingのリポジトリにあるモニタリング系のリソースでリクエスト数とPod数の推移を可視化したかったがうまくいかなかった



    • iptables v1.6.0: can't initialize iptables table 'nat': Permission denied (you must be root)
      についてはsecurityContextを場当たり的にいじったyamlを準備して適用し、Grafanaが表示され一部のメトリクスが送信されたが、肝心のメトリクスはほとんど送信されてなさそう(少なくとも表示はされない)