Chapter 3. Keeping Containers Fresh and Updateable
This section describes processes and practices that ensure that containers are fresh and updateable.
3.1. Never Put Naked Pulls in FROM Instructions
When writing Dockerfiles, always list the registry from which you’re pulling in the FROM commands you use. In Red Hat’s case, that means that you have to include the whole name of the Red Hat Container Registry.
This is a naked pull:
$ docker pull rhel7
This is not a naked pull:
$ docker pull registry.redhat.com/rhel7
3.2. Using Docker Caching to Your Advantage
This section explains how to use Docker caching to make your Dockerfiles more efficient for your workflow.
3.2.1. Order Instructions to Take Advantage of Caching
Docker assumes that each instruction is deterministic. Docker assumes that these instructions are not associative. If it encounters the same instructions in the same order, it caches the results. This means that if you have the instruction
FROM foo: dnf -y update in the same two Dockerfiles in the same order, Docker will create the same base image from that point.
Always perform installations in Dockerfiles in the same order to take advantage of Docker caching. Break tasks into thematic components (for example "adding users" or "updating software") in order to take best advantage of Docker’s caching utilites.
3.2.2. Deliberately Break Caching In Some Situations
Sometimes it is advantageous to subvert Docker’s default caching behavior.
There are three methods to force "yum update" to run:
(1) On a single machine, delete and recreate the image.
(2) Insert a nonce command (for example "echo nonce") in order to break caching
(3) Use a buildsystem like the OpenShift buildsystem
3.3. Ensuring the Provenance of Containers
Running a private registry is the easiest way to ensure container provenance. OpenShift and Satellite are Red Hat products that have built-in image service options. Avoid including sensitive information in images. If anyone overhears them, it won’t be a problem. This means that you should always use transport layer security (TLS), but you don’t need to use authentication. Things that need to remain confidential should be abstracted up a level to the orchestration level, which we will discuss in the Kubernetes and OpenShift sections of this document.
Also consider signing container images for these benefits:
- authenticating container authorship
- container integrity
See this Knowledgebase article for information on signing container images.
3.4. Leveraging Kubernetes and OpenShift to Ensure that Containers are Immutable
Immutable containers are containers that have no state.
Immutability improves security by decreasing the damage that can be done by a local compromise. Immutable images themselves have no secrets and save no state that could get corrupted. Immutable containers are trivial to verify because they never change.
3.4.1. Ways to Leverage Kubernetes and OpenShift
This section describes ways to leverage Kubernetes and OpenShift to create immutable container images.
- Using Volume Mounting - Use Kubernetes to bring in external data that is mutable (for instance, WordPress content or a database).
- Using Services - Kubernetes and OpenShift have matchmaking services. A container can be designed to depend generically on a database, and the details of logging into that database can be provided at runtime.
- Using Templating - This is the same idea as using the matchmaking services of Kubernetes and Openshift applied to buildtime. Dockerfiles provide no way of making users with a certain UID run on a certain cluster. Use plugins to the OpenShift buildsystem to customize a given build.
- Using Github Repositories - Use docker pull to pull in live content at runtime, for example from a private or public git repository. OpenShift has features that take this to another level: they allow you to ignore all the container details and have an application all of whose details are hosted in a github repo.
3.4.2. Leveraging Kubernetes to Ensure that Containers Do Not Store Secrets or Sensitive Information
Kubernetes has a "secrets" functionality that allows memory-hosted secrets to be inserted as virtual files at runtime. This should be used for all confidential information such as authentication details and encryption keys.