With the recent launches of ImageLayers and Lorry, the CTL Labs team has been thinking a lot about the deployment of containerized applications. Over the past month we've prototyped a few ideas that tackle container deployment in different ways. Today we're happy to announce our first project, Zodiac. Built with small teams and single developers in mind, it is designed to be lightweight and plug into the existing Docker tooling. Zodiac makes it easy to deploy and rollback containerized applications. Our focus is on simplicity and ease of adoption, not features. We welcome your feedback in helping us to chart the future path for Zodiac.

Standing on the shoulders of giants, on the shoulders of other giants

At this point Zodiac is more or less a wrapper around Docker Compose. This is very much intentional, and, we encourage developers to use Docker Compose for specifying application configuration. Many applications are already described with a Docker Compose file, so it seemed natural to leverage that for remote deployments. Since we're just wrapping Docker Compose, you can do a remote deployment with Zodiac by simply pointing the client at a remote Docker endpoint. Similarly, if you want to deploy to a cluster you can reference a Docker Swarm endpoint. Zodiac has the same limitations with Docker Swarm as Docker Compose does.

Minimal pre-requisites and dependencies

Zodiac only requires that a Docker endpoint is exposed via TCP. Docker Machine can fulfill this pre-requisite, and we recommend using it, as Zodiac was designed to play nicely with Docker Machine managed endpoints. On the client machine Zodiac will rely on Docker Compose being installed, along with the Zodiac binary. Installation Instructions can be found in the Zodiac README.

Zodiac in action

zdemo]

Let's walk through a simple example of deploying with Zodiac. With Docker Compose and Zodiac installed locally, and a Docker Machine-provisioned endpoint set up remotely, I can do my first deploy.

So, in my local environment, I have a simple docker-compose.yml file:

#docker-compose.yml
demosite:
  build: .
  ports:
    - "80:80"


And the following Dockerfile for the demosite service:

FROM nginx
MAINTAINER Alex Welch

ADD . /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]


And in an index.html file we just have a string of text:

$ cat index.html
Initial Deployment.


The above compose and Dockerfile are intended to be brief and simple for example purposes. Real-world applications will often have multiple services, options, etc.

Zodiac offers a verify command to ensure the endpoint is responding and using the correct version of Docker or Swarm.

$ eval "$(docker-machine env qa)" # tell both Docker and Zodiac to interact with my "qa" docker machine endpoint
$ zodiac verify
Successfully verified endpoint: tcp://12.34.56.78:2376


With that we can do our first deploy.

$ zodiac deploy
Deploying your application...
Creating zodiac_demosite_1
Successfully deployed 1 container(s)

Now let's list the deployments

$ zodiac list
ID      DEPLOY DATE             SERVICES                MESSAGE
1       2015-08-03 16:49:11     zodiac_demosite_1


At this point we can curl the endpoint, and if we do a docker ps against the remote Docker host we'll see the running containers:

$ curl http://12.34.56.78 # confirm the site is responding
Initial Deployment.

$ echo $DOCKER_HOST
tcp://12.34.56.78:2376 # i.e. we are pointing at the remote Docker endpoint
$ docker ps # against remote docker endpoint
CONTAINER ID        IMAGE                                                              COMMAND                CREATED             STATUS              PORTS                         NAMES
547957411d17        a01d3f72d8e76e883f894cacd651769facb46252678f3d998db677d0e532ca60   "nginx -g 'daemon of   41 seconds ago      Up 40 seconds       0.0.0.0:80->80/tcp, 443/tcp   zodiac_demosite_1


We can see here the Image is not what we would've expected. This is because Zodiac snapshots the image's actual layer id at the time of deploy, enabling accurate rollbacks. This is probably the most important component of Zodiac. If an image tag is updated to point at a new layer ID after the deployment has happened, the rollback history would be inaccurate. By recording the actual layer ID we preserve the ability to rollback.

I'll update some of the my source files, and deploy again, but this time with a message.

$ echo "Now with updated text." > index.html # update a file that comprises part of the image
$ zodiac deploy -m "updated copy"
Deploying your application...
Creating zodiac_demosite_1
Successfully deployed 1 container(s)

$ zodiac list
ID      DEPLOY DATE             SERVICES                MESSAGE
2       2015-08-03 16:51:59     zodiac_demosite_1       updated copy
1       2015-08-03 16:49:11     zodiac_demosite_1


Okay, now we've got two deployments in the history (the top being the current deployment). If we curl the endpoint or look at the containers we'll notice our updates.

$ curl http://12.34.56.78
Now with updated text.
$ docker ps # against remote docker endpoint
CONTAINER ID        IMAGE                                                              COMMAND                CREATED              STATUS              PORTS                         NAMES
723169939fa0        46af78669c27d86601c7b1ed30dda5d012032777473d9698bcc83c1ed74787de   "nginx -g 'daemon of   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp, 443/tcp   zodiac_demosite_1

Notice the IMAGE for zodiac_demosite_1 has a different name now (77e9f456...). This is because we built a new image with the edits to the README.md baked in.

Let's say the change to index.html was a bad idea, and we need to rollback.

$ zodiac rollback
Rolling back your application...
Creating zodiac_demosite_1
Successfully rolled back to deployment: 1

$ zodiac list
ID      DEPLOY DATE             SERVICES                MESSAGE
3       2015-08-03 16:54:02     zodiac_demosite_1       Rollback to: #1
2       2015-08-03 16:51:59     zodiac_demosite_1       updated copy
1       2015-08-03 16:49:11     zodiac_demosite_1


Notice the additional entry and message in the list output. $ curl http://12.34.56.78 Initial Deployment.

$ docker ps # against remote docker endpoint
CONTAINER ID        IMAGE                                                              COMMAND                CREATED             STATUS              PORTS                         NAMES
b78ab6773a2d        a01d3f72d8e76e883f894cacd651769facb46252678f3d998db677d0e532ca60   "nginx -g 'daemon of   3 minutes ago       Up 3 minutes        0.0.0.0:80->80/tcp, 443/tcp   zodiac_demosite_1


Now the IMAGE for zodiac_demosite_1 is back to the same as it was in the initial deploy. Rollback successful!

What Next?

From here, I recommend kicking the tires yourself. We had specific use cases in mind for Zodiac, but have tried to leave it open-ended. Again, community feedback is welcome via Github Issues.

Stay tuned. Next in our deployment series, Brian Dehamer will talk about another take on deployment, one that we use for our Production Docker deployments.