ApacheMesosのガイド

1。概要

通常、さまざまなアプリケーションを同じマシンクラスターにデプロイします。たとえば、最近では、ApacheSparkやApacheFlinkなどの分散処理エンジンとApacheCassandraなどの分散データベースを同じクラスター内に配置するのが一般的です。

Apache Mesosは、そのようなアプリケーション間で効果的なリソース共有を可能にするプラットフォームです。

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

2.クラスターの共有

多くのアプリケーションはクラスターを共有する必要があります。概して、2つの一般的なアプローチがあります。

  • クラスターを静的にパーティション分割し、各パーティションでアプリケーションを実行します
  • 一連のマシンをアプリケーションに割り当てます

これらのアプローチにより、アプリケーションを互いに独立して実行できますが、高いリソース使用率は達成されません。

たとえば、短時間だけ実行され、その後に非アクティブな期間が続くアプリケーションについて考えてみますこれで、静的マシンまたはパーティションをこのアプリケーションに割り当てたため、非アクティブ期間中に未使用のリソースがあります。

非アクティブ期間中に空きリソースを他のアプリケーションに再割り当てすることで、リソースの使用率を最適化できます。

Apache Mesosは、アプリケーション間の動的なリソース割り当てを支援します。

3. Apache Mesos

上で説明した両方のクラスター共有アプローチでは、アプリケーションは、実行している特定のパーティションまたはマシンのリソースのみを認識します。ただし、Apache Mesosは、クラスター内のすべてのリソースの抽象的なビューをアプリケーションに提供します。

後で説明するように、Mesosはマシンとアプリケーション間のインターフェースとして機能します。これは、クラスター内のすべてのマシンで使用可能なリソースをアプリケーションに提供します。これは、頻繁にアプリケーションによって解放されたリソース含めるには、この情報を更新完了状態に達しています。これにより、アプリケーションは、どのマシンでどのタスクを実行するかについて最良の決定を下すことができます。

Mesosがどのように機能するかを理解するために、そのアーキテクチャを見てみましょう。

この画像は、Mesos(ソース)の公式ドキュメントの一部です。ここで、HadoopMPIは、クラスターを共有する2つのアプリケーションです。

ここに示されている各コンポーネントについては、次のいくつかのセクションで説明します。

3.1。Mesosマスター

マスターはこのセットアップのコアコンポーネントであり、リソースの現在の状態をクラスターに格納します。さらに、リソースやタスクなどに関する情報を渡すことにより、エージェントとアプリケーション間のオーケストレーターとして機能します。

マスターに障害が発生すると、リソースとタスクに関する状態が失われるため、高可用性構成でデプロイします。上の図に示されているように、Mesosはスタンバイマスターデーモンを1つのリーダーとともにデプロイします。これらのデーモンは、障害が発生した場合に状態を回復するためにZookeeperに依存しています。

3.2。Mesosエージェント

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

次のセクションでは、アプリケーションがこれらのエージェントでタスクをスケジュールおよび実行する方法を説明します。

3.3。Mesosフレームワーク

Mesosを使用すると、アプリケーションはマスターと対話する抽象コンポーネントを実装して、クラスター内の使用可能なリソース受け取り、さらにそれらに基づいてスケジューリングを決定できます。これらのコンポーネントはフレームワークとして知られています。

Mesosフレームワークは、次の2つのサブコンポーネントで構成されています。

  • スケジューラ–アプリケーションがすべてのエージェントで利用可能なリソースに基づいてタスクをスケジュールできるようにします
  • エグゼキュータ–すべてのエージェントで実行され、そのエージェントでスケジュールされたタスクを実行するために必要なすべての情報が含まれています

このプロセス全体は、次のフローで表されます。

まず、エージェントはリソースをマスターに報告します。この時点で、マスターはこれらのリソースを登録済みのすべてのスケジューラーに提供します。このプロセスはリソースオファーと呼ばれ、次のセクションで詳しく説明します。

次に、スケジューラーは最適なエージェントを選択し、マスターを介してそのエージェントに対してさまざまなタスクを実行します。エグゼキュータが割り当てられたタスクを完了するとすぐに、エージェントはリソースをマスターに再公開します。マスターは、クラスター内のすべてのフレームワークに対して、このリソース共有プロセスを繰り返します。

Mesosを使用すると、アプリケーションはカスタムスケジューラとエグゼキュータをさまざまなプログラミング言語で実装できます。スケジューラーのJava実装はスケジューラー・インターフェースを実装する必要があります

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 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) { } }

ご覧のとおり、これは主に、特にマスターと通信するためさまざまなコールバックメソッドで構成されています

