AU294 · Red Hat Enterprise Linux Automation with Ansible

Custom Container Images

Chapter 5 of 8

Objective: Design custom container images following good practice, and build them using Dockerfiles/Containerfiles.

Overview

Pre-built images cover common services, but real applications usually need a custom image that layers application code and configuration onto a suitable base. Designing that image well, and building it in a repeatable way, is a core skill for anyone shipping containerized software.

Key Concepts

Design principles for custom images

Well-designed images are small, single-purpose, and predictable: one main process per container, no unnecessary packages, no secrets baked into layers, and configuration supplied at runtime (environment variables, mounted config, or command arguments) rather than hardcoded.

Choosing a base image

Custom images almost always start FROM an existing base — a minimal RHEL UBI (Universal Base Image) for general-purpose builds, or a language-specific base (for example a Node.js or Python UBI image) when the application's runtime dependencies are already well understood.

Dockerfile / Containerfile instructions

A build file is a sequence of instructions executed in order; Podman calls the file a Containerfile (identical syntax to a Dockerfile).

Instruction Purpose
FROM Selects the base image the new image is built on top of
RUN Executes a command at build time, committing the result as a new layer
COPY / ADD Copies files from the build context into the image
ENV Sets an environment variable available at build and run time
EXPOSE Documents which port(s) the container listens on
USER Sets the user the container process runs as
CMD Default command run when the container starts (can be overridden)
ENTRYPOINT The fixed executable a container always runs; CMD supplies default arguments to it

Building an image

podman build reads a Containerfile and a build context (the directory of files available to COPY/ADD), executing each instruction to produce a final, tagged image.

# Containerfile
FROM registry.access.redhat.com/ubi8/ubi-minimal
RUN microdnf install -y httpd && microdnf clean all
COPY ./site/ /var/www/html/
EXPOSE 8080
USER 1001
CMD ["httpd", "-D", "FOREGROUND"]

Layer order and build cache

Podman caches each layer and reuses it on subsequent builds if nothing that produced it has changed. Ordering instructions from least-likely-to-change (installing packages) to most-likely-to-change (copying application source) keeps rebuilds fast, since only the layers after the first change need to be rebuilt.

Working with It

Containerfile == Dockerfile

Podman reads the exact same syntax as Docker; the file is usually still named Dockerfile, or Containerfile if you want the vendor-neutral name explicitly.

Order Instructions for Cache Efficiency

Put steps that change rarely (installing OS packages) before steps that change often (copying your application code) so a code change doesn't invalidate the expensive package-installation layer.

Don't Run as Root by Default

Unless the application genuinely requires root, set a non-root USER in the image — running as root inside the container is one of the most common avoidable security mistakes in custom images.

Building and running a custom image

$ podman build -t quay.io/myorg/mysite:1.0 .
$ podman run -d --name mysite -p 8080:8080 quay.io/myorg/mysite:1.0

Watch Out For

Common Pitfall

Unless the application genuinely requires root, set a non-root USER in the image — running as root inside the container is one of the most common avoidable security mistakes in custom images.

Key Terms

Containerfile
A build instruction file (Podman's name for a Dockerfile).
Base image
The starting image a custom image is built FROM.
Build context
The set of local files available to COPY/ADD during a build.
CMD vs. ENTRYPOINT
CMD supplies default (overridable) arguments; ENTRYPOINT is the fixed command that runs.
Build cache
Reuse of previously-built, unchanged layers to speed up subsequent builds.

Review Questions

  1. What is the difference between CMD and ENTRYPOINT?
  2. Why should the instruction that installs OS packages usually appear before the instruction that copies application code?
  3. What security practice should you apply to the USER instruction, and why?
  4. What is the build context, and what command supplies it?
  5. Name two properties of a well-designed container image besides being small.