前回の続きで、今回はPod数が多い場合のメリットを考えていきます。
ぶっちゃけ、こういうところまで考え込まずに運用しちゃうほうが、幸せだと思います。私は、古いゲーマーなので、穴や弱点を探すのがすきだからやってみている、そういう感じなのです。
Nodeリソースの有効活用
(少) ☓ <---> ○ (多)Node にてサービス用Pod に割り当て可能なリソースは、Nodeで必要な量を引いた allocatable な CPU や Memory、そしてそこから基盤用Podリソースを差し引くことで確認できます。(参考)
Node Memory が 4096 MiB だとすると、allocatable Memory が 4000 MiB などに減る。そこに 1000 MiB の Pod を起動しようとしたら、ちょうど 4Pod 起動できることになるけど、allocatable Memory が 3999 MiB だとすると、3Pod しか起動できなくなる、と。
Node や Pod Requests の量を正確に計算すれば、できるだけ無駄なくPodを起動できるかもしれないけど、実際にはサービス表記上の GiB を普通の bytes に開いてみたら、想像と結構違ったとか、そもそもインスタンスタイプも単純な倍々スペックなわけではない、とかによってビタ起動での運用は難しいと思うわけです。
上の例だと、1Pod 起動できないことで 999 MiB のメモリが宙ぶらりんなまま、Pod 不足になれば次のNodeを起動してしまうことになります。具体的に言えば、最大で ( Pod Requests – 1 ) のリソースが余るリスクを含んでいることになります。
つまり、Pod 割当リソースが小さいほど、そのリスクが小さくなるということです。
ただし、1Pod で No Limit の場合だけは、シンプルにノーリスクです。
だからPod数を細かく多くしたらいい、というわけではなく、少なくする場合に調整ミスしたら、より無駄が大きくなるから気をつけろ、という話に帰着するものだと思います。
インスタンスタイプの変化・混合
(少) ☓ <---> ○ (多)例えば Pod あたり 1000m, 4GiB とすると、4vCPU, 16GiB のインスタンスだろうと、16vCPU, 64GiB だろうと普通に複数Pod起動して運用できますが、Pod あたり 8000m, 32GiB の場合、後者のインスタンスでしかそもそも稼働できないことになります。
元のPodが細かいほどに、Deployment での調整なしに、AutoscalingGroupでのインスタンスタイプの変更をしやすくなるという側面があります。
それだけだと、おとなしく調整ちゃんとしろやって話になりそうですが、インスタンスタイプを混合させるとなるとどうでしょう。特に、C系, M系, R系ではメモリ量が倍々に増えていくので、少ないC系に合わせるという意味では、小さいPodにも優位性があるといえなくもないです。
上のリソース効率活用の話と合わせて、Podが大きいと余剰リソースが大きくなるリスクがあり、そのリスクはインスタンスタイプの容量が小さいほど、余剰の割合が高いことになります。調整が悪いと、M, R系では起動してるのにC系では起動すらできないこともあるかもしれません。
タイプの混合が、負荷分散にとってどうなん、というのはまた別の話として、小さい方が扱いやすく、大きいとリソースを活用しきれないリスクとなる可能性があるんじゃあるまいか。という意識を片隅に置いておきましょう。
ダウン時のリスク
(少) ☓ <---> ○ (多)ALB -> Nodes -> Pod(s) という構成において、ある 1Pod がダウンした場合にどうなるか、というのを考えてみます。
ダウンタイム
元が1Podの場合、それが落ちたらゼロPodになります。元が複数Podの場合、1Pod落ちたら (元のPod数 – 1) が残ります。
1Podダウン時の状態は2つに分けられ、ゼロPod か 1以上Pod かになります。
ゼロPodの場合は、ALBからのヘルスチェックによるNodeの deregister で分散対象から外され、1以上Podの場合は Deployment の liveness_probe によって対処されることになります。
ヘルスチェックの条件がどちらも同じ、ここでは 5秒毎2回 でダウン判定とすると──
ゼロPodでは ALB ダウン判定がでるまで最大で10秒の間、(1 / Node数) の確率でエラーになります。1Pod以上の場合、Deploymentにより最大で10秒の間、((1 / Nodes数) * (1 / 元Pods数)) の確率でエラーになります。
つまり、Pod数の数が多いほど、エラー遭遇率が減少することになります。
負荷集中
ダウン後のPod数が1以上の場合、残ったPodの負荷は、元の数での負荷よりも (元Pod数 / 残Pod数) 倍に高くなります。2Podで平均50%稼働から、1Pod になると100% になるし、
4Podで平均30%からなら、1Pod あたり平均40% になります。
最大Pod数を多く設定したからといって、常に多いというわけではなく、1~最大 のどこかにいることになりますが、Pod数が多い状態であるほど、ダウン時の残されたPodのリスクは少なくなるということです。
チェック判定時間は ALB > Deployment
考察というか現実的な結果ですが、ダウン判定は、5秒毎2回とした場合になぜ最大10秒と書いているかというと、OKが出た直後にダウンした場合、0.1~5.0秒で1回目、5.1~10秒で2回目となるからです。最小の方を考えると、ダウン直後に判定が走る場合なので、最終チェックから4.9秒目にダウンしたとすると、5.0秒で1回目、5.1~10秒で2回目となり、5.1秒が最小ということになります。
という監視の基礎みたいな振り返りをした上で、ALBとDepoymentの判定条件について考えておきます。
例えば、Deploymentで最大の10秒判定、ALBで最小の5.1秒判定になると、どういうことになるかというと、DeploymentがダウンPodを分散対象から外す前に、ALB targetGroup から先に除外されてしまう可能性があるということです。
その可能性は、((1 / 元Pod数) ** 判定回数) かつ、ALB判定時間 < Deployment判定時間 になる場合なので、結構小さくも無視できるほどではないというものです。まだ元気なPod群が残るNodeなのに外されてしまうと、復帰判定が出るまでの間は、負荷分散としては余剰リソースを生み出す無駄が発生してしまいます。
なので、ALB最小判定時間 よりも Deployment最大判定時間 を小さくしておくと、この問題を回避できます。
ALB 3s * 3回 > Deployment 2s * 3回
とかだと、ジャストな感じですね。
まだまだ色々ありそう
いったんいろんな考察ポイントについて挙げていってみましたが、で、結局どーすればってとこは、また今度ってことにしておきます。ひとつひとつ大事に考察していくのです(キリッ
今回挙げていったポイントは、あくまで検証に目をつけて、数値を採取したり計算した上で、その周辺を考えていっただけなので、まだまだポイントはあると思います。
例えば、監視で全Podにエージェントを仕込むとして、監視サービスの方でホスト数でお金がかかるとしたら、Pod数が多い方が不利になりますし。CPUの振り分け挙動も、Podが多くなるとアレッとなることもしばしばあったり。
めっちゃ少なくして1Node1Pod にしますって言った時に、それってKubernetesでやる意味あんの?って突っ込まれたら、いやあるんですけどモニョモニョみたいになって迷子になってみたり?
新しい項目とか構成とか、見かけるたびに、リソース周りはどうなるだろう?って考えて、ベストに近づけていくことになると思うんですよ。
という感じで、思いついたら少しずつ独り雑談していくのです。