Azure Container Apps 環境における ピアツーピア 暗号化
はじめに
この記事は、Azure Container Apps 環境 において ピアツーピア 暗号化 を構成して触ってみた記録です。
手順
ドキュメントより引用
Azure Container Apps では、環境内のピアツーピア TLS 暗号化がサポートされています。 この機能を有効にすると、Azure Container Apps 環境スコープ内で有効なプライベート証明書を使用して、環境内のすべてのネットワーク トラフィックが暗号化されます。 これらの証明書は、Azure Container Apps によって自動的に管理されます。
Note 既定では、ピアツーピア暗号化は無効になっています。 アプリケーションに対してピアツーピア暗号化を有効にすると、高負荷のシナリオで応答の待機時間が長くなり、最大スループットが低下する可能性があります。

1 受信 TLS トラフィックは、環境のエッジにあるイングレス プロキシで終了します。
2 環境内のイングレス プロキシとの間のトラフィックは、プライベート証明書で TLS 暗号化され、受信側で復号化されます。
3 アプリ A からアプリ B の FQDN への呼び出しは、まずエッジ イングレス プロキシに送信され、TLS で暗号化されます。
4 アプリ B のアプリ名を使用してアプリ A からアプリ B に対して行われた呼び出しは、アプリ B に直接送信され、TLS で暗号化されます。 アプリと Java コンポーネント間の呼び出しは、アプリ間通信と同じ方法で処理され、TLS で暗号化されます。
どういうことか。とりあえず試してみる。

"peerTrafficConfiguration": {
"encryption": {
"enabled": true
}
},
このときアプリ側は何も変更していません。
すなわち、受信側の Nginx は 80 ポートで待ち受ける状態のままであり、アプリ自体は平文でリクエストを受け付けることになります。
また、画面上とプロパティでの変更がわかるだけで、ログなどからも P2P 暗号化が実行されていることは判断できません。
検証と考察
アプリケーションに対してピアツーピア暗号化を有効にすると、高負荷のシナリオで応答の待機時間が長くなり、最大スループットが低下する可能性があります。
との文章から実験してみました。
先に結論
P2P Encryption を有効化する場合、想定のパフォーマンスが得られるか、ユーザー影響がないかの確認は必須です。
Azure Container Apps の Peer-to-Peer (P2P) Encryption を OFF/ON で切り替え、同一条件(vegeta / 30s / timeout=60s / 1KB応答)で負荷試験を行い、成功率・p95レイテンシ・スループットを比較した。横軸は vegeta がレポートする実測の送信レート(rate)を用いる。1
結果として、レプリカ数を 1 に固定した場合、P2P ON は高負荷域で成功率が急低下し、スループットも大きく落ち込む挙動が確認できた(図1・図3)。一方で P2P OFF は同等の負荷条件でも成功率を維持できた。

図1(成功率):P2P OFF/ON とレプリカ数(1 / 3)を変えたときの成功率比較。高負荷域での成功率低下は主に「P2P ON × レプリカ1」で顕著
注記:横軸は vegeta report の rate(実際に送れた送信レート)を使用。高負荷域ではクライアント側/サーバ側の要因で targetRate を維持できず、report の rate が低下することがあるため、比較の基準として rate を採用した。1

図2(p95):p95レイテンシ比較(対数軸)。成功率が同等でも、P2P ON の方が p95 が増加する帯域があり、待機時間の増加として現れる。
注記:p95 グラフは値のレンジ(ms〜秒)差が大きいため、可視化のために対数軸を使用。

