Apache Mesosのガイド

  • link:/category/architecture/ [アーキテクチャ]

[[Apache Mesos]]
=== 1. 概要

通常、さまざまなアプリケーションを同じマシンのクラスターにデプロイします。 たとえば、最近ではlink:/apache-spark[Apache Spark]やlink:/apache-flink[Apache Flink]のような分散処理エンジンを使用することが一般的です同じクラスター内のlink:/cassandra-with-java[Apache Cassandra]などのデータベース。
  • Apache Mesosは、このようなアプリケーション間で効果的なリソース共有を可能にするプラットフォームです。*

    この記事では、最初に、同じクラスターにデプロイされたアプリケーション内のリソース割り当てのいくつかの問題について説明します。 後で、Apache Mesosがアプリケーション間のリソース使用率を向上させる方法を確認します。

[[cluster sharing]]
=== 2. クラスタの共有

多くのアプリケーションはクラスターを共有する必要があります。 概して、2つの一般的なアプローチがあります。
  • クラスターを静的にパーティション分割し、それぞれでアプリケーションを実行します
    パーティション

  • 一連のマシンをアプリケーションに割り当てます

    これらのアプローチにより、アプリケーションは互いに独立して実行できますが、高いリソース使用率は達成されません。
    たとえば、*非アクティブの期間が続く短い期間のみ実行されるアプリケーションを考えてみましょう*。今、このアプリケーションに静的なマシンまたはパーティションを割り当てているので、*非アクティブな期間中に未使用のリソースがあります*。
    非アクティブ期間中に他のアプリケーションに空きリソースを再割り当てすることにより、リソース使用率を最適化できます。
    Apache Mesosは、アプリケーション間の動的なリソース割り当てを支援します。

[[Apache Mesos]]
=== 3. Apache Mesos

前述の両方のクラスター共有アプローチでは、アプリケーションは、実行している特定のパーティションまたはマシンのリソースのみを認識します。 ただし、Apache Mesosは、クラスター内のすべてのリソースの抽象ビューをアプリケーションに提供します。
すぐにわかるように、Mesosはマシンとアプリケーション間のインターフェースとして機能します。 クラスター内のすべてのマシンで使用可能なリソースをアプリケーションに提供します。*この情報を頻繁に更新して、完了ステータスに達したアプリケーションによって解放されたリソースを含めます。 これにより、アプリケーションはどのタスクをどのマシンで実行するかについて最良の決定を下すことができます。
Mesosの仕組みを理解するために、https://mesos.apache.org/documentation/latest/architecture/ [architecture]を見てみましょう。
link:/uploads/Mesos-arch-100x69.jpg%20100w []
この画像は、Mesosの公式ドキュメントの一部です。 ここで、_Hadoop_と_MPI_は、クラスターを共有する2つのアプリケーションです。
次のいくつかのセクションで、ここに示す各コンポーネントについて説明します。

[[mesos master]]
==== 3.1. メソスマスター

マスターはこのセットアップのコアコンポーネントであり、クラスター内のリソースの現在の状態を格納します。 さらに、リソースやタスクなどの情報を渡すことにより、エージェントとアプリケーション間の**オーケストレーターとして機能します。
マスターで障害が発生すると、リソースとタスクに関する状態が失われるため、高可用性構成で展開します。 上の図からわかるように、Mesosは1人のリーダーとともにスタンバイマスターデーモンをデプロイします。 これらのデーモンは、障害が発生した場合に状態を回復するためにZookeeperに依存しています。

[[mesos agent]]
==== 3.2. Mesosエージェント

Mesosクラスターは、すべてのマシンでエージェントを実行する必要があります。 これらのエージェントは、*リソースを定期的にマスターに報告し*、*アプリケーションが実行するようにスケジュールしたタスクを受け取ります*。 このサイクルは、スケジュールされたタスクが完了するか失われると繰り返されます。
次のセクションでは、アプリケーションがこれらのエージェントでタスクをスケジュールおよび実行する方法について説明します。

