Building Container Images

From Leo's Notes
Last edited on 23 February 2022, at 22:16.

Container build Tools

The easiest way to build container images is to use Docker and a Dockerfile. However, since Docker isn't cool anymore, there are some other options that may be worth looking into which will be covered in the following subsections.

Docker

The tried and true method to build container images. However, Docker itself can be a bit resource heavy and requires a daemon to be running on the system.

A container is defined in a Dockerfile and built with the docker build command.

BuildKit

BuildKit is a new code base for building containers that's been part of Docker since version 19.03. It supports some interesting features such as more efficient caching, concurrent dependency resolution, and distributed workers, among other things. A container build is typically faster because of these performance enhancements. Best of all, it uses the same Dockerfile format as Docker.

A container build can be done right now by export the DOCKER_BUILDKIT=1 environment variable while calling docker build.

Buildah

Buildah is the container building component of podman, Red Hat's replacement to Docker. Containers are built with Dockerfiles (though not all features are supported) using the the podman build command.

Container build tips

Review the Dockerfile best practices at https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

RUN commands

Try to bundle multiple RUN commands together to reduce the number of container image layers and their overall size. Concatenating multiple commands with && or ; (with set-x, which stops the script when a non zero exit code is returned).

RUN set -ex; \
   yum -y install vim; \
   yum clean all

## or,
RUN yum -y install vim && yum clean all

Remember to clean up at the end of each RUN to minimize the layer sizes. Things you can do include:

  • rm -rf /tmp/* /var/cache/* /var/tmp/*
  • Clean up package manager caches and remove unnecessary build-only packages

Better yet, use multi-staged builds (https://docs.docker.com/develop/develop-images/multistage-build/) to bundle the results of multiple layers into one.

FROM centos:8 as build
RUN yum -y update
RUN yum -y install vim

FROM centos:8
## We can copy specific artifacts from the build container. Eg:
COPY --from=build /usr/bin/vim /usr/bin/vim


Yum/dnf-based package managers

Yum or dnf package managers are used on Red Hat based distros.

  • Install packages with yum -y install to avoid prompts
  • At the end of the run, yum -y clean all, and clean up caches rm -rf /var/cache afterwards

Apt-based package managers

Apt-based package managers are used on Ubuntu and Debian based distros.

  • Prepend your apt install command with DEBIAN_FRONTEND=noninteractive to prevent some packages from prompting interactive input (tzdata for example), which leads to indefinite waiting for an user input. Don't set this as an environment variable since it will linger after the container is built.
  • At the end of the run, rm -rf /var/lib/apt/lists/* to clean up.

apk-based package managers

Alpine Linux uses the apk package manager. One neat feature with apk is the ability to group packages into virtual groups which we can uninstall with one command.

  • Create virtual groups for build dependencies using apk add --virtual=build-deps make gcc. These groups can later be removed with apk del --purge build-deps.
  • At the end of the run, /var/cache/apk/* to clean up.