「詰める」と「散らす」の動力学 - 原理・原則から理解するコンテナ配置戦略 #openshiftjp / OpenShift Run 2019

332f89cc697355902a817506b6995f2b?s=47 y_taka_23
December 20, 2019

「詰める」と「散らす」の動力学 - 原理・原則から理解するコンテナ配置戦略 #openshiftjp / OpenShift Run 2019

OpenShift.Run 2019 で使用したスライドです。

Kubernetes において Deployment の作成から Pod の起動に至る流れは隠蔽されており、通常エンドユーザは Pod がどの Node に配置されるのかを気にする必要はありません。しかし、運用がある程度の規模になると、CPU やメモリなどクラスタが持っているリソースを効果的に使い切り、さらに障害時にサービスが停止しないようにするためには、Pod の配置にまで気を配る必要があります。

Pod をどの Node に配置するかの判断をスケジューリングと呼びます。本講演では Kubernetes のスケジューリングの仕組みと、それを運用する上で現実的に生じる課題、さらにその対策までをまとめて包括的に解説しました。

イベント概要:https://openshift.connpass.com/event/151473/
ブログ記事:https://ccvanishing.hateblo.jp/entry/2019/12/21/212423

なお、本講演のストーリーは CloudNative Days Tokyo 2019 での内容が元になっており、そこに 2019 年 7 月から半年間で実装された新機能などの解説を追加して再構成してあります。

CloudNative Days Tokyo 2019:https://speakerdeck.com/ytaka23/cloudnative-days-tokyo-2019

332f89cc697355902a817506b6995f2b?s=128

y_taka_23

December 20, 2019
Tweet

