Kubernetes Overview · Part 0404

Docker vs containerdWhich one does Kubernetes use?

CRI, OCI, dockershim, ctr, nerdctl and crictl: the difference between Docker and containerd, and why Docker images still work on Kubernetes.

Translated from the original Bangla with AI. If anything reads oddly, the Bangla version is the reference.

Diagram comparing the old Kubernetes runtime path through dockershim and Docker Engine with the modern path through CRI and containerd
FIG. 04 Docker builds the image; Kubernetes runs it with containerd, through the CRI.

Previous tutorial: Kubernetes Architecture →

The main question of this tutorial:

In older Kubernetes tutorials, we often see:

Kubernetes + Docker

But in modern Kubernetes tutorials, we more often see:

Kubernetes + containerd

So the questions are:

  • Has Docker been removed from Kubernetes?
  • Do Docker images no longer run on Kubernetes?

Answer:

What changed is mainly the container runtime architecture.

1. In the beginning, Kubernetes used Docker

Docker became very popular because it made using containers much easier.

Example commands:

docker run nginx
docker ps
docker logs <container-id>

Docker was developer-friendly.

So when Kubernetes started, it orchestrated Docker containers.

Concept:

Kubernetes
    ↓
Docker
    ↓
Containers

In other words:

2. The problem: Kubernetes didn’t want to support only Docker

Later, more container runtimes started to appear.

For example:

  • rkt / Rocket
  • containerd
  • CRI-O

Then the problem became:

Like this:

Kubernetes
├── Docker integration
├── containerd integration
├── CRI-O integration
└── rkt integration

That is hard to maintain.

So Kubernetes introduced a standard interface.

Its name:

3. What is the CRI?

CRI = Container Runtime Interface

It is the standard communication interface between Kubernetes and a container runtime.

Simple definition:

Concept:

Kubernetes
    ↓
   CRI
    ↓
Container Runtime

The runtime can be:

  • containerd
  • CRI-O
  • other compatible runtimes

Because of this, Kubernetes doesn’t have to worry so much about runtime-specific implementations.

If a runtime is CRI-compatible, Kubernetes can communicate with it.

4. Why is the CRI useful?

Imagine Kubernetes talked to every runtime directly, in a different way for each.

Then the architecture would be:

            Kubernetes
          /     |      \
         ↓      ↓       ↓
     Docker  CRI-O  containerd

And every integration would have to be maintained separately.

After the CRI:

             Kubernetes
                 ↓
                CRI
          ┌──────┼──────┐
          ↓      ↓      ↓
     containerd CRI-O  Others

In other words:

5. What is OCI?

Another important term: OCI

OCI stands for:

OCI defines industry standards for containers.

The tutorial covers two important OCI specifications:

OCI Image Specification

Defines: what format a container image has.

OCI Runtime Specification

Defines: how a container runtime runs a container.

Simple idea:

This standardization is very important.

Because:

An image built with Docker can also be run with containerd.

6. Why does OCI matter so much?

Say you build an image with Docker:

docker build -t myapp .

Result:

Docker-built Image

Now, the Kubernetes node doesn’t have Docker Engine.

What it has is:

containerd

It can still run the image.

Because:

Docker Image
     ↓
OCI Image Standard
     ↓
containerd understands it

That is the key point.

7. What is Docker, really?

Many beginners think:

But Docker is not just a runtime.

Docker is a full container platform.

It includes many components and features:

  • Docker CLI
  • Docker API
  • Image build tools
  • Networking
  • Volumes
  • Security features
  • containerd
  • runc

Conceptually:

Docker Platform
│
├── Docker CLI
├── Docker API
├── Build Tools
├── Networking
├── Volumes
├── containerd
└── runc

So:

8. What is containerd?

containerd is a lower-level container runtime component.

It was originally part of the Docker ecosystem, and later evolved into an independent project.

Simple definition:

Concept:

Docker
  ↓
containerd
  ↓
runc
  ↓
Container

Modern Kubernetes can communicate with containerd directly, through the CRI.

Kubernetes
    ↓
   CRI
    ↓
containerd

So a Kubernetes node doesn’t need the full Docker platform just to run containers.

9. What is runc?

The tutorial’s Docker architecture also includes runc.

Simplified hierarchy:

Docker
  ↓
containerd
  ↓
runc
  ↓
Linux Container

runc is an even lower-level OCI runtime.

As a beginner, this mental model is enough:

Docker = high level

containerd = runtime manager

runc = very low-level container execution