[[mesos framework]]
==== 3.3. Mesosフレームワーク

Mesosでは、アプリケーションがマスターと対話する抽象コンポーネントを実装して、クラスター内の使用可能なリソースを受け取り、さらにそれらに基づいてスケジューリングの決定を行うことができます。 これらのコンポーネントはフレームワークとして知られています。
Mesosフレームワークは、2つのサブコンポーネントで構成されています。
  • Scheduler –アプリケーションが以下に基づいてタスクをスケジュールできるようにする
    すべてのエージェントで利用可能なリソース

  • Executor –すべてのエージェントで実行され、すべての情報が含まれます
    そのエージェントでスケジュールされたタスクを実行するために必要

    このプロセス全体を次のフローで示します。
     
    link:/uploads/Mesos-flow-100x72.jpg%20100w []
    最初に、エージェントはリソースをマスターに報告します。 この時点で、マスターはこれらのリソースをすべての登録済みスケジューラーに提供します。 このプロセスはリソースオファーと呼ばれ、次のセクションで詳しく説明します。
    次に、スケジューラは最適なエージェントを選択し、マスターを通じてさまざまなタスクを実行します。 エグゼキュータが割り当てられたタスクを完了するとすぐに、エージェントはリソースをマスターに再公開します。 マスターは、クラスター内のすべてのフレームワークに対してこのリソース共有プロセスを繰り返します。
    Mesosを使用すると、アプリケーションはさまざまなプログラミング言語でカスタムスケジューラとエグゼキュータを実装できます。 スケジューラのJava実装は、*実装* * * __ Scheduler __interface *である必要があります。
public class HelloWorldScheduler implements Scheduler {

    @Override
    public void registered(SchedulerDriver schedulerDriver, Protos.FrameworkID frameworkID,
      Protos.MasterInfo masterInfo) {
    }

    @Override
    public void reregistered(SchedulerDriver schedulerDriver, Protos.MasterInfo masterInfo) {
    }

    @Override
    public void resourceOffers(SchedulerDriver schedulerDriver, List<Offer> list) {
    }

    @Override
    public void offerRescinded(SchedulerDriver schedulerDriver, OfferID offerID) {
    }

    @Override
    public void statusUpdate(SchedulerDriver schedulerDriver, Protos.TaskStatus taskStatus) {
    }

    @Override
    public void frameworkMessage(SchedulerDriver schedulerDriver, Protos.ExecutorID executorID,
      Protos.SlaveID slaveID, byte[] bytes) {
    }

    @Override
    public void disconnected(SchedulerDriver schedulerDriver) {
    }

    @Override
    public void slaveLost(SchedulerDriver schedulerDriver, Protos.SlaveID slaveID) {
    }

    @Override
    public void executorLost(SchedulerDriver schedulerDriver, Protos.ExecutorID executorID,
      Protos.SlaveID slaveID, int i) {
    }

    @Override
    public void error(SchedulerDriver schedulerDriver, String s) {
    }
}
ご覧のように、特にマスターとの通信用の*さまざまなコールバックメソッドで構成されています*。
同様に、エグゼキューターの実装は__Executor __interfaceを実装する必要があります。
public class HelloWorldExecutor implements Executor {
    @Override
    public void registered(ExecutorDriver driver, Protos.ExecutorInfo executorInfo,
      Protos.FrameworkInfo frameworkInfo, Protos.SlaveInfo slaveInfo) {
    }

    @Override
    public void reregistered(ExecutorDriver driver, Protos.SlaveInfo slaveInfo) {
    }

    @Override
    public void disconnected(ExecutorDriver driver) {
    }

    @Override
    public void launchTask(ExecutorDriver driver, Protos.TaskInfo task) {
    }

    @Override
    public void killTask(ExecutorDriver driver, Protos.TaskID taskId) {
    }

    @Override
    public void frameworkMessage(ExecutorDriver driver, byte[] data) {
    }

