Thursday, February 16, 2017

Tips for Building Docker Images

A few tips for building Docker images, from my experience so far.

Stick with the conventions

As with most tools, it's best to start with the conventions and stick with them unless you have a very compelling reason to customize ("you are not special").   Some of the important conventions with Docker images:

  • Put your Dockerfile in the root directory of your project (git repo).
  • Base your image on another image! This allows you to inherit all the environment variables and such from the parent. Also, if it's in docker hub, you can refer to the documentation.
  • Add ENV, ENTRYPOINT and EXPOSE instructions in your Dockerfile.  This will tell image users how to configure your image.
  • Add comments to indicate what files / directories can be overridden with 'volumes' for configuration.
  • Use ARG to allow you to pass in a variable during build time.   This is really good for version numbers, etc.

Create The Image

To create the image, just do docker build from the root directory of the project:
docker build -t test-image --force-rm .

Where:
  • -t test-image : gives the image a name (tag) in the local docker environment.
  • --force-rm : removes intermediate containers

Parameterized Image Building with ARG

If you have an image where you need to download a version of some file and you would like to not update the Dockerfile for every version, you can use ARG to define a variable that you can pass in to docker build like this:

Dockerfile

FROM openjdk:8-jre-alpine

EXPOSE 9324

ARG ELASTICMQ_VERSION=0.13.2

CMD ["java", "-jar", "-Dconfig.file=/elasticmq/custom.conf", "/elasticmq/server.jar"]
COPY custom.conf /elasticmq/custom.conf

ADD "https://s3-eu-west-1.amazonaws.com/softwaremill-public/elasticmq-server-${ELASTICMQ_VERSION}.jar" /elasticmq/server.jar

  • The ARG defines ELASTICMQ_VERSION as an expected argument at build time.
You can then build this image, overriding the ELASTICMQ_VERSION, like this:
docker build -t=my-elasticmq:${VER} --force-rm --build-arg ELASTICMQ_VERSION=${VER}
Where:
  • -t test-image : gives the image a name (tag) in the local docker environment.
  • --force-rm : removes intermediate containers



Explore The Image

So, if you want to shell around and look at what is in the image, you can do that easily with:

docker run -it --rm --entrypoint /bin/bash test-image
Where
  • -it : runs an interactive terminal session
  • --rm : removes the container on exit (this is really useful! Saves on having to clean up containers all the time.)
  • --entrypoint /bin/bash : the shell you want to use. We want to override the entry point so the container won't fully start whatever it usually does.
  • test-image : The image we want to start, if you gave it a name.

Tuesday, February 7, 2017

Install Groovy in an Alpine-based Docker Image

If you're making a custom image based on an Alpine Linux image, you may have a little trouble installing things that require bash, like Groovy.    I tried using SDKMAN, but unfortunately I encountered a lot of problems with compatibility of unzip, and other tools.   In my case I'm creating an image based on Tomcat and I want Groovy for doing some configuration work.

First, we install the Alpine packages we need:
  1. bash
  2. curl
  3. zip
  4. libstdc++ (Gradle needed this, but I don't think Groovy does :shrug:)

RUN apk add --update bash libstdc++ curl zip && \
    rm -rf /var/cache/apk/*

Now we need a workaround for fact that Groovy's shell scripts start with #!/bin/sh :

# Workaround  https://issues.apache.org/jira/browse/GROOVY-7906 and other 'busybox' related issues.
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

Now we can install Groovy. This could probably be done a little more optimally, but it works:
# Install groovy
# Use curl -L to follow redirects
# Also, use sed to make a workaround for https://issues.apache.org/jira/browse/GROOVY-7906
RUN curl -L https://bintray.com/artifact/download/groovy/maven/apache-groovy-binary-2.4.8.zip -o /tmp/groovy.zip && \
    cd /usr/local && \
    unzip /tmp/groovy.zip && \
    rm /tmp/groovy.zip && \
    ln -s /usr/local/groovy-2.4.8 groovy && \
    /usr/local/groovy/bin/groovy -v && \
    cd /usr/local/bin && \
    ln -s /usr/local/groovy/bin/groovy groovy

As always, any suggestions about how to make it better, let me know.