✦ For everyone, free.

Practical knowledge for real and everyday life

Home

18.3.1.1 Kubernetes OCI Images

A focused guide to Kubernetes OCI Images, connecting core concepts with practical Docker and container operations.

Kubernetes OCI images refers to the actual standardized format, defined by the Open Container Initiative's image and distribution specifications, that makes an image built with Docker meaningfully interoperable with containerd or any other CRI-compliant runtime a Kubernetes node happens to be running, and understanding the structure of this format directly clarifies what is actually being pulled, verified, and unpacked when a Kubernetes node schedules a pod.

The OCI image manifest structure

An OCI image is not a single file but a structured manifest referencing a configuration blob and an ordered list of filesystem layer blobs, each individually content-addressed by its own digest:

docker manifest inspect my-api:1.4.2
{
  "schemaVersion": 2,
  "config": { "digest": "sha256:abc123...", "size": 1234 },
  "layers": [
    { "digest": "sha256:def456...", "size": 5678901 },
    { "digest": "sha256:789abc...", "size": 234567 }
  ]
}

When containerd on a Kubernetes node pulls this image, it retrieves the manifest first, then separately retrieves each referenced layer blob, verifying each one's content against its declared digest before unpacking it, which is the same fundamental process regardless of whether the image was originally built with Docker or any other OCI-compliant tool.

The OCI distribution specification

Separately from the image format itself, the OCI distribution specification defines the actual HTTP API a registry implements for pushing and pulling images, which is what allows a containerd-based Kubernetes node to pull from the same registry a Docker-based build pipeline pushed to, since both sides are implementing the identical, standardized protocol:

curl -H "Accept: application/vnd.oci.image.manifest.v1+json" \
  https://registry.example.com/v2/my-api/manifests/1.4.2

This standardized API is precisely why no special configuration is needed on the registry side to support Kubernetes specifically; any registry implementing the distribution specification correctly is already compatible with any OCI-compliant client, whether that client is Docker, containerd, or another tool entirely.

Digest verification during the pull

Each layer's content is independently hashed and compared against its manifest-declared digest as part of the pull process, which is what guarantees the actual bytes a Kubernetes node retrieves and runs are genuinely, verifiably identical to what was originally pushed, with no possibility of silent corruption or substitution going undetected:

crictl pull registry.example.com/my-api:1.4.2

This verification happens transparently as part of the normal pull process; it is not an optional, separately invoked security check, which means image integrity verification is structurally built into how OCI images are pulled by any compliant runtime, including the containerd implementation Kubernetes nodes actually use.

OCI annotations versus Docker labels

The OCI specification defines a standardized set of annotation keys for common image metadata, source revision, creation timestamp, version, which overlaps conceptually with Docker's own LABEL instruction but follows a specific, standardized naming convention intended for broad, cross-tool interoperability:

LABEL org.opencontainers.image.revision="a1b2c3d4"
LABEL org.opencontainers.image.created="2024-06-01T12:00:00Z"

Using these standardized org.opencontainers.image.* annotation keys, rather than arbitrary, project-specific label names, for genuinely standard metadata maximizes the chance that any tool, including Kubernetes-adjacent tooling that specifically looks for these standardized keys, can correctly interpret and surface that metadata without needing project-specific knowledge of a custom labeling convention.

How containerd unpacks layers onto a node

After pulling and verifying each layer, containerd extracts and stacks them using the same union filesystem concept (commonly overlayfs on Linux) that Docker itself uses internally, which is why the actual on-disk filesystem behavior, copy-on-write semantics, layer sharing across multiple images, behaves consistently whether the underlying runtime happens to be Docker Engine or containerd running directly under Kubernetes.

crictl images
ctr images list

Inspecting an image's pulled, unpacked state directly through containerd's own tooling on a Kubernetes node confirms this consistency, showing the same fundamental layered structure that docker images and docker history would reveal for the identical image pulled through Docker instead.

Common mistakes

  • Assuming an image needs any special preparation or reformatting specifically for Kubernetes compatibility, when any genuinely OCI-compliant image, including anything built with docker build, already satisfies the requirement.
  • Not understanding that registry compatibility comes from the standardized OCI distribution specification, leading to unnecessary concern about whether a given registry "supports" Kubernetes specifically.
  • Using arbitrary, custom label keys for genuinely standard metadata, missing the interoperability benefit of the standardized org.opencontainers.image.* annotation convention.
  • Assuming digest verification during a pull is an optional, separately configured security feature, rather than recognizing it as a structurally built-in property of how OCI images are pulled by any compliant client.
  • Expecting meaningfully different filesystem layering or copy-on-write behavior between Docker Engine and containerd, when both implement the same underlying union filesystem concept against the same OCI layer format.

Kubernetes OCI images are, at the format level, identical regardless of which tool built them, since the OCI image and distribution specifications are precisely what guarantees this interoperability, and understanding the manifest structure, digest verification, and standardized annotation convention clarifies exactly what is actually happening, and why no special translation is needed, when an image built with Docker gets pulled and run by containerd on a Kubernetes node.