    @Override
    public void shutdown(ExecutorDriver driver) {
    }
}
スケジューラとエグゼキュータの動作バージョンについては、後のセクションで説明します。

[[mesos resources]]
=== 4. 資源管理

[[resource offers]]
==== 4.1. リソース提供

前述したように、エージェントはリソース情報をマスターに公開します。 次に、マスターはこれらのリソースをクラスターで実行されているフレームワークに提供します。 このプロセスは_resource offer._と呼ばれます。
リソースの提供は、リソースと属性の2つの部分で構成されます。
リソースは、メモリ、CPU、ディスクなどのエージェントマシンのハードウェア情報を公開するために使用されます。
すべてのエージェントに5つの定義済みリソースがあります。
  • CPU

  • gpus

  • mem

  • ディスク

  • ports

    これらのリソースの値は、次の3つのタイプのいずれかで定義できます。
  • Scalar –浮動を使用して数値情報を表すために使用
    1.5Gのメモリなどの小数値を許可するポイント番号

  • Range –スカラー値の範囲を表すために使用されます。たとえば、
    ポート範囲

  • Set –複数のテキスト値を表すために使用

    デフォルトでは、Mesosエージェントはマシンからこれらのリソースを検出しようとします。
    ただし、状況によっては、エージェントでカスタムリソースを構成できます。 このようなカスタムリソースの値も、上記のいずれかのタイプである必要があります。
    たとえば、次のリソースを使用してエージェントを起動できます。
--resources='cpus:24;gpus:2;mem:24576;disk:409600;ports:[21000-24000,30000-34000];bugs(debug_role):{a,b,c}'
ご覧のように、事前定義されたリソースのいくつかと、__ bugs __which of of___set __typeという名前の1つのカスタムリソースでエージェントを設定しました。
リソースに加えて、エージェントはKey-Value属性をマスターに公開できます。 これらの属性は、エージェントの追加のメタデータとして機能し、スケジュール決定のフレームワークを支援します。
有用な例は、*エージェントを異なるラックまたはゾーンに追加し*、*同じラックまたはゾーンでさまざまなタスクをスケジュールして*データの局所性を実現することです。
--attributes='rack:abc;zone:west;os:centos5;level:10;keys:[1000-1500]'
リソースと同様に、属性の値はスカラー、範囲、またはテキストタイプのいずれかです。

[[mesos roles]]
==== 4.2. リソースの役割

現代の多くのオペレーティングシステムは、複数のユーザーをサポートしています。 同様に、Mesosは同じクラスター内の複数のユーザーもサポートします。 これらのユーザーは_roles_として知られています。 各ロールをクラスター内のリソースコンシューマーと見なすことができます。
このため、Mesosエージェントは、異なる割り当て戦略に基づいて、異なる役割でリソースを分割できます。 さらに、フレームワークはクラスター内でこれらのロールをサブスクライブし、異なるロールの下でリソースをきめ細かく制御できます。
たとえば、組織内のさまざまなユーザーにサービスを提供している*アプリケーションをホストするクラスターを考えてみましょう。 そのため、リソースをロールに分割することにより、すべてのアプリケーションが互いに分離して動作することができます*。
さらに、フレームワークはこれらのロールを使用してデータの局所性を実現できます。
たとえば、クラスターに__producer __and __consumerという名前の2つのアプリケーションがあるとします。 __ここで、__producer __はデータを永続ボリュームに書き込みます。永続ボリュームは後で読み取ることができます。 ボリュームを_producer._と共有することで、__consumer __applicationを最適化できます。
Mesosでは、複数のアプリケーションが同じロールにサブスクライブできるため、永続ボリュームをリソースロールに関連付けることができます。 さらに、__ producer __と__consumer __の両方のフレームワークは、同じリソースロールにサブスクライブします。 したがって、__ consumer __applicationは、__ producer __applicationと同じボリュームでデータ読み取りタスクを起動できるようになりました。

