ElasticQuotaTree およびタスクキューを活用したリソース利用率の向上
AI、機械学習(ML)、バッチワークロード向けにクラスターを複数チームで共有する場合、リソース競合やリソースの未使用(アンダーユーティライゼーション)が一般的な課題となります。ElasticQuotaTree、ack-kube-queue、ack-scheduler を連携して使用することで、組織単位(チーム、部門)に階層的なリソースクォータを割り当て、ジョブを自動的にキューに格納し、保証された最小リソース量を満たせない場合にリソースを再取得(リクレーム)できます。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
ack-koordinator コンポーネントがインストールされていること。「ack-koordinator をインストールする」をご参照ください。
仕組み
リソース割り当てとジョブスケジューリングを管理するために、以下の 3 つのコンポーネントが連携して動作します。
ElasticQuotaTree:組織単位(チーム、部門)に対応する階層的なリソースクォータを定義します。ツリー内の各リーフノードは 1 つ以上の名前空間に対応し、その名前空間に送信されたジョブは、当該ノードのクォータに従って制限されます。
ack-kube-queue:クラスター内の新規ジョブを監視し、各ジョブに対して QueueUnit を作成して、ジョブの名前空間に基づいて適切なキューにルーティングします。ジョブは、要求されるリソース量が利用可能なクォータ内に収まるまでキュー内で待機し、その後、スケジューラに解放されます。
ack-scheduler:キューから解放されたジョブを実行するノードを選択します。
ジョブの最小リソース要件を満たすことができない場合、スケジューリングシステムは、現在の使用量が min 割り当て量を超えて使用中のクォータノードから、自動的にリソースを再取得(リクレーム)します。
ElasticQuotaTree を使用したリソースクォータの設定
ElasticQuotaTree はツリー構造を用いて、チームごとに CPU、メモリ、GPU のリソースクォータを割り当てます。以下の例では、devops、algorithm(text および video のサブチームを含む)、infrastructure(test サブチームを含む)という 3 つの部門を持つ企業を想定し、それぞれ専用の名前空間にマップしています。
クォータルール
ElasticQuotaTree を作成する前に、構成が以下の制約を満たしていることを確認してください。
| ルール | 要件 |
|---|---|
| 名前空間の配置 | 名前空間はリーフノードにのみマウント可能であり、親ノードには名前空間を配置できません。 |
| ノードの min/max | 任意のノードにおいて、min は max 以下である必要があります。 |
| 親ノードの min ルール | 親ノードの min は、子ノードの min 値の合計以下である必要があります。 |
| 親ノードの max ルール | 親ノードの max は、いずれかの子ノードの max 以下である必要があります。 |
パラメーターのセマンティクス:
| パラメーター | デフォルト値 | 動作 |
|---|---|---|
min | 0 | 保証されるリソースの下限値です。min が 0 の場合でもジョブの送信は可能です。ただし、システムはこれらのリソースを保証しません。クォータの min が満たされない場合、スケジューラは、現在の使用量が自身の min を超過しているノードからリソースを再取得(リクレーム)します。 |
max | NA(無制限) | リソースの上限値です。ジョブはこの値を超えるリソースを使用できません。 |
名前空間および ElasticQuotaTree の作成
以下のマニフェストを適用して、名前空間を作成し、クォータツリーを定義します。YAML 内のコメントでは、各ノードが上記の図とどのように対応しているかを示しています。
---
apiVersion: v1
kind: Namespace
metadata:
name: devops
---
apiVersion: v1
kind: Namespace
metadata:
name: text1
---
apiVersion: v1
kind: Namespace
metadata:
name: text2
---
apiVersion: v1
kind: Namespace
metadata:
name: video
---
apiVersion: v1
kind: Namespace
metadata:
name: test1
---
apiVersion: v1
kind: Namespace
metadata:
name: test2
---
apiVersion: scheduling.sigs.k8s.io/v1beta1
kind: ElasticQuotaTree
metadata:
name: elasticquotatree # ElasticQuotaTree は 1 つだけサポートされます。
namespace: kube-system # 効果を発揮させるためには、kube-system 名前空間内に作成する必要があります。
spec:
root:
name: root # ルートノード:クラスター全体のクォータ
min:
cpu: 100
memory: 50Gi
nvidia.com/gpu: 16
max:
cpu: 100
memory: 50Gi
nvidia.com/gpu: 16
children:
- name: devops # ルートの子ノード
min:
cpu: 20
memory: 10Gi
nvidia.com/gpu: 4
max:
cpu: 40
memory: 20Gi
nvidia.com/gpu: 8
namespaces:
- devops
- name: algorithm # ルートの子ノード;text および video の親ノード
min:
cpu: 50
memory: 25Gi
nvidia.com/gpu: 10
max:
cpu: 80
memory: 50Gi
nvidia.com/gpu: 14
children:
- name: text # algorithm の子ノード
min:
cpu: 40
memory: 15Gi
nvidia.com/gpu: 8
max:
cpu: 40
memory: 30Gi
nvidia.com/gpu: 10
namespaces:
- text1
- text2
- name: video # algorithm の子ノード
min:
cpu: 12
memory: 12Gi
nvidia.com/gpu: 2
max:
cpu: 14
memory: 14Gi
nvidia.com/gpu: 4
namespaces:
- video
- name: infrastructure # ルートの子ノード;test の親ノード
min:
cpu: 30
memory: 15Gi
nvidia.com/gpu: 2
max:
cpu: 50
memory: 30Gi
nvidia.com/gpu: 4
children:
- name: test # infrastructure の子ノード
min:
cpu: 30
memory: 15Gi
nvidia.com/gpu: 2
max:
cpu: 50
memory: 30Gi
nvidia.com/gpu: 4
namespaces:
- test1
- test2ack-kube-queue を使用したジョブキューの管理
ElasticQuotaTree を適用すると、ack-kube-queue はクラスター内の各リーフノードに対して自動的にキューを作成します。各リーフノードのクォータは、1 つのキューにマップされます。名前空間に送信されたジョブは、当該名前空間のクォータノードに対応するキューに割り当てられます。
キューの関連付け
ack-kube-queue 内のコントローラーが、クラスター内のキュー資源を自動的に管理します。このコントローラーは ElasticQuotaTree に基づいて維持され、ElasticQuotaTree で定義されたクォータと名前空間の関連付けを、対応するキューにマップします。
たとえば、video 名前空間は、algorithm ノード(root の子ノード)のリーフノードです。ack-kube-queue は、このノードに対して root-algorithm-video という名前のキューを作成します。この video 名前空間に RayJob を送信すると、ack-kube-queue は QueueUnit を作成し、それを root-algorithm-video キューにルーティングします。
RayJob が要求するリソース総量が root-algorithm-video の利用可能なクォータ内に収まれば、ジョブはキューから取り出され、ack-scheduler にノード割り当てのために渡されます。
キューの動作と suspend ハンドオフ
ack-kube-queue は、RayJob の spec.suspend フィールドを介してジョブの実行を制御します。
spec.suspend: trueを指定して RayJob を送信します。これにより、KubeRay オペレーターが即時に Pod を作成することを防ぎます。ack-kube-queue がジョブを検出し、QueueUnit を作成して、対応するキューに配置します。
キューイングポリシーがジョブの進行を許可すると、ack-kube-queue は
spec.suspendをfalseに設定します。KubeRay オペレーターがこの変更を検知し、Pod を作成します。その後、ack-scheduler が Pod をノードに割り当てます。
ジョブがキュー内で停止しているように見える場合は、spec.suspendがfalseに設定されているかを確認してください。設定されていない場合は、ジョブのリソース要求が割り当てられたキューの利用可能なクォータ内に収まっているかを確認してください。