11 septembre 2014

Docker : Build vs Run

Dockerfile allows to build a Docker image using a pure text approach, for sure not comparable to Puppet/Chef recipes power, but anyway a major topic in Docker adoption.

Lot's of developers first use Docker to reproduce target environment on local machine, as a simpler and lightweight alternative to vagrant/virualbox. They can test application on local workstation using a Dockerfile that looks like :

FROM centos

RUN apt-get -y install tomcat7
COPY target/foo.war /home/tomcat/webapps

CMD catalina run

For them, Docker is used to run application in an environment that is mostly equivalent to production server, not to prepare deployment to a Docker production environment (some do, but most team are just running prototype, not actual docker production).

Others use Docker to get a strictly reproducible and self contained build system. Dockerfile is used to declare and setup all build and test tools involved in the build process. No need anymore to document the "how to build the project". docker build then replace the mvn install command as first command a developer will use when he joins a project.

They will use a Dockerfile that looks like :

FROM ubuntu:14.04

RUN apt-get -y install openjdk-7
ADD http://.../apache-maven-3.2.3-bin.tar.gz /opt
ENV PATH /opt/maven/bin
ADD ....

CMD mvn install

When you want to use both approach on same project, you hit a Docker limitation (so far, work in progress). You can't use a single Dockerfile to first build the application binaries from source, then create a separated docker image as production delivery to include it. I'm not sure what are Docker plan on this topic, maybe nested Dockerfiles ?

Anyway, here is a possible approach : your build system will have 3 sequential steps

  1. build the "build from sources" Docker image
  2. run this image
  3. build the "create application delivery" Docker image

the idea is to design the "build from source" Dockerfile so a docker run will export the built artifact. Simplest option is for it to just cat the binaries to stdout, so you run

docker build -t build-from-source . 
docker run build-from-source > app.war

Another option is to use docker cp command, to extract built artifact from the image. But this require the "build from source" docker image to be ran at least once as a container (can be a stopped container). That's probably due to the way "cp" command is implemented, attaching to the container.

Just like I can add a file from a ftp or http server hosting my binaries, I'd like the ADD Dockerfile command to support getting file from another image, maybe using a docker protocol scheme :

ADD docker://build-from-source:latest/build/target/app.war

I've created https://github.com/docker/docker/issues/7992 for this purpose. Comments and alternative ideas are welcome.