図3(スループット):実効スループット比較。成功率低下(503混在)が発生すると throughput も連動して下がる。
また、別途記事にする予定ですが、
P2P Encryption を実現するために、Ingress の Envoyt とは別に App 毎(レプリカ毎?)に Envoy Sidecar の存在することが推測されます。
検証
copilot との検証方針相談抜粋
- 測りたい「P2P のオーバーヘッド」と経路の選び方
P2P 暗号化は Envoy↔Envoy 間の TLS(アプリ直前は HTTP のまま)。P2P の有無が効くのは、内部 Envoy 経路を通るときです。
よって、**比較対象は appname 経路(例: http://<APP_NAME>/)**がベスト。
外部FQDN や internal FQDN は Ingress Envoy(エッジ)経由かつ L7 終端が混ざるため、P2P の効果だけを純粋に切り出しにくいです。
もし外部の比較も載せたい場合は、「説明つき参考値」として別枠にしましょう
- フェアな A/B(OFF→ON)にするための前提合わせ
アプリ/環境の固定化
固定 200 応答のエンドポイントを用意(例: /bench/1kb)。
アプリのリビジョン固定(トラフィック 100% を単一リビジョンに)。
レプリカ固定(minReplicas = maxReplicas。例:3→3 など)でオートスケールのゆらぎ排除。
CPU/メモリ要求/制限を明記(スロットリングで数字が割れます)。
**スケールトリガ(KEDA)**に関わるメトリクスが揺れないよう、全テストを短時間に連続で実施。
Dapr 無効(有効だと別オーバーヘッドが載ります)。
Warmup:本番計測の前に 10〜30 秒のウォームアップを必ず入れる。
クライアント(負荷側)の健全性
同一環境の別アプリから実行(ネットワーク経路を最短に)。
Keep-Alive 有効(-k)。
ulimit / sysctl を確認(ulimit -n, net.ipv4.ip_local_port_range, net.core.somaxconn など)。
複数クライアントの併用(例:3ポッド×ab で分散)か、wrk / vegeta の利用を検討。
タイムアウト(-s)や コネクション失敗の扱いを明記。
同一マシン内で “同時に” OFF/ON を打たない(リソース取り合い防止)。
宛先のアプリには 1KBの応答を返すエンドポイント を用意しました。
また、ab は HTTP/1.0 のみしか対応していないようなので、クライアント側は vegeta を使ってみます。
curl -L \
https://github.com/tsenart/vegeta/releases/download/v12.12.0/vegeta_12.12.0_linux_amd64.tar.gz \
-o vegeta.tar.gz
tar xfz vegeta.tar.gz
mv vegeta /usr/local/bin/vegeta
なお、クライアント側はレプリカ数1で固定とし、そのレプリカに接続したコンソールから vegeta を実行しています。
実験条件(比較の前提)
- 負荷ツール:vegeta(rate固定、duration=30s、timeout=60s、connectionsは主に500)
- 経路:環境内から http://node-app/...(アプリ名)でアクセス(P2P 影響が出る“環境内”経路)
- 応答サイズ:約 1KB(bytes/1024)
- スケール条件:min/max を 1固定と3固定で比較
- P2P:OFF / ON を切り替え
- 観測指標:Success率、503発生、throughput(実効RPS)、latency(p95/p99)、wait(攻撃後に処理が掃けるまでの時間)
Replica Count = 1 (min/max =1)
まずはターゲットレプリカ数は 1 で固定とします。
結果サマリ
A. **min=max=1(レプリカ1固定)**での OFF vs ON
✅ P2P OFF(安定)
rate=1000 でも Success 100% / 503 なし
throughput もほぼレートに追従(約 997 rps)
高負荷でも「処理しきれずに落ちる」挙動は出ていない(テールは伸びるが耐える)
❌ P2P ON(高負荷で不安定化)
rate=800 までは Success 100%
rate=850 で突然崩壊(Success 55%前後、503大量、waitが約29秒級に肥大化)
900/950/1000 でも 503が大量継続し、実効 throughput が大幅低下(数百rps)
node-app が 正常性プローブに応答できず再起動(=アプリ側も余力を失っていた)
👉 解釈:
レプリカ1固定では、P2P ON により「高負荷での余力」が削られ、待ち(wait/テールレイテンシ)が増え、ある点から 503 と再起動を伴って最大スループットが実質的に低下しました。
→ ドキュメントの「高負荷で待機時間増+最大スループット低下の可能性」を 強く裏付けます。
P2P Encription OFF
for r in 200 400 600 800 1000; do
echo "== P2P OFF | rate=$r =="
echo "GET http://node-app/misc/response/bytes/1024" \
| vegeta attack -rate=$r -duration=30s -connections=500 -timeout=60s \
| tee "p2pOFF_rate${r}.bin" \
| vegeta report | tee "p2pOFF_rate${r}.txt"
sleep 60
done
== P2P OFF | rate=200 ==
Requests [total, rate, throughput] 6000, 200.03, 200.01
Duration [total, attack, wait] 29.999s, 29.996s, 3.06ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.766ms, 15.155ms, 3.418ms, 5.017ms, 21.951ms, 392.743ms, 512.294ms
Bytes In [total, mean] 6288000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:6000
Error Set:
== P2P OFF | rate=400 ==
Requests [total, rate, throughput] 12000, 400.03, 399.99
Duration [total, attack, wait] 30.001s, 29.997s, 3.579ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.578ms, 30.881ms, 5.004ms, 38.575ms, 102.352ms, 597.963ms, 784.483ms
Bytes In [total, mean] 12576000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:12000
Error Set:
== P2P OFF | rate=600 ==
Requests [total, rate, throughput] 18000, 600.04, 599.66
Duration [total, attack, wait] 30.017s, 29.998s, 19.285ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.595ms, 70.526ms, 12.371ms, 100.657ms, 617.169ms, 797.208ms, 970.544ms
Bytes In [total, mean] 18864000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:18000
Error Set:
== P2P OFF | rate=800 ==
Requests [total, rate, throughput] 23988, 797.92, 797.79
Duration [total, attack, wait] 30.068s, 30.063s, 4.779ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.626ms, 110.843ms, 84.151ms, 123.449ms, 720.002ms, 901.831ms, 1.098s
Bytes In [total, mean] 25139424, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:23988
Error Set:
== P2P OFF | rate=1000 ==
Requests [total, rate, throughput] 30000, 1000.03, 996.92
Duration [total, attack, wait] 30.093s, 29.999s, 93.557ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.834ms, 229.28ms, 100.854ms, 744.124ms, 1.238s, 1.623s, 2.101s
Bytes In [total, mean] 31440000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:30000
Error Set:
P2P Encryption ON
for r in 200 400 600 800 1000; do
echo "== P2P ON | rate=$r =="
echo "GET http://node-app/misc/response/bytes/1024" \
| vegeta attack -rate=$r -duration=30s -connections=500 -timeout=60s \
| tee "p2pON_rate${r}.bin" \
| vegeta report | tee "p2pON_rate${r}.txt"
sleep 60
done
== P2P ON | rate=200 ==
Requests [total, rate, throughput] 6000, 200.03, 200.02
Duration [total, attack, wait] 29.998s, 29.995s, 2.118ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.509ms, 46.483ms, 2.343ms, 8.931ms, 527.538ms, 870.207ms, 1.016s
Bytes In [total, mean] 6288000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:6000
Error Set:
== P2P ON | rate=400 ==
Requests [total, rate, throughput] 12000, 400.03, 400.01
Duration [total, attack, wait] 30s, 29.998s, 1.822ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.221ms, 50.724ms, 2.194ms, 33.662ms, 665.859ms, 858.394ms, 929.849ms
Bytes In [total, mean] 12576000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:12000
Error Set:
== P2P ON | rate=600 ==
Requests [total, rate, throughput] 18000, 600.04, 599.84
Duration [total, attack, wait] 30.008s, 29.998s, 10.285ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.121ms, 62.491ms, 8.461ms, 60.879ms, 100.304ms, 1.304s, 1.599s
Bytes In [total, mean] 18864000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:18000
Error Set:
== P2P ON | rate=800 ==
Requests [total, rate, throughput] 24000, 800.06, 766.56
Duration [total, attack, wait] 31.294s, 29.998s, 1.297s
Latencies [min, mean, 50, 90, 95, 99, max] 1.22ms, 239.447ms, 22.184ms, 641.823ms, 1.56s, 3.506s, 3.998s
Bytes In [total, mean] 25141776, 1047.57
Bytes Out [total, mean] 0, 0.00
Success [ratio] 99.95%
Status Codes [code:count] 200:23989 503:11
Error Set:
503 Service Unavailable
== P2P ON | rate=1000 ==
Requests [total, rate, throughput] 30000, 1000.04, 275.89
Duration [total, attack, wait] 1m3s, 29.999s, 33.244s
Latencies [min, mean, 50, 90, 95, 99, max] 1.392ms, 1.882s, 1.361s, 2.726s, 3.786s, 24.156s, 55.745s
Bytes In [total, mean] 19779629, 659.32
Bytes Out [total, mean] 0, 0.00
Success [ratio] 58.16%
Status Codes [code:count] 200:17448 503:12552
Error Set:
503 Service Unavailable
Copilot による解説
-
結果サマリ(あなたの 1 回スイープ)
✅ P2P OFF(全レートで安定)Success 100%(200〜1000 全て 503 なし)
Throughput ≈ Rate(200→200 / 400→400 / … / 1000→997)
Rate を上げるほど レイテンシのテールが伸びる(p95/p99 が顕著に増加)⚠️ P2P ON(800 から兆候、1000 で崩壊)
200/400/600 は Success 100%
800:Success 99.95%(503=11)、Throughput 766 rps、wait 1.297s
1000:Success 58.16%(503=12552)、Throughput 275 rps、wait 33.244s(攻撃終了後も処理が掃けない)ON の 1000 は完全に 飽和して崩れた状態です。
wait が 33 秒というのが決定的で、「30 秒間の入力に対して捌き切れず、キュー(処理待ち)が残っている」ことを示します。 -
重要ポイント:Throughput が落ちると “Bytes mean” も落ちる理由
P2P ON の 1000 で Bytes In [mean] が 1048 → 659 に落ちていますよね。
これは 503 応答が 200 応答より小さいため、成功率が下がるほど平均バイト数が減る、という挙動です。
つまり ON の 1000 は **「処理できない→503 を返して早期に落としている」**状態で、単に遅いのではなく “拒否” が混ざっています。 -
「どこが限界か」を 2 種類の SLO で言語化(ブログ向け)
ブログにするなら、同じデータでも **評価基準(SLO)**を置くと一気に説得力が増します。
A) 成功率基準(例:Success ≥ 99.9%)P2P OFF:1000 まで OK(Success 100%)
P2P ON:800 はギリOK(99.95%)、1000 は NG(58.16%)
→ P2P ON の安定上限は 800〜1000 の間B) レイテンシ基準も入れる(例:p95 < 1s)
OFF 800 の p95 = 720ms → OK
ON 800 の p95 = 1.56s → NG
ON 600 の p95 = 100ms → OK→ “成功率はOKでもレイテンシSLOではNG” という形で、
P2P ON は 800 で既にテールが悪化している、と書けます。 -
何が起きているか(技術的な解釈)
前提:min=max=1(単一レプリカ)、かつ P2P は Envoy↔Envoy の TLSを増やします。
P2P ON で 800→1000 で崩れる理由として自然なのは:Envoy 間 TLS による CPU/暗号処理コスト増
接続管理(TLS セッション確立・再利用・バッファ)が増えて プロキシ側が先に飽和
飽和すると Envoy が upstream を守るために 503 を返し始める(いわゆる過負荷遮断)
その結果、throughput が rate に追いつかず wait が伸びる(キューが掃けない)ここがポイントで、アプリは 80 のままでも、P2P ON で増えるコストは “プロキシ側” に乗ります。
1 レプリカ固定だと、この追加コストがそのまま限界点低下として表れやすいです。 -
次にやると “結論が固まる” 追加実験(おすすめ順)
✅ (1) 境界を細かく刻む(最優先)
いま分かったのは「ON は 800 は概ねOK、1000は崩壊」です。
境界を確定させるために 850 / 900 / 950 を追加してください。
for r in 200 600 800 850 900 950 1000; do
echo "== P2P ON | rate=$r =="
echo "GET http://node-app/misc/response/bytes/1024" \
| vegeta attack -rate=$r -duration=30s -connections=500 -timeout=60s \
| tee "p2pON_rate${r}.bin" \
| vegeta report | tee "p2pON_rate${r}.txt"
sleep 60
done
== P2P ON | rate=200 ==
Requests [total, rate, throughput] 6000, 200.03, 200.02
Duration [total, attack, wait] 29.997s, 29.995s, 1.969ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.328ms, 31.974ms, 2.001ms, 4.39ms, 284.911ms, 680.216ms, 957.357ms
Bytes In [total, mean] 6288000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:6000
Error Set:
== P2P ON | rate=600 ==
Requests [total, rate, throughput] 18000, 600.04, 599.80
Duration [total, attack, wait] 30.01s, 29.998s, 12.36ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.181ms, 52.625ms, 9.887ms, 78.406ms, 100.362ms, 1.092s, 1.248s
Bytes In [total, mean] 18864000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:18000
Error Set:
== P2P ON | rate=800 ==
Requests [total, rate, throughput] 24000, 800.04, 797.67
Duration [total, attack, wait] 30.088s, 29.999s, 89.092ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.364ms, 150.07ms, 34.79ms, 111.632ms, 930.548ms, 2.707s, 3.296s
Bytes In [total, mean] 25152000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:24000
Error Set:
== P2P ON | rate=850 ==
Requests [total, rate, throughput] 25500, 850.01, 239.54
Duration [total, attack, wait] 58.964s, 30s, 28.964s
Latencies [min, mean, 50, 90, 95, 99, max] 3.867ms, 2.096s, 1.306s, 2.591s, 3.353s, 27.707s, 56.266s
Bytes In [total, mean] 16156565, 633.59
Bytes Out [total, mean] 0, 0.00
Success [ratio] 55.39%
Status Codes [code:count] 200:14124 503:11376
Error Set:
503 Service Unavailable
== P2P ON | rate=900 ==
Requests [total, rate, throughput] 27000, 900.03, 448.65
Duration [total, attack, wait] 36.18s, 29.999s, 6.181s
Latencies [min, mean, 50, 90, 95, 99, max] 1.193ms, 1.868s, 1.173s, 2.26s, 2.861s, 27.918s, 29.997s
Bytes In [total, mean] 18296461, 677.65
Bytes Out [total, mean] 0, 0.00
Success [ratio] 60.12%
Status Codes [code:count] 200:16232 503:10768
Error Set:
503 Service Unavailable
== P2P ON | rate=950 ==
Requests [total, rate, throughput] 28500, 950.02, 322.34
Duration [total, attack, wait] 52.672s, 29.999s, 22.672s
Latencies [min, mean, 50, 90, 95, 99, max] 2.146ms, 2.07s, 1.492s, 2.644s, 3.266s, 25.839s, 49.761s
Bytes In [total, mean] 19176087, 672.85
Bytes Out [total, mean] 0, 0.00
Success [ratio] 59.57%
Status Codes [code:count] 200:16978 503:11522
Error Set:
503 Service Unavailable
== P2P ON | rate=1000 ==
Requests [total, rate, throughput] 29978, 999.05, 398.59
Duration [total, attack, wait] 42.382s, 30.007s, 12.375s
Latencies [min, mean, 50, 90, 95, 99, max] 1.222ms, 1.831s, 1.291s, 2.444s, 3.151s, 25.173s, 29.946s
Bytes In [total, mean] 19267961, 642.74
Bytes Out [total, mean] 0, 0.00
Success [ratio] 56.35%
Status Codes [code:count] 200:16893 503:13085
Error Set:
503 Service Unavailable
Replica Count = 3 (min/max =3)
結果サマリ
✅ 200〜1000 rps 領域(成功率はどちらも 100%)
P2P OFF:rate=1000 で Success 100%、p95 ≈ 105ms、p99 ≈ 506ms(max ≈ 798ms)
P2P ON:rate=1000 で Success 100%、p95 ≈ 515ms、p99 ≈ 1529ms(max ≈ 2095ms)
→ 成功率/throughput は同等なのに、P2P ON は p95 が約 4.9倍(1000 rps 時)で、テールが明確に悪化しています。
✅ より高負荷(1500〜2400 指定付近)での傾向
この領域では、vegeta の「実際に送れた rate」が落ちたり(クライアント側も影響)、throughput が 約 1.1k〜1.35k rps あたりで頭打ちになっています(OFF/ONともに似た水準)。
ただし、待機時間(レイテンシ)は秒台(p95 2〜3s)へ上がりやすい。
P2P ON では一部条件で 503 が混じることもありました(例:2000で503が少数発生)。
❌ さらに攻めた領域(2500 指定)
P2P ON:Success 68%・503大量(11981)で崩壊
P2P OFF:この run では Success 100% を維持(ただし throughput は 1087 rps まで落ち、遅延は上昇)
→ “最大スループット低下” というより ON の方が高負荷域で不安定化(503が出やすい) が見えます。
P2P Encryption OFF
for r in 200 600 800 850 900 950 1000 1500 2000 2400 2500; do
echo "== P2P OFF min=max=3 | rate=$r =="
echo "GET http://node-app/misc/response/bytes/1024" \
| vegeta attack -rate=$r -duration=30s -connections=500 -timeout=60s \
| tee "p2pOFF_rate${r}.bin" \
| vegeta report | tee "p2pOFF_rate${r}.txt"
sleep 60
done
== P2P OFF min=max=3 | rate=200 ==
Requests [total, rate, throughput] 6000, 200.03, 200.01
Duration [total, attack, wait] 29.998s, 29.996s, 2.371ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.357ms, 6.418ms, 2.035ms, 2.757ms, 4.046ms, 201.721ms, 564.908ms
Bytes In [total, mean] 6288000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:6000
Error Set:
== P2P OFF min=max=3 | rate=600 ==
Requests [total, rate, throughput] 18000, 600.04, 599.87
Duration [total, attack, wait] 30.007s, 29.998s, 8.594ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.157ms, 32.638ms, 4.636ms, 49.246ms, 103.094ms, 716.37ms, 1.082s
Bytes In [total, mean] 18864000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:18000
Error Set:
== P2P OFF min=max=3 | rate=800 ==
Requests [total, rate, throughput] 24000, 800.06, 799.93
Duration [total, attack, wait] 30.003s, 29.998s, 4.649ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.128ms, 40.898ms, 7.512ms, 99.742ms, 110.912ms, 601.832ms, 877.693ms
Bytes In [total, mean] 25152000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:24000
Error Set:
== P2P OFF min=max=3 | rate=850 ==
Requests [total, rate, throughput] 25501, 850.03, 847.41
Duration [total, attack, wait] 30.093s, 30s, 92.923ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.081ms, 29.081ms, 6.831ms, 95.4ms, 100.578ms, 537.216ms, 883.05ms
Bytes In [total, mean] 26725048, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:25501
Error Set:
== P2P OFF min=max=3 | rate=900 ==
Requests [total, rate, throughput] 27000, 900.00, 897.22
Duration [total, attack, wait] 30.093s, 30s, 92.892ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.084ms, 25.92ms, 7.207ms, 97.762ms, 100.415ms, 328.1ms, 804.031ms
Bytes In [total, mean] 28296000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:27000
Error Set:
== P2P OFF min=max=3 | rate=950 ==
Requests [total, rate, throughput] 28500, 950.04, 947.07
Duration [total, attack, wait] 30.093s, 29.999s, 94.14ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.113ms, 55.121ms, 8.411ms, 100.912ms, 170.275ms, 697.727ms, 981.401ms
Bytes In [total, mean] 29868000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:28500
Error Set:
== P2P OFF min=max=3 | rate=1000 ==
Requests [total, rate, throughput] 30000, 1000.05, 997.00
Duration [total, attack, wait] 30.09s, 29.998s, 92.047ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.082ms, 50.246ms, 9.33ms, 101.234ms, 104.625ms, 505.986ms, 797.85ms
Bytes In [total, mean] 31440000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:30000
Error Set:
== P2P OFF min=max=3 | rate=1500 ==
Requests [total, rate, throughput] 37579, 1189.40, 1156.45
Duration [total, attack, wait] 32.495s, 31.595s, 900.036ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.34ms, 973.954ms, 708.491ms, 2.135s, 2.614s, 3.364s, 4.791s
Bytes In [total, mean] 39382792, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:37579
Error Set:
== P2P OFF min=max=3 | rate=2000 ==
Requests [total, rate, throughput] 38487, 1270.10, 1245.34
Duration [total, attack, wait] 30.905s, 30.302s, 602.641ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.284ms, 1.113s, 890.641ms, 2.364s, 2.932s, 3.562s, 5.798s
Bytes In [total, mean] 40334376, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:38487
Error Set:
== P2P OFF min=max=3 | rate=2400 ==
Requests [total, rate, throughput] 40387, 1341.76, 1328.76
Duration [total, attack, wait] 30.394s, 30.1s, 294.453ms
Latencies [min, mean, 50, 90, 95, 99, max] 2.432ms, 676.901ms, 501.361ms, 1.399s, 1.75s, 2.098s, 2.79s
Bytes In [total, mean] 42325576, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:40387
Error Set:
== P2P OFF min=max=3 | rate=2500 ==
Requests [total, rate, throughput] 33580, 1115.89, 1087.04
Duration [total, attack, wait] 30.891s, 30.093s, 798.55ms
Latencies [min, mean, 50, 90, 95, 99, max] 3.475ms, 1.223s, 1.117s, 2.288s, 2.481s, 3.143s, 3.803s
Bytes In [total, mean] 35191840, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:33580
Error Set:
P2P Encryption ON
for r in 200 600 800 850 900 950 1000 1500 2000 2400 2500; do
echo "== P2P ON | rate=$r =="
echo "GET http://node-app/misc/response/bytes/1024" \
| vegeta attack -rate=$r -duration=30s -connections=500 -timeout=60s \
| tee "p2pON_rate${r}.bin" \
| vegeta report | tee "p2pON_rate${r}.txt"
sleep 60
done
== P2P ON | rate=200 ==
Requests [total, rate, throughput] 6000, 200.03, 200.01
Duration [total, attack, wait] 29.998s, 29.995s, 2.844ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.612ms, 8.508ms, 2.654ms, 3.633ms, 5.296ms, 240.284ms, 608.754ms
Bytes In [total, mean] 6288000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:6000
Error Set:
== P2P ON | rate=600 ==
Requests [total, rate, throughput] 18000, 600.04, 599.95
Duration [total, attack, wait] 30.003s, 29.998s, 4.758ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.243ms, 57.934ms, 5.802ms, 83.229ms, 526.809ms, 990.908ms, 1.3s
Bytes In [total, mean] 18864000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:18000
Error Set:
== P2P ON | rate=800 ==
Requests [total, rate, throughput] 24000, 800.05, 799.97
Duration [total, attack, wait] 30.001s, 29.998s, 3.269ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.298ms, 75.065ms, 8.319ms, 101.431ms, 560.743ms, 1.055s, 1.602s
Bytes In [total, mean] 25152000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:24000
Error Set:
== P2P ON | rate=850 ==
Requests [total, rate, throughput] 25500, 850.03, 849.75
Duration [total, attack, wait] 30.009s, 29.999s, 9.792ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.267ms, 52.311ms, 8.321ms, 100.336ms, 113.035ms, 807.267ms, 1.109s
Bytes In [total, mean] 26724000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:25500
Error Set:
== P2P ON | rate=900 ==
Requests [total, rate, throughput] 27000, 900.03, 897.45
Duration [total, attack, wait] 30.085s, 29.999s, 86.409ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.206ms, 74.297ms, 9.377ms, 102.474ms, 429.619ms, 983.735ms, 1.686s
Bytes In [total, mean] 28296000, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:27000
Error Set:
== P2P ON | rate=950 ==
Requests [total, rate, throughput] 28501, 950.04, 949.95
Duration [total, attack, wait] 30.003s, 30s, 2.653ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.219ms, 81.67ms, 11.426ms, 102.866ms, 316.991ms, 909.911ms, 1.303s
Bytes In [total, mean] 29869048, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:28501
Error Set:
== P2P ON | rate=1000 ==
Requests [total, rate, throughput] 29998, 996.83, 996.43
Duration [total, attack, wait] 30.105s, 30.094s, 11.984ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.309ms, 118.728ms, 90.398ms, 200.712ms, 515.415ms, 1.529s, 2.095s
Bytes In [total, mean] 31437904, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:29998
Error Set:
== P2P ON | rate=1500 ==
Requests [total, rate, throughput] 40889, 1357.73, 1260.32
Duration [total, attack, wait] 32.418s, 30.116s, 2.302s
Latencies [min, mean, 50, 90, 95, 99, max] 1.85ms, 1.005s, 799.669ms, 1.998s, 2.609s, 3.765s, 5.598s
Bytes In [total, mean] 42821791, 1047.27
Bytes Out [total, mean] 0, 0.00
Success [ratio] 99.92%
Status Codes [code:count] 200:40857 503:32
Error Set:
503 Service Unavailable
== P2P ON | rate=2000 ==
Requests [total, rate, throughput] 41945, 1393.75, 1313.92
Duration [total, attack, wait] 31.899s, 30.095s, 1.804s
Latencies [min, mean, 50, 90, 95, 99, max] 3.713ms, 1.07s, 800.896ms, 2.31s, 2.818s, 3.881s, 6.6s
Bytes In [total, mean] 43928472, 1047.29
Bytes Out [total, mean] 0, 0.00
Success [ratio] 99.92%
Status Codes [code:count] 200:41913 503:32
Error Set:
503 Service Unavailable
== P2P ON | rate=2400 ==
Requests [total, rate, throughput] 41468, 1368.63, 1346.62
Duration [total, attack, wait] 30.794s, 30.299s, 495.113ms
Latencies [min, mean, 50, 90, 95, 99, max] 1.737ms, 740.83ms, 502.925ms, 1.698s, 2.003s, 2.94s, 4.301s
Bytes In [total, mean] 43458464, 1048.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:41468
Error Set:
== P2P ON | rate=2500 ==
Requests [total, rate, throughput] 38041, 1231.05, 748.77
Duration [total, attack, wait] 34.804s, 30.901s, 3.903s
Latencies [min, mean, 50, 90, 95, 99, max] 1.837ms, 3.395s, 2.791s, 6.644s, 8.089s, 14.574s, 24.303s
Bytes In [total, mean] 28689921, 754.18
Bytes Out [total, mean] 0, 0.00
Success [ratio] 68.51%
Status Codes [code:count] 200:26060 503:11981
Error Set:
503 Service Unavailable
Copilot による解説抜粋
⚠️ 1500〜2400 指定では「指定レートを維持できず、実効1.3k付近で頭打ち」
あなたの出力(要点):
-rate=1500 → report上 rate=1357.73, throughput=1260.32、p95=2.6s、503=32
-rate=2000 → report上 rate=1393.75, throughput=1313.92、p95=2.8s、503=32
-rate=2400 → report上 rate=1368.63, throughput=1346.62、p95=2.0s、503=0
ここで大事なのは、1500/2000/2400 と指定を上げても、実測の “rate(送信レート)” が約 1.36〜1.39k に張り付いている点です。
= サーバというより、往復遅延や接続制約により、クライアントがそれ以上の一定レート送信を維持できていない可能性が高いです(or サービスがその辺りで飽和してレイテンシが増え、結果として送れなくなる)。
この領域では “最大処理能力(キャパ)”が約 1.35k rps 付近に見える、というのが読み取りになります。
for c in 500 1000 2000; do
echo "== P2P ON min=max=3 | targetRate=2000 | connections=$c =="
echo "GET http://node-app/misc/response/bytes/1024" \
| vegeta attack -rate=2000 -duration=30s -connections=$c -timeout=60s \
| tee "p2pON_r2000_c${c}.bin" \
| vegeta report | tee "p2pON_r2000_c${c}.txt"
sleep 90
done
== P2P ON min=max=3 | targetRate=2000 | connections=500 ==
Requests [total, rate, throughput] 40958, 1364.03, 1331.12
Duration [total, attack, wait] 30.735s, 30.027s, 707.731ms
Latencies [min, mean, 50, 90, 95, 99, max] 3.418ms, 901.432ms, 701.628ms, 1.799s, 2.21s, 3.309s, 5.5s
Bytes In [total, mean] 42881020, 1046.95
Bytes Out [total, mean] 0, 0.00
Success [ratio] 99.89%
Status Codes [code:count] 200:40912 503:46
Error Set:
503 Service Unavailable
== P2P ON min=max=3 | targetRate=2000 | connections=1000 ==
Requests [total, rate, throughput] 39042, 1301.57, 1223.63
Duration [total, attack, wait] 31.901s, 29.996s, 1.905s
Latencies [min, mean, 50, 90, 95, 99, max] 2.516ms, 2.107s, 1.812s, 4.237s, 4.91s, 6.04s, 8.098s
Bytes In [total, mean] 40909490, 1047.83
Bytes Out [total, mean] 0, 0.00
Success [ratio] 99.98%
Status Codes [code:count] 200:39035 503:7
Error Set:
503 Service Unavailable
== P2P ON min=max=3 | targetRate=2000 | connections=2000 ==
Requests [total, rate, throughput] 48916, 1620.05, 407.52
Duration [total, attack, wait] 49.696s, 30.194s, 19.502s
Latencies [min, mean, 50, 90, 95, 99, max] 3.615ms, 2.879s, 2.195s, 4.513s, 6.281s, 21.668s, 39.709s
Bytes In [total, mean] 24531446, 501.50
Bytes Out [total, mean] 0, 0.00
Success [ratio] 41.40%
Status Codes [code:count] 200:20252 503:28664
Error Set:
503 Service Unavailable
503 の要因
vegeta encode p2pON_r2000_c2000.bin | grep '"code":503' | head -n 10
{"attack":"","seq":1254,"code":503,"timestamp":"2026-01-19T06:59:52.700218003Z","latency":1290149538,"bytes_out":0,"bytes_in":114,"error":"503 Service Unavailable","body":"dXBzdHJlYW0gY29ubmVjdCBlcnJvciBvciBkaXNjb25uZWN0L3Jlc2V0IGJlZm9yZSBoZWFkZXJzLiByZXRyaWVkIGFuZCB0aGUgbGF0ZXN0IHJlc2V0IHJlYXNvbjogY29ubmVjdGlvbiB0aW1lb3V0","method":"GET","url":"http://node-app/misc/response/bytes/1024","headers":{"Content-Length":["114"],"Content-Type":["text/plain"],"Date":["Mon, 19 Jan 2026 06:59:53 GMT"]}}
body を base64 decode すると
upstream connect error or disconnect/reset before headers. retried and the latest reset reason: connection timeout
Discussion