これはREGXAを否定的に評価するための記事ではありません。REGXAのVPSを買うべきかどうかを誰かに勧めるつもりもありません。単に、開発者として過ごしたある一日の記録です。
2 vCPU、2 GB RAM、60 GB NVMeのKVM VPSへ通常のworkloadを移しました。Nginxは動作しており、backendも動作していました。それでもサーバーは極端に過負荷になったかのように振る舞いました。requestが溜まり、TLS処理が遅くなり、connectionが長時間残り、一部のrequestはHTTP 504になるまで2分以上かかりました。
CPU accountingを見た瞬間、調査の方向が変わった
最初に疑ったのは単純でした。VPS内の何かがCPUを使い切っているのではないか。そこでmpstatを実行しました。
Average CPU steal: 92.58%
CPU 0 steal: 90.62%
CPU 1 steal: 94.57%
CPU user: 4.02%
CPU system: 1.47%
CPU iowait: 0.41%
CPU idle: 0.53%
個々のsampleでも89〜98%前後のstealが繰り返し出ていました。CPU pressureも非常に高い状態でした。
CPU PSI some avg10: 79.47
CPU PSI some avg60: 75.32
CPU PSI some avg300: 76.31
Load average: 5.85 / 5.75 / 5.73
2 vCPUしかないVMです。Linuxでは実行可能なprocessがCPU待ちになっていました。重要だったのは通常のapplication CPU usageではなく、%stealでした。
CPU使用率が高いこととCPU stealが高いことは別の問題
application自体がprocessorを使い切っているなら、userやsystemが高くなるはずです。しかし実際は、おおよそ次のような状態でした。
user: 4%
system: 1%
steal: 93%
CPU stealとは、virtual CPUが実行可能な状態なのにhypervisorからscheduleされなかった時間です。私がVPSからCPU timeを奪われていたと表現するとき、それはこのvirtualization上の技術的な意味です。REGXAが私専用だったphysical coreを意図的に他の顧客へ割り当てた、とまでは証明できませんし、guest側のmetricから意図を推測することもできません。確認できるのは、guest側に実行可能な処理がありながらCPU scheduling timeを受け取れていなかったという事実です。
production trafficをすべて外した
それでも一つ反論が残ります。自分のworkloadそのものが原因ではないか。そこでその変数を取り除きました。稼働中のworkloadを別のサーバーへ移し、このVPSへのproduction trafficを止め、queueが解消するのを待ってから同じ測定を繰り返しました。
Average CPU steal: 94.17%
CPU 0 steal: 95.56%
CPU 1 steal: 92.83%
CPU user: 1.95%
CPU system: 0.59%
CPU iowait: 0.59%
CPU idle: 2.18%
結果はさらに悪化しました。個別のvmstat sampleでは依然として91〜98%のstealが出ており、最大15個のrunnable processがCPUを待っていました。重要なのは次の組み合わせです。
user: 1.95%
system: 0.59%
steal: 94.17%
applicationはほとんど何もしていません。それでもVMはほぼすべてのscheduling timeを失っていました。通常のproduction loadでは説明できなくなりました。
localhostでさえ異常に遅かった
127.0.0.1経由でもHTTPSをテストしました。これならpublic DNS、自宅側のISP、地理的距離、外部network pathを切り離せます。
trafficがある状態では、10回のlocalhost HTTPSのうち4回がTLS handshake中に失敗しました。成功したrequestでも29.30、22.77、12.25、11.87、11.12、9.40秒かかりました。TLS handshakeだけで約9秒かかったケースもありました。
production trafficを外すとlocalhostは改善しましたが、依然として不安定でした。0.061、0.745、0.830、0.873、1.010、1.117、1.121、2.188、3.355秒です。同じlocal operationがあるときは約61 ms、次の試行では3.3秒以上という差が出ました。
stack全体に影響が出ていた
ある時点では約450のestablished connection、122のorphaned connection、110のFIN-WAIT-1、33のCLOSE-WAITがありました。localhost backendのlisten queueは約14〜15、HTTPS側は約40まで増えました。
NginxではHTTP 504まで142.857、138.902、135.064、129.819、128.657秒かかったrequestが記録されました。他にも67〜130秒ほど開き続けたrequestがありました。約35〜41秒のsecure-connect timeout、database network timeout、遅延したTLS処理も確認しました。
個別に見ればNginx、database、network、backendの別々の問題に見えます。しかしtimeoutを伸ばしても、hypervisorがscheduleしていないCPU timeは増えません。
RAMやdiskでは説明できなかった
VPSには約1.0〜1.1 GiBのRAMがまだavailableで、swapはほぼ使われていませんでした。OOM eventもOOM killerの動作もありません。filesystemの使用率は約20%で、約44 GB空いていました。重要なCPU測定中のI/O waitは1%未満でした。
memory不足でもdisk満杯でもなく、applicationが失われたCPUを消費していたわけでもありません。支配的なmetricはやはり%stealでした。
正常なKVM VPSはまったく違う結果だった
通常trafficを処理中の別のKVM VPSで同じ種類の診断を実行しました。
Average CPU steal: 0.02%
CPU idle: 87.86%
CPU PSI avg10: 0.29
CPU PSI avg60: 0.63
CPU PSI avg300: 0.49
Load average: 0.47 / 0.33 / 0.14
10回のlocalhost HTTPSはすべて約37〜69 msでした。さらに別の、本当にbusyなproduction VPSでは、user CPU 61.71%、system CPU 5.08%、idle 24.09%、stealはわずか0.13%でした。
今ではこの違いを重視しています。自分のsoftwareがprocessorを実際に使っていてVPSがbusyなのと、guestが測定時間の90%以上をhypervisor待ちで失うのはまったく別の問題です。
最終的にREGXA自身がinfrastructure contentionを認めた
VM内から確認できるのはguest側だけです。physical host、scheduler configuration、CPU quota、隣接VMの状態は見えません。REGXA側では確認できます。
最終的にsupportから、このVPSはshared CPU infrastructure上にあり、CPU resourceは複数のvirtual machineで共有され、physical nodeのloadによってperformanceが変動すると説明されました。またFrankfurt infrastructureは特に需要が高い状態であり、私が観測していた高いCPU stealはresource contention on the underlying infrastructureによるものだと明言されました。
さらに、現在のinfrastructureではCPU quotaやscheduling policyを変更したり、このshared VPSへ追加のdedicated CPU resourceを割り当てたりできないとのことでした。提示された技術的な解決策は、utilizationの低いlocationへの移動でした。
これは、私が以前目にしたdedicated CPU coresやguaranteed resourcesという表現とは整合させにくい説明でした。hostの正確なconfigurationは見えないため、直接の仕組みがCPU overcommitment、quota、scheduler weighting、throttling、あるいはその組み合わせだったかは断定できません。意図も証明できません。しかしそこまで主張する必要もありません。Linuxは継続的に92〜94%のstealを示し、REGXA自身がその高いstealをshared infrastructure上のcontentionに起因すると説明しました。
返金でもさらにやり取りが必要だった
infrastructure側の問題が明確になった時点で、VPSを別のnodeへ移して検証を続ける気はありませんでした。serviceを解約し、支払った金額を返してほしいと考えました。
最初に提示されたのは全額ではなく一部のみで、しかも支払いに使ったカードではなくREGXA account balanceへの返金でした。serviceを離れたい状況でprovider内のcreditは、私にとって返金と同じではありません。
そのため返信を続け、100%をoriginal payment methodへ戻すよう求めました。最終的にREGXAは同意し、全額を元の支払い方法へ返金しました。full refundはexceptionとして扱われたと説明されました。
最終的に全額が戻った点は評価しています。ただしsupportがinfrastructure contentionを認めた後にも、金銭面の解決のために追加で押し続ける必要があったことも、この経験の一部です。
この経験から残ったルール
最も簡単に陥りそうだったのは、application側のoptimizationを続けることでした。Nginx設定を変える、timeoutを伸ばす、concurrencyを下げる、retryを追加する、MongoDBを調べる、backend codeを書き直す。個別の症状は変わったかもしれません。しかし重要な問いには答えられません。なぜCPU stealが90%を超えているのか。
今ではSSHが使える、Nginxが起動する、health endpointが200を返す、という確認だけでは終わりません。新しいVPSでは%user、%system、%iowait、%idle、%steal、CPU PSI、run queue、localhost latencyも確認します。
そして怪しいと感じたらworkloadを外して再測定します。このケースでは、それが最も明確な結果を出しました。
CPU user: 1.95%
CPU system: 0.59%
CPU steal: 94.17%
すべてのREGXA VPSが同じ挙動をするとは言っていません。私がテストしたのは1台で、その1台で起きたことを記録しただけです。ただ、そのVPSでは証拠が非常に明確でした。applicationはほとんどCPUを使っていないのに、Linuxはvirtual CPU timeの大部分がhypervisorによるscheduling待ちで失われていると示していました。その後REGXAも、まさにその挙動をshared infrastructure上のresource contentionに起因すると説明しました。
だから記録しています。点数を付けるためでも、購入を勧めたり止めたりするためでもありません。開発者として過ごした一日の記録であり、今後VPSで決して無視しないmetricを知った出来事だからです。