Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Stackdriver外部指標で実現するKubernetes HPAの自動スケーリング

By Eran ChetzroniNov 5, 20185 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

1 ce4zye4gpmytsbthtlntwq

通常、KubernetesのデプロイはCPUやメモリ使用量などを基準にスケールさせますが、外部指標を使ってスケールさせたい場面もあります。本記事では、任意のStackdriver指標を使ってHorizontal Pod Autoscaler (HPA) による自動スケーリングを構成する手順を解説します。題材として、Google Cloud HTTP/SロードバランサーのRequest Per Second (RPS) を使用します。

1 ce4zye4gpmytsbthtlntwq

Stackdriver指標を使ったKubernetes Horizontal Pod Autoscalerの自動スケーリング

さっそく始めよう

まずは、新しいGoogle Kubernetes Engine (GKE) クラスターを作成します。

gcloud beta container clusters create "hpa-with-stackdriver-metrics" --zone "us-central1-a" \
--username "admin" \
--cluster-version "1.10.7-gke.6" \
--machine-type "n1-standard-1" \
--image-type "COS" \
--disk-type "pd-standard" \
--disk-size "100" --scopes \ "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"
--num-nodes "3" \
--enable-cloud-logging \
--enable-cloud-monitoring \
--addons HorizontalPodAutoscaling,HttpLoadBalancing \
--enable-autoupgrade --enable-autorepair

ポイントは `enable-cloud-monitoring` です。これによってStackdriver Monitoringの指標を読み取れるようになります。

Custom Metrics Stackdriver Adapterのデプロイ

カスタム指標アダプターは、Stackdriverの指標をKubernetes APIに取り込む役割を担います。これによってHPAがこれらの指標を参照し、それに応じた動作ができるようになります。詳細は後述のトラブルシューティングのセクションをご覧ください。

GKEオブジェクトからStackdriverに保存された指標へアクセスできるようにするには、クラスターに Custom Metrics Stackdriver Adapter をデプロイする必要があります。

Custom Metrics Adapterを動かすには、まず以下のコマンドを実行し、必要な認可ロールを作成できる権限をユーザーに付与してください。

kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole cluster-admin \
--user "$(gcloud config get-value account)"

続いて、Stackdriverから指標を読み取るためのアダプター本体をデプロイします。

kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter.yaml

Deploymentを作成する

次に、シンプルなnginxアプリケーションをデプロイし、後ほどHTTP/Sロードバランサーで計測したRPSに応じてスケールさせます。

以下のファイルを作成してください: deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
selector:
app: nginx

そしてデプロイします。

kubectl apply -f deployment.yaml

LoadBalancer Ingressを作成する

Ingressファイルを作成します: ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: basic-ingress
spec:
backend:
serviceName: nginx
servicePort: 80

Ingressを適用します。

kubectl apply -f ingress.yaml

HorizontalPodAutoscalerオブジェクトを作成する

ここが本記事の肝です。

外部指標 (external metric)* を使用し、metricName には以下を指定します。

loadbalancing.googleapis.com|https|

補足: Stackdriver指標の一覧は こちら で確認できます。 Metrics Explorer を使うのも便利です。

さらに、対象のロードバランサーの指標だけに絞り込むため、metricSelectorも指定します。

LBのforwarding ruleを確認してみましょう。

$ kubectl describe ingress basic-ingress
Name: basic-ingress
Namespace: default
Address: 35.190.3.165
Default backend: nginx:80 (10.48.2.11:80)
Rules:
Host Path Backends
---- ---- --------
* * nginx:80 (10.48.2.11:80)
Annotations:
backends: {"k8s-be-32432--ffd629d77b6630de":"HEALTHY"}
forwarding-rule: k8s-fw-default-basic-ingress--ffd629d77b6630de
target-proxy: k8s-tp-default-basic-ingress--ffd629d77b6630de
url-map: k8s-um-default-basic-ingress--ffd629d77b6630de

これで、設定にラベルマッチを追加できます (ラベル「forwarding_rule_name」に注目)。

metricSelector:
matchLabels:
resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de

最終的なファイルは次のようになります: hpa.yaml

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: nginx
spec:
minReplicas: 1
maxReplicas: 5
metrics:
- external:
metricName: loadbalancing.googleapis.com|https|request_count metricSelector: matchLabels: resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de targetAverageValue: "1" type: External scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx

