├── .gitignore ├── README.md ├── bin ├── service.bash └── start-scheduler.bash ├── pom.xml └── src └── main └── scala └── mesosphere └── mesos_scala ├── ExampleScheduler.scala └── Main.scala /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target 3 | *.iml 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mesos Getting Started Tutorial 2 | 3 | This tutorial explains how to build a simple distributed fault-tolerant framework on top of Mesos. 4 | The example framework can launch a given number of instances of a command across a cluster, 5 | and will restart the command if it fails. 6 | 7 | ## Prerequisites 8 | 9 | ### Install Autotools and Libtool 10 | 11 | Mesos uses autotools to build. Install via Homebrew on a Mac: 12 | 13 | brew install autoconf automake libtool 14 | 15 | ### Install Mesos 16 | 17 | The Mesos repo on Github is currently out of sync. You can use the one below for now. 18 | 19 | git clone https://git-wip-us.apache.org/repos/asf/incubator-mesos.git 20 | cd incubator-mesos 21 | ./bootstrap 22 | mkdir build 23 | cd build 24 | ../configure 25 | make 26 | make install 27 | 28 | ### Run Zookeeper and Mesos 29 | 30 | Mesos ships with Zookeeper. Run these commands in different terminal windows to start a local Zookeeper, Mesos master, and Mesos slave. 31 | 32 | cd /path/to/incubator-mesos/build/third_party/zookeeper-3.3.4 33 | cp conf/zoo_sample.cfg conf/zoo.cfg 34 | ./bin/zkServer.sh start 35 | 36 | /usr/local/sbin/mesos-master --zk=zk://localhost:2181/mesos 37 | 38 | /usr/local/sbin/mesos-slave --master=zk://localhost:2181/mesos 39 | 40 | ### Add the Mesos JAR to your local Maven Repo 41 | 42 | mvn install:install-file -Dfile=/path/to/incubator-mesos/build/src/mesos-0.13.0.jar -DgroupId=org.apache.mesos -DartifactId=mesos -Dversion=0.13.0 -Dpackaging=jar -------------------------------------------------------------------------------- /bin/service.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while true; do 4 | echo "$(date) pid $$ running on $MESOS_SLAVE_ID" >> /tmp/mesos-sample-service.out 5 | sleep 10 6 | done 7 | -------------------------------------------------------------------------------- /bin/start-scheduler.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z ${MESOS_HOME+x} ] 4 | then 5 | echo "MESOS_HOME is not set. Defaulting to /usr/local" 6 | MESOS_HOME='/usr/local' 7 | else 8 | echo "MESOS_HOME is set to: $MESOS_HOME" 9 | fi 10 | 11 | export MESOS_NATIVE_LIBRARY=$(find "$MESOS_HOME" -name libmesos.dylib -or -name libmesos.so | head -n1) 12 | echo "MESOS_NATIVE_LIBRARY set to $MESOS_NATIVE_LIBRARY" 13 | 14 | FRAMEWORK_HOME=$(dirname $0)/../ 15 | 16 | # Start the scheduler 17 | java -cp $FRAMEWORK_HOME/target/mesos-getting-started-*.jar mesosphere.mesos_scala.Main $@ 18 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | mesosphere 8 | mesos-getting-started 9 | 0.1-SNAPSHOT 10 | 11 | 12 | 2.10.1 13 | 0.14.0 14 | 2.4.1 15 | 16 | 17 | 18 | 19 | 20 | org.scala-lang 21 | scala-library 22 | ${scala.version} 23 | 24 | 25 | 26 | org.apache.mesos 27 | mesos 28 | ${mesos.version} 29 | 30 | 31 | 32 | com.google.protobuf 33 | protobuf-java 34 | ${protobuf.version} 35 | 36 | 37 | 38 | 39 | 40 | 41 | mesosphere-public-repo 42 | Mesosphere Public Repo 43 | http://s3.amazonaws.com/mesosphere-maven-public/ 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-shade-plugin 52 | 2.0 53 | 54 | 55 | 56 | *:* 57 | 58 | META-INF/*.SF 59 | META-INF/*.DSA 60 | META-INF/*.RSA 61 | 62 | 63 | 64 | 65 | 66 | 67 | package 68 | 69 | shade 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.scala-tools 77 | maven-scala-plugin 78 | 2.15.2 79 | 80 | 81 | 82 | compile 83 | testCompile 84 | 85 | 86 | 87 | 88 | ${scala.version} 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/scala/mesosphere/mesos_scala/ExampleScheduler.scala: -------------------------------------------------------------------------------- 1 | package mesosphere.mesos_scala 2 | 3 | import org.apache.mesos.{SchedulerDriver, Scheduler} 4 | import org.apache.mesos.Protos._ 5 | import java.util 6 | import java.util.logging.Logger 7 | import org.apache.mesos.state.State 8 | 9 | /** 10 | * A Mesos framework scheduler that runs a given number of instances of a command across a cluster. 11 | * 12 | * @author Tobi Knaup 13 | */ 14 | 15 | class ExampleScheduler(command: String, numInstances: Int, state: State) extends Scheduler { 16 | 17 | val log = Logger.getLogger(getClass.getName) 18 | val currentInstancesStateName = "currentInstances" 19 | // Keep track of how many instances are currently running 20 | var currentInstances = fetchCurrentInstances() 21 | 22 | def registered(driver: SchedulerDriver, p2: FrameworkID, p3: MasterInfo) {} 23 | 24 | def reregistered(driver: SchedulerDriver, p2: MasterInfo) {} 25 | 26 | def resourceOffers(driver: SchedulerDriver, offers: util.List[Offer]) { 27 | import scala.collection.JavaConversions._ 28 | 29 | for (offer <- offers) { 30 | log.info("Received offer " + offer) 31 | 32 | if (currentInstances < numInstances) { 33 | val taskId = TaskID.newBuilder 34 | .setValue("task_" + System.currentTimeMillis()) 35 | 36 | val cpuResource = Resource.newBuilder 37 | .setName("cpus") 38 | .setType(Value.Type.SCALAR) 39 | .setScalar(Value.Scalar.newBuilder().setValue(1)) 40 | 41 | val commandInfo = CommandInfo.newBuilder 42 | .setValue(command) 43 | 44 | val task = TaskInfo.newBuilder 45 | .setName(taskId.getValue) 46 | .setTaskId(taskId) 47 | .setSlaveId(offer.getSlaveId) 48 | .addResources(cpuResource) 49 | .setCommand(commandInfo) 50 | .build 51 | 52 | log.info("Launching task " + taskId.getValue) 53 | driver.launchTasks(offer.getId, List(task)) 54 | currentInstances += 1 55 | storeCurrentInstances() 56 | } else { 57 | log.info("Declining offer") 58 | driver.declineOffer(offer.getId) 59 | } 60 | } 61 | } 62 | 63 | def offerRescinded(driver: SchedulerDriver, p2: OfferID) {} 64 | 65 | def statusUpdate(driver: SchedulerDriver, status: TaskStatus) { 66 | log.info("Received status update " + status) 67 | 68 | if (status.getState.eq(TaskState.TASK_FAILED)) { 69 | currentInstances -= 1 70 | storeCurrentInstances() 71 | } 72 | } 73 | 74 | def frameworkMessage(driver: SchedulerDriver, executor: ExecutorID, slave: SlaveID, p4: Array[Byte]) {} 75 | 76 | def disconnected(driver: SchedulerDriver) {} 77 | 78 | def slaveLost(driver: SchedulerDriver, slave: SlaveID) {} 79 | 80 | def executorLost(driver: SchedulerDriver, executor: ExecutorID, slave: SlaveID, p4: Int) {} 81 | 82 | def error(driver: SchedulerDriver, error: String) {} 83 | 84 | private def fetchCurrentInstances(): Int = { 85 | val bytes = state.fetch(currentInstancesStateName).get.value 86 | 87 | val count = if (bytes.length > 0) { 88 | bytes(0).toInt 89 | } else { 90 | 0 91 | } 92 | 93 | log.info("Fetched current instance count " + count) 94 | count 95 | } 96 | 97 | private def storeCurrentInstances(): Unit = { 98 | val oldVar = state.fetch(currentInstancesStateName).get 99 | val data = Array(currentInstances.toByte) 100 | val newVar = state.store(oldVar.mutate(data)) 101 | 102 | if (data.sameElements(newVar.get.value)) { 103 | log.info("Successfully stored current instance count " + currentInstances) 104 | } else { 105 | log.warning("Failed to store current instance count") 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/scala/mesosphere/mesos_scala/Main.scala: -------------------------------------------------------------------------------- 1 | package mesosphere.mesos_scala 2 | 3 | import org.apache.mesos.MesosSchedulerDriver 4 | import org.apache.mesos.Protos._ 5 | import org.apache.mesos.state.ZooKeeperState 6 | import java.util.concurrent.TimeUnit 7 | 8 | /** 9 | * @author Tobi Knaup 10 | */ 11 | 12 | object Main extends App { 13 | // Every Mesos framework needs a name 14 | val frameworkName = "mesos_scala-0.0.1" 15 | // URL of the Mesos master 16 | val master = args(0) 17 | // The command to run 18 | val command = args(1) 19 | // Number of instances to run 20 | val numInstances = args(2).toInt 21 | 22 | // FrameworkID uniquely identifies a framework and can be used for failover 23 | val frameworkId = FrameworkID.newBuilder 24 | .setValue(frameworkName) 25 | .build 26 | // FrameworkInfo describes a framework 27 | val frameworkInfo = FrameworkInfo.newBuilder 28 | .setName(frameworkName) 29 | .setId(frameworkId) 30 | .setUser("") // Let Mesos assign the user 31 | .setFailoverTimeout(60.0) // Allow a 60 second window for failover 32 | .build 33 | 34 | // Create a state object backed by ZK 35 | val state = new ZooKeeperState("localhost:2181", 10, TimeUnit.SECONDS, "/getting-started-state") 36 | // Create the scheduler, the driver, and run it 37 | val scheduler = new ExampleScheduler(command, numInstances, state) 38 | val driver = new MesosSchedulerDriver(scheduler, frameworkInfo, master) 39 | driver.run() 40 | } --------------------------------------------------------------------------------