比較:kubernetesとOpenStack Novaのスケジューリング
比較対象
OpenStack Novaでインスタンス配備時に、nova.confに定義されているschedulorを使用して、配備先を決めます。 kubernetesの管理対象はpodであり、そもそも仮想化方式が違うが、「アルゴリズムに従ってリソースの配備先を決める」という意味で、 スケジューリングの考え方を比較可能だと思います。
復習:OpenStack Novaのスケジューリング
参照先:https://docs.openstack.org/mitaka/config-reference/compute/scheduler.html
基本的な考え方として、フレーバーに定義されているリソースの容量に応じて、nova schedulerは以下の流れてインスタンスの配備先を決めます:
- 定義されたfilterで複数の配備先ホストを選定
- 選定されたホストのそれぞれのweight値を算出
- weight値の大きい順で優先順位を付けていく
filterをする際に、いろいろな条件でホストを選別します。デフォルトの設定でも、以下の9種類のfilterが適用されます。
- RetryFilter:まだ選別されたことがないホスト、つまり、「新鮮」なホストを優先する
- AvailabilityZoneFilter:指定されるAvailability-zoneに所属するホストのみ選出
- RamFilter:必要なメモリ容量があるかをチェック
- DiskFilter:必要なディスク容量があるかどうかをチェック
- ComputeFilter:最も基本なチェック、nova-computeサービス自体は稼働中であれば通過
- ComputeCapabilitiesFilter:フレーバーに定義されるメタデータをチェック
- ImagePropertiesFilter:イメージに定義されるメタデータをチェック
- ServerGroupAntiAffinityFilter:同じグループに所属するインスタンスを違うホストに分散配置、高可用性の場面を想定
- ServerGroupAffinityFilter:同じグループに所属するインスタンスを同じホストに集中配置、データ依存の場面を想定
それ以外もたくさんのfilterが用意されており、高級的な使用方法ならカスタマイズのfilterも作成可能です。
極端の場面以外に、filterを通過できるホストは複数であるので、さらに「重み(weight)」をそれぞれのホストに対して算出します。 式として:
重み = 属性値1*乗数1 + 属性値2*乗数2 + 属性値3*乗数3 + ...
具体的な属性はコンピュータドライブに依存します。計算する際に、各属性値は正規化されているため、数値単位による影響がないです。 使用する属性値と乗数はそれぞれnova.confの中に指定できます。
最後に、重みの値は最も大きいホストを配備先として決めます。
比較しながら、Kubernetesでスケジューリングを勉強
参照先:https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/
まず、対象はコンテナになっているが、スケジューリングの基本機能は変わりません。ワークロードの管理単位であるpodの起動先ノードを見つけることです。 簡単に並べてみると、podはインスタンスに相当し、ノードはcomputeノードあるいはHypervisorに相当するでしょう。 上記ドキュメントをみると、デフォルトのスケジューラーは「kube-scheduler」というものです。podの稼働条件に合わせて、 「feasible」のノードをまず選出して、選出されたノードに対して、「score」を算出し、高い「score」のノードにpodを起動させます。 これまでに、openstackの用語と比べて、新しい用語が出て来るが、処理プロセスには違いがないと思います。
これから差異が出始めます。OpenStackの場合は、インスタンスを作成する際に、フレーバーを指定することで、「これぐらいのリソースを使用する予定ですよ」 と自己申告します。本当にいっぱいまで使用するかは別として、まず確保してくれる必要があります。 これに対して、podを起動する際に、「request」と「limit」を両方申告します。つまり、「最低でもこれぐらい欲しいが、 余裕があればもっと割り当てくれればよくて、上限値を超えないように約束します。」という柔軟性が効く申告方法となります。 ノード上のkubeletサービスが、各podの申告内容をまとめて管理し、システムの安定性に影響しない限り、「みんな自由に使ってください」という ポリシーで管理します。 OpenStackの場合は、リソースを追加で確保もできるが、「resize(フレーバー変更)」のような運用操作は必要です。 仮想マシンからコンテナに切り替えることで、より柔軟にリソースの配分ができて、利用率が向上したと考えられるでしょう。
OverCommitの場面はどうなるか
仮想マシンベースのクラウド環境では、リソース使用率を上げるために、「overcommit」を使用することは大前提です。overcommit対象のリソースが CPU、メモリ、ストレージなどいろんな種類があります。今回、CPUとメモリを例として、仮想マシンとコンテナプラットフォームの違う考え方を考察したい と思います。
参照先:https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-resource-requests-and-limits
OpenStack環境のHypervisorは何種類もありますが、デフォルトのKVMの場合はどうなるでしょうか:
- CPUはovercommitによって負荷超過になる場合は、物理CPUは100%以上使えないので、想定通り輻輳になります
- メモリの場合は、物理メモリが足りなければ、SWAP領域への書き出しが始めるので、処理が遅くなります。 結果的に、「勝手に仮想マシンを止まるとダメなので、同じHypervisor上に稼働するみなんの仮想マシンを一緒に我慢してください」というポリシーです。
kubernetesの環境なら、処理の仕方は違います。上記Googleのドキュメントに書いてあるように、負荷超過が発生する際に、ロジックに従って、 リソースを食うpodを停止させます。優先度として: 第一優先:そもそもrequestを設定していない、つまりリソースの申告はそもそもしないもの 第二優先:requestのリソースを超えており、かつプライオリティが低いもの、過少申告しおり、かつVIPではないので、しょうがない 第三優先:プライオリティが同じであれば、requestを最も超えているもの、つまり、一番改善効果を見込めるので
最後に
スケジューリングの話はこれぐらい簡単のものではないので、今後継続的にふり返しが必要と思います。 コンテナ基盤になると、柔軟性と自由度は以前の仮想マシンのプラットフォームより改善したが、それ分を注意を払う必要があリます。 今まで、仮想マシンを停止させないように、いろいろな工夫があるが、「Cloud-Nativeの時代なら、アプリは停止するもの」ということ を常に意識して開発を行うと無難でしょう。