March 31, 2022·5 min read·engineeringkubernetesruntimesoperations

Dockershim readiness includes the agents around the workload

March's readiness guidance separates application image compatibility from node runtime and agent dependencies. cri-dockerd also gives Docker Engine users a path through the coming removal.

A cluster can run portable application images and still depend on Docker Engine for its security or telemetry. That is the operational distinction in Kubernetes' March 31 readiness guidance. The coming removal of dockershim affects the connection between kubelet and its container runtime, but the consequences extend to software that has independently attached itself to Docker.

The guidance identifies two ways forward for a cluster that uses Docker Engine. Operators can replace the node runtime with an alternative supported through the Container Runtime Interface, or preserve Docker Engine through cri-dockerd. Those options change different parts of the operating environment. Choosing between them requires knowing why Docker is present in the first place.

I find this a useful correction to the broad argument about Docker deprecation. Kubernetes' reconciliation model benefits from a standard runtime boundary. The actual migration still has to account for agents and administrative tools whose dependencies never passed through that boundary.

There are two separate compatibility questions

The first question is what consumes an application image. The March post explains that Docker-built images comply with the Open Container Initiative format and work with Kubernetes-supported runtimes. Using Docker Desktop to build containers does not establish a cluster-side dependency on Docker Engine.

The second question is what software expects from the node. An agent that talks to Docker Engine requires the daemon's interface, regardless of the format of the agent's own image. Changing the runtime may leave that agent able to start while removing the service it needs to perform its job.

These questions can produce different answers in the same cluster. An ordinary application may require no image changes. A node-level integration may require configuration changes or a replacement. Describing the cluster as either affected or unaffected without identifying the dependency loses information that operators need.

The December 2020 explanation already called out the Docker socket at /var/run/docker.sock. Its example remains useful because a socket mount makes the dependency concrete. The container is asking to communicate with a host service. Image portability does not promise that host service will exist.

This is also why checking only the application's manifests can be insufficient. The March guidance explicitly mentions telemetry and security agents for both self-managed and managed clusters. Outsourcing node provisioning does not prove that every additional agent uses a runtime-independent interface.

What CRI standardizes

The original CRI design places kubelet on the client side of a gRPC interface. ImageService covers image operations. RuntimeService covers pod sandboxes and container lifecycle operations. A runtime implementation or an adapter supplies the server side.

That boundary allows Kubernetes to manage workloads without directly embedding each runtime's integration into kubelet. It does not require every piece of node software to use CRI. A security product can still choose a Docker-specific API, and a provisioning script can still invoke Docker commands. Such choices create dependencies outside the standardized kubelet path.

The distinction also explains the adapter's role. Docker Engine does not implement CRI itself. Dockershim provides the translation inside Kubernetes today. When Kubernetes removes that code, the kubelet still needs a CRI-speaking endpoint. The fact that Docker includes containerd internally does not make Docker Engine itself a CRI endpoint.

Keeping those interfaces separate avoids misleading language about containers ceasing to work. The runtime's responsibility includes pulling an image and running the requested containers. An administrative integration can depend on additional operations that the application workload never uses. A successful application start proves only the relevant portion of that arrangement.

Keeping Docker Engine through cri-dockerd

The March announcement says Mirantis and Docker have released and are maintaining cri-dockerd as a replacement adapter. That makes continued Docker Engine use an explicit option for the coming Kubernetes release. It also moves the adapter's maintenance outside the kubelet codebase.

This option has a specific architectural meaning. Kubelet retains its CRI relationship, with cri-dockerd providing the translation to Docker Engine. An operator who needs Docker compatibility can preserve that part of the environment while changing how Kubernetes reaches it.

That differs from moving directly to containerd. The March guidance describes containerd as a graduated CNCF project and the runtime inside Docker itself. Replacing Docker Engine with containerd removes a layer from the kubelet's execution path, but it also changes the node service available to Docker-dependent tools.

Neither choice can be inferred solely from the presence of Dockerfiles in a source repository. A Dockerfile concerns image construction. Continued dependence on the Engine concerns runtime-side services. The useful design decision starts with the latter requirement.

My preference is to remove unnecessary node dependencies when the environment permits it. Preserving an adapter is still a coherent choice when a concrete integration requires Docker Engine. The requirement should be named, because it determines which interface the operating environment must continue to provide.

Managed defaults do not describe every node

The March post says Amazon EKS, Azure AKS, and Google GKE now default to containerd. It also qualifies that observation with guidance about node customizations and checking the actual runtime. A service's current default does not establish the configuration of every existing node pool.

That distinction is particularly relevant to long-lived infrastructure. A provider can change its default for new provisioning without retroactively replacing every existing environment. Custom images and agents introduce another source of variation. The guidance therefore directs operators to inspect their own nodes instead of deriving their state from a product name.

The same reasoning applies within a single cluster. Node configurations are the relevant evidence for runtime dependence. An application team's local build tool is evidence about a different environment. Combining those observations into one statement about whether an organization uses Docker obscures the migration's scope.

The January commitment promised documentation and outreach after survey responses showed remaining migration work. March supplies a more concrete decision structure. Establish whether kubelet uses Docker Engine, then account for other software that depends on the daemon. For an affected runtime path, choose a replacement runtime or the external adapter.

As of March 31, removal remains part of the upcoming 1.24 release. The practical change in readiness guidance is the clarity of the dependency model. An image can remain unchanged while the node runtime changes, and an agent can need migration even where the provider already supplies containerd. Those cases need separate owners and separate descriptions of the interface each component expects.