Containerize Application

Resources

Docker Documentation

Docker for Beginners

Docker: Getting Started

Develop with Docker

Docker: Samples

Introduction

The resources mentioned above contain tutorials to get up-and-running with Docker develop and code containerization. Below you will find some specific content highlighted for clarity.

Dockerfile

Docker builds images automatically by reading the instructions from a Dockerfile -- a text file that contains all commands, in order, needed to build a given image. A Dockerfile adheres to a specific format and set of instructions which you can find at Dockerfile reference.

Multi-stage Build

Multi-stage builds are a method of organizing a Dockerfile to minimize the size of the final container, improve run time performance, allow for better organization of Docker commands and files, and provide a standardized method of running build actions. A multi-stage build is done by creating different sections of a Dockerfile, each referencing a different base image. This allows a multi-stage build to fulfill a function previously filled by using multiple docker files, copying files between containers, or running different pipelines.

For more specific reading on the specifications of a multi-stage Docker build please see here.

By default, the stages are not named, and you refer to them by their integer number, starting with 0 for the first FROM instruction. However, you can name your stages, by adding an AS <NAME> to the FROM instruction. This example improves the previous one by naming the stages and using the name in the COPY instruction. This means that even if the instructions in your Dockerfile are re-ordered later, the COPY doesn’t break.

When you build your image, you don’t necessarily need to build the entire Dockerfile including every stage. You can specify a target build stage using the --target <NAME> build flag. A docker build without the --target flag will build the final stage.

When using multi-stage builds, you are not limited to copying from stages you created earlier in your Dockerfile. You can use the COPY --from instruction to copy from a separate image, either using the local image name, a tag available locally or on a Docker registry, or a tag ID. The Docker client pulls the image if necessary and copies the artifact from there.

You can pick up where a previous stage left off by referring to it when using the FROM directive.

Scratch Image

The scratch image is Docker’s reserved empty image, which is useful in the context of building base images (such as debian and busybox) or super minimal images. As of Docker 1.5.0, FROM scratch is a no-op in the Dockerfile, and will not create an extra layer in our image. The FROM scratch command signals to the build process that we want the next command in the Dockerfile to be the first filesystem layer in our image.

Since the scratch image doesn’t contain anything, it is ideal to be used as a final build image to contain the binary that was statically compiled in the build stage so that it can be executed in a minimalistic Docker container. To create statically compiled binaries, we can build them from code in, for example, Go or C/C++.

Note, an interpreted language, such as Python, needs a base system and required libraries. An Alpine container would be smallest. (See this blog post for a tutorial.)

Build Multi-platform Images

When building an image using docker build the image is built for the platform the command is run on. In other words, when building on an Intel based 64-bit linux OS the resulting image will be for the linux/amd64 platform.

If you want to build for a different target platform or for multiple target platforms you can use the buildx Docker plugin. See the working with buildx documentation page.

When the current builder instance is backed by the "docker-container" driver, you can specify multiple platforms together. In this case, it builds a manifest list which contains images for all specified architectures. When you use this image in docker run or docker service, Docker picks the correct image based on the node’s platform.

A useful blog post about building for ARM platforms on a linux host can be found here. For macOS and Windows using Docker Desktop the this post might be useful. Under the hood this uses QEMU with the tool set called binfmt_misc.