Docker image building patterns

As we all know, Docker containers are a fantastic way to optimally and organically encapsulate complex build processes. Typically, any software package requires a host of dependencies. As indicated in Chapter 9, Microservices Architecture Patterns, every microservice is being developed and delivered as a Docker image. Each microservice has its own code repository (GitHub) and its own CI build job.  Microservices can be coded using any programming language. Let us focus on the Java language here. If a service is built and run using a compiled language (Java, Go, and so on), then the build environment can be separated from the runtime environment. A Java service's Dockerfile.build is from the openjdk-7-jdk directory and its Dockerfile is from the openjdk-7-jre directory which is substantially smaller than JDK.

For the Java programming language, it requires additional tooling and processes before its microservices become executable. However, the JDK are not required when a compiled program is running. Another reason is that the JDK is a bigger package when compared with the Java Runtime Environment (JRE). Furthermore, it seems farsighted to develop and reuse a repeatable process and a uniform environment for deploying microservices. It is therefore paramount to package the Java tools and packages into containers. This setup allows the building of Java-based microservices on any machine, including a CI server, without any specific environmental requirements such as JDK version, profiling and testing tools, OS, Maven, environment variables, and so on.

Resultantly, for every service, there are two Dockerfiles: one for service runtime and the second is packed with the required tools to build the service. First, it is all about crafting the Dockerfile.build file, which can speed up the Maven build. Now, it is straightforward to compile and run the microservice on any machine (local or remote). This segregated approach goes a long way in simplifying the continuous integration (CI) process.

The recipe is as follows:

  1. Build file: Have one Dockerfile with all the tools and packages required to build any service. Name it Dockerfile.build.
  2. Run file: Have another Dockerfile with all the packages required to run the service. Keep both files along with the service code.
  3. Build a new builder image, create a container from it, and extract build artifacts using volumes or the docker cp command.
  4. Build the service image.

Thus, segregating the building process from the runtime process stands well for the intended success of the containerization paradigm. One is to perform a build and another is to ship the results of the first build without the penalty of the build-chain and tooling in the first image. Terra Nullius has posted the relevant details at http://blog.terranillius.com/post/docker_builder_pattern/. The builder pattern describes the setup that developers have to follow for building a container. It generally involves two Docker images:

  • A build image with all the build tools installed, capable of creating production-ready application files
  • A service image capable of running the application

The basic idea behind the builder pattern is simple: create additional Docker images with the required tools (compilers, linkers, and testing tools), and use these images to produce lean, secure, and production-ready Docker images.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset