Keep Chef out of your Docker containers
Keep Chef out of your Docker containers
Configuration Management tools can be of good use to provision your Docker containers. But you don’t want these tools to end up in your Docker images. Using Data Volume Containers and Docker Compose, we can do this and still have a slim image. Here is how you can do it with Chef.
TL;DR
Prepare a docker-compose.yml
file with 3 services: chef
, chefdata
and app
.
chef
exposes/opt/chef
as a volumechefdata
contains the Chef setup (client.rb
, json, cookbooks) exposed on/tmp/chef
app
uses the volumes from the former and runs Chef to provision itself- commit
app
container as an image. - Use your application image! :-)
Get an image with Chef installed
I went over to the Docker Hub to search for an image with Chef preinstalled, but exposed as
a volume. The last part of my request was usually not fulfilled so I created an image
myself: releasequeue/chef-client
Since using floating versions like latest
is not proper release management, I also tag my
images with the Chef version installed.
Prepare your application Chef setup
Create a folder containing your Berksfile
for your application or service:
source "https://supermarket.getchef.com"
cookbook 'rq-web-api', git: 'git@github.com:releasequeue/rq-web-api-cookbook.git'
Retrieve the cookbook and its dependencies:
$ berks update
$ berks vendor chef/cookbooks
Cooking your cookbooks container
To run Chef succesfully, we need three parts:
- a config file
- a JSON data file
- the set of cookbooks
We retrieved the cookbook in the previous step. In the chef folder, add a Chef configuration file and a JSON data file to complete the setup:
cookbook_path ["/tmp/chef/cookbooks"]
ssl_verify_mode :verify_peer
{
"run_list": [
"recipe[rq-web-api]"
]
}
With all these parts in place, we can create another data volume container containing all of the above. Put a Dockerfile in the chef folder with these contents:
FROM tianon/true
# Add the Chef cookbooks and runtime info to a volume container
COPY zero.rb first-boot.json /tmp/chef/
COPY cookbooks /tmp/chef/cookbooks/
# Create volumes
VOLUME /tmp/chef
Provisioning the application container
Now we are ready to create our application container. Add your application Dockerfile
to the top-level project folder:
FROM ubuntu:14.04.2
# Configure your application here
# Startup commands
ENTRYPOINT /usr/bin/start-server
To get the 3 containers running together, we use docker compose
with this input file:
# Build a data volume container with the chef cookbooks on it.
chefdata:
build: chef
# Attach the image containing the Chef installation
chef:
name: chef
image: releasequeue/chef-client:12.0.3-1
# Override default command to use this container as a volume container
command: /bin/true
# Build the application container using Chef and the cookbooks on the attached volumes
rqwebapi:
name: rqwebapi
build: .
volumes_from:
- chefdata
- chef
environment:
PATH: "/opt/chef/bin:$PATH"
entrypoint: "/opt/chef/bin/chef-client"
command: "-c /tmp/chef/zero.rb -z -j /tmp/chef/first-boot.json"
and run docker-compose up
. You should see all three containers be created and Chef
running from the last one. Once it has done, the containers all stop gracefully.
Baking the application image
We have to find the container id of our application container after running docker compose
:
$ docker-compose up
Creating rqwebapidocker_chef_1...
Creating rqwebapidocker_chefdata_1...
Creating rqwebapidocker_rqwebapi_1...
Attaching to rqwebapidocker_rqwebapi_1
rqwebapi_1 | [2015-03-14T17:47:08+00:00] INFO: Started chef-zero at http://localhost:8889 with repository at /tmp/chef
rqwebapi_1 | One version per cookbook
rqwebapi_1 |
...
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74315e335f2b rqwebapidocker_rqwebapi:latest "/opt/chef/bin/chef- 3 minutes ago Exited (0) About a minute ago rqwebapidocker_rqwebapi_1
e7eb9f4f206b rqwebapidocker_chefdata:latest "/true" 3 minutes ago Exited (0) 3 minutes ago rqwebapidocker_chefdata_1
dbff11d1294b releasequeue/chef-client:12.0.3-1 "/bin/true" 3 minutes ago Exited (0) 3 minutes ago rqwebapidocker_chef_1
Let’s commit our application container to an image and tag it:
$ docker commit 74315e335f2b releasequeue/rq-web-api
18f53343b0cbe8f36005fee6878ad7fede7d202dd33e787b1713c039e9c3c516
$ docker push releasequeue/rq-web-api
...
Run your application
We have our provisioned image, so let’s start our application:
$ docker run -i -t releasequeue/rq-web-api
[2015-03-14 18:02:11] INFO WEBrick 1.3.1
[2015-03-14 18:02:11] INFO ruby 2.1.5 (2014-11-13) [x86_64-linux]
[2015-03-14 18:02:11] INFO WEBrick::HTTPServer#start: pid=98 port=3000
...
Since the volumes of Chef or the cookbooks are no longer there, this image is free of any provisioning tools and much smaller as a result.
Happy container cooking!