[[mesos reservation]]
==== 4.3. リソース予約

ここで、Mesosがクラスターリソースを異なるロールにどのように割り当てるかに関して疑問が生じる場合があります。 Mesosは予約を通じてリソースを割り当てます。
予約には2つのタイプがあります。
  • 静的予約

  • ダイナミック予約

    静的予約は、前のセクションで説明したエージェント起動時のリソース割り当てに似ています。
 --resources="cpus:4;mem:2048;cpus(baeldung):8;mem(baeldung):4096"
ここでの唯一の違いは、Mesosエージェントが* _baeldung _。*という名前のロールに対して8つのCPUと4096mのメモリを予約することです。
動的予約では、静的予約とは異なり、ロール内のリソースを入れ替えることができます。 Mesosを使用すると、フレームワークおよびクラスターオペレーターは、リソース提供への応答としてのフレームワークメッセージまたはhttps://mesos.apache.org/documentation/latest/reservation/#examples[HTTPエンドポイント]を介して、リソースの割り当てを動的に変更できます。
Mesosは、ロールなしのすべてのリソースを(*)という名前のデフォルトロールに割り当てます。 マスターは、サブスクライブしたかどうかに関係なく、すべてのフレームワークにそのようなリソースを提供します。

[[mesos weights]]
==== 4.4. リソースの重みと割り当て

通常、Mesosマスターは公平性戦略を使用してリソースを提供します。 重み付けされたDominant Resource Fairness(wDRF)を使用して、リソースが不足している役割を識別します。 マスターは、これらのロールをサブスクライブしているフレームワークにより多くのリソースを提供します。
イベントはアプリケーション間でリソースを公平に共有することはMesosの重要な特性ですが、必ずしも必要なわけではありません。 リソースフットプリントの低いアプリケーションとリソース要求の高いアプリケーションをホストするクラスターを想定します。 このような展開では、アプリケーションの性質に基づいてリソースを割り当てる必要があります。
Mesosでは、フレームワークがロールにサブスクライブし、そのロールのウェイトの値を追加することで、より多くのリソースを要求できます。したがって、*ウェイト1とウェイト2の2つのロールがある場合、Mesosはフェアシェアの2倍を割り当てます2番目の役割へのリソース。*
リソースと同様に、https://mesos.apache.org/documentation/latest/weights/#operator-http-endpoint [HTTPエンドポイント]を使用して重みを設定できます。
Mesosは、重みのあるロールにリソースを公平に配分することに加えて、*ロールの最小リソースが割り当てられるようにします*。
Mesosでは、リソースロールにクォータを追加できます。*クォータは、ロールが受け取ることが保証されるリソースの最小量を指定します*。

[[mesos framework]]
=== 5. 実装フレームワーク

前のセクションで説明したように、Mesosでは、アプリケーションが選択した言語でフレームワークの実装を提供できます。 Javaでは、フレームワークは、フレームワークプロセスのエントリポイントとして機能するメインクラスと、前述の__Scheduler __and __Executor __discussedの実装を使用して実装されます。

[[mesos framework]]
==== 5.1. フレームワークのメインクラス

スケジューラとエグゼキュータを実装する前に、フレームワークのエントリポイントを最初に実装します。
  • マスターに自身を登録します

  • executorランタイム情報をエージェントに提供します

  • スケジューラーを開始します

    最初にhttps://search.maven.org/search?q=g:org.apache.mesos%20AND%20a:mesos&core=gav[Maven依存関係]をMesosに追加します。
<dependency>
    <groupId>org.apache.mesos</groupId>
    <artifactId>mesos</artifactId>
    <version>0.28.3</version>
