Crane is a Docker orchestration tool similar to Docker Compose
with extra features and ultra-fast bind-mounts on Mac.

Command Line Interface

Crane is a very light wrapper around the Docker CLI. This means that most commands just call the corresponding Docker command, but for all targeted services. The basic format is crane <command> <target>, where <command> corresponds to a Docker command, and <target> either is a single container or a group.

When executing commands, keep the following 2 rules in mind:

  1. Crane will apply the command ONLY to the target
  2. Crane will do with other services in the configuration whatever it takes in order for (1) to succeed

As an example, imagine you have a container web depending on container database. When you execute crane run web, then Crane will start database first, then run web (recreating web if it already exists). If you would like to apply the command to all dependencies of the target, you can use --extend (then database would be recreated as well).

Following is a list of commands that map 1:1 to Docker commands:

Command Explanation and Options
create Creates containers. Any existing containers are removed first.
run Runs containers. Any existing containers are removed first. If target is not set to detach explicitly in the configuration, it will attach to it (can be disabled by passing --detach).
rm Removes containers if they exist. Use --force to kill running containers first, and --volumes to remove associated volumes, too.
start Starts containers if they are not running yet. Non-existing containers are created first.
stop Stops containers if they are running.
kill Kills containers if they are running.
pause Pauses containers if they are running.
unpause Resumes containers if they are paused.
exec Starts container first if not running yet, and adds --tty --interactive to docker exec by default. Optionally specify --privileged and --user USER.
logs Logs of containers are multiplexed. Use --follow to follow log output. --tail, --timestamps, --colorize and --since TIMESTAMP are supported, too.
stats Display resource information about the containers. Use --no-stream to disable auto-refresh.
push Pushes images of the targeted services.
pull Pulls images for the targeted services.

In addition, Crane provides the following commands:

Command Explanation and Options
provision Calls Docker's pull if no Dockerfile is specified. Otherwise it builds the image, optionally with disabled cache by passing --no-cache.
up / lift Provisions and runs containers in one go. Use --no-cache to disable build cache. If target is not set to detach explicitly in the configuration, it will attach to it (can be disabled by passing --detach).
status Displays information similar to docker ps for the given target.
generate Passes the targeted portion of the config through given --template and outputs the result to STDOUT or given --output file.

Excluding/limiting targets

If you want to exclude a container or a whole group from a Crane command, you can specify this with --exclude <reference> (or via CRANE_EXCLUDE). The flag can be repeated to exclude several services or groups (use a multi-line environment variable value to pass several references via CRANE_EXCLUDE).

Excluded services' declaration and references in the configuration file will be completely ignored, so their dependencies will also be excluded (unless they are also required by other non-excluded services).

Apart from excluding services, it is also possible to limit the target to just one container or group with --only (or via CRANE_ONLY). The flag cannot be repeated. Containers outside the targets will not be considered by Crane then.

Ad hoc commands

If you pass a command on the CLI to up/lift or run, Crane will add a timestamp to the container name (e.g. foo will become foo-1447155694523), making it possible to have multiple containers based on the same Crane config.

Container Prefixes

The CLI accepts a global --prefix flag, which is just prepended to each container, network and volume name. A common use case for this feature is to launch a set of services in parallel, e.g. for CI builds. Container prefixes can also be supplied by the CRANE_PREFIX environment variable.

Another option is to set a permanent prefix in the configuration itself. This can be done via the top-level setting prefix: "foo". It is also possible to automatically use the name of the folder where the configuration file is located to match docker-compose's behaviour. Simply set prefix: true in the configuration.

 

Configuration

The configuration defines a map of services in either JSON or YAML. Crane can read from multiple configuration files and merge them. By default it reads (in this order) docker-compose.yml, docker-compose.override.yml, crane.yml and crane.override.yml. This can be overwritten by passing --config (multiple times if desired) or setting CRANE_CONFIG (use colons to specify multiple files). If the given paths are relative, Crane searches for the configuration in the current directory, then recursively in the parent directory.

An example config looks like this:

services:
  cranedev:
    image: michaelsauter/golang:1.7
    rm: true
    interactive: true
    tty: true
    volume: ["$GOPATH:/go"]
    workdir: /go/src/github.com/michaelsauter/crane
    command: ["sh"]

The map of services consists of the name of the container mapped to the container configuration, which is made up of the following keys. You may specify either the "Compose key" or the "Crane key", both have the same effect.

Crane / Compose key Type Notes
imagestringIf not given, the service name will be used
buildobjectMaps to docker build. Keys:
  • context (string)
  • file/dockerfile (string)
  • build-arg/args (array/mapping)
