SkillAgentSearch skills...

Centurion

A mass deployment tool for Docker fleets

Install / Use

/learn @newrelic/Centurion
About this skill

Quality Score

0/100

Category

Operations

Supported Platforms

Universal

README

[!WARNING] This project is archived and no longer actively maintained. While the code remains available for reference, no new features or bug fixes are planned.

Centurion

A deployment tool for Docker. Takes containers from a Docker registry and runs them on a fleet of hosts with the correct environment variables, host volume mappings, and port mappings. Supports rolling deployments out of the box, and makes it easy to ship applications to Docker servers.

We're using it in our production infrastructure.

Centurion works in a two part deployment process where the build process ships a container to the registry, and Centurion ships containers from the registry to the Docker fleet. Registry support is handled by the Docker command line tools directly so you can use anything they currently support via the normal registry mechanism.

If you haven't been using a registry, you should read up on how to do that before trying to deploy anything with Centurion.

Commercial Docker Registry Providers:

Open-source:

Status

This project is archived and no longer maintained. No new feature work or bug fixes are planned. The initial commit on GitHub contains one roll-up commit of all our internal code. See the CONTRIBUTORS file for the contributors to the original internal project.

Installation

Centurion is a Ruby gem. It assumes that you have a working, modern-ish Ruby (1.9.3 or higher). On Ubuntu 12.04 you can install this with the ruby-1.9.1 system package, for example. On OSX this is best accomplished via rbenv and ruby-build which can be installed with Homebrew or from GitHub.

Once you have a running, modern Ruby, you simply:

$ gem install centurion

With rbenv you will now need to do an rbenv rehash and the commands should be available. With a non-rbenv install, assuming the gem dir is in your path, the commands should just work now.

Configuration

Centurion expects to find configuration tasks in the current working directory tree.

We recommend putting all your configuration for multiple applications into a single repo rather than spreading it around by project. This allows a central choke point on configuration changes between applications and tends to work well with the hand-off in many organizations between the build and deploy steps. If you only have one application, or don't need this you can decentralize the config into each repo.

It will look for configuration files in either ./config/centurion or ..

The pattern at New Relic is to have a configs repo with a Gemfile that sources the Centurion gem. If you want Centurion to set up the structure for you and to create a sample config, you can simply run centurionize once you have the Ruby Gem installed.

Centurion ships with a simple scaffolding tool that will setup a new config repo for you, as well as scaffold individual project configs. Here's how you run it:

$ centurionize -p <your_project>

centurionize relies on Bundler being installed already. Running the command will have the following effects:

  • Ensure that a config/centurion directory exists
  • Scaffold an example config for your project (you can specify the registry)
  • Ensure that a Gemfile is present
  • Ensure that Centurion is in the Gemfile (if absent it just appends it)

Any time you add a new project you can scaffold it in the same manner even in the same repo.

Writing configs

If you used centurionize you will have a base config scaffolded for you. But you'll still need to specify all of your configuration.

Configs are in the form of a Rake task that uses a built-in DSL to make them easy to write. Here's a sample config for a project called "radio-radio" that would go into config/centurion/radio-radio.rake:

namespace :environment do
  task :common do
    set :image, 'example.com/newrelic/radio-radio'
    host 'docker-server-1.example.com'
    host 'docker-server-2.example.com'
  end

  desc 'Staging environment'
  task :staging => :common do
    set_current_environment(:staging)
    env_vars YOUR_ENV: 'staging'
    env_vars MY_DB: 'radio-db.example.com'
    host_port 10234, container_port: 9292
    host_port 10235, container_port: 9293
    host_volume '/mnt/volume1', container_volume: '/mnt/volume2'
  end

  desc 'Production environment'
  task :production => :common do
    set_current_environment(:production)
    env_vars YOUR_ENV: 'production'
    env_vars MY_DB: 'radio-db-prod.example.com'
    host_port 22234, container_port: 9292
    host_port 23235, container_port: 9293
    command ['/bin/bash', '-c', '/path/to/server -e production']
  end
