Kubernetes 运行时: 从 Docker 到 CRI
深入解析 Kubernetes 容器运行时接口 (CRI), 对比 containerd, CRI-O 以及 Kata, gVisor 等安全沙箱技术.
在 Kubernetes 中, Container Runtime (容器运行时) 是负责拉取镜像, 运行容器的底层组件. 随着 Kubernetes 移除对 Docker 的直接支持 (Dockershim), 运行时领域进入了 CRI (Container Runtime Interface) 时代.
| 目标 | 描述 |
|---|
| 解耦 | Kubelet 与运行时实现分离 |
| 标准化 | 统一的 gRPC 接口 |
| 可扩展 | 支持多种运行时 |
// RuntimeService - 容器生命周期
service RuntimeService {
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse);
rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse);
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse);
rpc StartContainer(StartContainerRequest) returns (StartContainerResponse);
rpc StopContainer(StopContainerRequest) returns (StopContainerResponse);
}
// ImageService - 镜像管理
service ImageService {
rpc ListImages(ListImagesRequest) returns (ListImagesResponse);
rpc PullImage(PullImageRequest) returns (PullImageResponse);
rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse);
}
CRI 引入了 Pod Sandbox 概念:
| 组件 | 职责 |
|---|
| Snapshotter | 文件系统快照 (overlayfs) |
| Content Store | 镜像内容存储 |
| Runtime | 容器生命周期管理 |
| Shim | 容器进程托管, 允许 containerd 重启 |
# /etc/containerd/config.toml
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
| 工具 | 用途 |
|---|
ctr | containerd 原生 CLI |
nerdctl | Docker 兼容 CLI |
crictl | CRI 调试工具 |
- 单一职责: 仅为 Kubernetes 服务
- 轻量: 无守护进程, 由 conmon 托管
| 特性 | 描述 |
|---|
| 版本对齐 | CRI-O 版本与 K8s 版本对齐 |
| OpenShift 默认 | Red Hat 生态首选 |
| 无 daemon | conmon 托管容器进程 |
| 风险 | 描述 |
|---|
| 共享内核 | 容器共享宿主机内核 |
| 容器逃逸 | 内核漏洞可能导致逃逸 |
| 多租户隔离 | 不同租户容器需要强隔离 |
| 特性 | 值 |
|---|
| 隔离 | 硬件虚拟化 (VM) |
| 性能 | 中 (虚拟化开销) |
| 兼容性 | 极好 |
| 启动时间 | ~150ms (Firecracker) |
配置 RuntimeClass:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata
handler: kata
---
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
runtimeClassName: kata
| 特性 | 值 |
|---|
| 隔离 | 用户态内核 |
| 性能 | 中 (syscall 拦截) |
| 兼容性 | 部分 syscall 不支持 |
| 启动时间 | 毫秒级 |
| 特性 | runc | Kata | gVisor |
|---|
| 隔离技术 | Namespace/Cgroups | VM | 用户态内核 |
| 隔离强度 | 低 | 极高 | 高 |
| 性能 | 极高 | 中 | 中 |
| 兼容性 | 完美 | 极好 | 一般 |
| 适用场景 | 通用 | 多租户/不可信代码 | 高安全 Web |
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: high-security
handler: kata
overhead:
podFixed:
memory: "120Mi"
cpu: "250m"
scheduling:
nodeSelector:
security: high
apiVersion: v1
kind: Pod
metadata:
name: untrusted-workload
spec:
runtimeClassName: high-security
containers:
- name: app
image: untrusted-image:latest
image/
├── index.json # 镜像索引 (多架构)
├── blobs/ # 内容寻址存储
│ ├── sha256:... # 配置
│ └── sha256:... # 层
└── oci-layout # 版本标识
| 策略 | 描述 |
|---|
Always | 每次都拉取 |
IfNotPresent | 本地不存在时拉取 |
Never | 不拉取, 必须本地存在 |
apiVersion: v1
kind: Secret
metadata:
name: registry-secret
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64>
---
apiVersion: v1
kind: Pod
spec:
imagePullSecrets:
- name: registry-secret
# 配置
cat /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
# 常用命令
crictl pods # 列出 Pod
crictl ps # 列出容器
crictl images # 列出镜像
crictl logs <container-id> # 查看日志
crictl exec -it <id> sh # 进入容器
crictl inspect <id> # 检查容器
# ctr 命令
ctr namespaces ls
ctr -n k8s.io containers ls
ctr -n k8s.io images ls
# nerdctl (Docker 兼容)
nerdctl -n k8s.io ps
nerdctl -n k8s.io images
# 1. 安装 containerd
apt install containerd
# 2. 配置 Kubelet
# /var/lib/kubelet/kubeadm-flags.env
--container-runtime-endpoint=unix:///run/containerd/containerd.sock
# 3. 重启 Kubelet
systemctl restart kubelet
kubectl get nodes -o wide
# 查看 CONTAINER-RUNTIME 列
总结: K8s 运行时的演进方向是 "更轻, 更专, 更安全". containerd 解决了 "专" 的问题, 而 Kata Containers 正在解决 "安全" 的边界.