shpkpr¶
shpkpr is a tool for controlling and observing applications/tasks running on Marathon and Chronos. shpkpr is designed to provide a simple command-line interface to Marathon and Chronos (similiar to the heroku
command-line tool) for use both manually and with CI tools like jenkins.
- Free software: MIT license
- Documentation: https://shpkpr.readthedocs.org.
- Quick start guide: https://shpkpr.readthedocs.io/en/latest/contributing.html#get-started
Features¶
- List/show detailed application info
- Deploy applications (using Jinja2 templates)
- Zero-downtime application deploys when used with Marathon-LB
- List/show detailed cron task info
- Deploy cron tasks (using Jinja2 templates)
Contents¶
Installation¶
Using Pip¶
At the command line:
$ pip install shpkpr
Or, if you have virtualenvwrapper installed:
$ mkvirtualenv shpkpr
$ pip install shpkpr
If you prefer to use shpkpr from git (WARNING: May be unstable), then:
$ pip install -e git+https://github.com/shopkeep/shpkpr.git#egg=shpkpr
Once installed, shpkpr should be available on your $PATH
:
$ shpkpr --marathon_url=http://marathon.mydomain.com:8080 show
Using Docker¶
shpkpr is also available in a prebuilt Docker image if you’d prefer to run it in a container. A list of the available tags can be found on the Docker hub:
$ docker pull shopkeep/shpkpr:v4.1.2
$ docker pull shopkeep/shpkpr:master
Once the image is downloaded, you can use shpkpr with docker run
:
$ docker run -ti shopkeep/shpkpr:master shpkpr --marathon_url=http://marathon.mydomain.com:8080 show
A simple way to avoid having to repeat the long docker run
command is to use a bash alias:
$ alias shpkpr="docker run -ti -e SHPKPR_MARATHON_URL=http://marathon.mydomain.com:8080 shopkeep/shpkpr:master shpkpr"
$ shpkpr show
Command-Line Usage¶
shpkpr is mostly self-documenting, and documentation for any command or subcommand can be viewed by passing the --help
parameter, e.g.
$ shpkpr --help
Usage: shpkpr [OPTIONS] COMMAND [ARGS]...
A tool to manage applications running on Marathon.
Options:
--help Show this message and exit.
Commands:
apps Manage Marathon applications.
cron Manage Chronos jobs.
For more detailed usage instructions see the sections linked below:
Configuration¶
Environment Variables¶
All configuration options and arguments are fully documented in the --help
screen for each command, however, shpkpr
additionally allows most options to be specified as environment variables with the prefix SHPKPR_
.
For example, to avoid having to specify the Marathon API URL with each command, you could do:
$ export SHPKPR_MARATHON_URL=http://my.marathon.install.mydomain.com:8080
$ shpkpr show myapp
Which is functionally equivalent to:
$ shpkpr --marathon_url=http://my.marathon.install.mydomain.com:8080 show myapp
Options specified on the command line will always take precedence over those in the environment.
See documentation for individual commands for exact option/environment variable names.
Working With Templates¶
When deploying a new application or scheduled task, shpkpr renders the application or task definition (typically a JSON document) using Jinja2. This allows the user to interpolate values into the template prior to deployment if required. This is particularly useful for keeping configuration or secret data out of your codebase and injecting it just in time prior to the deployment.
Passing values to a template¶
For the following sections we’ll use the shpkpr apps deploy
command as an example (the documentation that follows applies equally to other commands that render templates too), assuming the following environment variables have been set (so we don’t have to repeat them in the examples below):
$ export SHPKPR_MARATHON_URL=http://my.marathon.install.mydomain.com
And that we’re using the Standard default template. The only required values for this template are MARATHON_APP_ID
(the id of the application to be deployed) and DOCKER_REPOTAG
(the image and tag for the docker container to be deployed).
shpkpr offers two methods of passing values to a template at deploy time, both are described below:
On the command line¶
The most straightforward way to provide values for a template in shpkpr is on the command line. Each one of the commands which renders a template (currently shpkpr apps deploy
and shpkpr cron set
) accepts an arbitrary list of key=value
pairs which are passed directly to the template being rendered:
$ shpkpr apps deploy MARATHON_APP_ID=my-app DOCKER_REPOTAG=my.docker.registry/my-image:latest
Note
When passing template values on the command line, they must always be passed after any options (those parameters starting with -
or --
).
In the environment¶
It may be more convenient to use environment variables to pass values to a template, so shpkpr supports this method too. When reading values from the environment, shpkpr respects the value of the -e/--env-prefix
option (defaults to SHPKPR_
). This means that if you wanted to set the values for the required variables in the standard template you would set them like so:
$ export SHPKPR_MARATHON_APP_ID=my-app
$ export SHPKPR_DOCKER_REPOTAG=my.docker.registry/my-image:latest
You can then deploy with a simple:
$ shpkpr apps deploy
If you prefer not to prefix your environment variables and just slurp up all of them, you can do:
$ export MARATHON_APP_ID=my-app
$ export DOCKER_REPOTAG=my.docker.registry/my-image:latest
$ shpkpr apps deploy -e ""
Handling Missing Values¶
When rendering a template, if a value does not have a default and is not provided by the user, then shpkpr will exit with a non-zero exit code and display a helpful error message.
$ shpkpr apps deploy --marathon-url http://marathon MARATHON_APP_ID=my-app
Error: Unable to render template: 'DOCKER_REPOTAG' is undefined
Writing Templates¶
The Jinja2 documentation is the best place to go to find general advice on syntax, features and how the language works, so we’ll only cover shpkpr specific bits in this section.
Formatting¶
Currently, shpkpr only supports JSON templates. That is, after rendering the template must be valid JSON or shpkpr will exit with a non-zero exit code.
Given the following template:
{
"id": "my-app",
"cpus": 0.1,
"mem": 512,
"instances": 1,
"cmd": {{CMD}}
}
shpkpr will raise an error here due to the lack of quote-marks around {{CMD}}
which would result in invalid JSON (as shpkpr escapes quote-marks in interpolated values).
Currently shpkpr doesn’t produce particularly helpful error messages here, but that will be fixed in a future release.
$ shpkpr apps deploy --marathon-url http://marathon MARATHON_APP_ID=my-app -t resources/usage/templates/broken.json.tmpl CMD="echo Hello!"
Error: Unable to parse rendered template as JSON, check variables
Using Filters¶
From the Jinja docs:
Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (
|
) and may have optional arguments in parentheses. Multiple filters can be chained. The output of one filter is applied to the next.For example,
{{ name|striptags|title }}
will remove all HTML Tags from variable`name
and title-case the output (title(striptags(name))
).Filters that accept arguments have parentheses around the arguments, just like a function call. For example:
{{ listx|join(', ') }}
will join a list with commas (str.join(', ', listx)
).
In addition to the built-in filters shipped by Jinja, shpkpr provides a number of additional filters to assist with writing your templates, from validation/typing to text transformation.
-
shpkpr.template_filters.
filter_items
(value, startswith=None, strip_prefix=False)¶ Jinja2 filter used to filter a dictionary’s keys by specifying a required prefix.
Returns a list of key/value tuples.
{{ my_dict|filter_items }} {{ my_dict|filter_items("MY_PREFIX_") }} {{ my_dict|filter_items("MY_PREFIX_", True) }}
This is most useful in combination with the special _all_env variable that shpkpr injects into every template. For example, to iterate over only the template variables that start with
LABEL_
you could do:{% for k, v in _all_env|filter_items("LABEL_", strip_prefix=True) %} "{{k}}": "{{v}}", {% endfor %}
-
shpkpr.template_filters.
require_int
(*args, **kwargs)¶ Jinja2 filter used to enforce an integer type in a template.
Accepts optional min/max constraints.
{{ my_integer_value|require_int }} {{ my_integer_value|require_int(min=0) }} {{ my_integer_value|require_int(min=0, max=20) }} {{ my_integer_value|require_int(max=20) }}
-
shpkpr.template_filters.
require_float
(*args, **kwargs)¶ Jinja2 filter used to enforce an float type in a template.
Accepts optional min/max constraints.
{{ my_float_value|require_float }} {{ my_float_value|require_float(min=0) }} {{ my_float_value|require_float(min=0, max=20) }} {{ my_float_value|require_float(max=20) }}
-
shpkpr.template_filters.
slugify
(value, *args, **kwargs)¶ Jinja2 filter used to slugify a string.
All arguments are passed directly to python-slugify’s
slugify
function, see the library’s documentation for further information.{{ my_string|slugify }} {{ my_string|slugify(max_length=20) }}
Dictionary access to all variables¶
Sometimes it’s useful to be able to access all template variables in a single dictionary object. This allows for iteration over the variables, or indirect access (using the value of another variable as the key).
shpkpr provides the special _all_env
variable for this purpose. It can be used as follows:
{# Iterate over all template variables beginning with "LABEL_" #}
{% for k, v in _all_env|filter_items("LABEL_") %}
"{{k}}": "{{v}}",
{% endfor %}
{# Regular dictionary-style access is available if necessary #}
{{ _all_env[SOME_VARIABLE] }}
Manage Marathon Applications¶
shpkpr apps show¶
Show detailed information for one or more applications.
shpkpr apps show [OPTIONS]
Options
-
--output-format
<output_formatter>
¶ Serialization format to use when printing config data to stdout. [default: json]
-
-a
,
--application
<application_id>
¶ ID of the Marathon application to target. This should be the full path of the application (including slashes) e.g.
/mygroup/myapp
. The first/root slash is optional, shpkpr will add it if missing.
-
--dry-run
¶
Enables “dry run” mode. When enabled, shpkpr will avoid making changes to any remote systems. This can be used to safely test deployments or jobs before performing any destructive operations. Exactly what constitues a “dry run” varies by operation type and deployment strategy, see command documentation for further information.
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--marathon-url
<marathon_url>
¶ URL of the Marathon API to use. [required]
Environment variables
-
SHPKPR_OUTPUT_FORMAT
¶ Provide a default for
--output-format
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_MARATHON_URL
¶ Provide a default for
--marathon-url
shpkpr apps deploy¶
Deploy one or more applications to Marathon.
shpkpr apps deploy [OPTIONS] [ENV_PAIRS]...
Options
-
--force
¶
Force update even if a deployment is in progress.
-
-t
,
--template
<template_names>
¶ Path of the template to use for deployment.
-
--template-dir
<template_path>
¶ Base directory in which your templates are stored.
-
-e
,
--env-prefix
<env_prefix>
¶ Prefix used to filter environment variables used for templating. [default: SHPKPR]
-
--timeout
<timeout>
¶ Maximum amount of time (in seconds) to wait for a deployment to finish before aborting. [default: 300]
-
--strategy
<deployment_strategy>
¶ Deployment strategy to utilise. [default: standard]
-
--marathon-lb-url
<marathon_lb_client>
¶ URL for Marathon-LB used during blue/green deployment.
-
--dry-run
¶
Enables “dry run” mode. When enabled, shpkpr will avoid making changes to any remote systems. This can be used to safely test deployments or jobs before performing any destructive operations. Exactly what constitues a “dry run” varies by operation type and deployment strategy, see command documentation for further information.
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--marathon-url
<marathon_url>
¶ URL of the Marathon API to use. [required]
Arguments
-
ENV_PAIRS
¶
Optional argument(s)
Environment variables
-
SHPKPR_TEMPLATE_DIR
¶ Provide a default for
--template-dir
-
SHPKPR_DEPLOYMENT_STRATEGY
¶ Provide a default for
--strategy
-
SHPKPR_MARATHON_LB_URL
¶ Provide a default for
--marathon-lb-url
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_MARATHON_URL
¶ Provide a default for
--marathon-url
shpkpr apps run¶
Run a one-off task in a “production-like” environment.
Uses the current deployment configuration to start up a container locally and run a single command. Environment variables and secrets are extracted from the current deployment configuration and injected into the container when starting up.
shpkpr apps run [OPTIONS] COMMAND [ENV_PAIRS]...
Options
-
--vault-addr
<vault_addr>
¶ URL of the Vault API to use.
-
--vault-token
<vault_token>
¶ Token used to authenticate with Vault.
-
-t
,
--template
<template_names>
¶ Path of the template to use for deployment.
-
--template-dir
<template_path>
¶ Base directory in which your templates are stored.
-
-e
,
--env-prefix
<env_prefix>
¶ Prefix used to filter environment variables used for templating. [default: SHPKPR]
Arguments
-
COMMAND
¶
Required argument
-
ENV_PAIRS
¶
Optional argument(s)
Environment variables
-
SHPKPR_VAULT_ADDR
¶ Provide a default for
--vault-addr
-
SHPKPR_VAULT_TOKEN
¶ Provide a default for
--vault-token
-
SHPKPR_TEMPLATE_DIR
¶ Provide a default for
--template-dir
Manage Chronos Jobs¶
shpkpr cron show¶
List application configuration.
shpkpr cron show [OPTIONS]
Options
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--chronos-url
<chronos_url>
¶ URL of the Chronos endpoint to use [required]
-
--chronos-version
<chronos_version>
¶ Verson of the Chronos endpoint to use [default: 3.0.2]
-
-j
,
--job-name
<job_name>
¶ Restrict command to specific job.
-
--output-format
<output_formatter>
¶ Serialization format to use when printing config data to stdout. [default: json]
Environment variables
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_CHRONOS_URL
¶ Provide a default for
--chronos-url
-
SHPKPR_CHRONOS_VERSION
¶ Provide a default for
--chronos-version
-
SHPKPR_OUTPUT_FORMAT
¶ Provide a default for
--output-format
shpkpr cron set¶
Add or Update a job in chronos.
shpkpr cron set [OPTIONS] [ENV_PAIRS]...
Options
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--chronos-url
<chronos_url>
¶ URL of the Chronos endpoint to use [required]
-
--chronos-version
<chronos_version>
¶ Verson of the Chronos endpoint to use [default: 3.0.2]
-
--vault-addr
<vault_addr>
¶ URL of the Vault API to use.
-
--vault-token
<vault_token>
¶ Token used to authenticate with Vault.
-
-t
,
--template
<template_names>
¶ Path of the template to use for deployment.
-
--template-dir
<template_path>
¶ Base directory in which your templates are stored.
-
-e
,
--env-prefix
<env_prefix>
¶ Prefix used to filter environment variables used for templating. [default: SHPKPR]
Arguments
-
ENV_PAIRS
¶
Optional argument(s)
Environment variables
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_CHRONOS_URL
¶ Provide a default for
--chronos-url
-
SHPKPR_CHRONOS_VERSION
¶ Provide a default for
--chronos-version
-
SHPKPR_VAULT_ADDR
¶ Provide a default for
--vault-addr
-
SHPKPR_VAULT_TOKEN
¶ Provide a default for
--vault-token
-
SHPKPR_TEMPLATE_DIR
¶ Provide a default for
--template-dir
shpkpr cron delete¶
shpkpr cron delete [OPTIONS] JOB_NAME
Options
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--chronos-url
<chronos_url>
¶ URL of the Chronos endpoint to use [required]
-
--chronos-version
<chronos_version>
¶ Verson of the Chronos endpoint to use [default: 3.0.2]
Arguments
-
JOB_NAME
¶
Required argument
Environment variables
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_CHRONOS_URL
¶ Provide a default for
--chronos-url
-
SHPKPR_CHRONOS_VERSION
¶ Provide a default for
--chronos-version
shpkpr cron delete-tasks¶
shpkpr cron delete-tasks [OPTIONS] JOB_NAME
Options
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--chronos-url
<chronos_url>
¶ URL of the Chronos endpoint to use [required]
-
--chronos-version
<chronos_version>
¶ Verson of the Chronos endpoint to use [default: 3.0.2]
Arguments
-
JOB_NAME
¶
Required argument
Environment variables
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_CHRONOS_URL
¶ Provide a default for
--chronos-url
-
SHPKPR_CHRONOS_VERSION
¶ Provide a default for
--chronos-version
shpkpr cron run¶
shpkpr cron run [OPTIONS] JOB_NAME
Options
-
--allow-insecure-auth
¶
Allow HTTP basic authentication when not using SSL.
-
--username
<username>
¶ Username used for HTTP Basic Authentication to Marathon/Chronos.
-
--password
<password>
¶ Password used for HTTP Basic Authentication to Marathon/Chronos.
-
--chronos-url
<chronos_url>
¶ URL of the Chronos endpoint to use [required]
-
--chronos-version
<chronos_version>
¶ Verson of the Chronos endpoint to use [default: 3.0.2]
Arguments
-
JOB_NAME
¶
Required argument
Environment variables
-
SHPKPR_USERNAME
¶ Provide a default for
--username
-
SHPKPR_PASSWORD
¶ Provide a default for
--password
-
SHPKPR_CHRONOS_URL
¶ Provide a default for
--chronos-url
-
SHPKPR_CHRONOS_VERSION
¶ Provide a default for
--chronos-version
Deploying applications¶
This section contains a detailed overview on how shpkpr manages deployment of applications to Marathon. Each available deployment strategy is documented in-depth, both at a high level and with detailed API documentation.
Standard Deployment¶
-
class
shpkpr.deployment.
StandardDeployment
(marathon_client, timeout, app_definitions, **kw)¶ StandardDeployment implements Marathon’s basic deployment workflow and uses the primitives provided by the Marathon API to perform a standard rolling deploy according to application settings.
This deployment strategy is best suited for non-web-facing applications or those that can tolerate minor downtime during deployment e.g. consumer or worker applications.
-
execute
(force=False)¶ Execute standard Marathon deployment.
-
Standard default template¶
shpkpr includes a set of default templates which are used when a custom template is not supplied by the user at runtime. The default template used for standard deployments is included below for reference purposes.
{#
Built-In Standard Template
This file contains the default template used by shpkpr when performing a
standard Marathon deployment if no custom template is provided. The goal of
this template is to provide a comprehensive application definition that
works in a wide variety of usecases.
With sensible defaults for most options, it should be possible to deploy a
new or updated application with very minimal configuration.
This template makes some assumptions about your application:
- It is configured via environment variables (12-factor style).
- It runs in a Docker container.
- It exposes at most one TCP port.
Only 2 values are required to deploy an application using this template:
- MARATHON_APP_ID: The name of the deployed application on Marathon
- DOCKER_REPOTAG: The repotag of the Docker image to be deployed
If the application being deployed exposes services via HTTP, it is
recommended that a health check is also configured using the
`MARATHON_HEALTH_CHECK_PATH` variable.
#}
{
{#
Unique identifier for the app consisting of a series of names separated
by slashes. Each name must be at least 1 character and may only contain
digits (0-9), dashes (-), dots (.), and lowercase letters (a-z). The
name may not begin or end with a dash.
When choosing an ID for your application it should meet the following
criteria:
- Be descriptive: A good ID should easily identify what is running. If
in doubt, the GitHub repo name is a good starting point.
- Good: `authentication-service`, `godoc`, `core-documentation`
- Bad: `incongruous-elderberries`, `banana-for-scale`, `so-service-much-http-wow`
- Use a common prefix: Where an application has more than one type of
service to run, all instances should use a common prefix to enable
easy categorization and sorting.
- Good: `some-service/consumer`, `some-service/web`
- Bad: `consumer-some-service`, `web-some-service`
#}
"id": "{{MARATHON_APP_ID}}",
{#
The command that is executed on task startup. This is optional, if not
defined it defaults to the CMD configured in the Docker container at
build time.
#}
{% if MARATHON_CMD is defined %}"cmd": "{{MARATHON_CMD}}",{% endif %}
{#
The number of CPU shares this application needs per instance.
The `cpus` parameter is used for two different purposes in two different
contexts, by Mesos when scheduling tasks across the cluster, and by the
Linux Kernel when scheduling processes on a single machine.
Mesos uses the `cpus` value when deciding on which slave a task should
be scheduled. Mesos treats `cpus` values as absolute, e.g. A box with 4
available CPUs would never be assigned a set of tasks whose `cpus`
values total more than 4. Mesos does not (yet) support oversubscription
of resources at the cluster level.
When a task has been scheduled to a given slave, Linux's scheduler takes
over. An application's `cpus` value is used by CFS (Linux's Completely
Fair Scheduler) to assign a *minimum* relative share of all CPU
resources on the slave, e.g. If a slave is running 3 CPU-bound tasks,
each with a `cpus` value of `0.1`, each will be allocated
*approximately* 33% of the available CPU time. If not all of the tasks
on a given slave are CPU-bound, CFS will allow the spare CPU resource to
be consumed by any task that requires it.
To summarize:
- Marathon’s cpu setting is used for 2 distinct purposes.
- Mesos treats the value as absolute and uses it to schedule a task to a
specific slave with the available resources.
- Linux treats the value as a relative weight, ensuring that an
application has access to a *minimum* share of CPU time (equivalent to
`task.cpus / sum([t.cpus for t in tasks])`).
When choosing a value for `cpus` you should take care not to request
more resources than your application needs:
- Is my application typically CPU bound?
- No: `0.1` is a good default value for `cpus`.
- Yes: `0.5` is a good starting value from which the application can
be monitored and adjusted up/down as necessary.
#}
"cpus": {{MARATHON_CPUS|default(0.1)|require_float(min=0.1)}},
{#
The amount of memory in MB that is needed for the application per
instance. If an application exceeds the amount of memory it has been
allocated, Marathon will kill and restart the task to reclaim the RAM.
#}
"mem": {{MARATHON_MEM|default(128)|require_int(min=0)}},
{# The number of instances of this application to start. #}
"instances": {{MARATHON_INSTANCES|default(1)|require_int(min=0)}},
"container": {
"type": "DOCKER",
"docker": {
{# The name/tag of the Docker image to use. #}
"image": "{{DOCKER_REPOTAG}}",
{% block docker_port_mappings %}
{% if DOCKER_EXPOSED_PORT is defined %}
"portMappings": [
{
{#
Port number within the container that should be exposed
to the host.
#}
"containerPort": {{DOCKER_EXPOSED_PORT|require_int}},
{#
Setting `hostPort` to `0` allows Marathon to dynamically
allocate a port on the host to map.
#}
"hostPort": {{DOCKER_HOST_PORT|default(0)}},
"protocol": "tcp"
}
],
{% endif %}
{% endblock %}
"parameters": [
{#
Passes `--init` to the `docker run` command.
#}
{"key": "init", "value": "{{DOCKER_INIT|default("false")}}"}{% if _all_env|filter_items("LABEL_") %},{% endif %}
{% block docker_labels %}
{% for key, value in _all_env|filter_items("LABEL_", strip_prefix=True) %}
{"key": "label", "value": "{{key|slugify}}={{value}}"}{% if not loop.last %},{% endif %}
{% endfor %}
{% endblock %}
],
"network": "BRIDGE"
}
},
{#
Key value pairs that get added to the environment variables of each task
that is started by Marathon.
#}
"env": {
{% for key, value in _all_env|filter_items("ENV_", strip_prefix=True) %}
"{{key}}": "{{value}}"{% if not loop.last %},{% endif %}
{% endfor %}
},
{#
Attaching metadata to apps can be useful to expose additional
information to other services, so Marathon has the ability to place
labels on apps (for example, you could label apps “staging” and
“production” to mark services by their position in the pipeline).
#}
{% block labels %}
"labels": {
{% for key, value in _all_env|filter_items("LABEL_", strip_prefix=True) %}
"{{key}}": "{{value}}"{% if not loop.last %},{% endif %}
{% endfor %}
},
{% endblock %}
{#
Secrets allow Marathon to retrieve secret data from configured "secret
stores" at application startup time. This allows decoupling of how
secrets are provisioned from the how applications make use of those
secrets at runtime.
Secrets functionality requires the use of a custom Marathon plugin, none
is enabled by default. The exact mechanism by which secrets are made
available to the application at runtime depends on the plugin being
used.
Docs: https://mesosphere.github.io/marathon/docs/plugin.html
#}
"secrets": {
{% for key, value in _all_env|filter_items("SECRET_", strip_prefix=True) %}
"{{key}}": {"source": "{{value}}"}{% if not loop.last %},{% endif %}
{% endfor %}
},
{#
Constraints control where apps run to allow optimizing for either fault
tolerance (by spreading a task out on multiple nodes) or locality (by
running all of an applications tasks on the same node). Constraints have
three parts: a field name, an operator, and an optional parameter. The
field can be the hostname of the agent node or any attribute of the
agent node.
Docs: https://mesosphere.github.io/marathon/docs/constraints.html
#}
"constraints": [
["hostname", "MAX_PER", "{{MARATHON_GROUP_BY|default(2)|require_int(min=1)}}"]
],
{#
During an upgrade all instances of an application get replaced by a new
version. The upgradeStrategy controls how Marathon stops old versions
and launches new versions.
Docs: https://mesosphere.github.io/marathon/docs/deployments.html#rolling-restarts
#}
"upgradeStrategy": {
{#
A number between 0 and 1 that is multiplied with the instance count.
This is the minimum number of healthy nodes that do not sacrifice
overall application purpose. Marathon will make sure, during the
upgrade process, that at any point of time this number of healthy
instances are up.
#}
"minimumHealthCapacity": {{MARATHON_MIN_HEALTH_CAPACITY|default(0)|require_float(min=0, max=1)}},
{#
A number between 0 and 1 which is multiplied with the instance
count. This is the maximum number of additional instances launched
at any point of time during the upgrade process.
#}
"maximumOverCapacity": {{MARATHON_MAX_OVER_CAPACITY|default(0)|require_float(min=0, max=1)}}
},
{#
An array of checks to be performed on running tasks to determine if they
are operating as expected. Health checks begin immediately upon task
launch.
An HTTP health check is considered passing if (1) its HTTP response code
is between 200 and 399, inclusive, and (2) its response is received
within the `timeoutSeconds` period.
If a task fails more than `maxConsecutiveFailures` health checks
consecutively, that task is killed causing Marathon to start more
instances.
#}
{% block healthchecks %}
{% if MARATHON_HEALTH_CHECK_PATH is defined and MARATHON_HEALTH_CHECK_PATH %}
"healthChecks": [
{
{#
Path to endpoint exposed by the task that will provide health
status. Example: “/path/to/health”.
#}
"path": "{{MARATHON_HEALTH_CHECK_PATH}}",
{#
Protocol of the requests to be performed. One of “MESOS_HTTP”,
“MESOS_HTTPS”, “MESOS_TCP”, or “Command”.
#}
"protocol": "{{MARATHON_HEALTH_CHECK_PROTOCOL|default("MESOS_HTTP")}}",
{#
A port index of `0` tells Marathon to make requests to the
application on the first exposed port. In this case, since the
template can only ever expose a single port
(`DOCKER_EXPOSED_PORT` above) it's safe to hardcode this value.
#}
"portIndex": 0,
{#
Health check failures are ignored within this number of
seconds of the task being started or until the task becomes
healthy for the first time.
#}
"gracePeriodSeconds": {{MARATHON_HEALTH_CHECK_GRACE_PERIOD_SECONDS|default(300)|require_int(min=0)}},
{# Number of seconds to wait between health checks. #}
"intervalSeconds": {{MARATHON_HEALTH_CHECK_INTERVAL_SECONDS|default(10)|require_int(min=0)}},
{#
Number of seconds after which a health check is considered a
failure regardless of the response.
#}
"timeoutSeconds": {{MARATHON_HEALTH_CHECK_TIMEOUT_SECONDS|default(5)|require_int(min=0)}},
{#
Number of consecutive health check failures after which the
unhealthy task should be killed.
#}
"maxConsecutiveFailures": {{MARATHON_HEALTH_CHECK_MAX_CONSECUTIVE_FAILURES|default(3)|require_int(min=0)}}
}
],
{% endif %}
{% endblock %}
{#
Configures the termination signal escalation behavior of executors when
stopping tasks. Sets the number of seconds between the executor sending
SIGTERM to a task it is about to stop and then finally sending SIGKILL
to enforce the matter.
Using this grace period, tasks should perform orderly shutdown
immediately upon receiving SIGTERM.
#}
"taskKillGracePeriodSeconds": {{MARATHON_TASK_KILL_GRACE_PERIOD_SECONDS|default(10)|require_int(min=0)}}
}
Blue/Green Deployment¶
-
class
shpkpr.deployment.
BlueGreenDeployment
(marathon_client, marathon_lb_client, timeout, app_definitions, **kw)¶ BluegreenDeployment implements a blue-green deployment strategy for web-facing applications on Marathon.
It builds on the primitives provided by Marathon to provide zero-downtime deployments for HTTP services exposed to the web via Marathon-LB.
-
execute
(force=False)¶ Execute a bluegreen deployment.
The mechanics of a bluegreen deploy mean that we have to deploy each application in turn (instead of the all-at-once approach used for standard deployments).
NOTE: We could maybe be smarter here in future e.g. perhaps don’t tear down old stacks until _all_ new stacks are running so we can roll back partially successful invocations.
-
Blue/Green default template¶
shpkpr includes a set of default templates which are used when a custom template is not supplied by the user at runtime. The default template used for blue/green deployments is included below for reference purposes.
See also
- Standard default template
- Default template for standard deployments (from which this one extends).
{#
Built-In Blue-Green deployment Template
This file contains the default template used by shpkpr when performing a
blue-green deployment if no custom template is provided. The goal of
this template is to provide a comprehensive application definition that
works in a wide variety of usecases.
This template contains only those sections (blocks) which need to be
changed/overridden from the standard deployment template.
This template makes some assumptions about your application (in addition to
those made by the standard template from which this extends):
- It exposes exactly one TCP port.
- It has a domain/hostname.
- It exposes a healthcheck endpoint.
Several values are required to deploy an application using this template (in
addition to those required by the standard template):
- DOCKER_EXPOSED_PORT: Port number within the container that should be
exposed to the host.
- MARATHON_HEALTH_CHECK_PATH: The path of an HTTP endpoint exposed by the
application for healthcheck purposes.
- HAPROXY_VHOST: The hostname(s) that that will be proxied to the
application (comma separated if multiple).
#}
{% extends "marathon/default/standard.json.tmpl" %}
{% block docker_port_mappings %}
{# Applications using blue-green deployment _must_ expose a port. #}
"portMappings": [
{
"containerPort": {{DOCKER_EXPOSED_PORT|require_int}},
"hostPort": 0,
"protocol": "tcp"
}
],
{% endblock %}
{% block healthchecks %}
{# Applications using blue-green deployment _must_ define a healthcheck. #}
"healthChecks": [
{
"path": "{{MARATHON_HEALTH_CHECK_PATH}}",
"protocol": "{{MARATHON_HEALTH_CHECK_PROTOCOL|default("MESOS_HTTP")}}",
"portIndex": 0,
"gracePeriodSeconds": {{MARATHON_HEALTH_CHECK_GRACE_PERIOD_SECONDS|default(300)|require_int(min=0)}},
"intervalSeconds": {{MARATHON_HEALTH_CHECK_INTERVAL_SECONDS|default(10)|require_int(min=0)}},
"timeoutSeconds": {{MARATHON_HEALTH_CHECK_TIMEOUT_SECONDS|default(5)|require_int(min=0)}},
"maxConsecutiveFailures": {{MARATHON_HEALTH_CHECK_MAX_CONSECUTIVE_FAILURES|default(3)|require_int(min=0)}}
}
],
{% endblock %}
{% block labels %}
{#
Blue-Green deployments require a number of custom labels to be set at deploy
time. The required labels are configured below.
For full details of all available configuration options that can be set with
labels see https://github.com/mesosphere/marathon-lb/blob/master/Longhelp.md
#}
"labels": {
{#
Deployment group to which this app belongs. Used to identify
applications that should be considered when performing a blue-green
deploy.
#}
"HAPROXY_DEPLOYMENT_GROUP": "{{HAPROXY_DEPLOYMENT_GROUP|default(MARATHON_APP_ID)|slugify}}",
{#
HAProxy group per service. This allows you to assign the application to
either the `internal` or `external` group as appropriate.
#}
"HAPROXY_GROUP": "{{HAPROXY_GROUP|default("internal")}}",
{#
The hostname(s) that that will be proxied to the application (comma
separated if multiple).
#}
"HAPROXY_0_VHOST": "{{HAPROXY_VHOST}}"
{# Allow setting arbitrary labels as in the parent template. #}
{% for key, value in _all_env|filter_items("LABEL_", strip_prefix=True) %}
{% if loop.first %},{% endif %}
"{{key}}": "{{value}}"{% if not loop.last %},{% endif %}
{% endfor %}
},
{% endblock %}
Chronos deployments¶
Default template¶
shpkpr includes a set of default templates which are used when a custom template is not supplied by the user at runtime. The default template used for chronos jobs is included below for reference purposes.
{#
Built-In Job Template
This file contains the default template used by shpkpr when adding a new
Chronos job if no custom template is provided.
With sensible defaults for most options, it should be possible to deploy a
new or updated application with very minimal configuration.
This template makes some assumptions about your job:
- It is configured via environment variables (12-factor style).
- It runs in a Docker container.
4 values are required to run a job using this template:
- CHRONOS_NAME: The name of the Chronos job
- CHRONOS_OWNER: The email address of the team responsible for the job
- CHRONOS_CMD: The command that will be executed by Chronos
- DOCKER_REPOTAG: The repotag of the Docker image to be used
#}
{
{#
The job name. Must match the following regular expression: "([\w\.-]+)".
#}
"name": "{{CHRONOS_JOB_NAME}}",
{#
The email address of the person/team responsible for the job.
#}
"owner": "{{CHRONOS_OWNER}}",
{#
The actual command that will be executed by Chronos.
#}
"command": "{{CHRONOS_CMD}}",
"container": {
"type": "DOCKER",
{# The name/tag of the Docker image to use. #}
"image": "{{DOCKER_REPOTAG}}",
"parameters": [
{% block docker_labels %}
{% for key, value in _all_env|filter_items("LABEL_", strip_prefix=True) %}
{"key": "label", "value": "{{key|slugify}}={{value}}"}{% if not loop.last %},{% endif %}
{% endfor %}
{% endblock %}
],
"network": "BRIDGE"
},
{# The number of CPU shares this job needs. #}
"cpus": {{CHRONOS_CPUS|default(0.1)|require_float(min=0.1)}},
{# Amount of Mesos Memory (in MB) for this job. #}
"mem": {{CHRONOS_MEM|default(512)|require_int(min=0)}},
{# If set to "true", this job will not be run. #}
"disabled": {{CHRONOS_DISABLED|default("false")}},
{# Set the number of times the job will retry. #}
"retries": {{CHRONOS_RETRIES|default(2)}},
{#
Key value pairs that get added to the environment variables of each task
that is started by Chronos.
#}
"environmentVariables": [
{% for key, value in _all_env|filter_items("ENV_", strip_prefix=True) %}
{
"name": "{{key}}",
"value": "{{value}}"
}{% if not loop.last %},{% endif %}
{% endfor %}
],
{#
If Chronos misses the scheduled run time for any reason, it will still
run the job if the time is within this interval. Epsilon must be
formatted like an ISO 8601 Duration.
https://en.wikipedia.org/wiki/ISO_8601#Durations
#}
"epsilon": "{{CHRONOS_EPSILON|default("PT60S")}}",
{#
The scheduling for the job, in ISO8601 format. Consists of 3 parts
separated by `/`:
- The number of times to repeat the job: `Rn` to repeat `n` times, or
`R` to repeat forever
- The start time of the job. An empty start time means start
immediately. Our format is ISO8601: `YYYY-MM-DDThh:mm:ss.sTZD` (e.g.,
`1997-07-16T19:20:30.45+01:00`) where:
- `YYYY` = four-digit year
- `MM` = two-digit month (01 = January, etc.)
- `DD` = two-digit day of month (01 through 31)
- `hh` = two-digit hour in 24-hour time (00 through 23)
- `mm` = two-digit minute (00 through 59)
- `ss` = two-digit second (00 through 59)
- `s` = one or more digits representing a decimal fraction of a
second
- `TZD` = time zone designator (`Z` for UTC or `+hh:mm` or `-hh:mm`
for UTC offset)
- The run interval, defined following the "Duration" component of the
ISO8601 standard. `P` is required. `T` is for distinguishing M(inute)
and M(onth)––it is required when specifying Hour/Minute/Second. For
example:
- `P10M` = 10 months
- `PT10M` = 10 minutes
- `P1Y12M12D` = 1 year, 12 months, and 12 days
- `P12DT12M` = 12 days and 12 minutes
- `P1Y2M3DT4H5M6S` = 1 year, 2 months, 3 days, 4 hours, and 5
minutes
https://en.wikipedia.org/wiki/ISO_8601
#}
"schedule": "{{CHRONOS_SCHEDULE|default("R/2017-01-01T00:00:00.0Z/PT24H")}}",
{#
Secrets allow shpkpr to inject secret data from configured "secret
stores" at application deploy time. This allows decoupling of how
secrets are provisioned from the how applications make use of those
secrets at runtime.
#}
"secrets": {
{% for key, value in _all_env|filter_items("SECRET_", strip_prefix=True) %}
"{{key}}": {"source": "{{value}}"}{% if not loop.last %},{% endif %}
{% endfor %}
},
{# Whether the job runs in the background or not. #}
"async": false
}
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions¶
Report Bugs¶
Report bugs at https://github.com/shopkeep/shpkpr/issues.
If you are reporting a bug, please include:
- Your operating system name and version.
- Did you install with
pip
ordocker
? - Are you using a released version or a
git
checkout? - Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
Fix Bugs¶
Look through the GitHub issues for bugs. Anything tagged with “bug” is open to whoever wants to implement it.
Implement Features¶
Look through the GitHub issues for features. Anything tagged with “feature” is open to whoever wants to implement it.
Write Documentation¶
shpkpr could always use more documentation, whether as part of the official shpkpr docs, in docstrings, or even on the web in blog posts, articles, and such.
Submit Feedback¶
The best way to send feedback is to file an issue at https://github.com/shopkeep/shpkpr/issues.
If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that contributions are welcome :)
Get Started!¶
Ready to contribute? Here’s how to set up shpkpr
for local development.
Fork the
shpkpr
repo on GitHub.Clone your fork locally:
$ git clone git@github.com:your_name_here/shpkpr.git
Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:
$ mkvirtualenv shpkpr $ cd shpkpr/ $ pip install --editable .
Create a branch for local development:
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes, check that your changes pass the tests, including testing other Python versions with tox:
$ tox
To get tox, just pip install it into your virtualenv.
If you prefer, and have docker installed, you can also run the tests against all supported Python versions:
$ make test
You should also run the integration tests against a live marathon instance to manually verify that the tool still works as expected. Set the necessary environment variables as appropriate:
# URL of the Marathon API to use export SHPKPR_MARATHON_URL=http://marathon.somedomain.com:8080 # An application ID to use for testing, this should not exist on # Marathon prior to running the tests export SHPKPR_APPLICATION=my-dummy-application-for-testing # Docker repotag of the container that should be deployed to Marathon. # This container must be pullable by the mesos cluster; for testing # purposes it's probably easiest to use a container from the public # Docker hub. export SHPKPR_DOCKER_REPOTAG=goexample/outyet:latest # Port that should be exposed from the Docker container export SHPKPR_DOCKER_EXPOSED_PORT=8080 # An arbitrary label to be injected into the deploy template. This can # be any non-empty string for testing. export SHPKPR_DEPLOY_DOMAIN=somedomain.com
Then:
$ tox -e integration
Or:
$ make test.integration
If your changes are user-facing, you should update the documentation to reflect the changes you’ve made. shpkpr’s documentation is built with Sphinx and can be built using the
make
:$ pip install -r requirements-docs.txt $ make docs
While developing, you can watch the documentation for changes and rebuild as required by installing watchdog:
$ pip install watchdog $ make docs.watch
The built documentation is output to the
_build/html/
folder. The simplest way to view these docs is with Python’s built-in static webserverpython -m SimpleHTTPServer
.Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
- The pull request should include tests.
- If the pull request adds functionality, the docs should be updated. If applicable, add the feature to the list in README.rst.
- The pull request should work for Python 2.7, 3.4, 3.5 and 3.6, and for PyPy. Check https://travis-ci.org/shopkeep/shpkpr/pull_requests and make sure that the tests pass for all supported Python versions.