KubernetesのアーキテクチャでHTTP2のトラフィックが偏る問題を再現する

Hiroki Tanaka
google-cloud-jp
Published in
10 min readNov 14, 2019

KubernetesのアーキテクチャでHTTP2のトラフィックが偏る問題を再現する

HTTP2 は L7 で動作するプロトコルなので、L4 ロードバランサ (L4LB) をアーキテクチャ上で使っている場合は、特定のPodにトラフィックが偏ってしまうことが知られています。HTTP2 は1つの TCP コネクションの中にHTTPリクエストを多重化して送ることでオーバーヘッドを削減出来ますが、逆に L4 LB だけでは、1つの TCP コネクションを使い続けてしまうので、結果としてリクエストが偏ります。

※ GCPは、L4 ロードバランサはGCPでは Network Load Balancing として提供しています。(https://cloud.google.com/load-balancing/docs/network/)

実際にどの程度偏るのか?自分の環境で検証をしてみたい方もいると思いますので、GKE 上で検証する方法を解説します。

gcloud のコマンドや、各種リソースの yaml ファイルも載せていますので、できるだけすぐにご自身の環境で再現できるようにしました。

GKE クラスタ( 3 Node x 3 Pod )をL4 LB or L7 LBで外部に公開するというシンプルなアーキテクチャです。

Step1 GKEのクラスタを作る

GKE のクラスタを作る前に事前準備として gcloud コマンドのアップデートをしておきましょう。gcloud 自体のインストール方法はこちら

#gcloudコマンドのアップデート
gcloud components update
#kubectlコマンドのインストール
gcloud components install kubectl
#PROJECTの設定
gcloud config set project [PROJECT_ID]

gcloud のコンポーネントのアップデートが完了したら GKE クラスタを作成します。

gcloud container clusters create load-imbalanced-cluster \
--zone us-central1-c \
--num-nodes 3 \
--cluster-version 1.14.7-gke.10 \
--enable-stackdriver-kubernetes \
--enable-ip-alias

後で HTTP2 で負荷をかけたときの数字をモニタリングするために Stackdriver の Kubernetes monitoring 機能を有効にします。

VPC native も後からこれが必要な機能の検証をするために有効にしておきます。(次の記事で解説予定)

Step2 Kubernetes 上で負荷試験用の Web サービスを構築

GKE で Kubernetes のクラスタの構築が完了した後は、リソースをyaml ベースで作っていきます。以下の Deployment のyaml で、httpリクエストのヘッダ内容をそのまま返すウェブサービスを立ち上げることが出来ます。Pod 数は9個として指定しています。

#my-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: echoheaders
spec:
replicas: 9
template:
metadata:
labels:
app: echoheaders
spec:
containers:
- name: echoheaders
image: k8s.gcr.io/echoserver:1.10
ports:
- containerPort: 8443

このyamlファイルを kubectl apply -f my-deployment.yaml で適用します。

kubectl get pods -o wide で正しくPodが起動しているかも確認してください。

以下の様にSTATUSがすべてRunningになっていれば正しく起動しています。

Step3 L4LBを使ってウェブサービスを外部に公開する。

以下の yamlファイルで、外部向けにこのウェブサービスの Expose が可能です。L4 LB を使うために Service の Type は LoadBalancer を使います。今回はトラフィックが偏ることを確認するための検証なので、わかりやすさのために、externalTrafficPolicy : Local を指定してPodが存在するNode外にトラフィックが転送されないようにします。

#my-lb.yaml
apiVersion: v1
kind: Service
metadata:
name: echoheaders
labels:
app: echoheaders
spec:
type: LoadBalancer
externalTrafficPolicy : Local
ports:
- port: 443
targetPort: 8443
protocol: TCP
selector:
app: echoheaders

同様に kubectl apply -f my-lb.yamlで適用して、kubectl get serviceで正常に作成されたか確認して下さい。

以下のようにEXTERNAL-IPが表示されれば正しく作成されています。

Step4 負荷をかけるためのクライアントを作成する。

今回は Ubuntu 18.04 に負荷ツールをインストールします。 負荷ツールは nghttp2 を使います。

gcloud compute instances create ubuntu1804 \
--image-family ubuntu-1804-lts \
--image-project gce-uefi-images

インスタンス作成完了後に、SSH でログインして HTTP2 の負荷試験をかけるのに必要なパッケージをインストールします。

sudo apt-get update
sudo apt-get install nghttp2

nghttp2 をインストールすると、h2loadというツールも使えるようになります。以下のコマンドで実際に負荷がかけられます。(-n でリクエスト数を指定)

h2load https://34.68.208.192/ -n 1000

※IP アドレスは実際の環境によって異なります。kubectl get service で表示されたアドレスを指定してください。

StackDriver Monitoring のダッシュボードを確認してみましょう。

画面左側の Resources からMetrics Explorer を選択してください。

Resource Type に Kubernetes Podを、Metrics にByte Recievedを選択し、Filterで namespace_name = defaultを設定すると、Pod毎に受信したデータの Byte 数がグラフで表示されます。

表示されたグラフを見ると9個ある Pod のうち1つの Pod だけにリクエストが集中していることがわかります。

トラフィックの偏りが確認できたところで一度 Service(L4LB)を削除します。

kubecel delete -f my-lb.yaml

Step5 L7LBを作成し再度テストを行う。

今度は L7 LBを使うことで、この偏りが解消されるかどうか見ていきましょう。

※GCPは、L7 ロードバランサ HTTP(S) Load Balancing として提供しています。 (https://cloud.google.com/load-balancing/docs/https/)

L7 LBを使うには、まず Node port を作成します。

#my-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
cloud.google.com/app-protocols: ‘{“my-port”:”HTTP2"}’
name: echoheaders
labels:
app: echoheaders
spec:
type: NodePort
externalTrafficPolicy : Local
ports:
— port: 443
targetPort: 8443
protocol: TCP
name: my-port
selector:
app: echoheaders

これまでと同じように

kubectl apply -f my-nordport.yaml 

で作成します。

このあと Ingress を作成することで GCP の L7 LBが作成されますが、そのとき SSL の証明書が必要になるので事前に作成しておきます。

以下のコマンドで自前の証明書を作成します。

openssl req -x509 -nodes -newkey rsa:2048 -keyout selfsigned.key -out selfsigned.crt -days 3650

作成した証明書を Kubernetes の Secret に登録します。

kubectl create secret tls selfsigned --cert selfsigned.crt --key selfsigned.key

Secret に登録した証明書情報を使うように指定した Ingress のyaml ファイル

#my-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echomap
spec:
tls:
- secretName: selfsigned
backend:
serviceName: echoheaders
servicePort: 443

これも同じように

kubectl apply -f my-ingress.yaml 

で作成します。

しばらく待つと L7 LB が作成されます。kubectl get ingressコマンドを実行すると外部に公開されたIPアドレスがわかります。

h2load https://107.178.240.***/ -n 1000

そのアドレスに向けて、Step4で作成したクライアントから同様にHTTP2のトラフィックを送ります。

Stackdriverのダッシュボードを確認してみると L4LB のときとは異なり、トラフィックが分散されています。

この記事では、自分で検証環境を作る方法と、負荷試験の手順について解説しました。

次回の記事では更に他の環境での検証と、L4 LB を使ったアーキテクチャ上で、偏りを解消するワークアラウンドについて解説する予定です。

--

--

Hiroki Tanaka
google-cloud-jp

Customer Engineer at @GoogleCloud. Disclaimer. Opinions are my own, NOT the views of my employer. All posts here are my personal opinion.