第2章 スタートガイド: クラウドバランシングの例
サンプルを使用して、基本的な Red Hat Business Optimizer Solver の開発を紹介します。
クラウドコンピューターを複数台所有し、そのクラウドコンピューターで複数のプロセスを実行する必要があると仮定します。コンピューターに各プロセスを割り当てる必要があります。
以下のハード制約を満たす必要があります。
すべてのコンピューターで、プロセスの合計容量を処理するのに必要なハードウェア最小要件を満たす必要があります。
- CPU 容量: コンピューターには、最低でも、そのコンピューターに割り当てられたプロセスで必要とされる合計の CPU 処理能力が必要です。
- メモリー容量: コンピューターには、最低でも、そのコンピューターに割り当てられたプロセスで必要とされる合計のメモリーが必要です。
- ネットワーク容量: コンピューターには、最低でも、そのコンピューターに割り当てられたプロセスで必要とされる合計のネットワーク帯域幅が必要です。
以下のソフト制約を最適化する必要があります。
1 つまたは複数のプロセスが割り当てられたコンピューターにはそれぞれ、保守コストが発生します (コンピューターごとに固定)。
- コスト: 合計保守コストを最小限に抑えます。
これは、ビンパッキング 問題にあたります。以下に、簡単なアルゴリズムを使用して、制約が 2 つ (CPU およびメモリー) があるコンピューター 2 台に、4 つのプロセスを割り当てるという簡単な例を紹介します。
ここで使用しているアルゴリズムは FFD (First Fit Decreasing) アルゴリズムです。このアルゴリズムでは、最初に大きいプロセスを割り当ててから、残りのスペースに小さいプロセスを割り当てていきます。図からも分かるように、これは黄色いプロセス D を割り当てる容量が残っていないため、最適ではありません。
Business Optimizer は、よりスマートな、別のアルゴリズムを使用して、より適した解 (ソリューション) を見つけます。また、データ (プロセス、コンピューター) と制約 (ハードウェア要件やその他の制約) の両方を増やして評価します。
以下のまとめは、「マシンの再割当て (Google ROADEF 2012)」 で説明されている、より多くの制約を使用した高度な実装や、この例に該当します。
| 問題の規模 | コンピューター | プロセス | 探索空間 |
|---|---|---|---|
|
コンピューター 2 台、プロセス 6 件 |
2 |
6 |
64 |
|
コンピューター 3 台、プロセス 9 件 |
3 |
9 |
10^4 |
|
コンピューター 4 台、プロセス 12 件 |
4 |
12 |
10^7 |
|
コンピューター 100 台、プロセス 300 件 |
100 |
300 |
10^600 |
|
コンピューター 200 台、プロセス 600 件 |
200 |
600 |
10^1380 |
|
コンピューター 400 台、プロセス 1200 件 |
400 |
1200 |
10^3122 |
|
コンピューター 800 台、プロセス 2400 件 |
800 |
2400 |
10^6967 |
2.1. ドメインモデルの設計 リンクのコピーリンクがクリップボードにコピーされました!
ドメインモデル を使用すると、どのクラスがプランニングエンティティーで、どのプロパティーがプランニング変数かが分かります。また、制約の簡素化、パフォーマンスの向上、これからのニーズに備えた柔軟性の向上もサポートします。
2.1.1. ドメインモデルの設計 リンクのコピーリンクがクリップボードにコピーされました!
ドメインモデルを作成するには、問題の入力データを表現するオブジェクトすべてを定義します。この例では、オブジェクトはプロセスとコンピューターです。
ドメインモデルの個別のオブジェクトは、ソリューションおよび入力データを含む、問題の完全なデータセットを表現する必要があります。以下の例では、このオブジェクトにコンピューターの一覧とプロセスの一覧を格納します。プロセスごとにコンピューターが 1 台割り当てられ、コンピューター間のプロセスの配分が解となります。
手順
- ドメインモデルのクラス図を作成します。
- 正規化して複製データを削除します。
クラスごとに サンプルインスタンス を記述します。サンプルインスタンスは、プランニングの目的に関連するエンティティープロパティーです。
Computer: 特定のハードウェアが搭載され、特定の保守コストが発生するコンピューターを表します。この例では、
cpuPower、memory、networkBandwidth、costがComputerクラスのサンプルインスタンスです。Process: デマンドのあるプロセスを表します。このプロセスは、Planner によってComputerに割り当てられます。ProcessのサンプルインスタンスはrequiredCpuPower、requiredMemoryおよびrequiredNetworkBandwidthです。CloudBalance: コンピューター間のプロセスの配分を表します。CloudBalance には、特定のデータセットのComputerおよびProcessがすべて含まれます。全データセットおよび解を表すオブジェクトの場合は、score を格納するサンプルインスタンスが必要です。Business Optimizer は異なる解のスコアを計算して、比較します。最高スコアの解が最適解となります。このため、
CloudBalanceのサンプルインスタンスはscoreになります。
プランニング中にどの関係 (またはフィールド) が変化するか判断します。
Planning entity (プランニングエンティティー): 解決中に Business Optimizer が変更可能なクラス。この例では、別のコンピューターにプロセスを移動できるので
Processクラスです。- Business Optimizer が変更できな入力データを表現するクラスは、問題ファクト として知られています。
-
Planning variable (プランニング変数): 解決時に変化するプランニングエンティティークラスのプロパティー。この例では、
Processクラスのcomputerプロパティーがそれにあたります。 -
Planning solution (プランニングの解): 問題への解を表現するクラス。このクラスは、プランニングエンティティーをすべて含むデータセットを表す必要があります。この例では、
CloudBalanceクラスがそれにあたります。
以下の UML クラスの図では、Business Optimizer のコンセプトにすでにアノテーションが付けてあります。
examples/sources/src/main/java/org/optaplanner/examples/cloudbalancing/domain ディレクトリーに、この例のクラス定義が含まれています。
2.1.2. Computer クラス リンクのコピーリンクがクリップボードにコピーされました!
Computer クラスは、データを保存する Java オブジェクトで、POJO (Plain Old Java Object) として知られています。一般的には、この種類のクラスがよく使用されます。
例2.1 CloudComputer.java
public class CloudComputer ... {
private int cpuPower;
private int memory;
private int networkBandwidth;
private int cost;
... // getters
}
2.1.3. Process クラス リンクのコピーリンクがクリップボードにコピーされました!
Process クラスは、解決中に変更されるクラスです。
Business Optimizer に、プロパティー computer を変更できることを指示する必要があります。これには、クラスに @PlanningEntity のアノテーションを付けて、getComputer() ゲッターに @PlanningVariable のアノテーションを付けます。
当然ながら、Business Optimizer が解決中にプロパティーを変更できるように、この computer プロパティーにはセッターも必要です。
例2.2 CloudProcess.java
@PlanningEntity(...)
public class CloudProcess ... {
private int requiredCpuPower;
private int requiredMemory;
private int requiredNetworkBandwidth;
private CloudComputer computer;
... // getters
@PlanningVariable(valueRangeProviderRefs = {"computerRange"})
public CloudComputer getComputer() {
return computer;
}
public void setComputer(CloudComputer computer) {
computer = computer;
}
// ************************************************************************
// Complex methods
// ************************************************************************
...
}
Business Optimizer は、computer プロパティーに割り当てるのに、どの値を選択できるのかを把握しておく必要があります。これらの値は、プランニングの解の CloudBalance.getComputerList() メソッドから取得し、現在のデータセットに含まれる全コンピューターのリストを返します。
CloudProcess.getComputer() 上にある @PlanningVariable の valueRangeProviderRefs パラメーターは、CloudBalance.getComputerList() 上にある @ValueRangeProvider の id に一致する必要があります。
ゲッターの代わりにフィールドにアノテーションも使用できます。
2.1.4. CloudBalance クラス リンクのコピーリンクがクリップボードにコピーされました!
CloudBalance クラスには @PlanningSolution アノテーションが付いています。
このクラスは、全コンピューターおよびプロセスの一覧を格納し、プランニングの問題と (初期化されている場合) プランニングの解の両方を表します。
CloudBalance クラスには、以下の主要な属性が含まれます。
このクラスは、Business Optimizer が変更可能なプロセスコレクションを保持します。ゲッター
getProcessList()に@PlanningEntityCollectionPropertyアノテーションを付けて、Business Optimizer が変更できるプロセスを Business Optimizer で取得できるようにします。解を保存するには、Business Optimizer は、変更したプロセス一覧で、クラスの新規インスタンスを初期化します。-
これには
@PlanningScoreのアノテーションがついたscoreプロパティーも含まれており、このプロパティーは、現在の状態の解のScoreを指します。Business Optimizer は、解のインスタンス向けにScoreを計算すると自動的にこのプロパティーを更新するので、このプロパティーにはセッターが必要です。 -
特に、Drools でスコアの計算をする場合には、
computerListのプロパティーは@ProblemFactCollectionPropertyのアノテーションを付けて、Business Optimizer がコンピューターのリスト (問題ファクト) を取得し、Drools エンジンに公開できるようにする必要があります。
-
これには
例2.3 CloudBalance.java
@PlanningSolution
public class CloudBalance ... {
private List<CloudComputer> computerList;
private List<CloudProcess> processList;
private HardSoftScore score;
@ValueRangeProvider(id = "computerRange")
@ProblemFactCollectionProperty
public List<CloudComputer> getComputerList() {
return computerList;
}
@PlanningEntityCollectionProperty
public List<CloudProcess> getProcessList() {
return processList;
}
@PlanningScore
public HardSoftScore getScore() {
return score;
}
public void setScore(HardSoftScore score) {
this.score = score;
}
...
}
2.2. クラウドバランシングの Hello World の実行 リンクのコピーリンクがクリップボードにコピーされました!
「hello world」アプリケーションサンプルを実行して、Solver を例示します。
手順
- お好きな IDE にこの例をダウンロードして設定します。IDE へのサンプルのダウンロードおよび設定方法は、「IDE (IntelliJ、Eclipse、または Netbeans) での Red Hat Business Optimizer サンプルの実行」を参照してください。
以下の主要クラス
org.optaplanner.examples.cloudbalancing.app.CloudBalancingHelloWorldで実行設定を作成します。デフォルトでは、クラウドバランシングの Hello World は 120 秒間実行するように設定されています。
結果
このアプリケーションは、以下のコードを実行します。
例2.4 CloudBalancingHelloWorld.java
public class CloudBalancingHelloWorld {
public static void main(String[] args) {
// Build the Solver
SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource("org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml");
Solver<CloudBalance> solver = solverFactory.buildSolver();
// Load a problem with 400 computers and 1200 processes
CloudBalance unsolvedCloudBalance = new CloudBalancingGenerator().createCloudBalance(400, 1200);
// Solve the problem
CloudBalance solvedCloudBalance = solver.solve(unsolvedCloudBalance);
// Display the result
System.out.println("\nSolved cloudBalance with 400 computers and 1200 processes:\n" + toDisplayString(solvedCloudBalance));
}
...
}
このコードサンプルにより、以下が行われます。
Solver の設定をもとに
Solverを構築します (ここでは、クラスパスのcloudBalancingSolverConfig.xmlの XML ファイルを使用します)。Solverの構築がこの手順で最も複雑な部分です。詳細は、「Solver の設定」を参照してください。SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource( "org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml"); Solver solver<CloudBalance> = solverFactory.buildSolver();問題を読み込みます。
CloudBalancingGeneratorが無作為に問題を生成します。これは、たとえば、データベースなどから、実際の問題を読み込むクラスに置き換えてください。CloudBalance unsolvedCloudBalance = new CloudBalancingGenerator().createCloudBalance(400, 1200);問題を解決します。
CloudBalance solvedCloudBalance = solver.solve(unsolvedCloudBalance);結果を表示します。
System.out.println("\nSolved cloudBalance with 400 computers and 1200 processes:\n" + toDisplayString(solvedCloudBalance));
2.3. Solver の設定 リンクのコピーリンクがクリップボードにコピーされました!
Solver の設定ファイルは、解決のプロセスがどのように機能するかを指定します。このファイルはコードの一部とみなされます。ファイルには、examples/sources/src/main/resources/org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml という名前が指定されています。
例2.5 cloudBalancingSolverConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<!-- Domain model configuration -->
<scanAnnotatedClasses/>
<!-- Score configuration -->
<scoreDirectorFactory>
<easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>
<!--<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>-->
</scoreDirectorFactory>
<!-- Optimization algorithms configuration -->
<termination>
<secondsSpentLimit>30</secondsSpentLimit>
</termination>
</solver>
Solver の設定は、3 つの部分で構成されます。
ドメインモデル設定: Business Optimizer が変更可能なものは何ですか?
Business Optimizer にドメインクラスを指定する必要があります。ここでは、(
@PlanningEntityまたは@PlanningSolutionアノテーションに対して) クラスパス内の全クラスを自動的にスキャンします。<scanAnnotatedClasses/>スコアの設定: Business Optimizer はどのようにプランニング変数を最適化しますか?目的は何ですか?
ここでは、ハード制約とソフト制約を使用するため、
HardSoftScoreを使用します。ただし、Business Optimizer に、ビジネス要件に合ったスコアの計算方法を指定する必要があります。後ほど、スコアの計算方法を 2 種類 (基本的な Java 実装の使用、または Drools DRL の使用) 紹介します。<scoreDirectorFactory> <easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass> <!--<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>--> </scoreDirectorFactory>最適化アルゴリズム設定: Business Optimizer をどのように最適化しますか? この例では、(最適化アルゴリズムが明示的に設定されていないので) 30 秒間、デフォルトの最適化アルゴリズムを使用します。
<termination> <secondsSpentLimit>30</secondsSpentLimit> </termination>Business Optimizer は、数秒 (リアルタイム計画機能を使用する場合は 15 ミリ秒未満になる場合も) でも良い結果は得られるはずですが、時間が長くなればなるほど、結果は良くなります。高度なユースケースでは、ハードな時間制限以外に、終了の条件を使用することが適しています。
デフォルトのアルゴリズムでも、人的作業による計画や、多くの社内の実装よりもはるかに優れていますが、さらに良くするためには、詳細ベンチマーカーを使用して高度な調整 (Power tweak)を行うことができます。
2.4. スコアの設定 リンクのコピーリンクがクリップボードにコピーされました!
Business Oprtimizer は、スコアが最も高いソリューションを探します。この例では、HardSoftScore を使用し、Business Oprtimizer がハード制約に違反していない (またはハードウェア要件を満たす) ソリューションと、ソフト制約に違反する数が少ない (メンテナンスコストが低い) ソリューションを探します。
当然ながら、Business Optimizer には、これらのドメイン固有のスコア制約についても指定する必要があります。Java または Drools 言語を使用して制約を定義できます。
2.4.1. Java を使用したスコア計算の設定 リンクのコピーリンクがクリップボードにコピーされました!
スコア関数の定義方法の 1 つに、プレーン Java での EasyScoreCalculator インターフェース実装があります。
手順
cloudBalancingSolverConfig.xmlファイルで設定を追加するか、アンコメントします。<scoreDirectorFactory> <easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.optional.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass> </scoreDirectorFactory>calculateScore(Solution)メソッドを実装してHardSoftScoreインスタンスを返します。例2.6 CloudBalancingEasyScoreCalculator.java
public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance> { /** * A very simple implementation. The double loop can easily be removed by using Maps as shown in * {@link CloudBalancingMapBasedEasyScoreCalculator#calculateScore(CloudBalance)}. */ public HardSoftScore calculateScore(CloudBalance cloudBalance) { int hardScore = 0; int softScore = 0; for (CloudComputer computer : cloudBalance.getComputerList()) { int cpuPowerUsage = 0; int memoryUsage = 0; int networkBandwidthUsage = 0; boolean used = false; // Calculate usage for (CloudProcess process : cloudBalance.getProcessList()) { if (computer.equals(process.getComputer())) { cpuPowerUsage += process.getRequiredCpuPower(); memoryUsage += process.getRequiredMemory(); networkBandwidthUsage += process.getRequiredNetworkBandwidth(); used = true; } } // Hard constraints int cpuPowerAvailable = computer.getCpuPower() - cpuPowerUsage; if (cpuPowerAvailable < 0) { hardScore += cpuPowerAvailable; } int memoryAvailable = computer.getMemory() - memoryUsage; if (memoryAvailable < 0) { hardScore += memoryAvailable; } int networkBandwidthAvailable = computer.getNetworkBandwidth() - networkBandwidthUsage; if (networkBandwidthAvailable < 0) { hardScore += networkBandwidthAvailable; } // Soft constraints if (used) { softScore -= computer.getCost(); } } return HardSoftScore.valueOf(hardScore, softScore); } }
上記のコードを Map を使用して最適化し、processList を 1 回だけ反復した場合でも、インクリメンタルスコアの計算が行われないため、処理が遅いままです。
これを修正するには、インクリメント Java スコア計算か、Drools スコア計算を使用します。インクリメント Java スコア計算については、本書では触れません。
2.4.2. Drools を使用したスコア計算の設定 リンクのコピーリンクがクリップボードにコピーされました!
Drools ルール言語 (DRL) を使用して制約を定義できます。Drools スコア計算はインクリメント計算を使用します。この計算では、1 つまたは複数のスコアルールとしてすべてのスコア制約が記述されます。
スコア計算のデシジョンエンジンを使用すると、デシジョンテーブル (XLS または Web ベース)、Decision Central をはじめとした対応機能など、Drools の他の技術と統合できます。
手順
クラスパスに
scoreDrlリソースを追加してスコア機能としてデシジョンエンジンを使用します。cloudBalancingSolverConfig.xmlファイルで、設定を追加するか、アンコメントします。<scoreDirectorFactory> <scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl> </scoreDirectorFactory>ハード制約を作成します。これらの制約で、すべてのコンピューターに、十分な CPU、メモリー、ネットワーク帯域幅が割り当てられ、全プロセスがサポートされるようになります。
例2.7 cloudBalancingScoreRules.drl - ハード制約
... import org.optaplanner.examples.cloudbalancing.domain.CloudBalance; import org.optaplanner.examples.cloudbalancing.domain.CloudComputer; import org.optaplanner.examples.cloudbalancing.domain.CloudProcess; global HardSoftScoreHolder scoreHolder; // ############################################################################ // Hard constraints // ############################################################################ rule "requiredCpuPowerTotal" when $computer : CloudComputer($cpuPower : cpuPower) accumulate( CloudProcess( computer == $computer, $requiredCpuPower : requiredCpuPower); $requiredCpuPowerTotal : sum($requiredCpuPower); $requiredCpuPowerTotal > $cpuPower ) then scoreHolder.addHardConstraintMatch(kcontext, $cpuPower - $requiredCpuPowerTotal); end rule "requiredMemoryTotal" ... end rule "requiredNetworkBandwidthTotal" ... endソフト制約を作成します。この制約は、保守コストを最小限に抑えます。ハード制約に該当する場合にのみ適用されます。
例2.8 cloudBalancingScoreRules.drl - ソフト制約
// ############################################################################ // Soft constraints // ############################################################################ rule "computerCost" when $computer : CloudComputer($cost : cost) exists CloudProcess(computer == $computer) then scoreHolder.addSoftConstraintMatch(kcontext, - $cost); end
2.5. Solver の他の開発 リンクのコピーリンクがクリップボードにコピーされました!
上記の例が機能するようになったので、さらに開発を進めてみてください。たとえば、ドメインモデルを改良して、以下のような制約を追加してみてください。
-
すべての
プロセスがサービスに属する。コンピューターはクラッシュする可能性があるため、同じサービスを実行するプロセスは、別のコンピューターに割り当てる必要がある (または割り当てなければならない)。 -
すべての
コンピューターがビルに設置されている。ビルが火災にあう可能性があるので、同じサービスのプロセスは、別のビルに設置されているコンピューターに割り当てる必要がある (割り当てなければならない)。