10. The relationship between Docker and containerd

This is very important.

Docker and containerd are not completely unrelated competitors.

Actually:

Concept:

Docker CLI
    ↓
Docker Engine
    ↓
containerd
    ↓
runc
    ↓
Container

In other words, containerd is an important part of the runtime pieces underneath the Docker platform.

11. So how did Kubernetes use Docker before?

This is where dockershim comes in.

Docker was created before Kubernetes had the CRI.

So Docker Engine didn’t fit Kubernetes’s CRI model directly.

Kubernetes therefore built a compatibility layer: dockershim

Simple definition:

Architecture:

Kubernetes
    ↓
dockershim
    ↓
Docker
    ↓
containerd
    ↓
runc

It worked.

But it added an extra layer to the architecture.

12. Why was dockershim needed?

Kubernetes wanted:

Kubernetes
    ↓
   CRI
    ↓
Runtime

But Docker didn’t follow that CRI structure directly at the time.

So:

Kubernetes
    ↓
dockershim
    ↓
Docker

dockershim acted as a translation/compatibility bridge.

13. Why did Kubernetes remove Docker support?

dockershim was removed in Kubernetes version 1.24.

Meaning:

But once again:

Docker images still work.

Important distinction:

Docker Runtime Integration ❌

Docker Images ✅

14. What did the architecture look like before?

Before:

Kubernetes
    ↓
dockershim
    ↓
Docker Engine
    ↓
containerd
    ↓
runc
    ↓
Container

Lots of layers.

15. Modern Architecture

Now:

Kubernetes
    ↓
CRI
    ↓
containerd
    ↓
runc
    ↓
Container

In other words, the unnecessary middle layer has been removed.

Very simple:

BEFORE

Kubernetes
↓
dockershim
↓
Docker
↓
containerd

NOW

Kubernetes
↓
CRI
↓
containerd

This is one of the main concepts of the whole tutorial.

16. Docker vs containerd

FeatureDockercontainerd
TypeFull container platformContainer runtime
User-friendly CLIYesLimited / lower-level
Build imagesYesNot its main focus
Run containersYesYes
Networking toolsYesRuntime-focused
Volume supportYesLower-level
Developer friendlyVeryComparatively low-level
Kubernetes runtimeNot Docker Engine directlyCommonly used
Best forDevelopment/local workflowsKubernetes runtime

Simple summary:

And:

17. Car Analogy

An easy analogy:

Docker

The whole car:

  • Dashboard
  • Steering
  • Air Conditioning
  • Seats
  • Controls
  • Engine

containerd

The car’s engine / core running mechanism

In other words:

Docker = Full Car

containerd = Engine

It isn’t an exact technical mapping, but it helps with the concept.

18. Do Docker images still work on Kubernetes?

Yes.

This is one of the most important points of the tutorial.

You can build an image locally with Docker:

docker build -t myapp .

Then push it to a registry.

Example:

Docker
   ↓
Build Image
   ↓
Container Registry
   ↓
Kubernetes
   ↓
containerd
   ↓
Run Image

Because the image is OCI-compatible.

19. Practical Workflow

Developer machine:

Code
 ↓
Dockerfile
 ↓
docker build
 ↓
Image

Then:

Image
 ↓
Registry

Kubernetes cluster:

Registry
 ↓
containerd pulls image
 ↓
Pod starts
 ↓
Container runs

So even though Docker is no longer directly connected to Kubernetes as its runtime:

20. Now let’s understand the CLI tools

The tutorial introduced three command-line tools:

  • ctr
  • nerdctl
  • crictl

These are easy to confuse.

So let’s look at them one at a time.

21. What is ctr?

ctr comes with containerd.

It is a: low-level containerd CLI

It is mainly used for:

  • Testing
  • Debugging
  • Low-level containerd interaction

Example:

ctr images pull docker.io/library/redis:latest
ctr run docker.io/library/redis:latest redis

Simple definition:

It isn’t very user-friendly for a normal, everyday developer workflow.

22. What is nerdctl?

nerdctl is a: Docker-like CLI for containerd

If you know Docker, nerdctl commands will feel familiar.

Example:

nerdctl run nginx
nerdctl ps
nerdctl logs <container-id>

See how it mirrors Docker:

docker run   →   nerdctl run
docker ps    →   nerdctl ps

Simple definition:

23. Why is nerdctl useful?

Say your machine doesn’t have Docker Engine.

But it does have:

containerd

And you want a Docker-style experience.

Then you can use nerdctl.