ここでtargetAverageValueを使っている点に注目してください。これは1つのレプリカが処理できる指標値の総量を指定するもので、レプリカ間で分担できる作業やリソースを表す指標を扱うときに役立ちます。今回のケースでは、各レプリカが1 RPSを処理する想定です。実運用では、ご自身の要件に合わせて調整してください。

動作確認

まずはロードバランサーへトラフィックを流していきます。

先ほどのコマンドの結果から:

kubectl describe ingress basic-ingress

IngressのパブリックIPアドレスは 35.190.3.165 です。

では、このエンドポイントにリクエストを送り続けてみましょう 🥊

while true ; do curl -Ss -k --write-out '%{http_code}\n' --output /dev/null http://35.190.3.165/ ; done

HorizontalPodAutoscalerに変化があるか確認します。

kubectl describe hpa nginx-hpa

この時点では指標がまだ収集されていないため警告が表示されることがありますが、数分後にはMetricsセクションに値が入っているはずです。

Name: nginx-hpa
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"autoscaling/v2beta1","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"name":"nginx-hpa","namespace":"default"},"spec":{"ma...
CreationTimestamp: Wed, 31 Oct 2018 18:18:28 +0200
Reference: Deployment/nginx
Metrics: ( current / target )
"loadbalancing.googleapis.com|https|request_count" (target average value): 1034m / 1
Min replicas: 1
Max replicas: 5

そして「Events」セクションには次のように表示されます。

Events:
Type Reason Age From Message
...
Normal SuccessfulRescale 2m horizontal-pod-autoscaler New size: 2; reason: external metric loadbalancing.googleapis.com|https|request_count(&LabelSelector{MatchLabels:map[string]string{resource.labels.forwarding_rule_name: k8s-fw-default-basic-ingress--ffd629d77b6630de,},MatchExpressions:[],}) above target

無事に発射成功です! 🚀

トラブルシューティング

  1. 指標がKubernetesのexternal metrics APIに正しく取り込まれているかを確認する手軽な方法は、APIを直接たどってみることです。metricSelectorが正しく機能しているかの確認にも使えます。

まず、kubernetes proxyを起動します。

kubectl proxy --port=8080

続いて、ローカルホストからアクセスします。

http://localhost:8080/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_count

結果の抜粋は以下のとおりです。

{
"kind": "ExternalMetricValueList",
"apiVersion": "external.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com%7Chttps%7Crequest_count"
},
"items": [\
{\
"metricName": "loadbalancing.googleapis.com|https|request_count",\
"metricLabels": {\
"metric.labels.cache_result": "DISABLED",\
"resource.labels.backend_target_type": "BACKEND_SERVICE",\
"resource.labels.backend_name": "k8s-ig--ffd629d77b6630de",\
...\
"resource.labels.forwarding_rule_name": "k8s-fw-default-basic-ingress--ffd629d77b6630de",\
...\
},\
"timestamp": "2018-11-01T08:41:30Z",\
"value": "2433m"\
}\
]
}

うまくいきました!


2. アダプターの挙動を確認するもう1つの方法は、ログを見ることです。まずはcustom-metricsのPodを一覧表示します。

$ kubectl get pods -n custom-metrics
NAME READY
custom-metrics-stackdriver-adapter-c4d98dc54-2n4jz 1/1

続いて、ログを追跡します。

$ kubectl logs custom-metrics-stackdriver-adapter-c4d98dc54-2n4jz -n custom-metrics
...
I1104 06:42:11.125627 1 trace.go:76] Trace[1192308782]: "List /apis/external.metrics.k8s.io/v1beta1/namespaces/default/loadbalancing.googleapis.com|https|request_count" (started: 2018-11-04 06:42:08.155209905 +0000 UTC m=+311951.293335726) (total time: 2.970372027s):
Trace[1192308782]: [2.97027864s] [2.970185564s] Listing from storage done
...

これらのログ、特にエラーメッセージからは多くの有益な情報が得られます。

まとめ

Stackdriverに収集・保存された外部指標の活用は、とてもシンプルで扱いやすい仕組みです。同じ要領で、Monitoring API経由でStackdriverに送信した独自のカスタム指標も利用できます。

本記事で使用したリソースは、こちらのGitHubリポジトリにまとめてあります。

他の記事も読みたい方は、ぜひブログをご覧いただくか、EranをTwitterでフォローしてください。