Catapult is a mesos framework SDK that abstracts the scheduler and executor interfaces into user friendly objects (with default callback implementations), and handles persistent storage for application state using apache zookeeper. Catapult allows you to turn your application into an application as a service letting you to spin 100s or 1000s of instances of your application. And the best part is, you don't have to worry about any of the multitenantcy logic (although you can still impose specific scheduling constraints if you want to).
Catapult has 3 major components. The SchedulerApp which is an object that handles application level orchestration logic. Mainly this object will be used to deploy SchedulerNodes. SchedulerNodes are where the meat of the orchestration logic is. SchedulerNodes will handle callbacks such as "running", "failed", or "message" which are all ultimately generated by the ExecutableApp objects. Finally the ExecutableApp object is what's physically running on each node. This is where you will start your database, kick off some jobs, or start some node of some proprietary application.
Catapult provides high availability / failover support by using apache zookeeper. You can deploy Catapult through Marathon OR (if you don't want a dependency on Marathon) you can deploy a cluster of systemd Catapult instances.
- java
- maven
Catapult is not currently hosted as a maven artifact so you'll need to download the source and built it manually :(. During the install phase of the build the jar file will be installed into your local maven repo.
git clone https://github.com/timbouvier/catapult.git
cd catapult
make install
After the install completes you can then import the catapult library to your project using a maven dependency.
<dependency>
<groupId>com.tbouvier.mesos</groupId>
<artifactId>sdk</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
The two main components to any mesos framework are the scheduler and executor. The scheduler is what orchestrates the application deployment and event handling, while the executor is where the application node physically runs. Catapult's scheduler and executor components each are made up of a few sub-components detailed below.
- Scheduler
- SchedulerNode
- An object you will extend to implement any node level logic and event processing
- SchedulerApp
- An object you will extend to implement any app level logic and event processing
- MesosAppListener
- An interface you will implement to handle any framework level logic and event processing
- SchedulerNode
- Executor
- ExecutableApp
- An interface you will implement so the catapult library can start your application node
- ExecutableApp
public class MyExecutableApp implements ExecutableApp {
@Override
public void restart(){
...
}
@Override
public int getExitStatus() {
...
}
@Override
public void message(AppExecutor appExecutor, byte[] message){
...
}
@Override
public void kill(){
...
}
@Override
public void run(AppExecutor executor){
...
}
}
import com.tbouvier.scheduler.Protos;
public class MyNode extends SchedulerNode {
public MyNode(Protos.SchedulerNodeInfo nodeInfo){
/*initialize parent obj with nodeInfo*/
super(nodeInfo);
}
@Override
public void failed(AppDriver appDriver){
/*default implementation will relaunch node*/
}
@Override
public void running(AppDriver appDriver){
/*default implementation does nothing*/
}
@Override
public void finished(AppDriver appDriver){
/*default implementation does nothing*/
}
@Override
public void killed(AppDriver appDriver){
/*default implementation relaunches node*/
}
@Override
public void message(AppDriver appDriver, byte[] data){
/*default implementation drops message*/
}
}
The schedulerNode callbacks are events for pretty much what they look like they're events for. The message callback is a communication channel between the SchedulerNode and the ExecutableApp (what's running on the physical node).
public class MyApplication extends SchedulerApp {
public MyApplication(){
super("my-app-name");
}
@Override
public void initialized(AppDriver appDriver, Protos.AppID appID){
/*containerLite info is a user friendly way of spinning docker containers + volume api supports
local host mounts as well as docker volumes*/
Protos.ContainerLiteInfo.Builder containerLiteInfo = Protos.ContainerLiteInfo.newBuilder()
.setDockerImage("executor-docker-image")
.addVolumes(Protos.SchedulerVolume.newBuilder()
.setHostMountPoint("mesos-shared-lib-path")
.setContainerMountPoint("mesos-shared-lib-path")
.setMode(org.apache.mesos.Protos.Volume.Mode.RO)
.build())
/*create nodeInfo*/
Protos.SchedulerNodeInfo nodeInfo = Protos.SchedulerNodeInfo.newBuilder()
.setSchedulerContainer( Protos.SchedulerContainer.newBuilder()
.setName("node-name")
.setCpus(1.0)
.setMemory(1024)
.setExecutor(true)//if this is a custom executor or not
.setContainerLiteInfo(containerLiteInfo.build())
.build())
.build();
/*Start launching nodes*/
MyNode node = new MyNode(nodeInfo);
appDriver.launchNode(node);
}
@Override
public void initFailed(){
/*
no driver supplied because it failed to create it
Most common reason for this to happen is if zookeeper
or mesos environment variables are misconfigured
*/
}
}
There are additional methods that can be overriden but the default implementations are almost always what you want. For instance, "message" can be overriden but its default implementation delivers the message to the schedulerNode object to which it belongs.
public class MyFrameworkListener implements MesosAppListener {
public void disconnected(AppFramework appFramework){
...
}
public void connected(AppFramework appFramework){
/*start deploying apps!*/
MyApplication app = new MyApplication();
appFramework.register(app);
}
public void applicationFailed(AppFramework appFramework){
...
}
}
Once the connected callback fires you can safely assume the library is initialized an ready to receive API callins.
import com.tbouvier.mesos.MesosAppFramework;
import com.tbouvier.mesos.scheduler.Protos;
public class SchedulerMain {
public static void main(String[] args){
Protos.SchedulerConfig schedulerConfig = Protos.SchedulerConfig.newBuilder()
.setZooKeeperInfo(Protos.ZookeeperInfo.newBuilder()
.setAddress("zk://my-zookeeper-ip-list")
.setRootNode("framework-root-zk-node-name")
.build())
.setMesosInfo(Protos.MesosInfo.newBuilder()
.setAddress("zk://my-zookeeper-ip-list/mesos/")
.build())
.setFrameworkName("my-framework")
.build();
MyFrameworkListener myFramework = new MyFrameworkListener();
new MesosAppFramework(schedulerConfig, myFramework).run();
}
}
public class ExecutorMain {
public static void main(String[] args){
new MesosApplicationFrameworkExecutor(new MyExecutableApp()).run();
}
}
After you familiarize yourself with the general architecture of catapult, please checkout the full example which uses dropwizard for the app deployer API endpoints, and docker containers for the scheduler, executor components!
- Apache Mesos
- Apache Zookeeper