</dependency>
次に、フレームワークに__HelloWorldMain __を実装します。 最初に行うことの1つは、Mesosエージェントでexecutorプロセスを開始することです。
public static void main(String[] args) {

    String path = System.getProperty("user.dir")
      + "/target/libraries2-1.0.0-SNAPSHOT.jar";

    CommandInfo.URI uri = CommandInfo.URI.newBuilder().setValue(path).setExtract(false).build();

    String helloWorldCommand = "java -cp libraries2-1.0.0-SNAPSHOT.jar com.baeldung.mesos.executors.HelloWorldExecutor";
    CommandInfo commandInfoHelloWorld = CommandInfo.newBuilder()
      .setValue(helloWorldCommand)
      .addUris(uri)
      .build();

    ExecutorInfo executorHelloWorld = ExecutorInfo.newBuilder()
      .setExecutorId(Protos.ExecutorID.newBuilder()
      .setValue("HelloWorldExecutor"))
      .setCommand(commandInfoHelloWorld)
      .setName("Hello World (Java)")
      .setSource("java")
      .build();
}
ここでは、最初にexecutorバイナリの場所を構成しました。 Mesosエージェントは、フレームワークの登録時にこのバイナリをダウンロードします。 次に、エージェントは指定されたコマンドを実行して、executorプロセスを開始します。
次に、フレームワークを初期化し、スケジューラーを開始します。
FrameworkInfo.Builder frameworkBuilder = FrameworkInfo.newBuilder()
  .setFailoverTimeout(120000)
  .setUser("")
  .setName("Hello World Framework (Java)");

frameworkBuilder.setPrincipal("test-framework-java");

MesosSchedulerDriver driver = new MesosSchedulerDriver(new HelloWorldScheduler(),
  frameworkBuilder.build(), args[0]);
最後に、マスターに登録する_MesosSchedulerDriver_を開始します。 登録を成功させるには、マスターのIPをプログラム引数_args [0] _としてこのメ​​インクラスに渡す必要があります。*
int status = driver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1;

driver.stop();

System.exit(status);
上記のクラスでは、_CommandInfo、ExecutorInfo、_、および_FrameworkInfo_はすべて、マスターとフレームワーク間のlink:/google-protocol-buffer[protobuf messages]のJava表現です。

[[mesos scheduler]]
==== 5.2. スケジューラーの実装

Mesos 1.0以降、* Javaアプリケーションからhttps://mesos.apache.org/documentation/latest/scheduler-http-api/[HTTPエンドポイント]を呼び出して、Mesosマスターとメッセージを送受信できます*。 これらのメッセージには、たとえば、フレームワークの登録、リソースオファー、オファーの拒否などが含まれます。
  • Mesos 0.28以前の場合、_Scheduler_インターフェイスを実装する必要があります*:

    ほとんどの場合、_Scheduler._の_resourceOffers_メソッドのみに焦点を当てます。スケジューラがリソースを受け取り、それに基づいてタスクを初期化する方法を見てみましょう。
    最初に、スケジューラがタスクにリソースを割り当てる方法を確認します。
@Override
public void resourceOffers(SchedulerDriver schedulerDriver, List<Offer> list) {

    for (Offer offer : list) {
        List<TaskInfo> tasks = new ArrayList<TaskInfo>();
        Protos.TaskID taskId = Protos.TaskID.newBuilder()
          .setValue(Integer.toString(launchedTasks++)).build();

        System.out.println("Launching printHelloWorld " + taskId.getValue() + " Hello World Java");

        Protos.Resource.Builder cpus = Protos.Resource.newBuilder()
          .setName("cpus")
          .setType(Protos.Value.Type.SCALAR)
          .setScalar(Protos.Value.Scalar.newBuilder()
            .setValue(1));

        Protos.Resource.Builder mem = Protos.Resource.newBuilder()
          .setName("mem")
          .setType(Protos.Value.Type.SCALAR)
          .setScalar(Protos.Value.Scalar.newBuilder()
            .setValue(128));
ここでは、タスクに1つのCPUと128Mのメモリを割り当てました。 次に、__ SchedulerDriver ___を使用して、エージェントでタスクを起動します。
        TaskInfo printHelloWorld = TaskInfo.newBuilder()
          .setName("printHelloWorld " + taskId.getValue())
          .setTaskId(taskId)
          .setSlaveId(offer.getSlaveId())
          .addResources(cpus)
          .addResources(mem)
          .setExecutor(ExecutorInfo.newBuilder(helloWorldExecutor))
          .build();

        List<OfferID> offerIDS = new ArrayList<>();
        offerIDS.add(offer.getId());

        tasks.add(printHelloWorld);

        schedulerDriver.launchTasks(offerIDS, tasks);
    }
}
または、_Scheduler_は、リソースオファーを拒否する必要性をしばしば見つけます。 たとえば、リソースの不足により__Scheduler resources___がエージェントでタスクを起動できない場合、そのオファーをすぐに拒否する必要があります。
schedulerDriver.declineOffer(offer.getId());

