Dockerfile Best Practices - take 2
Much has changed since my first Dockerfile best practices post. I'll leave the original post up for posterity and this post will include what has change and what you should do now.
1: Don't boot init
Containers model processes not machines. Even if you think that you need to do this you are probably wrong. Next...
2: Trusted builds
Even if you don't like the name it is an awesome feature. I have most of my github repositories added to trusted builds so that when I push a commit my new image is waiting on the index shortly after. Also, I don't have to have separate repositories with the Dockerfile to share with others, it is all in one place.
Just remember that this is not your playground for trying things out. Build locally first before you push. Docker ensures that what you build and run locally will build and run the same way when pushed elsewhere. Develop and test locally, commit and push, and wait for the official image on the index with trusted builds.
3: Don't upgrade in builds
Updates will be baked into the based images you don't need to apt-get upgrade
your containers. Because of the isolation that happens this can often fail if something is trying to modify init or make device changes inside a container. It also produces inconsistent images because you no longer have one source of truth of how your application should run and what versions of dependencies are included in the image.
If there are security updates that a base image needs, let upstream know so that they can update it for everyone and ensure that your builds are consistent again.
4: Use small base images
Some images are more bloated than others. I suggest using debian:jessie
as your base. If you are coming from ubuntu, you will find a more lightweight and happy home on debian. It's small and does not contain any unneeded bloat.
5: Use specific tags
This is important for your base images. Your FROM should always contain the full repository and tag for the base image that you are depending on. FROM debian:jessie
not just FROM debian
.
6: Group common operations
Your apt-get update
should be grouped with your apt-get install
. Also, you should take advantage of the \
to span multiple lines on your installs.
# Dockerfile for https://index.docker.io/u/crosbymichael/python/ FROM debian:jessie RUN apt-get update && apt-get install -y \ git \ libxml2-dev \ python \ build-essential \ make \ gcc \ python-dev \ locales \ python-pip RUN dpkg-reconfigure locales && \ locale-gen C.UTF-8 && \ /usr/sbin/update-locale LANG=C.UTF-8 ENV LC_ALL C.UTF-8
Just remember that layers and the cache are good. Don't be scared of having many layers because the cache is a life saver. Also you should use upstream packages when you can.
7: Use your own base images
If you are running python applications have a python base image. The Dockerfile in the previous example is used to build crosbymichael/python
that is used in many other images for python applications.
FROM crosbymichael/python RUN pip install butterfly RUN echo "root\nroot\n" | passwd root EXPOSE 9191 ENTRYPOINT ["butterfly.server.py"] CMD ["--port=9191", "--host=0.0.0.0"]
Or another:
FROM crosbymichael/python RUN pip install --upgrade youtube_dl && mkdir /download WORKDIR /download ENTRYPOINT ["youtube-dl"] CMD ["--help"]
As you can see this makes Dockerfiles that use your base very small and focused on the application that we are installing install.
Let me know what you think or if you have any other questions.