Cloud Native 13 min read

How Containerd ShimV2 Simplifies Integrating Kata Containers with Kubernetes

This article explains the challenges of CRI integration with multiple container runtimes, introduces Containerd ShimV2 as a standardized interface, and provides step‑by‑step instructions for configuring Kata Containers as a Kubernetes runtime using RuntimeClass or untrusted_workload_runtime methods.

Ops Development Stories
Ops Development Stories
Ops Development Stories
How Containerd ShimV2 Simplifies Integrating Kata Containers with Kubernetes

Introduction

From the article "CRI shim: how kubelet interacts with container runtimes (Part 1)" we learned three key points:

CRI is designed for Kubernetes, not OCI, so integrating VM‑based runtimes such as gVisor or Kata Containers often mismatches CRI assumptions and APIs.

Maintaining many CRI implementations (e.g., RedHat's cri‑o, containerd) is difficult because they all ultimately rely on runC, requiring separate shims for each runtime.

Multiple container runtimes serve different purposes, for example using a virtualized engine for untrusted or multi‑tenant workloads while using Docker for system components that cannot be virtualized.

These issues motivate the creation of Containerd ShimV2.

Containerd ShimV2

In 2018, the containerd community introduced the shimv2 API, which builds on CRI to provide a more mature and convenient way for users to integrate their own container runtimes.

ShimV2 adds a standardized layer between the CRI shim and the underlying Containerd runtime. Instead of a direct CRI → Containerd → runC path, the flow becomes CRI → Containerd → ShimV2 → (runC | Kata Container).

The main advantage is that each Pod can specify its own Shim. Containerd now starts or stops a ShimV2 instance, and the implementation of these start/stop operations is left to the developer.

For example, a Kata Containers maintainer can launch a Containerd Shim when creating a sandbox, then reuse that sandbox for subsequent container operations, eliminating the need to start a new shim for each container.

In practice, you keep the original CRI Containerd (using runC) and add a Kata Container shim implementation (kata‑Containerd‑Shimv2) on top.

Providing kata-runtime to Kubernetes

To run containers with kata-runtime, you must first install Kata on each node and then tell Kubernetes which workloads should use it. The steps below assume a CentOS system.

<code>$ source /etc/os-release
$ yum -y install yum-utils
$ ARCH=$(arch)
$ BRANCH="${BRANCH:-master}"
$ yum-config-manager --add-repo "http://download.opensuse.org/repositories/home:/katacontainers:/releases:/${ARCH}:/${BRANCH}/CentOS_${VERSION_ID}/home:katacontainers:releases:${ARCH}:${BRANCH}.repo"
$ yum -y install kata-runtime kata-proxy kata-shim
</code>

Verify hardware requirements (e.g., Intel VT‑x, ARM Hyp mode, IBM Power, IBM Z) and run the check command:

<code>$ kata-runtime kata-check
System is capable of running Kata Containers
System can currently create Kata Containers
</code>

Install a Kubernetes cluster with kubeadm, then generate the default containerd configuration:

<code>containerd config default > /etc/containerd/config.toml
</code>

Two methods can expose Kata to Kubernetes:

RuntimeClass – requires specific versions (Kata v1.5.0+, containerd v1.2.0+, Kubernetes v1.12.0+). Add the following to

config.toml

:

<code>[plugins.cri.containerd]
  no_pivot = false
[plugins.cri.containerd.runtimes]
  [plugins.cri.containerd.runtimes.runc]
    runtime_type = "io.containerd.runc.v1"
    [plugins.cri.containerd.runtimes.runc.options]
      NoPivotRoot = false
      NoNewKeyring = false
      ShimCgroup = ""
      IoUid = 0
      IoGid = 0
      BinaryName = "runc"
      Root = ""
      CriuPath = ""
      SystemdCgroup = false
  [plugins.cri.containerd.runtimes.kata]
    runtime_type = "io.containerd.kata.v2"
  [plugins.cri.containerd.runtimes.katacli]
    runtime_type = "io.containerd.runc.v1"
    [plugins.cri.containerd.runtimes.katacli.options]
      NoPivotRoot = false
      NoNewKeyring = false
      ShimCgroup = ""
      IoUid = 0
      IoGid = 0
      BinaryName = "/usr/bin/kata-runtime"
      Root = ""
      CriuPath = ""
      SystemdCgroup = false
</code>

The

kata

entry becomes the RuntimeClass handler.

untrusted_workload_runtime – for environments that do not meet the version requirements, add:

<code>[plugins.cri.containerd.untrusted_workload_runtime]
  runtime_type = "io.containerd.runtime.v1.linux"
  runtime_engine = "/usr/bin/kata-runtime"
</code>

After editing, reload and restart containerd:

<code>$ systemctl daemon-reload
$ systemctl restart containerd
</code>

Method 1: RuntimeClass

<code>kind: RuntimeClass
apiVersion: node.k8s.io/v1beta1
metadata:
  name: kata-containers
handler: kata
</code>

Create a pod that uses the RuntimeClass:

<code>apiVersion: v1
kind: Pod
metadata:
  name: kata-nginx
spec:
  runtimeClassName: kata-containers
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
</code>
<code>$ kubectl apply -f kata-pod.yaml
$ kata-runtime list
</code>

Method 2: untrusted_workload_runtime

Annotate the pod to request an untrusted workload:

<code>annotations:
  io.kubernetes.cri.untrusted-workload: "true"
</code>

Example pod definition:

<code>apiVersion: v1
kind: Pod
metadata:
  name: kata-nginx-untrusted
  annotations:
    io.kubernetes.cri.untrusted-workload: "true"
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
</code>
<code>$ kubectl apply -f kata-pod-untrusted.yaml
$ kata-runtime list
</code>

Conclusion

Kubernetes achieves extensibility by modularizing core features through interfaces and plugins. CRI was the first successful plugin interface, and building on it, Containerd ShimV2 offers a new integration path for custom runtimes.

With ShimV2 you no longer need to write a full CRI shim for each runtime; you can reuse containerd’s CRI support and implement only the ShimV2 layer.

This approach has become the community’s mainstream way to connect lightweight or virtualized runtimes such as Kata Containers, gVisor, and Firecracker to Kubernetes.

References

https://blog.csdn.net/yuchunyu97/article/details/109241723 https://github.com/kata-containers/documentation/blob/master/install/centos-installation-guide.md https://ustack.io/2019-11-21-container%E7%9B%B8%E5%85%B3%E6%A6%82%E5%BF%B5%E6%A2%B3%E7%90%86.html https://github.com/kata-containers/documentation/blob/master/how-to/how-to-use-k8s-with-cri-containerd-and-kata.md https://github.com/kubernetes/kubernetes/issues/73189 https://blog.zufardhiyaulhaq.com/kubernetes-with-cri-containerd-and-kata-containers/ https://www.chenshaowen.com/blog/how-to-integrate-kata-in-kubernetes-cluster.html

cloud-nativekubernetescontainerdkata containersruntimeclassshimv2
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.