EKS上にLocustをインストールしたのだが、ユーザーを増やしてもRPSが大して伸びない。リソースを調整してなるべく効率的に負荷をかけられるようにする。
CDKでEKSクラスタの作成からHelm ChartでのLocustのインストールまでを一気に行う - sambaiz-net
前提
実行するシナリオは次のGETリクエストを送るだけのもの。Chartの都合で0.x
系のlocustfileになっている。
from locust import HttpLocust, TaskSet, task
class MyTaskSet(TaskSet):
@task
def index(self):
self.client.get("/")
class MyUser(HttpLocust):
task_set = MyTaskSet
min_wait = 5
max_wait = 15
ちなみに負荷をかける対象はECS+Fargateに立ち上げたAPIサーバーで、こちらが問題にならないよう余裕を持って動かしている。 スケールするからといってAPI Gatewayなどに向けるとリクエスト数による多額の課金が発生し得るので注意だ。
ECSでアプリケーションを動かすBoilerplateを作った - sambaiz-net
なお、ファイルディスクリプタの数は元から十分大きかったため特に変更していない。
ファイルディスクリプタの上限を増やす - sambaiz-net
$ kubectl exec tryeksstackclusterchartlocustchart1abdd876-worker-d5c7b85cbq6sh -- /bin/sh -c "ulimit -n"
1048576
ワーカー数とリソースの割り当て
m5.large (2vCPU, メモリ10GiB)の2ノードに5ワーカーを立ち上げ負荷をかけたところ230RPS
あたりで頭打ちになってしまった。
Container Insightsのメトリクスを見るとワーカーPodのCPUの使用率が100%に張り付いていることが分かる。
CloudWatch Container InsightsでEKSのメトリクスを取得する - sambaiz-net
ノードのCPUは40%ほどしかリクエストされておらず同量のlimitsがかかっているので、ワーカーのリクエストCPUを増やすか数を増やせば簡単にRPSを増やせそうだ。
まずはリクエストCPUを100mから500mに増やした。2.5倍ではないのは40%の中にはkube-systemやContainer InsightsのDaemonSetが含まれているため。リクエスト率は90%ほどになった。
$ kubectl describe nodes
...
Non-terminated Pods: (9 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE
--------- ---- ------------ ---------- --------------- ------------- ---
amazon-cloudwatch cloudwatch-agent-sfz5l 200m (10%) 200m (10%) 200Mi (6%) 200Mi (6%) 5m49s
amazon-cloudwatch fluentd-cloudwatch-5vnhg 100m (5%) 0 (0%) 200Mi (6%) 400Mi (13%) 5m49s
default tryeksstackclusterchartlocustchart1abdd876-master-595c44cczwmcm 100m (5%) 100m (5%) 128Mi (4%) 128Mi (4%) 4m54s
default tryeksstackclusterchartlocustchart1abdd876-worker-fddd54db4d7xr 500m (25%) 500m (25%) 128Mi (4%) 128Mi (4%) 4m54s
default tryeksstackclusterchartlocustchart1abdd876-worker-fddd54dbz2kpj 500m (25%) 500m (25%) 128Mi (4%) 128Mi (4%) 4m54s
kube-system aws-node-875rb 10m (0%) 0 (0%) 0 (0%) 0 (0%) 6m39s
kube-system coredns-75b44cb5b4-7qpfl 100m (5%) 0 (0%) 70Mi (2%) 170Mi (5%) 4m54s
kube-system coredns-75b44cb5b4-gc6mv 100m (5%) 0 (0%) 70Mi (2%) 170Mi (5%) 4m54s
kube-system kube-proxy-wj8fk 100m (5%) 0 (0%) 0 (0%) 0 (0%) 6m39s
worker: {
replicaCount: 5,
config: {
configmapName: configmap.metadata.name,
},
resources: {
limits: {
cpu: '500m',
memory: '128Mi'
},
requests: {
cpu: '500m',
memory: '128Mi'
}
}
}
これで負荷をかけたところ1150RPS
まで伸び、リソースを増やした分だけ増えたことになる。
次にリクエストCPUは戻してワーカー数を同じく5倍の25にしてみたところ1070RPS
ほどに留まった。
ワーカー数を27まで増やしたところ先ほどと同程度のRPSが出た。
worker: {
replicaCount: 25,
config: {
configmapName: configmap.metadata.name,
},
resources: {
limits: {
cpu: '100m',
memory: '128Mi'
},
requests: {
cpu: '100m',
memory: '128Mi'
}
}
}
続いてワーカー数を2に減らしリクエストCPUを1250mにしようとしたが、1つがリソース不足で立ち上がらなかったため1100mにした。
worker: {
replicaCount: 2,
config: {
configmapName: configmap.metadata.name,
},
resources: {
limits: {
cpu: '1100m',
memory: '128Mi'
},
requests: {
cpu: '1100m',
memory: '128Mi'
}
}
}
}
これで1450RPS
まで出るようになった。
CPUの使用率が90%までしか上がっていないのはLocustのワーカーが1コアしか使わないため。
以上の結果から1コア(vCPU)を刻んでワーカー数を増やすよりも集中させた方が、多少リソースを埋めきれなくてもパフォーマンスが良いことが分かったので、 デフォルトの100mから1000mに変更しワーカー数もそれに合わせることにする。シナリオによっては併せてメモリを増やす。
インスタンスタイプ
ノードのCPUは十分使われるようになったがメモリはかなり余っている。
メモリが足りなくなるとOOMになってしまうので切り詰めすぎるのも良くないが、少なくとも今回の場合は汎用の m5.large (2 vCPU, 最大 3.1 GHz, 0.096USD/時間) * 2 より、コンピューティング特化のc系インスタンスの方が無駄なくリソースを使えそうだ。なお、その点ではインスタンスではなく使ったリソース量で課金されるFargateという選択肢もあって、
コストはvCPUあたり$0.04/時
と、ほとんどメモリを使わないなら良さそうだが、プロセッサがランダムで大抵c5のものより性能が低いので考慮しない。
ということで同サイズのc5.large (2 vCPU, 最大 3.6 GHz, 0.085USD/時間) * 2
にしたところ1570RPS
と、若干コストが下がりパフォーマンスが上がった。
メモリは割り当てを上げていないがまだ余裕がある。
次にワーカー数を倍にして、c5.large
で倍の4ノードに増やした場合と、ノード数はそのままサイズをc5.xlarge
に上げた場合を比較する。
サイズを上げるとリソースが倍になって料金も倍になるので総vCPUと料金は変わらない。
結果、前者は3130RPS
、後者は2990RPS
となった。これだけだとノードを増やした方が良さそうだが、
各ノードで走るDaemonSetの総リソースが少なくなるのと、1vCPUの確保しやすさから同量のリソースならノード数が少ない方が多くのワーカーを配置できる。
そこで後者のノードに目一杯6ワーカー配置すると3430RPS
となり逆転した。
さらに増やすとc5.large
16ノードで16ワーカー動かした場合は12300RPS
、c5.4xlarge
2ノードで30ワーカー動かした場合は15200RPS
と、その差が大きくなる。
ノード数が少ないとサブネットのIPアドレスやクォータの消費も抑えられるが、ネットワーク帯域幅はサイズを上げても倍になるわけではないので、そこがボトルネックになるとしたら分けた方が良い。
マスターのリソース
ここまでマスターのPodのリソースは変更していないが、ワーカーの数がそれほど多くないこともあってまだ余裕がある。
CPUをデフォルトの100mに戻し、インスタンスタイプをm5.4xlarge
に変更して増やしていったところ、900ワーカーで90%近くになった。メモリも限界だ。
デフォルトで100mしか割り当てられていないがしばらく大丈夫そうだ。
まとめ
- ワーカーのcpuをデフォルトの100mから1000mに上げる
- インスタンスタイプは帯域幅が問題にならない限り大きくした方が有利
- マスターはデフォルトのリソースでもしばらく大丈夫