end

This sets up a staging and production environment and defines a common task that will be run in either case. Note the dependency call in the task definition for the production and staging tasks. Additionally, it defines some host ports to map, sets which servers to deploy to, and sets a custom command. Some configuration will be provided to the containers at startup time, in the form of environment variables.

Most of the DSL items (host_port, host_volume, env_vars, host) can be specified more than once and will append to the configuration. However, there can only be one command; the last one will take priority.

You can also assign lambdas to environment variables. The lambda function will be invoked by Centurion during deploy time with server_hostname as its only argument. The server_hostname yielded will be the same string as was specified in the host argument for the server currently being deployed to.

This is useful to assign a sticky identity for each of the containers in the deploy. For example, a hash mapping hostname to another string which is different on each host.

  desc 'Host-specific env vars'
  task :production => :common do
    env_vars MEMBER_ID: lambda do |hostname| 
        {
          'web-server-1.company.net' => 'machine1'
          'web-server-2.company.net' => 'machine2'
        }[hostname]
      end
  end

You can cause your container to be started with a specific DNS server IP address (the equivalent of docker run --dns 172.17.42.1 ...) like this:

  task :production => :common do
    set :dns, [ '172.17.42.1' ]
    # ...
  end

Container Names

This is the name that shows up in the docker ps output. It's the name of the container, not the hostname inside the container. By default the container will be named using the name of the project as the base of the name.

If you want to name your container something other than the project name, use the name setting. The actual name for the created container will have a random hex string appended, to avoid name conflicts when you repeatedly deploy a project:

  task :common do
    set :name, 'backend'
    # ...
  end

With this, the container will be named something like backend-4f692997.

Container Labels

You may add arbitrary labels to your containers by calling labels with a hash. The call is cumulative, so you may express patterns like:

namespace :environment do
  task :common do
    set :image, 'example.com/newrelic/radio-radio'
    host 'docker-server-1.example.com'
    labels team: 'radio-ops'
  end

  desc 'Staging environment'
  task :staging => :common do
    labels environment: 'radio-staging'
    env_vars YOUR_ENV: 'staging'
  end
end

This would result in the container having two labels, as shown in a docker inspect example:

  "Labels": {
    "team": "radio-ops",
    "environment": "radio-staging"
  }

Hash keys and values will be stringified, so you may pass any object with a #to_s method.

Container Hostnames

If you don't specify a hostname to use inside your container, the container will be given a hostname matching the container ID. This probably is good for a lot of situations, but it might not be good for yours. If you need to have a specific hostname, you can ask Centurion to do that:

set :container_hostname, 'yourhostname'

That will make all of your containers named 'yourhostname'. If you want to do something more dynamic, you can pass a Proc or a lambda like this:

set :container_hostname, ->(hostname) { "#{hostname}.example.com" }

The lambda will be passed the current server's hostname. So, this example will cause ".example.com" to be appended to the hostname of each Docker host during deployment.

If you want to restore the old behavior from Centurion 1.6.0 and earlier, you can do the following:

set :container_hostname, ->(hostname) { hostname }

That will cause the container hostname to match the server's hostname.

Network modes

You may specify the network mode you would like a container to use via:

set :network_mode, 'networkmode'

Docker (and therefore Centurion) supports one of bridge (the default), host, and container:<container-id> for this argument.

In host and container... network modes, you may specify a host_port, container_port mapping, however the port numbers will only be used for container health checks. The mapping itself, while still passed via the API, will be ignored by Docker.

PID modes

You may specify the PID mode you would like a container to use via:

set :pid_mode, 'pidmode'

Docker (and therefore Centurion) supports one of nothing (the default), host, and container:<container-id> for this argument.

CGroup Resource Constraints

Limits on memory and CPU can be specified with the memory and cpu_shares settings. Both of these expect a 64-bit integer descri

View on GitHub
GitHub Stars1.8k
CategoryOperations
Updated16d ago
Forks113

Languages

Ruby

Security Score

95/100

Audited on Mar 18, 2026

No findings