Concept:

You
 ↓
nerdctl
 ↓
containerd
 ↓
Container

24. What is crictl?

Now a very important debugging tool for Kubernetes: crictl

crictl is a CLI from the Kubernetes community ecosystem.

It communicates with CRI-compatible runtimes.

Example runtimes:

  • containerd
  • CRI-O
  • Other CRI-compatible runtimes

Simple definition:

25. crictl Commands

Examples:

crictl ps

Shows running containers.

crictl images

Shows images.

crictl logs <container-id>

Shows logs.

crictl pods

Shows Pods / runtime-level pod sandboxes.

crictl exec -it <container-id> sh

Runs a command inside a container.

26. When should I use crictl?

Say a Kubernetes Pod isn’t starting properly.

Your initial investigation with kubectl isn’t giving you useful information.

Then you may need to go to the node and debug at the runtime level.

Example:

Kubernetes Pod Problem
        ↓
kubectl inspect
        ↓
Need deeper runtime debugging
        ↓
SSH / access Node
        ↓
crictl

At that point, these can be useful:

crictl ps -a
crictl logs <container-id>
crictl images

27. Should you create production containers manually with crictl?

Mainly: No.

crictl primarily debugging tool.

Because managing Kubernetes workloads is the responsibility of the Kubelet.

If you create a container manually, outside Kubernetes:

You
 ↓
crictl/manual runtime action
 ↓
Container

that container isn’t part of Kubernetes’s desired state.

So Kubernetes doesn’t treat that manually created container as a normal managed application.

The main principle in a Kubernetes environment:

For example:

  • Deployment
  • Pod
  • StatefulSet
  • DaemonSet

28. kubectl vs crictl

This is a very important difference.

kubectl

kubectl
   ↓
Kubernetes API
   ↓
Kubernetes Objects

Use it to:

  • Pods manage
  • Deployments manage
  • Services manage
  • Nodes inspect
  • Cluster state inspect

crictl

crictl
   ↓
CRI
   ↓
Container Runtime

Use it for: node/runtime-level troubleshooting.

29. ctr vs nerdctl vs crictl

ToolMain PurposeTalks ToTypical User
ctrLow-level debuggingcontainerdRuntime/admin
nerdctlDocker-like container managementcontainerdDeveloper/admin
crictlKubernetes runtime debuggingCRI runtimeKubernetes admin
kubectlKubernetes managementAPI ServerKubernetes user/admin

30. Easy Memory Trick

Remember these four lines:

kubectl  → Kubernetes manage
crictl   → Kubernetes runtime debug
nerdctl  → Docker-like containerd CLI
ctr      → Low-level containerd CLI

31. Docker Commands vs crictl Commands

Many of the commands look similar.

DockercrictlPurpose
docker pscrictl psRunning containers
docker imagescrictl imagesImages
docker logscrictl logsLogs
docker execcrictl execCommand inside a container
docker inspectcrictl inspectContainer details
docker versioncrictl versionVersion

But their purpose is different.

Docker: General-purpose container workflow

crictl: Kubernetes runtime troubleshooting

32. Which tool for which situation?

To build an image locally

Use: Docker

docker build -t myapp .

For Docker-like commands with containerd

Use: nerdctl

To debug containerd at a low level

Use: ctr

To troubleshoot a Kubernetes node’s runtime

Use: crictl

To manage Kubernetes applications

Use: kubectl

33. Full Architecture

Now let’s connect everything together.

Developer side:

Developer
   ↓
Docker
   ↓
Build Image
   ↓
OCI Image
   ↓
Registry

Kubernetes side:

Kubernetes
   ↓
Kubelet
   ↓
CRI
   ↓
containerd
   ↓
runc
   ↓
Container

This flow is very important.

34. Kubernetes Runtime Architecture

In more detail:

                 Kubernetes Control Plane
                          │
                          ▼
                       Kubelet
                          │
                          ▼
                         CRI
                          │
                          ▼
                     containerd
                          │
                          ▼
                        runc
                          │
                          ▼
                      Container

Here:

Kubernetes
says what needs to run.
Kubelet
Node-level agent.
CRI
Communication interface.
containerd
Manages the container lifecycle.
runc
Does the low-level container execution.

35. Docker Architecture vs Kubernetes Architecture

Docker

Developer
   ↓
Docker CLI
   ↓
Docker Engine
   ↓
containerd
   ↓
runc
   ↓
Container

Kubernetes

Kubernetes
   ↓
Kubelet
   ↓
