A container image is a static file that contains the necessary resources (packages, configuration, other dependencies) required to provision a container. It consists of multiple layered-filesystems and a Manifest file, containing its metadata.

Open Container Initiative (OCI) Specification

Open Container Initiative was established by The Linux Foundation in 2015 to provide

  • Runtime specification
  • Image specification
  • Distribution specification for container images.

A container image created from OCI Image specification should have

  • Layered Filesystem: Stores the packaged contents of the container.
  • Image Index and Manifest: Contains a manifest list that stores the container image metadata for multiple platforms.
  • Image Configuration: Arguments and environment variables for the applications inside the container.

Images created from Docker, Podman, and buildah follow OCI Image Specification.

Layered filesystems

Each filesystem layer stores changes performed on top of the previous layer. They are differentiated using their SHA256 digest.

Base and Image Layers

Base and Image Layers

To build a container image the developer has to use another container image as a base layer. The base layer container image could be chosen based on OS preference (like Fedora, Kali, etc.) or programming language preference (like gcc, python, etc.), or any other preference. On top of the base layer developers can add changes (like installing packages and dependencies, performing configuration changes, etc.) by creating stacked image layers.

The layers are cached on the host to reduce the build time and storage space required by the image. Cached layers could also be reused in the build process of different images, for example, two images (on the same host) using base layer ubuntu:22.0 will refer to the same cached layer.

When a container image is provisioned as a container, the base and image layers are locked as read-only.

Read-Write layer added on top of Image Layers

Read-Write layer added on top of Image Layers

A data state of image layers is generated and a writable container layer is added on top. It stores runtime changes (like file creation, configuration changes, etc.) and deleted with the container.

Image Index and Manifest

The Image Index of a container image contains the metadata related to the filesystem layers, the command to be executed once the container is provisioned and other information required by the container runtime.

To view the index of a container image, the manifest subcommand with inspect option could be used.

docker manifest inspect docker.io/library/httpd:latest

The output will be a JSON document with a manifest array containing references to multiple image variations based on the OS and CPU architecture of the host.

{
    "schemaVersion": 2,
    "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
    "manifests": [
        {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "size": 1366,
            "digest": "sha256:d866e5c91f31fc6a122aaf37149cc67ba2ca0de68ae73ab206747a190937967e",
            "platform": {
                "architecture": "amd64",
                "os": "linux"
            }
        },
        {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "size": 1366,
            "digest": "sha256:32588e5c7552750100ad3110a64d163b1881ce92216d67e828c42d3322c439d1",
            "platform": {
                "architecture": "arm",
                "os": "linux",
                "variant": "v5"
            }
        },
        {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "size": 1366,
            "digest": "sha256:67586a7e127abd9b362884172b575a43b3342725ae310c339f4f7d5e5bdba918",
            "platform": {
                "architecture": "arm",
                "os": "linux",
                "variant": "v7"
            }
        },
        {
            "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
            "size": 1366,
            "digest": "sha256:bc5f484630b50cec12a50035d22ed717d980c52c9871105e91e276ebcbee69a2",
            "platform": {
                "architecture": "arm64",
                "os": "linux",
                "variant": "v8"
            }
        }
    ]
}

An image could be built and distributed for different platforms with singular name and tag. The container engine verifies from the manifest if a compatible image is available for the host.

Image Configuration

The changes to be implemented on the filesystem during the creation of the container are stored in the image configuration . It also stores parameters and environment variables for applications inside the container.

docker image inspect image-name could be used to view the configuration of a container image in JSON format.

[
    {
        "Id": "sha256:daab1fa13f8608841399e8552ab7833e90307543509ced13cd40b3f7411632a3",
        "RepoTags": [
            "httpd:latest"
        ],
        "RepoDigests": [
            "httpd@sha256:76618ddd53f315a1436a56dc84ad57032e1b2123f2f6489ce9c575c4b280c4f4"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2023-03-07T20:24:12.062824222Z",
        "Container": "30df81c7e4e9fcec6990ff7a5aee09f0b8d765a65d575599d92fa3d2c2da6048",
        "DockerVersion": "20.10.23",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "HTTPD_PREFIX=/usr/local/apache2",
                "HTTPD_VERSION=2.4.56",
                "HTTPD_SHA256=d8d45f1398ba84edd05bb33ca7593ac2989b17cb9c7a0cafe5442d41afdb2d7c",
                "HTTPD_PATCHES="
            ],
            "Cmd": [
                "httpd-foreground"
            ],
            "Image": "sha256:a5930d7e9b3c7fa530cff2806db329c228e08a75d9db73314b3b5724c0858b60",
            "Volumes": null,
            "WorkingDir": "/usr/local/apache2",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null,
            "StopSignal": "SIGWINCH"
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 145140075,
        "VirtualSize": 145140075,
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:650abce4b096b06ac8bec2046d821d66d801af34f1f1d4c5e272ad030c7873db",
                "sha256:2309cdaf4afb9ce407a51c6d3ae54fb915bfbcc480e596e72417206902b4c51f",
                "sha256:849b101b0e3b3f03fad010462b6ab14f1372d449a1fb803750182547e7df3d28",
                "sha256:a30707f342ec97e05e57d91804d8ae3d5f93d5e6c9ef2c4e8a3d805b70b0d53e",
                "sha256:087e3023406cdd4c0765ee68d24c88da0c0335ac2ce82d991bd336a648c033c6"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

Distributing container images

Tarfile

Exporting containers

Containers could be exported as tar archives using the export subcommand.

docker export httpd-container > httpd.tar

To import this tarfile as a container image, the import subcommand is used.

docker import httpd.tar

import also supports URL for tarfile as an argument.

Exporting container images

To export container images as tarfile we can use the save subcommand. Output from the command needs to be redirected using > operator into a *.tar file.

docker save httpd:latest > httpd.tar

This tarfile could be loaded as a container image using the load subcommand and < operator.

docker load < httpd.tar

Container Registry

Distributing images using Container Registry

Distributing images using Container Registry

Container Registry is a service that allows the distribution of container images. It also provides features such as

  • Access control to images
  • Vulnerability scanning
  • Control over distribution
  • High availability

Some of the commonly used container registries are Quay.io, DockerHub, and Google Container Registry.

A registry account is necessary to distribute an image. The credentials from the registry will be used to log in from the container engine.

docker login quay.io

The image to be pushed should be tagged/renamed in format <registry-name>/<username>/<image-name>:<image-tag>, for example, quay.io/username/webserver:latest.

docker tag webserver:latest quay.io/username/webserver:latest

push subcommand is used to upload the tagged image to the registry.

docker push quay.io/username/webserver:latest

Creating images from existing containers

Commit running containers to container image

Commit running containers to container image

To create a container image from a running container, the commit subcommand could be used.

docker commit webserver-container quay.io/username/webserver:latest

Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my RSS Feed.

Resources

About the Open Container Initiative
OCI Image Manifest Specification
Image Layer Filesystem Changeset
OCI Image Configuration
About storage drivers
docker export
docker import
docker save
docker load
docker commit
docker push