Skip to main content
- Creating Images with Containerfiles
- A Containerfile lists a set of instructions that a container runtime uses to build a container image.
- We can use the image to create any number of containers.
- Each instruction causes a change that is captured in a resulting image layer.
- These layers are stacked together to form the resulting container image.
- Choosing a Base Image
- When we build an image, podman executes the instructions in the Containerfile and applies the changes on top of a container base image.
- A base image is the image from which your Containerfile and its resulting image is built.
- The base image you choose determines the Linux distribution and its components, such as:
- Package manager
- Init system
- Filesystem layout
- Preinstalled dependencies and runtimes
- The base image can also influence other factors, such as image size, vendor support, and processor compatibility.
- Red Hat provides container images intended as a common starting point for containers known as universal base images (UBI).
- These UBIs come in four variants: standard, init, minimal, and micro.
- Standard
- This is the primary UBI, which includes DNF, systemd, and utilities such as gzip and tar.
- Init
- Simplifies running multiple applications within a single container by managing them with systemd.
- Minimal
- This image is smaller than the init image.
- This image uses the microdnf minimal package manager instead of the full-sized version of DNF.
- Micro
- This is the smallest available UBI because it only includes the bare minimum number of packages.
- This image does not include a package manager.
- Red Hat also provides UBI-based images that include popular runtimes, such as Python and Node.js.
- These UBIs use Red Hat Enterprise Linux (RHEL) at their core and are available from the Red Hat Container Catalog.
- Containerfile Instructions
- Containerfiles use a small domain-specific language (DSL) consisting of basic instructions for crafting container images.
- FROM
- Sets the base image for the resulting container image. Takes the name of the base image as an argument.
- WORKDIR
- Sets the current working directory within the container. Instructions that follow the WORKDIR instruction run within this directory.
- COPY
- Copy files from the build host into the file system of the resulting container image.
- Relative paths use the host current working directory, known as the build context.
- Both instructions use the working directory within the container as defined by the WORKDIR instruction.
- ADD
- Copies files from URLs.
- Unpacking tar archives in the destination image.
- Since ADD instruction adds functionality that might not be obvious, prefer the COPY instruction for copying local files into the container image.
- RUN
- Runs a command in the container and commits the resulting state of the container to a new layer within the image.
- ENTRYPOINT
- Sets the executable to run when the container is started.
- The ENTRYPOINT instruction defines an executable, or command, that is always part of the container execution.
- For this additional arguments are passed to the provided command.
- Since we cannot override Entrypoint instruction directly at runtime of container as in case of CMD
- We can overwrite the entrypoint by using the podman run --entrypoint command
- CMD
- Runs a command when the container is started. This command is passed to the executable defined by ENTRYPOINT.
- Base images define a default ENTRYPOINT, which is usually a shell executable, such as Bash.
- The ENTRYPOINT and CMD instructions specify the command to execute when the container starts.
- A valid Containerfile must have at least one of these instructions.
- If we use the CMD instruction, then passing arguments to the container overrides the command provided in the CMD instruction.
- When a Containerfile specifies both ENTRYPOINT and CMD then CMD changes its behavior.
- The values provided to CMD are passed as default arguments to the ENTRYPOINT.
- The ENTRYPOINT and CMD instructions have two formats for executing commands
- Text array
- ENTRYPOINT ["executable", "param1", ... "paramN"]
- String form
- CMD executable param1 ... paramN
- The string form wraps the executable in a shell command such as sh -c "executable param1 … paramN".
- USER
- Changes the active user within the container.
- Instructions that follow the USER instruction run as this user.
- We should use a different user other than root for security reasons.
- LABEL
- Adds a key-value pair to the metadata of the image for organization and image selection.
- EXPOSE
- Adds a port to the image metadata indicating that an application within the container binds to this port.
- ENV
- Defines environment variables that are available in the container.
- We can declare multiple ENV instructions within the Containerfile.
- We can use the env command inside the container to view each of the environment variables.
- The ENV instruction lets you specify environment dependent configuration, for example,hostnames, ports or usernames.
- The containerized application can use the environment variables at runtime.
- To include an environment variable, use the key=value format.
- ARG
- Defines build-time variables, typically to make a customizable container build.
- We configure the ENV instructions by using the ARG instruction.
- This preserves the build-time variables for runtime.
- Use the ARG instruction to define build-time variables, typically to make a customizable container build.
- For example hosting envirnoment etc
- We can configure a default build-time variable value if the developer does not provide it at build time.
- While building the container image, use the --build-arg flag to set the value
- podman build --build-arg key=example-value
- If we do not provide the --build-arg flag, then Podman uses the default values during the build process.
- We can change the values during the build process without changing the Containerfile by using the ARG instruction.
- Mostly ENV instructions are configured using the ARG instruction.
- Thus preserving the build-time variables for runtime.
- VOLUME
- Defines where to store data outside of the container.
- It configures the path where Podman mounts persistent volume inside of the container.
- We can define more than one path to create multiple volumes.
- Use the VOLUME instruction to persistently store data.
- The value is the path where Podman mounts a persistent volume inside of the container.
- The VOLUME instruction accepts more than one path to create multiple volumes.
- When we inspect an image the Mountpoint field gives us the absolute path to the directory where the volume exists on our host file system.
- Volumes created from the VOLUME instruction have a random ID in the Name field and are considered Anonymous volumes.
- To remove unused volumes, use the podman volume prune command.
- To create a named volume by using the podman use volume create command.
- Use podman volume ls command to include the Mountpoint field of every volume.
- Each Containerfile instruction runs in an independent container by using an intermediate image built from every previous command.
- Each instruction is independent from other instructions in the Containerfile.
- We can specify an image name to identify an image.
- The image name is a string composed of letters, numbers, and some special characters.
- An image tag comes after the image name and is delimited by a colon (:).
- If we omit an image tag, podman uses the default latest tag.
- The full name of the image includes both a name and an optional image tag.
- Reducing the number of RUN instructions also reduces the number of resulting image layers.
- Merge all RUN instrusctions if possible.
- By using WORKDIR we don't need to change directory in other instructions.
- Advanced Containerfile Instructions
- The container must not be bound to a specific environment i.e. it should not require rebuild before deploying to a production environment.
- The container should not contain developer tools, such as a debugger, text editors, or compilers.
- The container should not contain build-time dependencies that are not necessary at runtime.
- The container should not generate a large volume of files stored on the copy-on-write file system, this limits the performance of the application.
- We can reduce image storage footprint by using the multistage container build pattern.
- We should customize container runtime with environment variables.
- We should use volumes to decrease the container size and increase the performance of writing files.
- Podman Secrets
- A secret is a blob of sensitive information required by containers at runtime.
- This can be usernames, passwords, or keys.
- For example credentials for connecting to a database etc.
- After creating the secret, we must instruct the application container to make the credentials available to the application when it starts.
- We use the podman secret subcommands for this.
- We can create secrets by using either a file, or by passing the sensitive information to the standard input (STDIN).
- To create a secret from a file, run the podman secret create subcommand specifying the name of the file containing the sensitive information, and the name of the secret to create as arguments.
- The output of the podman secret create subcommand displays the secret ID.
- The podman secret create command supports different drivers to store the secrets.
- We use the --driver or -d option to specify one of the supported secret drivers
- file(default)
- Stores the secret in a read-protected file.
- pass
- Stores the secret in a GPG-encrypted file.
- shell
- Manages the secret storage by using a custom script
- We can remove a secret by running the podman secret rm command, and providing the name of the secret as argument.
- Running Containers With Podman Secrets
- To make secrets available for use to a container, execute the podman run command with the --secret option, and specify the name of the secret as parameter.
- Use the --secret option multiple times for multiple secrets.
- When we use secrets, Podman retrieves the secret and places it on a tmpfs volume and then mounts the volume inside the container in the /run/secret directory as a file based on the name of the secret.
- To prevent secrets from being stored in an image, neither the podman commit nor podman export commands copy the secret data to an image or .tar file.
- Multistage Builds
- A multistage build uses multiple FROM instructions to create multiple independent container build processes, also called stages.
- Every stage can use a different base image and you can copy files between stages.
- The resulting container image consists of the last stage.
- Multistage builds can reduce the image size by only keeping the necessary runtime dependencies.
- For example if we are building a container with nodejs application we will follow following steps.
- The npm install command will install the required NPM packages, which includes packages that are only needed at build-time.
- The npm run build command uses the packages to create an optimized production-ready application build.
- Then, the container uses an HTTP server to expose the application by using the serve command.
- The above image contains both the build-time and runtime dependencies, which increases the image size.
- The resulting image also contains the Node.js runtime, which is not used at container runtime and may increase the attack surface of the container.
- To avoid above issue, define two stages:
- First stage: Build the application.
- Second stage: Copy and serve the static files by using an HTTP server, such as NGINX or Apache Server.
- Container Data Layers
- Container images use a copy-on-write (COW), layered file system. When you create a Containerfile, the RUN, COPY, and ADD instructions create layers.
- The layered COW file system ensures that a container remains immutable.
- On starting a container, Podman creates and mounts a thin, ephemeral, writable layer on top of the container image layers.
- On deleting the container, Podman deletes the writable thin layer.
- All container layers stay identical, except for the thin writable layer.
- Cache Image Layers
- Because all container layers are identical, multiple containers can share the layers.
- Podman caches immutable layers, and builds only those layers that are mutable or not cached.
- Caching decreases the build time.
- We can separate build and src file directories
- After copying build packaging files and Running the Compile and Install of dependencies.
- Copy the src folder separately.
- Since each time we run the RUN command a separte immutable layer is created.
- Thus if we change your application source code in the src directory and rebuild container image, then the dependency layer is cached and skipped, which reduces the build time.
- Reduce Image Layers
- Reduce the number of container image layers by chaining RUN instructions.
- We can chain commands by using the double ampersand (&&).
- We can also use the backslash character (\) to break a long command into multiple lines.
- Advantage of chaining commands is that it creates less container image layers, which typically results in smaller images.
- Chained commands are more difficult to debug and cache.
- We can also configure Podman to squash the layers.
- Use the --squash option to squash layers declared in the Containerfile.
- Use the --squash-all option to also squash the layers from the parent image.
- We can reduce the number of layers by using multistage builds in combination with chaining some commands.
- Refereces
Comments
Post a Comment