CRI
   ↓
containerd
   ↓
runc
   ↓
Container

Notice that both can share the same components at the bottom:

containerd
   ↓
runc

That is why Docker and the Kubernetes/containerd ecosystem have historically been so closely related.

36. Most Important Difference

Remember this:

And:

Docker:

Build
Run
Network
Volumes
CLI
Developer UX

containerd:

Pull images
Manage containers
Run containers
Runtime lifecycle

37. Very Important Misunderstanding

Many people say:

That is the wrong mental model.

Correct:

Docker Engine as Kubernetes runtime
→ No longer directly used through built-in dockershim

But:

Docker-built OCI Image
→ Works ✅

Because:

38. Image vs Runtime

Keep this clear too.

Image

An application’s packaged artifact.

Example:

myapp:1.0

Runtime

Runs containers from images.

Example:

containerd

Flow:

Image
  ↓
Runtime
  ↓
Running Container

So having a Docker image doesn’t mean you need Docker Engine.

39. CRI vs OCI

These two are easy to confuse.

CRI: Container Runtime Interface

Question:

Answer: CRI

Concept:

Kubernetes
   ↓
CRI
   ↓
Runtime

OCI: Open Container Initiative

Question:

Answer: OCI

Concept:

OCI
├── Image Specification
└── Runtime Specification

40. CRI vs OCI Easy Memory

Remember:

Kubernetes
   ↓
CRI
   ↓
containerd

And:

Docker Image
   ↓
OCI Standard
   ↓
Different compatible runtimes

41. Everything through the car analogy

Imagine:

Docker

The whole car.

  • Dashboard
  • Steering
  • Seats
  • Controls
  • Engine

containerd

Engine/core runtime.

ctr

The mechanic’s low-level diagnostic tool.

nerdctl

A Docker-like steering/control interface for containerd.

crictl

The Kubernetes mechanic’s runtime diagnostic tool.

kubectl

The remote/control panel for controlling the whole Kubernetes system.

42. Tool Mental Model

Docker      = Full container platform
containerd  = Container runtime
ctr         = Low-level containerd debugging
nerdctl     = Docker-like containerd CLI
crictl      = Kubernetes runtime debugging
kubectl     = Kubernetes management

43. The 7 most important points

1. Docker is a full platform

Docker
=
CLI + Build + Network + Volumes + Runtime Components

2. containerd is a runtime

containerd
↓
Runs/manages containers

3. Kubernetes uses the CRI

Kubernetes
↓
CRI
↓
containerd

4. OCI provides standardization

OCI
↓
Image + Runtime Standards

5. dockershim was a bridge

Kubernetes
↓
dockershim
↓
Docker

6. dockershim has been removed

Modern path:

Kubernetes
↓
CRI
↓
containerd

7. Docker images still work

Docker Build
↓
OCI Image
↓
containerd
↓
Kubernetes Container

44. Final Architecture Diagram

              DEVELOPMENT
                  │
                  ▼
             Docker CLI
                  │
                  ▼
             Docker Build
                  │
                  ▼
              OCI Image
                  │
                  ▼
          Container Registry
                  │
                  ▼
        ─────────────────────
              KUBERNETES
                  │
                  ▼
               Kubelet
                  │
                  ▼
                 CRI
                  │
                  ▼
             containerd
                  │
                  ▼
                runc
                  │
                  ▼
              Container

45. Final Simplified Explanation

The whole tutorial, simply put:

Kubernetes used to use Docker:

Kubernetes
↓
Docker

But Kubernetes wanted to support different container runtimes.

So the CRI (Container Runtime Interface) was created.

Because Docker didn’t fit the CRI architecture directly, Kubernetes used dockershim.

The architecture was:

Kubernetes
↓
dockershim
↓
Docker
↓
containerd

Later, Kubernetes removed dockershim.

Modern architecture:

Kubernetes
↓
CRI
↓
containerd
↓
runc
↓
Container

But Docker images still work.

Because:

So you can build an image with Docker:

docker build -t myapp .

and Kubernetes can run that image with containerd.

One-Line Summary

The most useful memory flow:

Docker
↓
Build OCI Image
↓
Registry
↓
Kubernetes
↓
Kubelet
↓
CRI
↓
containerd
↓
runc
↓
Container

And the tools:

kubectl  → Kubernetes manage
crictl   → Kubernetes runtime debug
nerdctl  → Docker-like containerd CLI
ctr      → Low-level containerd debug

Was this guide helpful?

Search the archive

Press Esc to close