def destroy(): """ Kill and remove a container. """ destroy = recv_proto(Destroy) # Acquire a lock for this container with container_lock(destroy.container_id.value): logger.info("Ensuring container %s is killed", destroy.container_id.value) stdout, _, return_code = invoke_docker("kill", [destroy.container_id.value], stdout=PIPE) if return_code > 0: logger.error("Failed to kill container, bad exit code (%d)", return_code) exit(1) logger.info("Removing container %s", destroy.container_id.value) stdout, _, return_code = invoke_docker("rm", [destroy.container_id.value], stdout=PIPE) if return_code > 0: logger.error("Failed to remove container, bad exit code (%d)", return_code) exit(1)
def containers(): """ List all running containers. Dumps out the containerizer.Containers proto which lists all of the container IDs. """ stdout, _, exit_code = invoke_docker("ps", stdout=PIPE, stderr=PIPE) if exit_code > 0: logger.error("Docker returned a bad status code (%d)" % exit_code) exit(1) running_containers = Containers() stdout.readline() # Read off the header for line in stdout: container_id = line.rstrip().split(" ")[-1] if len(container_id) > 0: container = running_containers.containers.add() container.value = container_id else: logger.error("Failed to parse container id, empty") exit(1) send_proto(running_containers)
def wait(): """ Wait for a running container to exit. """ wait = recv_proto(Wait) # Acquire a lock for this container with container_lock(wait.container_id.value, "wait"): logger.info("Waiting for container %s", wait.container_id.value) stdout, _, return_code = invoke_docker("wait", [wait.container_id.value], stdout=PIPE) if return_code > 0: logger.error("Failed to wait for container, bad exit code (%d)", return_code) exit(1) container_exit = int(stdout.readline().rstrip()) logger.info("Container exit code: %d", container_exit) termination = Termination() termination.killed = False termination.status = container_exit termination.message = "" send_proto(termination)
def destroy_container(container_id): logger.info("Ensuring container %s is killed", container_id.value) stdout, _, return_code = invoke_docker("kill", [container_id.value], stdout=PIPE) if return_code > 0: logger.error("Failed to kill container, bad exit code (%d)", return_code) return False logger.info("Removing container %s", container_id.value) stdout, _, return_code = invoke_docker("rm", [container_id.value], stdout=PIPE) if return_code > 0: logger.error("Failed to remove container, bad exit code (%d)", return_code) return False return True
def containers(): """ List all running containers. Dumps out the a Containers proto which lists all of the container IDs. """ stdout, _, exit_code = invoke_docker("ps", stdout=PIPE, stderr=PIPE) if exit_code > 0: logger.error("Docker returned a bad status code (%d)" % exit_code) exit(1) send_proto(parse_docker_ps(stdout))
def launch(): """ Launch a new Mesos executor in a Docker container. """ launch = recv_proto(Launch) # Acquire a lock for this container with container_lock(launch.container_id.value): logger.info("Preparing to launch container %s", launch.container_id.value) try: run_arguments = build_docker_args(launch) except Exception, e: logger.error("Caught exception: %s", e) raise # Re-raise the exception logger.info("Launching docker container") _, _, return_code = invoke_docker("run", run_arguments) if return_code > 0: logger.error("Failed to launch container") exit(1)
def launch(): """ Launch a new Mesos executor in a Docker container. """ launch = recv_proto(Launch) # Acquire a lock for this container with container_lock(launch.container_id.value): logger.info("Preparing to launch container %s", launch.container_id.value) # Build up the docker arguments arguments = [] # Set the container ID arguments.extend([ "--name", launch.container_id.value ]) # Configure the docker network to share the hosts arguments.extend([ "--net", "host" ]) # Configure the user if launch.HasField("user"): arguments.extend([ "-u", launch.user ]) # Figure out where the executor is if launch.HasField("executor_info"): executor = launch.executor_info.command.value uris = launch.executor_info.command.uris # Environment variables for env in launch.executor_info.command.environment.variables: arguments.extend([ "-e", "%s=%s" % (env.name, env.value) ]) else: logger.info("No executor given, launching with mesos-executor") executor = "%s/mesos-executor" % os.environ['MESOS_LIBEXEC_DIRECTORY'] uris = launch.task_info.command.uris # Environment variables for env in launch.task_info.command.environment.variables: arguments.extend([ "-e", "%s=%s" % (env.name, env.value) ]) # Download the URIs logger.info("Fetching URIs") if fetch_uris(launch.directory, uris) > 0: logger.error("Mesos fetcher returned bad exit code") exit(1) # Link the mesos native library native_library = os.environ['MESOS_NATIVE_LIBRARY'] arguments.extend(["-v", "%s:/usr/lib/%s" % (native_library, os.path.basename(native_library))]) # Set the resource configuration cpu_shares = 0 max_memory = 0 ports = set() # Grab the resources from the task and executor resource_sets = [launch.task_info.resources, launch.executor_info.resources] for resources in resource_sets: for resource in resources: if resource.name == "cpus": cpu_shares += int(resource.scalar.value) if resource.name == "mem": max_memory += int(resource.scalar.value) if resource.name == "ports": for port_range in resource.ranges.range: for port in xrange(port_range.begin, port_range.end + 1): ports.add(port) if cpu_shares > 0: arguments.extend(["-c", str(cpu_shares * 256)]) if max_memory > 0: arguments.extend(["-m", "%dm" % max_memory]) if len(ports) > 0: for port in ports: arguments.extend(["-p", ":%i" % port]) logger.info("Configured with executor %s" % executor) # Add the sandbox directory arguments.extend(["-v", "%s:/mesos-sandbox" % (launch.directory)]) arguments.extend(["-w", "/mesos-sandbox"]) # Set the MESOS_DIRECTORY environment variable to the sandbox mount point arguments.extend(["-e", "MESOS_DIRECTORY=/mesos-sandbox"]) # Pass through the rest of the mesos environment variables mesos_env = ["MESOS_FRAMEWORK_ID", "MESOS_EXECUTOR_ID", "MESOS_SLAVE_ID", "MESOS_CHECKPOINT", "MESOS_SLAVE_PID", "MESOS_RECOVERY_TIMEOUT", "MESOS_NATIVE_LIBRARY"] for key in mesos_env: if key in os.environ: arguments.extend(["-e", "%s=%s" % (key, os.environ[key])]) # Parse the container image image = None extra_args = [] if launch.HasField("executor_info"): image = launch.executor_info.command.container.image for option in launch.executor_info.command.container.options: extra_args.append(option.split(" ")) else: image = launch.task_info.command.container.image for option in launch.task_info.command.container.options: extra_args.append(option.split(" ")) if not image: image = os.environ["MESOS_DEFAULT_CONTAINER_IMAGE"] if not image: logger.error("No default container image") exit(1) url = urlparse(image) image = "" if url.netloc: image = url.netloc image += url.path # Pull the image logger.info("Pulling latest docker image: %s", image) _, _, return_code = invoke_docker("pull", [image]) if return_code > 0: logger.error("Failed to pull image (%d)", return_code) exit(1) # TODO(tarnfeld): Locking run_arguments = [ "-d", # Enable daemon mode "--net=bridge" # Bridge the network with the host ] run_arguments.extend(arguments) run_arguments.extend(extra_args) run_arguments.extend(["-e", "GLOG_v=5"]) run_arguments.append(image) run_arguments.extend(["sh", "-c"]) run_arguments.append(executor + " >> stdout 2>>stderr") logger.info("Launching docker container") _, _, return_code = invoke_docker("run", run_arguments) if return_code > 0: logger.error("Failed to launch container") exit(1)
def build_docker_args(launch): # Build up the docker arguments arguments = [] # Set the container ID arguments.extend([ "--name", launch.container_id.value ]) container_info = None # Figure out where the executor is if launch.HasField("executor_info"): executor = launch.executor_info.command.value uris = launch.executor_info.command.uris # Environment variables for env in launch.executor_info.command.environment.variables: arguments.extend([ "-e", "%s=%s" % (env.name, env.value) ]) if launch.executor_info.HasField("container"): container_info = launch.executor_info.container else: logger.info("No executor given, launching with mesos-executor") executor = "%s/mesos-executor" % os.environ['MESOS_LIBEXEC_DIRECTORY'] uris = launch.task_info.command.uris # Environment variables for env in launch.task_info.command.environment.variables: arguments.extend([ "-e", "%s=%s" % (env.name, env.value) ]) # Pull out the ContainerInfo from either the task or the executor if launch.executor_info.HasField("container"): container_info = launch.executor_info.container elif launch.task_info.HasField("container"): container_info = launch.task_info.container # Pull out DockerInfo if it's there docker_info = None if container_info and container_info.type == 1: # ContainerInfo.Type.DOCKER docker_info = container_info.docker # Configure the docker network to share the hosts net = "host" if docker_info: if docker_info.network == 1: # DockerInfo.Network.HOST pass elif docker_info.network == 2: # DockerInfo.Network.BRIDGE net = "bridge" elif docker_info.network == 3: # DockerInfo.Network.NONE net = "none" else: raise Exception("Unsupported docker network type") arguments.extend([ "--net", "%s" % net.lower() ]) # Configure the user if launch.HasField("user"): arguments.extend([ "-u", launch.user ]) # Download the URIs logger.info("Fetching URIs") if fetch_uris(launch.directory, uris) > 0: raise Exception("Mesos fetcher returned bad exit code") # Set the resource configuration cpu_shares = 0 max_memory = 0 ports = set() # Grab the resources from the task and executor resource_sets = [launch.task_info.resources, launch.executor_info.resources] for resources in resource_sets: for resource in resources: if resource.name == "cpus": cpu_shares += float(resource.scalar.value) if resource.name == "mem": max_memory += int(resource.scalar.value) if resource.name == "ports": for port_range in resource.ranges.range: for port in xrange(port_range.begin, port_range.end + 1): ports.add(port) if cpu_shares > 0.0: arguments.extend(["-c", str(int(cpu_shares * 1024))]) if max_memory > 0: arguments.extend(["-m", "%dm" % max_memory]) if len(ports) > 0: for port in ports: arguments.extend(["-p", ":%i" % port]) logger.info("Configured with executor %s" % executor) # Set the MESOS_DIRECTORY environment variable to the sandbox mount point arguments.extend(["-e", "MESOS_DIRECTORY=/mesos-sandbox"]) # Pass through the rest of the mesos environment variables mesos_env = ["MESOS_FRAMEWORK_ID", "MESOS_EXECUTOR_ID", "MESOS_SLAVE_ID", "MESOS_CHECKPOINT", "MESOS_SLAVE_PID", "MESOS_RECOVERY_TIMEOUT", "MESOS_NATIVE_LIBRARY"] for key in mesos_env: if key in os.environ: arguments.extend(["-e", "%s=%s" % (key, os.environ[key])]) # Add the sandbox directory arguments.extend(["-v", "%s:/mesos-sandbox" % (launch.directory)]) arguments.extend(["-w", "/mesos-sandbox"]) # Populate the docker arguments with any volumes to be mounted if container_info: for volume in container_info.volumes: volume_args = volume.container_path if volume.HasField("host_path"): volume_args = "%s:%s" % ( volume.host_path, volume.container_path ) if volume.HasField("mode"): if not volume.HasField("host_path"): raise Exception("Host path is required with mode") if volume.mode == Volume.Mode.RW: volume_args += ":rw" elif volume.mode == Volume.Mode.RO: volume_args += ":ro" else: raise Exception("Unsupported volume mode") arguments.extend(["-v", volume_args]) # Populate the docker arguments with any port mappings if docker_info: for port_mapping in docker_info.port_mappings: if port_mapping.host_port not in ports: raise Exception("Port %i not included in resources" % port_mapping.host_port) port_args = "%i:%i" % ( port_mapping.host_port, port_mapping.container_port ) if port_mapping.HasField("protocol"): port_args += "/%s" % (port_mapping.protocol.lower()) arguments.extend(["-p", port_args]) if docker_info.privileged: arguments.append('--privileged') if docker_info.parameters: for param in docker_info.parameters: if param.key: arguments.append(param.key) if param.value: arguments.append(param.value) extra_args = [] if docker_info: image = docker_info.image else: image = None if launch.HasField("executor_info"): image = launch.executor_info.command.container.image for option in launch.executor_info.command.container.options: extra_args.extend(option.split(" ")) else: image = launch.task_info.command.container.image for option in launch.task_info.command.container.options: extra_args.extend(option.split(" ")) if not image: image = os.environ["MESOS_DEFAULT_CONTAINER_IMAGE"] if not image: raise Exception("No default container image") # Parse the container image url = urlparse(image) if url.netloc: docker_image = "%s/%s" % (url.netloc, url.path.lstrip("/")) else: docker_image = url.path # Pull the image logger.info("Pulling latest docker image: %s", docker_image) _, _, return_code = invoke_docker("pull", [docker_image]) if return_code > 0: raise Exception("Failed to pull image (%d)", return_code) run_arguments = [ "-d", # Enable daemon mode ] run_arguments.extend(arguments) run_arguments.extend(extra_args) run_arguments.append(docker_image) run_arguments.extend(["sh", "-c"]) run_arguments.append(executor + " >> /mesos-sandbox/docker_stdout 2>> /mesos-sandbox/docker_stderr") return run_arguments
def launch(): """ Launch a new Mesos executor in a Docker container. """ launch = recv_proto(Launch) # Acquire a lock for this container with container_lock(launch.container_id.value): logger.info("Prepraring to launch container %s", launch.container_id.value) # Build up the docker arguments arguments = [] # Set the container ID arguments.extend([ "--name", launch.container_id.value ]) # Configure the user if launch.HasField("user"): arguments.extend([ "-u", launch.user ]) # Figure out where the executor is if launch.HasField("executor_info"): executor = launch.executor_info.command.value uris = launch.executor_info.command.uris # Environment variables for env in launch.executor_info.command.environment.variables: arguments.extend([ "-e", "%s=%s" % (env.name, env.value) ]) else: logger.info("No executor given, launching with mesos-executor") executor = "%s/mesos-executor" % os.environ['MESOS_LIBEXEC_DIRECTORY'] uris = launch.task_info.command.uris # Environment variables for env in launch.task_info.command.environment.variables: arguments.extend([ "-e", "%s=%s" % (env.name, env.value) ]) # Download the URIs logger.info("Fetching URIs") if fetch_uris(launch.directory, uris) > 0: logger.error("Mesos fetcher returned bad exit code") exit(1) # Link the mesos native library native_library = os.environ['MESOS_NATIVE_LIBRARY'] arguments.extend(["-v", "%s:/usr/lib/%s" % (native_library, os.path.basename(native_library))]) # Set the resource configuration for resource in launch.task_info.resources: if resource.name == "cpus": arguments.extend(["-c", str(int(resource.scalar.value * 256))]) if resource.name == "mem": arguments.extend(["-m", "%dm" % (int(resource.scalar.value))]) if resource.name == "ports": for port_range in resource.ranges.range: for port in xrange(port_range.begin, port_range.end + 1): arguments.extend(["-p", "%i:%i" % (port, port)]) logger.info("Configured with executor %s" % executor) # Add the sandbox directory arguments.extend(["-v", "%s:/mesos-sandbox" % (launch.directory)]) arguments.extend(["-w", "/mesos-sandbox"]) # Set the MESOS_DIRECTORY environment variable to the sandbox mount point arguments.extend(["-e", "MESOS_DIRECTORY=/mesos-sandbox"]) # Pass through the rest of the mesos environment variables mesos_env = ["MESOS_FRAMEWORK_ID", "MESOS_EXECUTOR_ID", "MESOS_SLAVE_ID", "MESOS_CHECKPOINT", "MESOS_SLAVE_PID", "MESOS_RECOVERY_TIMEOUT"] for key in mesos_env: if key in os.environ: arguments.extend(["-e", "%s=%s" % (key, os.environ[key])]) # Parse the container image image = None extra_args = [] if launch.task_info.HasField("executor"): image = launch.executor_info.command.container.image for option in launch.executor_info.command.container.options: extra_args.append(option.split(" ")) else: image = launch.task_info.command.container.image for option in launch.task_info.command.container.options: extra_args.append(option.split(" ")) if not image: image = os.environ["MESOS_DEFAULT_CONTAINER_IMAGE"] if not image: logger.error("No default container image") exit(1) url = urlparse(image) image = "" if url.netloc: image = url.netloc image += url.path # TODO(tarnfeld): Locking run_arguments = [ "-d", # Enable daemon mode "--net=bridge" # Bridge the network with the host ] run_arguments.extend(arguments) run_arguments.extend(extra_args) run_arguments.extend(["-e", "GLOG_v=5"]) run_arguments.append(image) run_arguments.extend(["sh", "-c"]) run_arguments.append(executor + " >> stdout 2>>stderr") logger.info("Launching docker container") _, _, return_code = invoke_docker("run", run_arguments) if return_code > 0: logger.error("Failed to launch container") exit(1)