同様に、エグゼキュータの実装が実装しなければならないエグゼキュータのインターフェイスを:

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) { } }

スケジューラーとエグゼキューターの操作バージョンについては、後のセクションで説明します。

4.リソース管理

4.1。リソースの提供

As we discussed earlier, agents publish their resource information to the master. In turn, the master offers these resources to the frameworks running in the cluster. This process is known as a resource offer.

A resource offer consists of two parts – resources and attributes.

Resources are used to publish hardware information of the agent machine such as memory, CPU, and disk.

There are five predefined resources for every Agent:

  • cpu
  • gpus
  • mem
  • disk
  • ports

The values for these resources can be defined in one of the three types:

  • Scalar – Used to represent numerical information using floating point numbers to allow fractional values such as 1.5G of memory
  • Range – Used to represent a range of scalar values – for example, a port range
  • Set – Used to represent multiple text values

By default, Mesos agent tries to detect these resources from the machine.

However, in some situations, we can configure custom resources on an agent. The values for such custom resources should again be in any one of the types discussed above.

For instance, we can start our agent with these resources:

--resources='cpus:24;gpus:2;mem:24576;disk:409600;ports:[21000-24000,30000-34000];bugs(debug_role):{a,b,c}'

As can be seen, we've configured the agent with few of the predefined resources and one custom resource named bugs which is of set type.

In addition to resources, agents can publish key-value attributes to the master. These attributes act as additional metadata for the agent and help frameworks in scheduling decisions.

A useful example can be to add agents into different racks or zones and then schedule various tasks on the same rack or zone to achieve data locality:

--attributes='rack:abc;zone:west;os:centos5;level:10;keys:[1000-1500]'

Similar to resources, values for attributes can be either a scalar, a range, or a text type.

4.2. Resource Roles

Many modern-day operating systems support multiple users. Similarly, Mesos also supports multiple users in the same cluster. These users are known as roles. We can consider each role as a resource consumer within a cluster.

Due to this, Mesos agents can partition the resources under different roles based on different allocation strategies. Furthermore, frameworks can subscribe to these roles within the cluster and have fine-grained control over resources under different roles.

For example, consider a cluster hosting applications which are serving different users in an organization. So by dividing the resources into roles, every application can work in isolation from one another.

Additionally, frameworks can use these roles to achieve data locality.

For instance, suppose we have two applications in the cluster named producer and consumer. Here, producer writes data to a persistent volume which consumer can read afterward. We can optimize the consumer application by sharing the volume with the producer.

Since Mesos allows multiple applications to subscribe to the same role, we can associate the persistent volume with a resource role. Furthermore, the frameworks for both producer and consumer will both subscribe to the same resource role. Therefore, the consumer application can now launch the data reading task on the same volume as the producer application.

4.3. Resource Reservation

Now the question may arise as to how Mesos allocates cluster resources into different roles. Mesos allocates the resources through reservations.

There are two types of reservations:

  • Static Reservation
  • Dynamic Reservation

Static reservation is similar to the resource allocation on agent startup we discussed in the earlier sections:

 --resources="cpus:4;mem:2048;cpus(baeldung):8;mem(baeldung):4096"

The only difference here is that now the Mesos agent reserves eight CPUs and 4096m of memory for the role named baeldung.

Dynamic reservation allows us to reshuffle the resources within roles, unlike the static reservation. Mesos allows frameworks and cluster operators to dynamically change the allocation of resources via framework messages as a response to resource offer or via HTTP endpoints.

Mesos allocates all resources without any role into a default role named (*). Master offers such resources to all frameworks whether or not they have subscribed to it.

4.4. Resource Weights and Quotas

Generally, the Mesos master offers resources using a fairness strategy. It uses the weighted Dominant Resource Fairness (wDRF) to identify the roles that lack resources. The master then offers more resources to the frameworks that have subscribed to these roles.

Event though fair sharing of resources between applications is an important characteristic of Mesos, its not always necessary. Suppose a cluster hosting applications that have a low resource footprint along with those having a high resource demand. In such deployments, we would want to allocate resources based on the nature of the application.

Mesos allows frameworks to demand more resources by subscribing to roles and adding a higher value of weight for that role. Therefore, if there are two roles, one of weight 1 and another of weight 2, Mesos will allocate twice the fair share of resources to the second role.

Similar to resources, we can configure weights via HTTP endpoints.

Besides ensuring a fair share of resources to a role with weights, Mesos also ensures that the minimum resources for a role are allocated.