[[mesos executor]]
==== 5.3. _Executor_の実装

前に説明したように、フレームワークのexecutorコンポーネントは、Mesosエージェントでアプリケーションタスクを実行する役割を果たします。
Mesos 1.0で_Scheduler_を実装するためにHTTPエンドポイントを使用しました。 同様に、executorにはhttps://mesos.apache.org/documentation/latest/executor-http-api/[HTTPエンドポイント]を使用できます。
前のセクションで、フレームワークがどのようにエージェントを構成してエグゼキュータープロセスを開始するかについて説明しました。
java -cp libraries2-1.0.0-SNAPSHOT.jar com.baeldung.mesos.executors.HelloWorldExecutor
特に、このコマンドは、__HelloWorldExecutor __をメインクラスと見なします。* _MesosExecutorDriver _ **を初期化してタスクを受信し、タスクステータスなどの他の情報を共有する_MesosExecutorDriver _ **を初期化するために、この_main_メソッドを実装します。
public class HelloWorldExecutor implements Executor {
    public static void main(String[] args) {
        MesosExecutorDriver driver = new MesosExecutorDriver(new HelloWorldExecutor());
        System.exit(driver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1);
    }
}
ここで最後に行うことは、フレームワークからタスクを受け入れ、エージェントでそれらを起動することです。 タスクを起動するための情報は、_HelloWorldExecutor:_内に含まれています。
public void launchTask(ExecutorDriver driver, TaskInfo task) {

    Protos.TaskStatus status = Protos.TaskStatus.newBuilder()
      .setTaskId(task.getTaskId())
      .setState(Protos.TaskState.TASK_RUNNING)
      .build();
    driver.sendStatusUpdate(status);

    System.out.println("Execute Task!!!");

    status = Protos.TaskStatus.newBuilder()
      .setTaskId(task.getTaskId())
      .setState(Protos.TaskState.TASK_FINISHED)
      .build();
    driver.sendStatusUpdate(status);
}
もちろん、これは単純な実装にすぎませんが、エグゼキューターが各ステージでマスターとタスクステータスを共有し、完了ステータスを送信する前にタスクを実行する方法について説明します。
場合によっては、エグゼキューターはデータをスケジューラーに送り返すこともできます。
String myStatus = "Hello Framework";
driver.sendFrameworkMessage(myStatus.getBytes());

[[apache mesos]]
=== 6. 結論

この記事では、同じクラスターで実行されているアプリケーション間のリソース共有について簡単に説明しました。 また、Apache Mesosを使用して、CPUやメモリなどのクラスターリソースの抽象ビューを使用して、アプリケーションが最大限の使用率を実現する方法についても説明しました。
後で、*さまざまな公平性ポリシーとロール*に基づいた*アプリケーション間のリソースの動的割り当て*について説明しました。* Mesosにより、アプリケーションはクラスタ内のMesosエージェントから*リソース提供に基づいて*スケジューリング決定を行うことができます。
最後に、JavaでMesosフレームワークの実装を見ました。
いつものように、すべての例はhttps://github.com/eugenp/tutorials/tree/master/libraries-2[over on GitHub]で入手できます。