/decking-example

A very simple example project which uses decking

Primary LanguageJavaScript

Example Decking Repository

Clusters are defined using decking, a Node.js tool to create, manage and run clusters of Docker containers.

Install it with npm install -g decking.

Walkthrough

Build images

First, pre-emptively seed the remote images we'll use. Decking will do this for you, but is mute during the execution. Doing it this way means you see all its happy little progress reassurances:

docker pull makeusabrew/mongodb
docker pull makeusabrew/redis
docker pull dockerfile/nodejs

This will take a while. Next, build the images:

$ decking build all
Looking up build data for decking/example-api
Building image decking/example-api from ./docker/api/Dockerfile
Uploading compressed context...
Step 0 : FROM dockerfile/nodejs
 ---> 9a76e1635147
... (snip) ...
Removing intermediate container 3bcf36cada57
Successfully built b8a5c36592d5

You can also single out an image from the images section of the decking.yaml: eg. decking build decking/example-api. Decking is performing the equivalent of running docker build -t decking/example-api ./docker/api

Create containers

$ decking create dev
Using overrides from group 'dev'
redis.dev    creating... ✔
mongodb.dev  creating... ✔
api.dev      creating... ✔
web.dev      creating... ✔
admin.dev    creating... ✔

They've been created, but aren't running:

$docker ps -a
CONTAINER ID        IMAGE                          COMMAND                CREATED             STATUS                       PORTS               NAMES
72e57ceb783d        decking/example-admin:latest   "node /app/src/admin   5 seconds ago       Exited (143) 3 seconds ago                       admin.dev
b791f1460cc7        decking/example-web:latest     "node /app/src/web"    5 seconds ago       Exited (143) 3 seconds ago                       web.dev
a75b1555ed95        decking/example-api:latest     "node /app/src/api"    5 seconds ago       Exited (143) 3 seconds ago                       admin.dev/api,api.dev,web.dev/api
e166f025ce43        makeusabrew/mongodb:latest     "usr/bin/mongod --no   5 seconds ago       Exited (0) 3 seconds ago                         admin.dev/api/db,api.dev/db,mongodb.dev,web.dev/api/db
88f4f3665e14        makeusabrew/redis:latest       "/redis-2.8.0/src/re   6 seconds ago       Exited (0) 3 seconds ago                         admin.dev/api/redis,api.dev/redis,redis.dev,web.dev/api/redis

Start the containers

$ decking start dev
Using overrides from group 'dev'
redis.dev    starting... ✔
mongodb.dev  starting... ✔
api.dev      starting... ✔
web.dev      starting... ✔
admin.dev    starting... ✔

Now they are running. Check the port mappings look like those here: the ports are both open and published to the docker container.

$ docker ps -a
CONTAINER ID        IMAGE                          COMMAND                CREATED             STATUS              PORTS                    NAMES
72e57ceb783d        decking/example-admin:latest   "node /app/src/admin   11 seconds ago      Up 1 seconds        0.0.0.0:8887->8887/tcp   admin.dev
b791f1460cc7        decking/example-web:latest     "node /app/src/web"    11 seconds ago      Up 1 seconds        0.0.0.0:8888->8888/tcp   web.dev
a75b1555ed95        decking/example-api:latest     "node /app/src/api"    11 seconds ago      Up 1 seconds        0.0.0.0:7777->7777/tcp   admin.dev/api,api.dev,web.dev/api
e166f025ce43        makeusabrew/mongodb:latest     "usr/bin/mongod --no   11 seconds ago      Up 2 seconds        27017/tcp                admin.dev/api/db,api.dev/db,mongodb.dev,web.dev/api/db
88f4f3665e14        makeusabrew/redis:latest       "/redis-2.8.0/src/re   12 seconds ago      Up 2 seconds        6379/tcp                 admin.dev/api/redis,api.dev/redis,redis.dev,web.dev/api/redis

Curl the "API" to get the visitors count (none, yet):

curl http://localhost:7777/visitors
{"count":"0"}

Visiting the "website" increments the visitors count by making a backend call to the API, which in turn increments a counter in redis:

$ curl http://localhost:8888/
You are visitor number 1 since the server started!
$ curl http://localhost:8888/
You are visitor number 2 since the server started!
  # Hit the API to see the visitors count
$ curl http://localhost:7777/visitors
{"count":"2"}
  # Faking a web hit of our own
$ curl -X PUT http://localhost:7777/visitors
{"count":"3"}
  # "Visiting" the admin dashboard:
  curl http://localhost:8887/visits

Decking files: images, containers, groups and clusters

images:

Specifies the name and path of local images to build.

images:
  "decking/example-api":   "./docker/api"
  "decking/example-admin": "./docker/admin"
  "decking/example-web":   "./docker/web"

In this example, the image named decking/example-api will be built using the Dockerfile within ./docker/api.

containers:

Configures the containers. See the detailed description of the container directives below.

clusters: map containers to groups

The clusters section defines what containers should be instantiated for each group. For instance, in the example decking file the production group only has api and web, whereas development runs api, web and admin containers.

groups: define environment-specific settings

Override settings for containers within the particular group. For instance, in the example decking.yaml, each of the environments defines their own port mapping, to avoid collisions.

Decking container directives

  • env -- Environment variables
  • dependencies -- Containers to link; these will be created first.
  • port -- Ports to publish to the docker machine
  • privileged -- Run in privileged mode
  • mount -- Volumes to share among other containers or the docker machine
  • mount-from -- Mounts all volumes defined on the given container
  • image -- The docker image to use
  • extra -- Other arguments to supply the entrypoint script

The only required directive for a container is image

image: The docker image to use

