I've been playing with docker a little bit now. And while is just playing and I don't have anything production ready, this are my insights on how docker containers should be provisioned/created.

You can have a single Dockerfile like this one:

# Based on the Ubuntu image  
FROM ubuntu  
# Bootstrap the node  
RUN apt-get update && aptitude -y safe-upgrade  
RUN sed -i 's/PermitRootLogin.*/PermitRootLogin yes/'
# [...]  
# A lot of other RUN commands  
# [...]

RUN cat /proc/mounts > /etc/mtab  
RUN mkdir -p /root/.ssh  
ADD authorized_keys /root/.ssh/authorized_keys  
RUN chmod 600 /root/.ssh/authorized_keys  

To build a container you just run:

$ docker build -t superawesomeOS/featureX .

This is one possible approach. From my point of view you are scripting but in a very unconfortable way. And you will have problems very soon. Every time Docker runs a RUN command, a new container is created and executes the given command. Then it executes the next command but having as base the last generated container that now will have both changes. I like that, but is not perfect because you cannot create more that 42 aufs layers[2]. Lets say that every aufs layer is a RUN/ADD execution.

What you should be doing instead of createing your script in a Dockerfile format is: create a regular bash script upload it to the server and then extract and execute it.

Lets create a script:

$ cat <<EOF > provision_container.sh  
apt-get update && aptitude -y safe-upgrade  
sed -i 's/PermitRootLogin.*/PermitRootLogin yes/'
# [...]  
# A lot of other RUN commands  
# [...]

cat /proc/mounts > /etc/mtab  
mkdir -p /root/.ssh  
cp /tmp/authorized_keys /root/.ssh/authorized_keys  
chmod 600 /root/.ssh/authorized_keys  

And our docker file would be something in the lines:

FROM ubuntu  
ADD provision_container.sh provision_container.sh  
ADD authorized_keys /tmp/authorized_keys  
RUN provision_container.sh  

To build the container you just need to do:

$ ls ./  
provision_container.sh Dockerfile authorized_keys  
$ docker build -t superawesomeOS/featureY .

We just need 3 actions (ADD/RUN) instead of a bunch of them. But still we have another drawback. We need to run as many ADD commands as files we want to upload and we have the limit of 42 aufs layers. On top of that with Dockerfile (and don't know why) does not allow absolute paths from the source file. From the documentation of Docker [1]:

ADD <src> <dest>

The ADD instruction will copy new files from <src> and add them to the
container's filesystem at path <dest>.

<src> must be the path to a file or directory relative to the source
directory being built (also called the context of the build) or a remote
file URL.

<dest> is the absolute path to which the source will be copied inside
the destination container.

All new files and directories are created with mode 0755, uid and gid

Not just don't allow absolute paths from the source, but it ignores the permissions of the file. The solution is exactly the same. Build a tar.gz with all your configurations and upload them to the server doing something like that:

# Generate a tar file like that:  
tar --append --file=config_files.tar /etc/resolv.conf /etc/hosts  
# If you can run this command again and the files will be appended:  
tar --append --file=config_files.tar /root/.ssh/authorized_keys  
# Create a zip (not required step)  
gzip config_files.tar

Now in our directory we should have:
:::code $ ls
config_files.tar.gz Dockerfile provision_container.sh

The Dockerfile would look like:

FROM ubuntu  
ADD provision_container.sh provision_container.sh  
ADD config_files.tar.gz config_files.tar.gz  
RUN tar zvxf config_files.tar.gz -D /  
RUN provision_container.sh  

With this 4 lines (could be 3 if you untar from provision_container.sh) we are we are ready to go. The files have the original permissions, are deployed in the correct paths and we don't have the limit of 42 actions of a Dockerfile.

In fact, your Dockerfile now is more generic and you can with very little effort make an script that fills for you de FROM and EXPOSE tags and you can use this Dockerfile for all your projects, exactly the same way.


[1] http://docs.docker.io/reference/builder/#add
[2] https://github.com/dotcloud/docker/issues/1171