怎样在mesos上开发
如果只是执行shell命令或docker image,使用CommandExecutor可以简化开发。当启动任务时,指定CommandInfo而不是ExecutorInfo可以简化开发。
开发时,很多类型、消息定义可以查阅include/mesos/mesos.proto
通信
mesos部件间通信使用libprocess库,其消息是immutable,通信的API包括以下几类:
- Scheduler API:framework调度器与master之间通信,内部通信通常只由SchedulerDriver使用
- Executor API:executor与slave之间通信
- 内部API:master与slave之间通信
- Operator API:由web UI操作者使用,与其他API不同的是,这类API通常是同步的
发送消息,是做一次HTTP POST请求,消息数据作为HTTP请求的body发送,例如以下是运行在10.0.1.7:53523的scheduler发送给framework的注册消息头:
POST /master/mesos.internal.RegisterFrameworkMessage HTTP/1.1
User-Agent: libprocess/scheduler(1)@10.0.1.7:53523
回复消息:
POST /scheduler(1)/mesos.internal.FrameworkRegisteredMessage HTTP/1.1
User-Agent: libprocess/[email protected]:5050
framework
可以在源代码目录MESOS_HOME/src/examples/看到FrameworkScheduler的例子。通过例子来理解MesosFrameworkScheduler并选择你喜欢语言进行执行。RENDLER提供了以C++,Go,Haskell,Java,Python和Scala语言所实现的Framework例子。
Scheduler API
你可以用C、C++、Java/Scala或者Python语言编写一个框架Scheduler,它需要继承Scheduler类(见下面Scheduler API)。Scheduler应当创建一个SchedulerDriver(负责Scheduler和Mesos master的通信),然后调用SchedulerDriver.run()函数。
用framework schedulers程序实现的回调接口。 声明如下代码位于:MESOS_HOME/include/mesos/scheduler.hpp 。
/*
* 空的虚拟的析构函数 (需要把析构函数实例化成子类).
*/
virtual ~Scheduler() {}
/*
* 函数在当Scheduler成功在MesosMaster中注册时被调用。
* FrameworkID是在Framework中由Master生成一个唯一的ID,用于区别其他Scheduler。
* MasterInfo以参数的形式提供当前的Master IP地址和端口。
*/
virtual void registered(SchedulerDriver* driver,
const FrameworkID& frameworkId,
const MasterInfo& masterInfo) = 0;
/*
* 函数在Scheduler再次在新当选的Master注册时被调用。
*只有当Scheduler以前被注册过时调用。
* MasterInfo以参数的形式表示新当选的Master的更新信息。
*/
virtual void reregistered(SchedulerDriver* driver,
const MasterInfo& masterInfo) = 0;
/*
* 函数在Scheduler与Master变成"无链接"时被调用。
* (举例来说, 当前Master关闭并由其他的Master接管)。
*/
virtual void disconnected(SchedulerDriver* driver) = 0;
/*
* 函数在资源已经被提供给这个Framework时被调用。最简单的offer仅包含一个简单slave的资源。
* 这些资源以一个offer的形式打包提供给当前Framework对象,除非发生异常情况,则不在提交。
* 第一种当前或者某个Framework拒绝了这些资源,才能够再次提交offer。
* (请查看 SchedulerDriver::launchTasks) 或者第二种情况取消了这些资源。
* (请查看 Scheduler::offerRescinded)。
* 注意:资源可能在同一时间提交给一个或者多个Framework(根据分配器的分配情况)。 * 如果上面的事情发生, 首先拿到offer的Framework将会使用这些资源来启动Tasks,导致其他Tasks获取
* offer的Framework取消这些资源的使用(或者某个Framework已经使用这些资源启动了Tasks,
* 这些Tasks将会伴随着TASK_LOST状态而失败,并发送过多的消息通知)。
*/
virtual void resourceOffers(SchedulerDriver* driver,
const std::vector<Offer>& offers) = 0;
/*
* 函数在某个offer不在有效时被调用。(举例来说, 节点不可用或者资源被其他Framework的offer占用)。
* 如下发生以下情况offer均不会撤销 (举例来说, 丢弃信息,Framework运行失败,等等。),
* 当Framework尝试启动那些没有有效offer的Tasks时,Framework会收到那些Tasks发送TASK_LOST的状态更新
* (请查看Scheduler::resourceOffers).
*/ virtual void offerRescinded(SchedulerDriver* driver,
const OfferID& offerId) = 0;
/*
* 函数在一个Tasks的状态发生变化时被调用。(举例来说, 一个节点(slave)丢失并且Tasks丢失,
* 一个Tasks完成并且Executors发送了一个状态更新回话,等等)。 如果使用隐式定义implicit
* acknowledgements, 以 _acknowledges_ 的收据作为这个状态的更新作为回调函数返回!
* 如果发生Scheduler无论何种原因在回调函数的时候终止(或者进程退出)另一个状态更新将会被提交
* (注意,无论如何,如果slave发送状态更新是丢失或者失败。在那段时间是不正确的)。
* 如果使用的是显示explicit acknowledgments,Scheduler必须在驱动中知道这个状态。
*/
virtual void statusUpdate(SchedulerDriver* driver,
const TaskStatus& status) = 0;
/*
* 函数在当Executors发送消息时被调用。
* 这些消息是尽力服务:在任何可靠的方式下,绝不期望Framework消息会被重新提交。 */
virtual void frameworkMessage(SchedulerDriver* driver,
const ExecutorsID& ExecutorsId,
const SlaveID& slaveId,
const std::string& data) = 0;
/*
* 函数在当某个slave确定不能找到时被调用。(举例来说,设备故障,网络隔离)。
* 绝大部分Framework会以在新的slave上重新启动所有Tasks的方式进行调度。
*/
virtual void slaveLost(SchedulerDriver* driver,
const SlaveID& slaveId) = 0;
/*
* 函数在Executors退出或者中断时被调用。注意:任何Tasks的运行将会自动生成TASK_LOST的状态更新。
*/
virtual void ExecutorsLost(SchedulerDriver* driver,
const ExecutorsID& ExecutorsId, const SlaveID& slaveId,
int status) = 0;
/*
* 函数在一个未被Scheduler或者Scheduler驱动不能捕获的错误发生时被调用。
* Scheduler驱动将会在这个回调函数执行之前执行。
*/
virtual void error(SchedulerDriver* driver,
const std::string& message) = 0;
Scheduler Driver API
Scheduler Driver负责scheduler的生命周期管理(start, stop, or wait to finish)及与master交互(启动/杀死任务等)
// 启动scheduler driver。必须最先被调用
virtual Status start();
// 停止scheduler driver。如果设置了'failover'标志为false,表示本framework永远不要与mesos重连。
// Mesos将unregister the framework并shutdown所有tasks and executors.
// 若'failover'是true,所有executors及tasks将继续运行以允许scheduler重连。
virtual Status stop(bool failover = false);
// Aborts the driver,之后scheduler的回调函数都不会被调用
// abort and stop的语义是区分的,代码能够检测到aborted driver, and
// 如果需要能够立刻启动另一个driver,'stop()'并非在'abort()'中自动被调用
virtual Status abort();
// 等待driver stopped或aborted, 可能会将当前线程无限期阻塞。
// 本函数的返回状态可以用来判断driver是否aborted
virtual Status join();
// 启动并立即joins the driver.
virtual Status run();
// 向mesos请求资源(请求格式参见mesos.proto,例如从指定的slaves请求资源。
// 可用的资源由Scheduler::resourceOffers回调函数异步给出.
virtual Status requestResources(const std::vector<Request>& requests);
// 启动给定的任务集。任何未用的资源会被declined. 指定的filters是作用在所有未使用的资源
// 注意所有offer必须属于同一个slave。使用空的tasks集合调用本函数将declines所有offers
virtual Status launchTasks(
const std::vector<OfferID>& offerIds,
const std::vector<TaskInfo>& tasks,
const Filters& filters = Filters());
// Kills the specified task. Note that attempting to kill a task is
// currently not reliable. If, for example, a scheduler fails over
// while it was attempting to kill a task it will need to retry in
// the future. Likewise, if unregistered / disconnected, the request
// will be dropped (these semantics may be changed in the future).
virtual Status killTask(const TaskID& taskId);
// Accepts the given offers and performs a sequence of operations on
// those accepted offers. See Offer.Operation in mesos.proto for the
// set of available operations. Any remaining resources (i.e., those
// that are not used by the launched tasks or their executors) will
// be considered declined. Note that this includes resources used by
// tasks that the framework attempted to launch but failed (with
// `TASK_ERROR`) due to a malformed task description. The specified
// filters are applied on all unused resources (see mesos.proto for
// a description of Filters). Available resources are aggregated
// when multiple offers are provided. Note that all offers must
// belong to the same slave.
virtual Status acceptOffers(
const std::vector<OfferID>& offerIds,
const std::vector<Offer::Operation>& operations,
const Filters& filters = Filters());
// Declines an offer in its entirety and applies the specified
// filters on the resources (see mesos.proto for a description of
// Filters). Note that this can be done at any time, it is not
// necessary to do this within the Scheduler::resourceOffers
// callback.
virtual Status declineOffer(
const OfferID& offerId,
const Filters& filters = Filters());
// Removes all filters previously set by the framework (via
// launchTasks()). This enables the framework to receive offers from
// those filtered slaves.
virtual Status reviveOffers();
// Inform Mesos master to stop sending offers to the framework. The
// scheduler should call reviveOffers() to resume getting offers.
virtual Status suppressOffers();
// Acknowledges the status update. This should only be called
// once the status update is processed durably by the scheduler.
// Not that explicit acknowledgements must be requested via the
// constructor argument, otherwise a call to this method will
// cause the driver to crash.
virtual Status acknowledgeStatusUpdate(const TaskStatus& status);
// Sends a message from the framework to one of its executors. These
// messages are best effort; do not expect a framework message to be
// retransmitted in any reliable fashion.
virtual Status sendFrameworkMessage(
const ExecutorID& executorId,
const SlaveID& slaveId,
const std::string& data);
// Allows the framework to query the status for non-terminal tasks.
// This causes the master to send back the latest task status for
// each task in 'statuses', if possible. Tasks that are no longer
// known will result in a TASK_LOST update. If statuses is empty,
// then the master will send the latest status for each task
// currently known.
virtual Status reconcileTasks(const std::vector<TaskStatus>& statuses);
Executor API
/*
* 函数在执行器驱动第一次成功链接到Mesos时被调用。特别的是,
* 调度器可以把一些数据内容传递给执行器的FrameworkInfo.ExecutorInfo数据区。
*/
virtual void registered(ExecutorDriver* driver,
const ExecutorInfo& executorInfo,
const FrameworkInfo& frameworkInfo,
const SlaveInfo& slaveInfo) = 0;
/*
* 函数在某节点重启后再次注册执行器时被调用。
*/
virtual void reregistered(ExecutorDriver* driver,
const SlaveInfo& slaveInfo) = 0;
/*
* 函数在节点要发送的执行器"无法链接"时调用。(举例来说, 节点由于升级导致的重启)。
*/
virtual void disconnected(ExecutorDriver* driver) = 0;
/*
* 函数在执行器要启动任务时调用。(通过Scheduler::launchTasks进行初始化)。
* 注意:任务必须属于线程、进程、或者简单的计算,否则直到执行器返回回调时,
* 没有任何回调函数会被调用。
*/
virtual void launchTask(ExecutorDriver* driver,
const TaskInfo& task) = 0;
/*
* 函数在调度器内正在运行的任务要终止时调用。(通过 SchedulerDriver::killTask)。
* 注意:函数将代表执行器发送没有状态更新。执行器需要对创建新的任务状态负责
* (换种说明, TASK_KILLED)并执行ExecutorDriver::sendStatusUpdate。
*/
virtual void killTask(ExecutorDriver* driver, const TaskID& taskId) = 0;
/*
* 函数在计算框架要传递给执行器信息时调用。这些消息是唯一正确的途径。
* 不要指望计算框架的信息以任何其他的可靠的方式重新提交。
*/
virtual void frameworkMessage(ExecutorDriver* driver,
const std::string& data) = 0;
/*
* 函数在执行器需要终止所有现在运行任务时被调用。
* 注意:函数在Mesos确定执行器将要终止所有的任务时,执行器不会发送终止状态的更新
* (举例来说, TASK_KILLED, TASK_FINISHED,TASK_FAILED, 等)而会创建TASK_LOST状态更新。
*/
virtual void shutdown(ExecutorDriver* driver) = 0;
/*
* 函数在执行器或者执行器驱动发生了一个致命性的错误是时被调用。驱动会在函数的回调之前终止。
*/
virtual void error(ExecutorDriver* driver, const std::string& message) = 0;
安装Framework
你需要把Framework放在集群的所有节点(slaves)。如果你运行需要HDFS,你可以把你的执行器放到HDFS。你可以通过ExecutorInfo参数把执行器放到HDFS这件事情告诉MesosSchedulerDriver的构造器。(举例来说:请示例代码查看src/examples/java/TestFramework.java)。
ExecutorInfo 是协议缓存信息类(在include/mesos/mesos.proto中进行定义), 并且你可以设置URI字段,例如“HDFS://path/to/executor/”. 或者, 你可以通过 frameworks_home 的配置项 (在这里进行定义: MESOS_HOME/frameworks) 告诉mesos节点守护器你所制定的执行器所存储的位置 (举例来说 所有节点(slave)均使用的NFS挂载方式), 然后设置ExecutorInfo为相对路径, 节点(slave)将预先提供frameworks_home的相对路径的值。
你一旦确定执行器在mesos的那些节点可以运行,你需要运行在Mesos管理器中注册的调度器,然后开始接收资源offer!
模块
模块提供了一种简单的方法,可以使Mesos轻松的被第三方模块扩展,而第三方模块却不必知道所有Mesos的内部细节。
调用Mesos的模块
命令行标志--modules 用于Mesos master ,slave 和测试指定的模块列表的加载情况和在内部子系统的可用情况。
使用 --modules=filepath
来指定列表模块 ,所指的文件中应包含被JSON格式化的字符串, 。“filepath”的格式应该是‘file:///path/to/file’ 或 ‘/path/to/file’.的形式。
使用--modules="{...}"
在命令行指定模块列表。
JSON字符串示例:
- 加载库libfoo.so ,其中包含两个模块org_apache_mesos_bar 和org_apache_mesos_baz。
{ "libraries": [ { "file": "/path/to/libfoo.so", "modules": [ { "name": "org_apache_mesos_bar", }, { "name": "org_apache_mesos_baz" } ] } ] }
- 从foo加载模块org_apache_mesos_bar和传递命令行参数X和Y值(模块加载org_apache_mesos_baz没有任何命令行参数):
{ "libraries": [ { "name": "foo", "modules": [ { "name": "org_apache_mesos_bar" "parameters": [ { "key": "X", "value": "Y", } ] }, { "name": "org_apache_mesos_baz" } ] } ] }
- 在命令行中指定
--modules='{"libraries":[{"file":"/path/to/libfoo.so", "modules":[{"name":"org_apache_mesos_bar"}]}]}'
库名
对于每一个library,至少有一个的“file”或“name”参数必须被指定。“file”参数可能指一个文件名(例如“libfoo.so”),相对路径(如“myLibs / libfoo.so”)或绝对路径(例如“/home/mesos/lib/libfoo.so”)。“name”参数是指库名称(如“foo”)。如果“name”被指定了,它会在当前平台上自动改为一个合适的库名称(如在Linux上:“foo”自动改为“libfoo.so”。在OS X 改为“libfoo.dylib”)。 如果库路径没有在“file”中指定参数,库就会搜索标准库的路径或目录:LD_LIBRARY_PATH(在OS X上是:DYLD_LIBRARY_PATH)。 如果“file”和“name”两个都指定了,“name”将被忽略。
可用的各种模块类型
分配器
Mesos master分配器定期确定哪些框架应该提供集群的可用资源。
在Mesos master加载自定义分配器,你需要:
- 介绍其给Mesos master,通过使用 --modules配置,
- 选择它作为分配器通过 --allocator。 例如,下面的命令将运行Mesos master,其内包含ExternalAllocatorModule:
./bin/mesos-master.sh --work_dir=m/work --modules="file://<modules-including-allocator>.json" --allocator=ExternalAllocatorModule
匿名模块
匿名模块不会收到任何回调, 它与他们的父进程共存。与其他命名模块不同的是,一个匿名模块并不直接提供或替代基本功能(如一个隔离器模块)。和装饰模块也不同,它也不直接提供添加或注入数据。匿名模块不需要任何特定的选择器(标志),Mesos master或slave通过--modules, 他们会立即被实例化
验证模块
验证模块允许第三方快速开发和插件的新身份验证方法。这样的模块可以支持PAM(LDAP、MySQL NIS,UNIX)对身份的验证。
Hook
类似于Apache web服务器模块,hooks允许开发一些功能,这些功能可以与内部组件紧密结合,但又不完全适合抽象为模块,只是定义了一些操作,这就是所谓的hooks。
可用的hook API定义在hook.hpp 。每个hook 定义了插入点和可用的上下文。一个例子就是传递给master 的LaunchTaskHook的任务信息。
一些hook可以获得一个对象(例如TaskInfo)并返回对象的全部或部分信息(例如task labels),这样hook可以动态调整其内容。这些hook被称为decorators。
在Mesos中加载一个hook,你需要:
- 在
--modules
引入相关模块 - 在
--hooks
标志选择hook
例如,这样启动agent:
./bin/mesos-agent.sh --master=<IP>:<PORT> --modules="file://<path-to-modules-config>.json" --hooks=TestTaskHook
隔离器
隔离器模块支持试验专门隔离和监视功能。这些例子可以是第三方的资源隔离机制的GPGPU硬件,网络,等等。
Master Contender and Detector
可以实现定制的master leader的选择及检测机制,而不是依赖于缺省的Zookeeper。
例如模块可以使用etcd或consul的服务。
要加载这种模块,需要:
- master启动时指定
--modules
- master启动时使用
--master_contender
及--master_detector
标志 - slave启动时使用
--master_detector
标志
例如:
./bin/mesos-master.sh --modules="file://<path-to-modules-config>.json" --master_contender=org_apache_mesos_TestMasterContender --master_detector=org_apache_mesos_TestMasterDetector
./bin/mesos-slave.sh --modules="file://<path-to-modules-config>.json" --master_detector=org_apache_mesos_TestMasterDetector