If the label is one of those in the images section, the container will use the locally-built image. Otherwise, it will be fetched from a docker repo.

Pulling in a remote image can take a long time. Since decking is driving docker's API directly, it can be hard to tell if decking is hung or just waiting. To avoid this tsoris, run docker pull [image/name] beforehand for any foreign images.

dependencies: Containers to link

Listing another container as a dependencies will (a) ensure it is constructed first, and (b) make it a linked container. This lets the api machine talk to redis and mongodb without knowing where or why or how they were created. The container name will be scoped by the cluster. In the dev cluster, these lines will link to the redis.dev and mongodb.dev containers.

containers:
  api:
    dependencies:
      - redis
      - mongodb:db

port: Publish ports to the docker machine

Pairs a port on the docker host machine with a port exposed in the container (using EXPOSE [portnum] in the Dockerfile).

containers:
  api:
    port:       ["8100:7777"]

The docker host machine's port is listed first -- the above pairs port 7777 on the api container with port 8100 on the docker machine. If you are running in a VM, 8100 is the one you'll forward.

env: Environment variables

Specify environment variables with a "VARNAME=value" string. Decking will prompt you for env vars with a value of '-' (a single unquoted dash)

groups:
  dev:
    options:
      env:  ["NODE_ENV=build"]
    containers:
      api:
        env: ["FAVORITE_COLOR=-"]

In this example, all machines in the dev group will have $NODE_ENV set to build. Additionally, when the api container is launched, decking will prompt for the value of the FAVORITE_COLOR variable.

mount: Share volumes among other containers or the docker machine

With just a path, this defines a volume for other containers to mount using he mount-from directive.

With a colon ("localname:remotename"), docker will mount given directory path on the docker machine at the second-given path on the container. Append :rw or :ro for read-write or read-only access. Boot2Docker users should make sure the VM host filesystem appears on the guest machine.

Example:

containers:
  api:
    mount:
      - "/api_exported"
      - ".:/data/this_repo:rw"

The contents of /api_exported from this container will appear on any container that lists this one as a mount-from.

The contents of the current directory will appear on the container at /data/this_repo, and will furthermore appear on any container that lists this container as a mount-from.

mount-from: Mount all volumes defined on the given container

Mounts all volumes that the given container defined. This container now has /api_exported (with data from the api container) and /data/this_repo (with data from the docker machine):

containers:
  web:
    mount-from:
      - "api"

Names will be suitably scoped to the cluster: the web.dev machine mounts volumes from the api.dev container.

extra: Other arguments to supply the entrypoint script

The decking.yaml file shows an example, passing two configuration args to the mongodb runner.

privileged: Run in privileged mode

What it says.

Forwarding ports for VM-hosted docker machines

The decking files here use the port directive to associate various container ports with ports on the docker UI container. However, if your docker machine is running as a VM (as any Mac OSX or windows installation will be), there is an additional step required to make it appear on the host OS.

An ad-hoc solution is to use an ssh tunnel: ssh [YOUR_USER@YOUR_DOCKER_MACHINE_IP] -L 8200:localhost:8200 -- the first number is the port on your host machine, the second is the target port on the docker machine. (Note that the port number on the docker container is irrelevant.) Boot2docker can do this easily by running eg. boot2docker ssh -L 8200:localhost:8200. Permanent setup of port forwarding is handled by the virtualization layer -- not shipyard or docker -- so you'll have to consult the VM provider documentation. Boot2Docker users can find several ways to accomplish port forwarding described in the Boot2Docker docs

Other topics

Cleanup

      # stop the containers
    $ decking stop dev
    redis.dev    stopping... ✔
    mongodb.dev  stopping... ✔
    api.dev      stopping... ✔
    web.dev      stopping... ✔
    admin.dev    stopping... ✔
      # remove them:
    $ docker rm app.dev web.dev admin.dev
      # Check that nothing relevant is there:
    $ docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
      # remove the project images
    # docker rmi decking/example-api decking/example-web decking/example-admin
    Deleted: 56d1fe739e646a58e77bb1e1c602ac0346f6fea631c495c4841cc4c2a21756b5
    Deleted: 3ba829b6b709a909507fac7995818171a7125cac6ae10646ecbd2786ec2d1655
    Deleted: e053fa2d4391ba343d415f91842b57693a5ed11edd0befde46be820560b2c804
    Untagged: decking/example-web:latest
    Deleted: 574640c9b988cda2c1125d598c49509d519ec8be555cee8ba69d96eb810daefe
    Deleted: 1e599bc9ece3534f463c6984bbfaf16ab7f6568886ed17bee84315affb9f074e
    ...

Fake what decking is doing

Here is the equivalent of decking create for the api and web commands in the dev cluster:

docker run --name api.dupe --env 'NODE_ENV=build' --publish "8100:7777" \
       --link mongodb.dev:db --link redis.dev:redis -v /api_exported -v $PWD:/data/this_repo \
      -d decking/example-api
docker run --name web.dupe --env 'NODE_ENV=build' --publish "8101:8888" \
      --link api.dupe:api --volumes-from=api.dupe \
      -d decking/example-web

Verify that the differences between our handcrafted command and decking's commands are inessential:

docker inspect api.dupe > /tmp/api-dupe.txt
docker inspect api.dev  > /tmp/api-dev.txt
diff -uw /tmp/api-{dev,dupe}.txt

The build command is basically doing

ln -s ./docker/api/Dockerfile ./Dockerfile
docker build -t decking/example-api .
rm ./Dockerfile

.dockerignore file

The .dockerignore file in the base of this repo blacklists paths that are unimportant to docker. Everything else is packaged and sent to docker whenever you build an image.