requires/depends_onarray Container dependencies
add-host/extra_hostsarray
blkio-weightinteger
blkio-weight-devicearray
cap-add/cap_addarray
cap-drop/cap_droparray
cgroup-parent/cgroup_parentstring
cidfilestring
cpu-periodinteger
cpu-quotainteger
cpusetinteger
cpu-sharesinteger
detach boolean
detach-keysstring
devices/devicearray
device-read-bpsarray
device-read-iopsarray
device-write-bparray
device-write-iopsarray
dnsarray
dns-optarray
dns-search/dns_searcharray
entrypointstring
env/environmentarray/mapping Can be declared as a string array with "key[=value]" items or a string-to-string mapping where each key: value will be translated to the corresponding "key=value" string.
env-file/env_filearray
exposearray
group-add/group_addarray
health-cmd
healthcheck>test
stringArray form of docker-compose is not (yet) supported.
health-interval
healthcheck>interval
string
health-retries
healthcheck>retries
integer
health-timeouts
healthcheck>timeout
string
hostnamestring
interactive/stdin_open boolean
ipstring
ip6string
ipcstring The container:id syntax is not supported, use container:name if you want to reuse another container IPC.
isolationstring
kernel-memorystring
label/labelsarray/mapping Can be declared as a string array with "key[=value]" items or a string-to-string mapping where each key: value will be translated to the corresponding "key=value" string.
label-filearray
link/linksarrayDoubles as a dependency when used without a custom network.
log-driverstringUse logging for docker-compose.
log-optarrayUse logging for docker-compose.
lxc-confarray
mac-address/mac_addressstring
memorystring
memory-reservationstring
memory-swapstring
memory-swappinessinteger
net/network_modestringThe container:id syntax is not supported, use container:name if you want to reuse another container network stack.
net-aliasarray
oom-kill-disableboolean
oom-score-adjstring
pidstring
privilegedboolean
publish/portsarray
publish-allboolean
read-only/read_onlyboolean
restartstring
rmboolean
security-opt/security_optarray
shm-size/shm_sizestring
sig-proxyboolean true by default
stop-signal/stop_signalstring
tmpfsarray
ttyboolean
ulimitarray
userstring
utsstring
volume/volumesarray In contrast to plain Docker, the host path can be relative.
volume-driver/volume_driverstring
volumes-fromarray
workdir/working_dirstring
cmd/commandarray/string Command to append to docker run (overwriting CMD).

See the Docker documentation for more details about the parameters.

Networks

Docker networks are supported via the top-level config networks. As links are not strict dependencies for containers attached to a user-defined network (but simply aliases), requires/depends_on can be used instead to indicate that a container must be started for another one to be functional. Networks are automatically created by Crane when necessary, and never cleaned up. When a prefix is used, it is also applied to the network.

services:
  foo:
    requires: ["bar"]
    net: qux
  bar:
    net: qux
networks:
  qux:

Volumes

Docker volumes are supported via the top-level config volumes. Volumes are automatically created by Crane when necessary, and never cleaned up. When a prefix is used, it is also applied to the volume.

services:
  foo:
    volume: ["bar:/path"]
volumes:
  bar:

Groups

Next to services, you can also specify groups, and then execute Crane commands against those groups. If you do not specify any target, the command will apply to all services. However, you can override this by specifying a default group. Groups of services can be specified like this (YAML shown):

services:
  database1:
    ../..
  database2:
    ../..
  service1:
    ../..
  service2:
    ../..
groups:
  default: ["service1", "database1"]
  databases: ["database1", "database2"]
  services: ["service1", "service2"]

This could be used like so: crane provision service1, crane run databases or crane up services. crane status is an alias for crane status default in this example. If default were not specified, then crane up would start database1, database2, service1 and service2.

Hooks

In order to run certain commands before or after key lifecycle events of containers, hooks can be declared in the configuration. They are run synchronously on the host where Crane is installed, outside containers, via an exec call. They may interrupt the flow by returning a non-zero status. If shell features more advanced than basic variable expansion is required, you should explicitly spawn a shell to run the command in (sh -c 'ls *').

Hooks are declared at the top level of the configuration, under the hooks key. See YAML example below:

services:
  service1:
    image: busybox
    detach: true
    command: ["sleep", "50"]
  service2:
    image: busybox
    detach: true
    command: ["sleep", "50"]
  service3:
    image: busybox
    detach: true
    command: ["sleep", "50"]
groups:
  foo:
    - service1
    - service2
  bar:
    - service2
    - service3
hooks:
  foo:
    post-start: echo container from foo started
  bar:
    post-stop: echo container from bar stopped
  service3:
    post-stop: echo container service3 stopped

Hooks can be defined on a group level (foo, bar) so that they apply to all services within that group, or directly on a container (service3). At most one hook can be registered per container and per event. When more than one hook is found for a given container and a given event, the following rules apply:

  • Container-defined hooks have priority over group-defined ones, so in the example above, only "container service3 stopped" will be echoed when stopping service3.
  • A fatal error will be raised at startup when 2 group-inherited hooks conflict. This is not the case in the previous example; even though foo and bar both contain service2, the hooks they declare are disjoint.

