Athlete
Athlete is a Capistrano-like deployment tool for deploying Docker containers into Marathon.
Warning: Athlete is at best a beta. It may not understand all Marathon responses, it lacks some features, and it doesn't do some basic error checking. Use at your own risk. Pull requests welcomed. YMMV.
Why Athlete exists
If you're a Ruby developer, Capistrano makes things so fantastically easy that you miss it when you start to package apps as Docker containers.
Athlete was written to become, internally, a simple Capistrano-like tool that allows developers to quickly and easily deploy to our Marathon cluster.
Features
- Simple DSL for defining Docker builds and deployments
- Detects deployment failures
- Simple (~750 LoC)
- Allows Marathon properties to be set outside deployments or forced
Installation
Add these lines to your application's Gemfile, in the :development
group:
group :development do
... some other gems ...
gem 'athlete'
end
And then execute:
bundle install --binstubs
Athlete isn't required at runtime.
Usage
Athlete performs two actions: building, and deploying.
An Athlete build runs docker build
, tags the image appropriately
(including the registry name if you're using a private registry),
and the pushes it to the registry. This push step can be skipped.
An Athlete deploy uses Marathon's REST API to deploy one of the Docker containers built in the build step or any arbitrary Docker container - you are not limited to only deploying containers you built.
Command line usage
(You may want to read the configuration section first, since you need a configuration before you can do anything.)
To get help with the CLI:
bin/athlete help
To build all builds in the configuration file:
bin/athlete build
To build all builds put not push them:
bin/athlete build --no-push
To deploy all deployments in the configuration file:
bin/athlete deploy
Configuration
Athlete is configured using a simple DSL.
By default, it expects the configuration file to be called athlete.rb
and
to be placed in the config
directory relative to the base of your app.
Todo: write a command to produce a template athlete.rb file
A build takes some information about the name you want to give your container, how it should be versioned, and what registry it should be pushed to.
A deployment takes some information about where to deploy to, how many instances to run, how much CPU and memory resource to allocate, and so on.
Here's an example of a simple image build and deploy:
Athlete::Build.define('my-image') do
registry 'my-registry:5000'
version '1'
end
Athlete::Deployment.define('my-app') do
marathon_url 'http://marathon:8080'
# image_name 'ubuntu:12.04'
# command ['/bin/sleep', '600']
# arguments ['something']
# environment_variables {'RACK_ENV' => 'production'}
build_name 'my-image'
cpus 1, :override
memory 128, :inherit
instances 1, :override
minimum_health_capacity 0.5, :inherit
end
This will build an image tagged my-registry:5000/my-image:1
, remembering
that the Docker image conventions are: registry_url:registry_port/image_name:image_version
.
Once built, it will deploy a container based on that image to the Marathon host
at http://marathon:8080
. It will request 1 CPU and 128MB of RAM from Marathon,
and run one instance of the container. See below for details of what the property
minimum_health_capacity
does.
Build DSL reference
Property | Required | Description |
---|---|---|
registry | no | The Docker registry to push to - if unspecified, we use the Docker Hub. |
version | yes | How to version the image; this can be any stringifiable object, or the symbol :git , which will version using the output of git rev-parse HEAD |
Deployment DSL reference
Some properties are defined with an extra parameter which is either
:override
or :inherit
- in the above example, instances 1, :override
is specified with :override
, and memory 128, :inherit
is specified with
:inherit
.
These properties are ones that can be varied in Marathon through other means.
For example, you may have a separate 'scaling' system that changes the number
of instances of a container in response to some parameter. Let's say that it
has acted to increase the number of instances to 5, and our athlete.rb
has
this line in the deployment section:
instances 1, :override
When you deploy, Athlete will see that Marathon's currently running value for
instances of the app is 5, and that you set it to 1 in your deployment configuration
with :override
, and it will force there to be only 1 instance after the deployment.
This would be non-ideal, since your scaling system is 'authoritative' for this property - it decides how many instances should run. Resetting it when you deploy could break production!
If you had set the instances property to be :inherit
, like so:
instances 1, :inherit
Then when you run a deploy, Athlete will completely ignore the instance value set in the configuration file, and just trust whatever is currently set in Marathon.
The only time all parameters will be sent to Marathon is when an app does not already exist. In that case, we have to supply some initial values to get the app going.
marathon_url
required: yes override/inherit: no
The URL to the Marathon REST API endpoint you're using.
build_name
required: yes (if not supplying image_name
)
override/inherit: no
The build name to get Docker image information from. You must
reference a build defined earlier in the athlete.rb
file. The name of a build
is the string supplied to the define
call. E.g.
Athlete::Build.define('my-image') do
registry 'my-registry:5000'
version '1'
end
You would reference this by setting:
build_name 'my-image'
in your deployment definition.
It is required if you are not specifying image_name
.
image_name
required: yes (if not supplying build_name
)
override/inherit: no
The Docker image name to deploy. You must specify the entire image name,
including whatever tagged version you want. E.g. ubuntu:12.04
.
It is required if you are not specifying build_name
.
command
required: no override/inherit: no
The command to run inside the Docker container. This will override
the CMD
section of the Dockerfile.
arguments
required: no override/inherit: no
Arguments to supply to the container's ENTRYPOINT. This should be an array of strings.
cpus
required: yes (on a cold deploy) override/inherit: yes
CPU resource to request for this app. This can be a fractional value (e.g. 0.1).
memory
required: yes (on a cold deploy) override/inherit: yes
Memory to request for this app in MB.
environment_variables
required: no override/inherit: no
Environment variables to pass into the container at startup - must be specified
as a hash of ENV_VAR_NAME => ENV_VAR_VALUE
.
instances
required: no override/inherit: yes
Number of instances of the container to run.
minimum_health_capacity
required: no override/inherit: yes
This description is taken from the Marathon documentation.
During an upgrade all instances of an application get replaced by a new version. The minimumHealthCapacity defines the minimum number of healthy nodes, that do not sacrifice overall application purpose. It is a number between 0 and 1 which is multiplied with the instance count. The default minimumHealthCapacity is 1, which means no old instance can be stopped, before all new instances are deployed. A value of 0.5 means that an upgrade can be deployed side by side, by taking half of the instances down in the first step, deploy half of the new version and then take the other half down and deploy the rest. A value of 0 means take all instances down immediately and replace with the new application.
volumes
An array of hashes, indicating how to mount volumes inside the container, specified like this:
[
{
"containerPath" => "/somepath",
"hostPath" => "/some/other/path",
"mode" => "RW"
}
]
Contributing
- Fork it ( https://github.com/forward3d/athlete/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new pull request
Acknowledgements
This repository includes code from the marathon_client gem. It is included with Athlete as Athlete requires some unreleased functionality, and some additional functionality, and it is not possible (rightly!) to make gems dependent on git repositories.
In accordance with the MIT license, the license information from marathon_client is included here,
and in the lib/marathon
directory, which are files covered by the below license:
Copyright (c) 2013 Tobi Knaup
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.