Transcript

  1. #openshiftjp #openshiftjp 「詰める」と「散らす」の動力学 原理・原則から理解するコンテナ配置戦略 チェシャ猫 (@y_taka_23) OpenShift.Run 2019 (2019/12/20)

  2. #openshiftjp

  3. #openshiftjp $ kubectl apply -f my-deployment.yaml deployment.apps/myapp created $ kubectl

    get pods -o wide NAME READY STATUS RESTARTS AGE NODE myapp-6dd86d77d-vweg2 1/1 Running 0 52s node-03 myapp-6dd86d77d-wt34h 1/1 Running 0 52s node-01 myapp-6dd86d77d-r4pag 1/1 Running 0 52s node-02
  4. #openshiftjp $ kubectl apply -f my-deployment.yaml deployment.apps/myapp created $ kubectl

    get pods -o wide NAME READY STATUS RESTARTS AGE NODE myapp-6dd86d77d-vweg2 1/1 Running 0 52s node-03 myapp-6dd86d77d-wt34h 1/1 Running 0 52s node-01 myapp-6dd86d77d-r4pag 1/1 Running 0 52s node-02 配置される Node が「よしなに」決まる
  5. #openshiftjp スケジューリング クラスタ上の Pod 配置をコントロールすること

  6. #openshiftjp #openshiftjp ? 「詰める」vs.「散らす」

  7. #openshiftjp #openshiftjp コンテナを「詰める」力学 • メリット:コストの抑制 ◦ 出来るだけ多くの Pod を Node

    に配置する ◦ リソース(CPU、Memory)を使い切ることを目指す • デメリット:サービスの可用性低下 ◦ Node の障害時に Pod がまとめて巻き込まれる ◦ Pod に比べて Node の起動は遅く、ロードスパイクに弱い
  8. #openshiftjp #openshiftjp コンテナを「散らす」力学 • メリット:サービスの可用性向上 ◦ 出来るだけ Pod を分散して配置する ◦

    ひとつの Node が障害されてもサービス全体は停止しない • デメリット:コストの上昇 ◦ 必然的に準備しておく Node の個数が増える ◦ リソースの断片化により Node の性能を使いきれない
  9. #openshiftjp #openshiftjp ? 「詰める」+「散らす」

  10. #openshiftjp #openshiftjp コンテナ配置は自分事 • 結局のところ「戦略」が必須 ◦ どちらかに全振りすると運用が廻らずに死ぬ ◦ クラスタにデプロイするアプリの性質にも依存 •

    戦略を考えるには「仕組み」への理解が必須 ◦ ブラックボックスは狙ってコントロールできない ◦ 何ができるか・できないのかを知っておく必要性
  11. #openshiftjp #openshiftjp ?最適なスケジューリングは 「詰める」と「散らす」の綱引きで成り立つ

  12. #openshiftjp #openshiftjp 本日のアジェンダ • スケジューラのアーキテクチャ • 正しく詰めて、効果的に散らす • より高度なスケジューリング

  13. #openshiftjp #openshiftjp スケジューラのアーキテクチャ How the Scheduler Effectively Works? 1

  14. #openshiftjp ユーザが kubectl コマンドで Deployment を作成 Node 上で Pod が起動

  15. #openshiftjp ユーザが kubectl コマンドで Deployment を作成 Node 上で Pod が起動

    この間、何が起こっているのか?
  16. #openshiftjp Scheduler Controller Manager API Server Kubelet Kubelet Kubelet User

  17. #openshiftjp ユーザが kubectl コマンドで Deployment を作成 Contoller Manager が Deployment

    から ReplicaSet を作成 Controller Manager が ReplicaSet から Pod を作成 Scheduler が Pod を配置する Node を選択 Node 上の Kubelet が自分に割り当てられた Pod を起動
  18. #openshiftjp ユーザが kubectl コマンドで Deployment を作成 Contoller Manager が Deployment

    から ReplicaSet を作成 Controller Manager が ReplicaSet から Pod を作成 Scheduler が Pod を配置する Node を選択 Node 上の Kubelet が自分に割り当てられた Pod を起動
  19. #openshiftjp Scheduler Controller Manager API Server Kubelet Kubelet Kubelet User

  20. #openshiftjp Queue Scheduling Cycle Binding Threads

  21. #openshiftjp Queue API Server から行き先未定の Pod を取り出してキューに格納

  22. #openshiftjp Scheduling Cycle Pod の行き先 Node を “ひとつずつ” 確定 スケジューラの心臓部分

    : node-02 : node-01 : node-03
  23. #openshiftjp Binding Threads Pod ごとに goroutine を生成して 行き先となる Node を

    API Server に登録
  24. #openshiftjp Queue Scheduling Cycle Binding Threads

  25. #openshiftjp Dequeue Filtering Scoring Node が未定の Pod を一つ選択 Pod の条件に合わない

    Node を除外する 残った Node から ベストを選ぶ Scheduling Cycle
  26. #openshiftjp Dequeue Node が未定の Pod を一つ選択 Scheduling Cycle

  27. #openshiftjp Filtering Pod の条件に合わない Node を除外する Scheduling Cycle

  28. #openshiftjp Scoring 残った Node から ベストを選ぶ Scheduling Cycle

  29. #openshiftjp #openshiftjp スケジューラの基本動作 • Pod と Node の紐付けのみを担当 ◦ 自分自身が

    Pod を作成するわけではない • Pod ひとつずつに対し以下を繰り返す ◦ キューから配置 Node が未定の Pod を取り出す ◦ Pod の条件から Node の候補を絞る ◦ 残った中からさらに最適な Node を選択する
  30. #openshiftjp #openshiftjp ? 例:具体的な Scheduling シナリオ

  31. #openshiftjp apiVersion: v1 kind: Pod metadata: name: my-app spec: containers:

    - name: my-app image: username/my-app:latest resources: requests: memory: 2Gi cpu: 200m Mem : 2 GiB CPU : 0.2 core
  32. #openshiftjp apiVersion: v1 kind: Pod metadata: name: my-app spec: containers:

    - name: my-app image: username/my-app:latest resources: requests: memory: 2Gi cpu: 200m Mem : 2 GiB CPU : 0.2 core メモリ 2 GiB と CPU 200 mcore 確保できる Node に配置してくれ
  33. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 2 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.4 / 2 core Mem : 3 GiB CPU : 0.2 core Mem : 4 MiB CPU : 0.2 core Mem : 7 / 8 GiB CPU : 0.4 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 / 8 GiB CPU : 0.4 / 2 core Mem : 2 GiB CPU : 0.2 core
  34. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 2 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.4 / 2 core Mem : 3 GiB CPU : 0.2 core Mem : 4 MiB CPU : 0.2 core Mem : 7 / 8 GiB CPU : 0.4 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 / 8 GiB CPU : 0.4 / 2 core Mem : 2 GiB CPU : 0.2 core Pod の条件に合わない Node を除外する Mem 不足で除外
  35. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 2 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.4 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 / 8 GiB CPU : 0.4 / 2 core Mem : 2 GiB CPU : 0.2 core 残った Node の中から ベストを選ぶ より余裕がある Mem : 3 GiB CPU : 0.2 core Mem : 4 MiB CPU : 0.2 core Mem : 7 / 8 GiB CPU : 0.4 / 2 core
  36. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 2 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.4 / 2 core Mem : 3 GiB CPU : 0.2 core Mem : 4 MiB CPU : 0.2 core Mem : 7 / 8 GiB CPU : 0.4 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.6 / 2 core Mem : 2 GiB CPU : 0.2 core
  37. #openshiftjp #openshiftjp ? 配置戦略は Pod の性質にも依存

  38. #openshiftjp • 主に Deployment で管理 • 長時間、稼働し続ける • できるだけ散らしたい Server

    系の Pod S S S S
  39. #openshiftjp • 主に Job で管理 • 一定時間で終了する • できるだけ詰め込みたい Batch

    系の Pod B B B B
  40. #openshiftjp #openshiftjp スケジューラの設定 • スケジューラ起動時に --config フラグを指定 ◦ 冗長化などの設定も含まれる ◦

    さらに ConfigMap 化しておいた Policy を参照 • Policy は用意された項目を組み合わせる ◦ Node を Filtering する条件 ◦ Scoring の順位付け関数と重み付け
  41. #openshiftjp apiVersion: kubescheduler.config.k8s.io/v1alpha1 kind: KubeSchedulerConfiguration leaderElection: leaderElect: false algorithmSource: policy:

    configMap: namespace: kube-system name: my-scheduler-policy
  42. #openshiftjp apiVersion: kubescheduler.config.k8s.io/v1alpha1 kind: KubeSchedulerConfiguration leaderElection: leaderElect: false algorithmSource: policy:

    configMap: namespace: kube-system name: my-scheduler-policy Policy を記述した ConfigMap
  43. #openshiftjp { "kind": "Policy", "apiVersion": "v1", "predicates": [ {"name": "PodFitsHostPorts"},

    {"name": "PodFitsHostResources"}, ... ], "priorities": [ {"name": "LeastRequestedPriority", "weight": 1}, {"name": "ServiceSpreadingPriority", "weight": 1}, ... ] } policy.cfg
  44. #openshiftjp { "kind": "Policy", "apiVersion": "v1", "predicates": [ {"name": "PodFitsHostPorts"},

    {"name": "PodFitsHostResources"}, ... ], "priorities": [ {"name": "LeastRequestedPriority", "weight": 1}, {"name": "ServiceSpreadingPriority", "weight": 1}, ... ] } policy.cfg 確保済みリソースが少ない Node を優先 = 散らす配置戦略
  45. #openshiftjp { "kind": "Policy", "apiVersion": "v1", "predicates": [ {"name": "PodFitsHostPorts"},

    {"name": "PodFitsHostResources"}, ... ], "priorities": [ {"name": "MostRequestedPriority", "weight": 1}, {"name": "ServiceSpreadingPriority", "weight": 1}, ... ] } policy.cfg 確保済みリソースが多い Node を優先 = 詰め込む配置戦略
  46. #openshiftjp #openshiftjp ! Kubernetes クラスタ内には 複数の Scheduler が存在できる

  47. #openshiftjp apiVersion: kubescheduler.config.k8s.io/v1alpha1 kind: KubeSchedulerConfiguration schedulerName: my-scheduler leaderElection: leaderElect: false

    algorithmSource: policy: configMap: namespace: kube-system name: my-scheduler-policy スケジューラに名前を付けておく
  48. #openshiftjp apiVersion: v1 kind: Pod metadata: name: my-app spec: schedulerName:

    my-scheduler containers: - name: my-app image: username/my-app:latest resources: requests: memory: 500Mi cpu: 200m どのスケジューラの管理下にするかを指定
  49. #openshiftjp User

  50. #openshiftjp #openshiftjp Section 1 のポイント • スケジューラは Pod を Node

    へ紐付ける ◦ Filtering で Node の候補を絞り、Scoring で順位付け • Policy によるスケジューリングの調整 ◦ あらかじめ定義されている項目の中から指定 • クラスタ内に複数のスケジューラが共存可能 ◦ Kubernetes の「舵輪型」アーキテクチャの恩恵
  51. #openshiftjp #openshiftjp 正しく詰めて、効果的に散らす More Adaptive Scheduling Scheme 2

  52. #openshiftjp #openshiftjp コンテナを「詰める」ための指針 • Pod の必要リソース量を正しく指定 ◦ 余裕がありすぎると無駄、ギリギリすぎると強制終了の危険 ◦ そもそも何をもって「正しいリソース量」とするのか

    • スパイク的な負荷上昇への対応 ◦ 効率的に詰めると定常的にリソースの遊びがなくなる ◦ クラウドであっても Node の起動は Pod に比較して 2 ケタ遅い
  53. #openshiftjp #openshiftjp 課題:正確な Pod のリソース量の指定

  54. #openshiftjp apiVersion: v1 kind: Pod metadata: name: my-app spec: containers:

    - name: my-app image: username/my-app:latest resources: requests: memory: 6Gi cpu: 200m Mem : 6 GiB CPU : 0.2 core 本当は 2 GiB で足りるはずだけど OOM で死なないように多めにしとこう
  55. #openshiftjp Mem : 6 / 8 GiB CPU : 0.2

    / 2 core Mem : 6 GiB CPU : 0.2 core
  56. #openshiftjp Mem : 6 / 8 GiB CPU : 0.2

    / 2 core (Mem : 2 GiB) (CPU : 0.2 core) 無駄
  57. #openshiftjp #openshiftjp ! 対策:Vertical Pod Autoscaler による 実データのフィードバック

  58. #openshiftjp #openshiftjp Vertical Pod Autoscaler (VPA) • Resource Requests は実使用量と無関係

    ◦ 少なすぎると危険、大きすぎるとお金の無駄 ◦ あらかじめ検証するにしても負荷テストが必要 ◦ 最終的には経験則で設定 • VPA により実測値に基づいた設定が可能に ◦ Pod を削除してから再作成する
  59. #openshiftjp Mem : 500 MiB CPU : 0.2 core Mem

    : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core
  60. #openshiftjp Mem : 500 MiB CPU : 0.2 core Mem

    : 500 MiB CPU : 0.2 core Mem : 500 MiB CPU : 0.2 core Mem : 1 GiB CPU : 0.5 core VPA Mem : 1 GiB CPU : 0.5 core Mem : 1 GiB CPU : 0.5 core
  61. #openshiftjp #openshiftjp VPA の構成要素 • Recommender ◦ 時系列データからリソース使用量の上限と下限を算出 • Updater

    ◦ 設定が上限と下限に収まっていない Pod を退去 • Admission Controller ◦ 新しい Pod の作成に割り込んで Request 値を上書き
  62. #openshiftjp Mem : 500 MiB CPU : 0.2 core Recommender

    Updater Mem: 500 MiB CPU : 0.2 core
  63. #openshiftjp Mem : 500 MiB CPU : 0.2 core Recommender

    Updater Mem: 500 MiB CPU : 0.2 core 200 <= Mem <= 400 0.1 <= CPU <= 1
  64. #openshiftjp Mem : 500 MiB CPU : 0.2 core Recommender

    Updater 200 <= Mem <= 400 0.1 <= CPU <= 1
  65. #openshiftjp Mem : 500 MiB CPU : 0.2 core Updater

    Mem : 500 MiB CPU : 0.2 core Recommender
  66. #openshiftjp Mem : 500 MiB CPU : 0.2 core Updater

    200 <= Mem <= 400 0.1 <= CPU <= 1 Recommender Admission Webhook Mem: 400 MiB CPU : 0.2 core
  67. #openshiftjp #openshiftjp 今すぐ立てたい Pod がある。 Node 上の Pod が邪魔なんだけど? ?

    課題:負荷スパイク時の実行ブロック
  68. #openshiftjp • 主に Deployment で管理 • 長時間、稼働し続ける • 出来るだけ散らしたい •

    今すぐ起動したい Server 系の Pod • 主に Job で管理 • 一定時間で終了する • 出来るだけ詰め込みたい • 余裕がある時に起動 Batch 系の Pod
  69. #openshiftjp S Mem : 3 GiB CPU : 0.2 core

    S Mem : 4 GiB CPU : 0.2 core B Mem : 1 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core S Mem : 4 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core B Mem : 2 GiB CPU : 0.2 core B Mem : 1 GiiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core S Mem : 6 GiB CPU : 0.2 core B Mem : 1 GiiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core S B Mem : 2 GiB CPU : 0.2 core
  70. #openshiftjp S Mem : 3 GiB CPU : 0.2 core

    S Mem : 4 GiB CPU : 0.2 core B Mem : 1 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core S Mem : 4 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core B Mem : 2 GiB CPU : 0.2 core B Mem : 1 GiiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core S Mem : 6 GiB CPU : 0.2 core B Mem : 1 GiiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core S B Mem : 2 GiB CPU : 0.2 core
  71. #openshiftjp #openshiftjp ! 対策:Priority と Preemption

  72. #openshiftjp #openshiftjp Priority と Preemption • Priority の指定 ◦ Scheduler

    のキューは優先度付きキューになる • Preemption ◦ Filtering で Node の候補が見つからなかった際に発動 ◦ 低優先度の Pod を強制的に追い出す ◦ できるだけ影響が小さくなる Node を選ぶ
  73. #openshiftjp Dequeue Filtering Node が未定の Pod を一つ選択 Pod の条件に合う Node

    が見つからない
  74. #openshiftjp Dequeue Filtering Node が未定の Pod を一つ選択 Pod の条件に合う Node

    が見つからない Preemption 自分より低優先度の Pod を追い出す
  75. #openshiftjp apiVersion: scheduling.k8s.io/v1 kind: PriorityClass matadata: name: server value: 10000

    description: "The higher priority" --- apiVersion: scheduling.k8s.io/v1 kind: PriorityClass matadata: name: batch value: 100 description: "The lower priority"
  76. #openshiftjp apiVersion: scheduling.k8s.io/v1 kind: PriorityClass matadata: name: server value: 10000

    description: "The higher priority" --- apiVersion: scheduling.k8s.io/v1 kind: PriorityClass matadata: name: batch value: 100 description: "The lower priority" 優先度:高 優先度:低
  77. #openshiftjp #openshiftjp Preemption のアルゴリズム • まず低優先度の Pod をすべて消したと仮定 ◦ 余裕ができなければその時点で可能性なし

    ◦ 可能性がある Node が見つかったら次に進む • そのあとで消した Pod をひとつずつ戻す ◦ 追い出さざるをえない Pod に対してペナルティを計算 ◦ 最終的に傷がもっとも浅く済む Node をターゲットに
  78. #openshiftjp #openshiftjp ? 例:具体的な Preemption シナリオ

  79. #openshiftjp 高 Mem : 3 GiB CPU : 0.2 core

    高 Mem : 4 GiB CPU : 0.2 core 低 Mem : 1 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core 高 Mem : 4 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core 低 Mem : 2 GiB CPU : 0.2 core 低 Mem : 1 GiiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core 高 Mem : 6 GiB CPU : 0.2 core 低 Mem : 1 GiiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core 高 低 Mem : 2 GiB CPU : 0.2 core
  80. #openshiftjp 高 Mem : 3 GiB CPU : 0.2 core

    高 Mem : 4 GiB CPU : 0.2 core Mem : 7 / 8 GiB CPU : 0.4 / 2 core 高 Mem : 4 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.2 / 2 core Mem : 6 / 8 GiB CPU : 0.2 / 2 core 高 Mem : 6 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core 高 まず低優先度 Pod を 全て追い出してみる 追い出しても無理 可能性あり 可能性あり
  81. #openshiftjp 高 Mem : 3 GiB CPU : 0.2 core

    高 Mem : 4 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core 高 Mem : 4 GiB CPU : 0.2 core Mem : 6 / 8 GiB CPU : 0.4 / 2 core Mem : 6 / 8 GiB CPU : 0.2 / 2 core 高 Mem : 6 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core 高 追い出した Pod を 戻せるところは戻す ひとつも戻せない 犠牲は 2 Pod ひとつは戻せる 犠牲は 1 Pod 低 Mem : 2 GiB CPU : 0.2 core 低 Mem : 1 GiB CPU : 0.2 core
  82. #openshiftjp 高 Mem : 3 GiB CPU : 0.2 core

    高 Mem : 4 GiB CPU : 0.2 core 低 Mem : 1 GiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core 高 Mem : 4 GiB CPU : 0.2 core Mem : 6 / 8 GiB CPU : 0.4 / 2 core 低 Mem : 2 GiB CPU : 0.2 core 低 Mem : 1 GiiB CPU : 0.2 core Mem : 8 / 8 GiB CPU : 0.6 / 2 core 高 Mem : 6 GiB CPU : 0.2 core 低 Mem : 1 GiiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core 高 追い出される
  83. #openshiftjp #openshiftjp ? 発展:Preemption による Job 中断の抑制

  84. #openshiftjp apiVersion: scheduling.k8s.io/v1 kind: PriorityClass matadata: name: server value: 10000

    preemptingPolicy: Never description: "The higher priority"
  85. #openshiftjp apiVersion: scheduling.k8s.io/v1 kind: PriorityClass matadata: name: server value: 10000

    preemptingPolicy: Never description: "The higher priority" Preemption を行わない
  86. #openshiftjp #openshiftjp コンテナを「散らす」ための指針 • 複数 Node 障害時の可用性を担保 ◦ 発生しうる広域障害の影響をあらかじめ洗い出し ◦

    障害時に「まとめて死ぬ」範囲をコントロール • 運用中に Pod が偏る状況が生じうる ◦ オートスケールや障害による Node の追加・削除 ◦ そのままにしておくと障害時に全滅の危険
  87. #openshiftjp #openshiftjp 今すぐ立てたい Pod がある。 Node 上の Pod が邪魔なんだけど? ?

    課題:複数 Node 障害時の可用性
  88. #openshiftjp

  89. #openshiftjp Rack Rack

  90. #openshiftjp Rack 障害(Pod 全滅) Rack

  91. #openshiftjp #openshiftjp 今すぐ立てたい Pod がある。 Node 上の Pod が邪魔なんだけど? ?

    対策:Topology Spread Constraint の指定
  92. #openshiftjp #openshiftjp Topology Spread Constraint • Label による Topology の指定

    ◦ Node を論理的にグルーピング ◦ 運用中に「まとめて故障する単位」に相当 • Topology Spread Constraint ◦ 各 Topology 間での Pod 個数の差の許容範囲を指定 ◦ 強制(Filtering)か優先(Scoring)かは選択可能
  93. #openshiftjp apiVersion: v1 kind: Pod metadata: name: mypod labels: foo:

    bar spec: containers: (snip) topologySpreadConstraints: - topologyKey: rack maxSkew: 1 labelSelector: matchLabels: foo: bar whenUnsatisfiable: DoNotSchedule
  94. #openshiftjp apiVersion: v1 kind: Pod metadata: name: mypod labels: foo:

    bar spec: containers: (snip) topologySpreadConstraints: - topologyKey: rack maxSkew: 1 labelSelector: matchLabels: foo: bar whenUnsatisfiable: DoNotSchedule Pod をカウントする単位
  95. #openshiftjp apiVersion: v1 kind: Pod metadata: name: mypod labels: foo:

    bar spec: containers: (snip) topologySpreadConstraints: - topologyKey: rack maxSkew: 1 labelSelector: matchLabels: foo: bar whenUnsatisfiable: DoNotSchedule 許容できる個数の差
  96. #openshiftjp apiVersion: v1 kind: Pod metadata: name: mypod labels: foo:

    bar spec: containers: (snip) topologySpreadConstraints: - topologyKey: rack maxSkew: 1 labelSelector: matchLabels: foo: bar whenUnsatisfiable: DoNotSchedule 分散対象 Pod の指定
  97. #openshiftjp apiVersion: v1 kind: Pod metadata: name: mypod labels: foo:

    bar spec: containers: (snip) topologySpreadConstraints: - topologyKey: rack maxSkew: 1 labelSelector: matchLabels: foo: bar whenUnsatisfiable: DoNotSchedule 満たせない場合は配置禁止(Filtering)
  98. #openshiftjp (1 Pod) (0 Pods) (0 Pods) (0 Pods) Rack

    (1 Pod) Rack (0 Pods)
  99. #openshiftjp (1 Pod) (0 Pods) 制約違反 (0 Pods) Rack (2

    Pods) Rack (0 Pods)
  100. #openshiftjp (1 Pod) (1 Pod) (0 Pods) (0 Pods) Rack

    (1 Pod) Rack (1 Pod)
  101. #openshiftjp (1 Pod) (1 Pod) (1 Pod) (0 Pods) Rack

    (2 Pods) Rack (1 Pod)
  102. #openshiftjp #openshiftjp 課題:運用中に生じる Pod の偏り

  103. #openshiftjp Mem : 2 GiB CPU : 0.2 core Mem

    : 2 / 8 GiB CPU : 0.2 / 2 core Mem : 2 MiB CPU : 0.2 core Mem : 2 / 8 GiB CPU : 0.2 / 2 core Mem : 2 GiB CPU : 0.2 core Mem : 2 / 8 GiB CPU : 0.2 / 2 core
  104. #openshiftjp Mem : 2 GiB CPU : 0.2 core Mem

    : 2 / 8 GiB CPU : 0.2 / 2 core Mem : 2 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.4 / 2 core Node 死亡 Mem : 2 GiB CPU : 0.2 core
  105. #openshiftjp Mem : 2 GiB CPU : 0.2 core Mem

    : 2 / 8 GiB CPU : 0.2 / 2 core Mem : 0 / 8 GiB CPU : 0 / 2 core Mem : 2 GiB CPU : 0.2 core Mem : 4 / 8 GiB CPU : 0.4 / 2 core Mem : 2 GiB CPU : 0.2 core 復活しても空のまま
  106. #openshiftjp #openshiftjp ! 対策:Descheduler による偏りの修正

  107. #openshiftjp #openshiftjp Descheduler • 運用に伴って崩れた Pod の配置を回復 • 特定の条件下で Pod

    を退去させる ◦ RemoveDuplicates : Replica が同じ Node に固まっている ◦ LowNodeUtilization : リソース使用率が一定以下 ◦ RemovePodsViolatingPodAntiAffinity : 他 Pod との相性に違反 ◦ RemovePodsViolatingNodeAffinity : Node との相性に違反
  108. #openshiftjp apiVersion: descheduler/v1alpha1 kind: DeschedulerPolicy strategies: LowNodeUtilization: enabled: true params:

    nodeResourceUtilizationThresholds: thresholds: memory: 20 cpu: 20 pods: 20 targetThresholds: memory: 50 cpu: 50 pods: 50
  109. #openshiftjp apiVersion: descheduler/v1alpha1 kind: DeschedulerPolicy strategies: LowNodeUtilization: enabled: true params:

    nodeResourceUtilizationThresholds: thresholds: memory: 20 cpu: 20 pods: 20 targetThresholds: memory: 50 cpu: 50 pods: 50 下限値(単位は %)
  110. #openshiftjp apiVersion: descheduler/v1alpha1 kind: DeschedulerPolicy strategies: LowNodeUtilization: enabled: true params:

    nodeResourceUtilizationThresholds: thresholds: memory: 20 cpu: 20 pods: 20 targetThresholds: memory: 50 cpu: 50 pods: 50 上限値(単位は %)
  111. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 1 GiB CPU : 0.2 core Mem : 1 GiB CPU : 0.2 core Mem : 3 / 8 GiB CPU : 0.6 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 3 GiB CPU : 0.1 core Mem : 6 / 8 GiB CPU : 0.5 / 2 core Mem : 0.5 GiB CPU : 0.8 core Mem : 0.5 / 8 GiB CPU : 0.8 / 2 core Mem : 20 % CPU : 20 % Pods : 20 % Thresholds Mem : 50 % CPU : 50 % Pods : 50 % Target Thresholds
  112. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 1 GiB CPU : 0.2 core Mem : 1 GiB CPU : 0.2 core Mem : 3 / 8 GiB CPU : 0.6 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 3 GiB CPU : 0.1 core Mem : 6 / 8 GiB CPU : 0.5 / 2 core Mem : 0.5 GiB CPU : 0.8 core Mem : 0.5 / 8 GiB CPU : 0.8 / 2 core Mem : 20 % CPU : 20 % Pods : 20 % Thresholds Mem : 50 % CPU : 50 % Pods : 50 % Target Thresholds Mem 遊びすぎ
  113. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 1 GiB CPU : 0.2 core Mem : 1 GiB CPU : 0.2 core Mem : 3 / 8 GiB CPU : 0.6 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 3 GiB CPU : 0.1 core Mem : 6 / 8 GiB CPU : 0.5 / 2 core Mem : 0.5 GiB CPU : 0.8 core Mem : 0.5 / 8 GiB CPU : 0.8 / 2 core Mem : 20 % CPU : 20 % Pods : 20 % Thresholds Mem : 50 % CPU : 50 % Pods : 50 % Target Thresholds Mem 使いすぎ
  114. #openshiftjp Mem : 1 GiB CPU : 0.2 core Mem

    : 1 GiB CPU : 0.2 core Mem : 1 GiB CPU : 0.2 core Mem : 3 / 8 GiB CPU : 0.6 / 2 core Mem : 1 GiB CPU : 0.2 core Mem : 2 GiB CPU : 0.2 core Mem : 3 / 8 GiB CPU : 0.4 / 2 core Mem : 0.5 GiB CPU : 0.8 core Mem : 3.5 / 8 GiB CPU : 0.9 / 2 core Mem : 20 % CPU : 20 % Pods : 20 % Thresholds Mem : 50 % CPU : 50 % Pods : 50 % Target Thresholds Mem : 3 GiB CPU : 0.1 core
  115. #openshiftjp #openshiftjp Section 2 のポイント • シンプルな原理だけでは最適な配置は難しい • 状況に応じて補完する機能やツールを使用 ◦

    Vertical Pod Autoscaler : 実測値に基づいて Requests を決めたい ◦ Preemption : 重要な Pod を優先して起動したい ◦ Topology Spread : 特定範囲内の Node の同時障害に耐えたい ◦ Descheduler : 運用中に生じた Pod の偏りを是正したい
  116. #openshiftjp #openshiftjp より高度なスケジューリング Build Your Own Strategy 3

  117. #openshiftjp #openshiftjp ? 発展:Kubernetes 外のドメイン知識を Pod の配置に反映させたい

  118. #openshiftjp #openshiftjp Scheduler Extender • JSON Webhook による処理の追加 • 拡張ポイントは

    4 箇所 ◦ Filter : Node の候補をさらに絞る ◦ Prioritized : Node の Scoring 関数を追加 ◦ Preempt : 退去させられる Pod の候補を絞る ◦ Bind : Pod の Node の紐付けの方法を定義
  119. #openshiftjp { "kind": "Policy", "apiVersion": "v1", "predicates": [(snip)], "priorities": [(snip)],

    "extenders": [{ "urlPrefix": "http://my-extender:8080", "filterVerb": "filter", "prioritizeVerb": "prioritize", "preemptVerb": "preempt", "bindVerb": "bind", "enableHttps": false }] } policy.cfg
  120. #openshiftjp { "kind": "Policy", "apiVersion": "v1", "predicates": [(snip)], "priorities": [(snip)],

    "extenders": [{ "urlPrefix": "http://my-extender:8080", "filterVerb": "filter", "prioritizeVerb": "prioritize", "preemptVerb": "preempt", "bindVerb": "bind", "enableHttps": false }] } policy.cfg Webhook サーバのエンドポイント
  121. #openshiftjp User Extender

  122. #openshiftjp User Extender http://my-extender:8080

  123. #openshiftjp User Extender

  124. #openshiftjp #openshiftjp Extender を利用したツール例 • kube-throttler (by PFN) ◦ 割り当て分を超えた

    Pod をリジェクトせず待機 • TopoLVM (by Cybozu) ◦ Node ごとの Persistent Volume のキャパシティを反映 • Stork (by Portworx) ◦ ネットワークストレージの状況を反映
  125. #openshiftjp #openshiftjp ? 発展:配置の算出に必要な時間を 可能な限り短縮したい

  126. #openshiftjp #openshiftjp Scheduling Framework • Interface を提供、本体と同時にビルド ◦ JSON Webhook

    である Extender よりオーバヘッドが軽い ◦ より細かい拡張ポイントが設けられている ◦ 計算結果を拡張ポイントをまたいで使いまわせる • Scheduler 自体の実装も整理 ◦ Kubernetes v1.17 では既存の実装を移植中
  127. #openshiftjp #openshiftjp ? 発展:アルゴリズム自体を最適化したい

  128. #openshiftjp #openshiftjp 開発中の特殊スケジューラ • kube-batch ◦ All or Nothing の配置

    (Co-Scheduling) に対応 ◦ 実証済み機能は機械学習プラットフォーム kubeflow で採用 • Poseidon ◦ Firmament Scheduler の Kubernetes 向け実装 ◦ Pod の配置をグラフ上の最小費用流問題に帰着
  129. #openshiftjp #openshiftjp Section 3 のポイント • Extender によるロジックの外部化 ◦ 外部の

    Webhook サーバとして稼働 • Framework によるソースコード内のカスタマイズ ◦ Extender と比較してレイテンシが向上 ◦ Kubernetes 本体も Scheduling Framework ベースに移行中 • その他、特殊なスケジューラも開発されている
  130. #openshiftjp #openshiftjp 本日のまとめ Recap of the Session

  131. #openshiftjp #openshiftjp まとめ • スケジューラの責務と仕組み ◦ Filtering + Scoring で

    Pod にあった Node を選択 • 配置戦略を効果的に運用するための追加施策 ◦ 愚直に配置するだけではカバーできない課題を解決 • より複雑なユースケースに対応するユーザ拡張 ◦ Extender、Framework 、特殊スケジューラ
  132. #openshiftjp #openshiftjp Have a Nice Scheduling! Presented by チェシャ猫 (@y_taka_23)