Lux

Gem Version

Shining some light on setting up and running various Docker things.

Lux is both a command line tool (based on Thor) and a library of useful routines that can be included in Rake tasks.

It uses the Docker API directly via the docker-api GEM and so does not require the docker client.

Installation

Add this line to your application's Gemfile:

gem 'lux'

And then execute:

$ bundle

Or install it yourself as:

$ gem install lux

Usage

If using a Docker server across the network, make sure that you have exported the environment variable DOCKER_URL (which is a true URL). Note that this is different to the Go docker client which uses DOCKER_HOST.

Command Line Tool

The lux command simplifies a number of Docker command line operations and provides unix-style commands for listing and removing images, as well as rake-like tasks for cleaning, tidying and clobbering containers and their images.

Type: lux help to get started

Handy Functions

To assist in creating Rakefiles where the location of container targets is on the Docker host use the module function Lux.dockerip as follows:

require 'lux'

DOCKER_IP = Lux.dockerip[0]

The function returns a second element which is a Ruby URI object representing the environment variable DOCKER_HOST or DOCKER_URL.

Rake Tasks

When trying to write Rakefiles that start Docker containers it is useful to have a Rake task that represents a container image. Then you can make other tasks depend on them. If the task (when invoked) checks the local Docker server for the image, and only executes the task body if the image is not found, or if the listed dependencies are out-of-date, then you can build Docker dependencies elegantly into the Rakefile.

Lux extends Rake with two new task types dockerimage and container. Both of them are specializations of the file task. They use the Docker server pointed to by the DOCKER_URL environment variable and do not query a remote registry.

dockerimage

This checks for the existence of an image with the task name in the Docker server. It uses the creation date found there as the timestamp and executes the associated action if it is out-of-date with respect to it's dependencies. This means you can rebuild the image based on other dependencies (such as the Dockerfile or content).

If you are using an image on a remote repository that you don't build locally then you can either define it and make the action a docker pull command or omit it altogether. In the former case you can then add this as a dependency to a container task to ensure that a running container always has the most up-to-date image. The latter case works as the Docker runtime will attempt to pull an image from a remote repository if it is not present locally.

container

This checks for the existence of a running container for a particular docker image. The task name is a compound name of the form containername@imagename. If the container named is not running then the action block is executed, typically it will be a Docker run command. If the container is already running then it's image is checked to see if that it is the same as specified. If not the container is destroyed and the action block executed. If the container image matches the requested one, then the creation date of the container is used as the task timestamp. This allows you to specify a dockerimage dependency and the container will then be restarted if the image is newer.

Note that you don't need a corresponding dockerimage task as the Docker runtime will attempt to pull an image from a remote repository if it is not present locally.

Examples

require 'lux/docker_tasks'

desc "Build tool image if the image is not found locally"
dockerimage 'quay.io/rasputin/tools:1.0' do |t|
  sh "docker build -t #{t.name} ."
end

desc "Build tool image if the image is not found or is older than the Dockerfile"
dockerimage 'quay.io/rasputin/tools:1.0' => 'Dockerfile' do |t|
  sh "docker build -t #{t.name} ."
end

desc "Make sure we have a running tools container"
container '[email protected]/rasputin/tools:1.0' do
  sh "docker run --name tools -d quay.io/rasputin/tools:1.0"
end

desc "Make sure we have a running tools container with the most up-to-date image"
container '[email protected]/rasputin/tools:1.0' => 'quay.io/rasputin/tools:1.0' do
  sh "docker run --name tools -d quay.io/rasputin/tools:1.0"
end

# A set of tasks to ensure you always have the latest version of a container
# stored on a remote registry

require 'lux/docker_tasks'

desc "Always see if there is a new version of busybox"
task :getbusybox do
  sh "docker pull busybox"
end

desc "Create a dockerimage task so we can depend on it"
dockerimage 'busybox' => :getbusybox

desc "Always run with the latest version"
container 'busy@busybox' => 'busybox' do
  sh "docker run -it --name busy busybox"
end

Tracing

When the Rake trace option is enabled the destruction (or attempted destruction if it's a dry-run) is recorded.

Namespaces

When using the dockerimage and container tasks with explicit tags (ie. containing a semi-colon), then normal Rake namespace rules apply: The repository name will be considered the namespace and the tag the taskname within the namespace. If this dockerimage task is declared in the same execution alongside a matching explicit namespace and task declaration, then the actions will accumulate.

task :default => 'busybox:greenest'

dockerimage 'busybox:greenest' do
  puts "Build busybox:greenest Docker image"
end

namespace :busybox do
  task :greenest do
    puts "Do the greenest task in the busybox namespace"
  end
end

This prints:

$ rake -f Rakefile.demo
Build busybox:greenest Docker image
Do the greenest task in the busybox namespace

Development

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release to create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

  1. Fork it ( https://github.com/townsen/lux/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request