The following hooks are currently available:

  • pre-build: Executed before building an image
  • post-build: Executed after building an image
  • pre-start: Executed before starting or running a container
  • post-start: Executed after starting or running a container
  • pre-stop: Executed before stopping, killing or removing a running container
  • post-stop: Executed after stopping, killing or removing a running container

Every hook will have the name of the container for which this hook runs available as the environment variable CRANE_HOOKED_CONTAINER.

A typical example for a hook is waiting for some service to become available:

until `docker exec postgres-build psql -c 'select now()' 1>/dev/null 2>/dev/null`
  do echo Waiting for Postgres to start ...
  sleep 1
done

 

Accelerated Mounts on Mac

Crane can optionally make use of Unison to have faster bind-mounts between the host and Docker for Mac. This feature is available in the PRO version only. Example configuration:

services:
  hello:
    image: alpine
    rm: true
    interactive: true
    tty: true
    volume:
      - "foo:/bar"
accelerated-mounts:
  "foo:/bar":

When Crane is asked to run the hello service, it automatically sets up an accelerated mount for foo:/bar. Behind the scenes, Crane starts a sync container (which is using Unison internally) connected to a plain Docker volume, which is then mounted to /bar in the hello container. From a user perspective however, everything looks as if the host directoy foo is directly bind-mounted to /bar in the container.

To reduce the amount of syncing that needs to be done, different containers or multiple instances of the same container definition share the same sync container and volume.

It is possible to customize each accelerated mount:

  • flags: Defaults to ["-auto", "-batch", "-ignore='Name {.git}'", "-confirmbigdel=false", "-contactquietly", "-prefer=newer"], but can be overriden using anything that you can pass to unison, see the manual.
  • uid/gid: Defaults to 0/0. Set this to the user/group ID the consuming container expects, e.g. 1000/1000.

To debug what is happening when the sync is not behaving as expected, you can use:
docker logs -f <sync-container>.

 

Advanced Usage

Parallelism

By default, Crane executes all commands sequentially. However, you might want to increase the level of parallelism for network-heavy operations, in order to cut down the overall run time. The --parallel/-l flag allows you to specify the level of parallelism for commands where network can be a bottleneck (namely provision and up). Passing a value of 0 effectively disable throttling, which means that all provisioning will be done in parallel.

Override image tag

By using a the --tag flag, it is possible to globally overrides image tags. If you specify --tag 2.0-rc2, an image name repo/app:1.0 is treated as repo/app:2.0-rc2. The CRANE_TAG environment variable can also be used to set the global tag.

Generate command

The generate command can transform (part of) the configuration based on a given template, making it easy to re-use the configuation with other tools. --template is a required flag, which should point to a Go template. By default, the output is printed to STDOUT. It can also be written to a file using the --output flag. If the given filename contains %s, then multiple files are written (one per container), substituting %s with the name of the container. For each container, an object of type ContainerInfo is passed to the template. If one file is generated for all targeted containers, a list of containers is located under the key Containers. This feature is experimental, which means it can be changed or even removed in every minor version update.

YAML alias/merge

YAML gives you some advanced features like alias and merge. They allow you to easily avoid duplicated code in your crane.yml file. As a example, imagine you need to define 2 different services: web and admin. They share almost the same configuration except the cmd declaration. And imagine you also need 2 instances for each one for using with a node balancer. Then you can declare them as simply:

services:
  web1: &web
    image: my-web-app
    link: ["db:db"]
    ...
    cmd: web
  web2: *web

  admin1: &admin { <<: *web, command: admin }
  admin2: *admin

As a summary, &anchor declares the anchor property, *alias is the alias indicator to simply copy the mapping it references, and <<: *merge includes all the mapping but let you override some keys.

Variable Expansion

Basic environment variable expansion (${FOO}, $FOO) is supported throughout the configuration, but advanced shell features such as command substitution ($(cat foo), `cat foo`) or advanced expansions (sp{el,il,al}l, foo*, ~/project, $((A * B)), ${PARAMETER#PATTERN}) are not as the Docker CLI is called directly. Use $$ for escaping a raw $.

A use case for this is to pass an environment variable from the CLI to the container "at runtime". If your configuration contains env: ["FOO=$FOO"], then FOO=hello crane run ubuntu bash has access to $FOO with the value of hello.

 

Compatibility Notes

While Crane can read Docker Compose configuration files (version 3), it differs in behaviour. Please make sure to read through the docs to compare. In addition, not all configuration options are supported yet.

Partial support:
  • networks - only one network is supported at the moment, e.g. networks: ['foo']
  • depends_on - accepts only array at the moment
  • build - only accepts an object, not a string (see #327)
No support:

Crane continues to work if any of those options are present, but they will have no effect.

  • external_links
  • domainname
  • ulimits
  • container_name
  • deploy/secrets
Copyright © 2018 Michael Sauter — Contact