Mesos allows us to add quotas to the resource roles. A quota specifies the minimum amount of resources that a role is guaranteed to receive.

5. Implementing Framework

As we discussed in an earlier section, Mesos allows applications to provide framework implementations in a language of their choice. In Java, a framework is implemented using the main class – which acts as an entry point for the framework process – and the implementation of Scheduler and Executor discussed earlier.

5.1. Framework Main Class

Before we implement a scheduler and an executor, we'll first implement the entry point for our framework that:

  • Registers itself with the master
  • Provides executor runtime information to agents
  • Starts the scheduler

We'll first add a Maven dependency for Mesos:

 org.apache.mesos mesos 0.28.3 

Next, we'll implement the HelloWorldMain for our framework. One of the first things we'll do is to start the executor process on the Mesos agent:

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(); }

Here, we first configured the executor binary location. Mesos agent would download this binary upon framework registration. Next, the agent would run the given command to start the executor process.

Next, we'll initialize our framework and start the scheduler:

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]);

Finally, we'll start the MesosSchedulerDriver that registers itself with the Master. For successful registration, we must pass the IP of the Master as a program argument args[0] to this main class:

int status = driver.run() == Protos.Status.DRIVER_STOPPED ? 0 : 1; driver.stop(); System.exit(status);

In the class shown above, CommandInfo, ExecutorInfo, and FrameworkInfo are all Java representations of protobuf messages between master and frameworks.

5.2. Implementing Scheduler

Since Mesos 1.0, we can invoke the HTTP endpoint from any Java application to send and receive messages to the Mesos master. Some of these messages include, for example, framework registration, resource offers, and offer rejections.

For Mesos 0.28 or earlier, we need to implement the Scheduler interface:

For the most part, we'll only focus on the resourceOffers method of the Scheduler. Let's see how a scheduler receives resources and initializes tasks based on them.

First, we'll see how the scheduler allocates resources for a task:

@Override public void resourceOffers(SchedulerDriver schedulerDriver, List list) { for (Offer offer : list) { List tasks = new ArrayList(); 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));

Here, we allocated 1 CPU and 128M of memory for our task. Next, we'll use the SchedulerDriver to launch the task on an agent:

 TaskInfo printHelloWorld = TaskInfo.newBuilder() .setName("printHelloWorld " + taskId.getValue()) .setTaskId(taskId) .setSlaveId(offer.getSlaveId()) .addResources(cpus) .addResources(mem) .setExecutor(ExecutorInfo.newBuilder(helloWorldExecutor)) .build(); List offerIDS = new ArrayList(); offerIDS.add(offer.getId()); tasks.add(printHelloWorld); schedulerDriver.launchTasks(offerIDS, tasks); } }

Alternatively, Scheduler often finds the need to reject resource offers. For example, if the Scheduler cannot launch a task on an agent due to lack of resources, it must immediately decline that offer:

schedulerDriver.declineOffer(offer.getId());

5.3. Implementing Executor

As we discussed earlier, the executor component of the framework is responsible for executing application tasks on the Mesos agent.

We used the HTTP endpoints for implementing Scheduler in Mesos 1.0. Likewise, we can use the HTTP endpoint for the executor.

In an earlier section, we discussed how a framework configures an agent to start the executor process:

java -cp libraries2-1.0.0-SNAPSHOT.jar com.baeldung.mesos.executors.HelloWorldExecutor

Notably, this command considers HelloWorldExecutor as the main class. We'll implement this main method to initialize the MesosExecutorDriver that connects with Mesos agents to receive tasks and share other information like task status:

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); } }

The last thing to do now is to accept tasks from the framework and launch them on the agent. The information to launch any task is self-contained within the 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); }

Of course, this is just a simple implementation, but it explains how an executor shares task status with the master at every stage and then executes the task before sending a completion status.

In some cases, executors can also send data back to the scheduler:

String myStatus = "Hello Framework"; driver.sendFrameworkMessage(myStatus.getBytes());

6. Conclusion

この記事では、同じクラスターで実行されているアプリケーション間のリソース共有について簡単に説明しました。また、Apache Mesosが、CPUやメモリなどのクラスターリソースの抽象的なビューを使用して、アプリケーションが最大の使用率を達成するのにどのように役立つかについても説明しました。

後で、さまざまな公平性ポリシーと役割に基づいた、アプリケーション間のリソース動的割り当てについて説明しましたMesosを使用すると、アプリケーションは、クラスター内のMesosエージェントからのリソースオファーに基づいてスケジューリングを決定できます。

最後に、JavaでのMesosフレームワークの実装を見ました。

いつものように、すべての例はGitHubで入手できます。