Kubernetes Runtime Class 原理与实现
Kubernetes Runtime Class 原理与实现
1. 引言
在 Kubernetes 集群中,容器运行时(Container Runtime)是支撑整个容器生态系统的核心组件。随着云原生技术的发展,单一的容器运行时已经无法满足所有场景的需求。不同的工作负载可能需要不同的运行时环境:
• 标准应用:使用 containerd 或 Docker • 安全敏感应用:使用 gVisor 或 Kata Containers • GPU 计算:使用 NVIDIA Container Runtime • Windows 容器:使用 Windows 特定的运行时
Kubernetes RuntimeClass 正是为了解决这种多运行时需求而设计的 API 资源。它允许用户在 Pod 级别指定容器运行时配置,为不同的工作负载选择最适合的运行时环境。
2. RuntimeClass 基本概念
2.1 什么是 RuntimeClass
RuntimeClass 是 Kubernetes 的一个集群级别的资源,用于定义不同的容器运行时配置。每个 RuntimeClass 包含以下信息:
1. 运行时处理器(Handler):标识要使用的运行时配置 2. 调度约束:指定可以运行该 RuntimeClass 的节点 3. Overhead:定义运行时本身消耗的资源 4. Scheduling:配置 Pod 调度相关的参数
2.2 核心组件架构
+----------------+ +-----------------+ +----------------+
| Pod | | RuntimeClass | | Container |
| | | | | Runtime |
| spec.runtime- |---->| spec.handler |---->| Implementation |
| ClassName | | | | (containerd, |
+----------------+ +-----------------+ | gVisor, etc.) |
+----------------+3. RuntimeClass 配置详解
3.1 基本配置示例
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor # RuntimeClass 名称
handler: runsc # 运行时处理器名称
overhead:
podFixed:
memory: "128Mi"
cpu: "250m"
scheduling:
nodeSelector:
runtime: gvisor # 选择具有 gVisor 运行时的节点
tolerations:
- key: "runtime"
operator: "Equal"
value: "gvisor"
effect: "NoSchedule"3.2 核心字段说明
handler (必需字段)
• 作用:标识要使用的运行时配置 • 格式:字符串,对应 CRI 运行时配置中的 "handler" • 示例: runsc(gVisor),kata(Kata Containers),nvidia(NVIDIA)
overhead (可选字段)
• 作用:定义运行时本身消耗的资源开销 • 用途:帮助调度器更准确地进行资源分配 • 字段: • podFixed: 固定资源开销(CPU、内存)• 实践建议:在规划资源配额(Quota)时,必须将 Overhead 计算在内。例如,如果 Pod 请求 1G 内存,Overhead 为 100M,则调度器会认为该 Pod 占用 1.1G 内存。如果忽略这一点,可能导致 ResourceQuota 超标或节点资源超卖。
scheduling (可选字段)
• 作用:配置 Pod 调度相关的约束 • 字段: • nodeSelector: 节点选择器• tolerations: 容忍度配置• 实践建议:建议配合节点标签(Node Labels)和污点(Taints)使用,确保特定运行时的 Pod 仅被调度到已安装并配置好对应运行时的节点上。
4. 运行时处理器配置
4.1 CRI 配置
RuntimeClass 依赖于容器运行时接口(CRI)的实现。需要在每个节点的 CRI 配置中定义处理器:
containerd 配置示例 (/etc/containerd/config.toml)
[plugins."io.containerd.grpc.v1.cri".containerd]
# 默认运行时
default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
# 标准 runc 运行时
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
# gVisor 运行时
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
# Kata Containers 运行时
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
runtime_type = "io.containerd.kata.v2"CRI-O 配置示例 (/etc/crio/crio.conf)
[crio.runtime]
# 默认运行时
default_runtime = "runc"
[crio.runtime.runtimes]
# 标准 runc 运行时
runc = {
runtime_path = "/usr/bin/runc"
}
# gVisor 运行时
runsc = {
runtime_path = "/usr/bin/runsc"
}
# Kata Containers 运行时
kata = {
runtime_path = "/usr/bin/kata-runtime"
}5. 常见运行时与多运行时配置实战
5.1 常见运行时实现
下表总结了 Kubernetes 生态中常见的容器运行时及其特点:
| 运行时名称 | 典型 Handler | 核心特点 | 适用场景 |
|---|---|---|---|
| gVisor | runsc | ||
| Kata Containers | kata | ||
| NVIDIA Runtime | nvidia | ||
| CRI-O | crio | ||
| Firecracker | fc | ||
| Windows Containers | runhcs |
5.2 多运行时环境配置实战(以 runc + NVIDIA 为例)
在实际生产环境中,我们经常需要在一个节点上同时支持普通应用和 GPU 加速应用。本节将演示如何在一个节点上配置 containerd,使其同时支持标准的 runc 和 nvidia-container-runtime。
5.2.1 环境准备
确保节点已安装:
• NVIDIA 显卡驱动 • Containerd • NVIDIA Container Toolkit • NVIDIA Device Plugin(Kubernetes 设备插件):用于在集群中暴露 nvidia.com/gpu资源,建议以 DaemonSet 部署。
# 部署 NVIDIA Device Plugin(请按集群版本选择合适的清单文件)
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.3/nvidia-device-plugin.yml
# 说明:
# - 插件在每个 GPU 节点上运行,向调度器与 kubelet 报告可用的 GPU。
# - 如需启用 MIG 或自定义资源声明,请参考官方文档调整参数。5.2.2 配置 Containerd 支持多运行时
我们需要修改 containerd 的配置文件 /etc/containerd/config.toml,定义两个运行时:
1. 默认运行时:使用 runc,用于普通 Pod。2. NVIDIA 运行时:使用 nvidia-container-runtime,用于 GPU Pod。
version = 2
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
# 设置默认运行时为 runc
default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
# 1. 配置标准 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]
BinaryName = ""
Root = ""
# 如使用 systemd cgroup,建议设置 SystemdCgroup = true
# 2. 配置 NVIDIA 运行时
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2" # 注意:这里类型仍然是 runc.v2
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime" # 指定 NVIDIA 二进制文件5.2.3 验证配置
修改完成后,重启 containerd 并验证:
# 重启 containerd
systemctl restart containerd
# 验证运行时是否已注册
crictl info | grep -A 10 "runtimes"
# 输出应包含 "runc" 和 "nvidia"5.2.4 对应的 RuntimeClass 定义
配置好节点后,我们需要在 Kubernetes 中定义对应的 RuntimeClass:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: nvidia
handler: nvidia # 对应 config.toml 中的 [plugins...runtimes.nvidia]
scheduling:
nodeSelector:
accelerator: nvidia-gpu # 确保调度到有 GPU 的节点5.2.5 版本与兼容性提示
• 容器运行时版本差异: containerd在 v1.6 与 v1.7+ 的 CRI 配置结构与字段存在细微差异,尤其在runtimes.*.options子项命名与默认行为上。请对照containerd官方文档确认当前版本推荐写法。• NVIDIA 集成差异:不同版本的 NVIDIA Container Toolkit( nvidia-container-runtime)在安装路径与与containerd的集成方式上可能存在差异;请以 NVIDIA 官方安装指南与发布说明为准。• 设备插件版本匹配: k8s-device-plugin的 YAML 与功能随 Kubernetes 版本更新,使用前请核对插件版本与集群版本的兼容矩阵。• 验证方法:在更新配置后,先使用 ctr/crictl层面的最小化容器进行拉起测试,再在 Kubernetes 中以最小化 Pod 验证,以快速定位问题(运行时或调度层)。
6. 使用示例
6.1 创建 RuntimeClass
在创建 RuntimeClass 之前,首先需要确保目标节点已经安装了对应的运行时,并打上了正确的标签。
# 1. 为支持 gVisor 的节点打上标签
kubectl label nodes node-1 runtime=gvisor
# 2. 创建 gVisor RuntimeClass
cat <<EOF | kubectl apply -f -
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
overhead:
podFixed:
memory: "128Mi"
cpu: "250m"
scheduling:
nodeSelector:
runtime: gvisor
tolerations:
- key: "runtime"
operator: "Equal"
value: "gvisor"
effect: "NoSchedule"
EOF
# 1. 为支持 Kata 的节点打上标签
kubectl label nodes node-2 runtime=kata
# 2. 创建 Kata Containers RuntimeClass
cat <<EOF | kubectl apply -f -
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata
handler: kata
overhead:
podFixed:
memory: "256Mi"
cpu: "500m"
scheduling:
nodeSelector:
runtime: kata
tolerations:
- key: "runtime"
operator: "Equal"
value: "kata"
effect: "NoSchedule"
EOF6.2 在 Pod 中使用 RuntimeClass
提示: handler 名称需与 containerd 注册一致;scheduling.nodeSelector 与节点标签需匹配。
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
runtimeClassName: gvisor # 使用 gVisor 运行时
containers:
- name: app
image: nginx:alpine
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
---
apiVersion: v1
kind: Pod
metadata:
name: vm-app
spec:
runtimeClassName: kata # 使用 Kata Containers 运行时
containers:
- name: app
image: nginx:alpine
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"# 示例 3:使用 NVIDIA RuntimeClass 运行 GPU 应用
apiVersion: v1
kind: Pod
metadata:
name: gpu-test
spec:
runtimeClassName: nvidia # 使用 NVIDIA 运行时
containers:
- name: cuda-vector-add
image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.8
resources:
limits:
nvidia.com/gpu: 1
command: ["/bin/bash", "-c"]
args: ["nvidia-smi && ./vectorAdd"]
# 注释:
# - 需要在 GPU 节点上部署 NVIDIA Device Plugin,以提供 nvidia.com/gpu 资源。
# - 该镜像包含 CUDA 样例程序,适合进行基础功能验证。# 示例 4:带有 requests/limits 的 GPU 工作负载(更严格的资源声明)
apiVersion: v1
kind: Pod
metadata:
name: gpu-strict
spec:
runtimeClassName: nvidia
containers:
- name: app
image: nvidia/cuda:12.4.1-runtime-ubuntu22.04
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
command: ["/bin/bash", "-c"]
args: ["nvidia-smi && sleep 3600"]
# 注释:
# - 同时声明 requests 与 limits 有助于更准确的调度与资源配额控制。
# - 若启用了 MIG,请参考设备插件文档对资源键进行相应调整。7. 工作原理与源码分析
7.1 API 服务器处理
RuntimeClass 的 API 处理逻辑位于 pkg/registry/node/runtimeclass:
// pkg/registry/node/runtimeclass/storage/storage.go
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
store := &genericregistry.Store{
NewFunc:func() runtime.Object { return &nodeapi.RuntimeClass{} },
NewListFunc:func() runtime.Object { return &nodeapi.RuntimeClassList{} },
// ... 其他配置
}
return &REST{store}, nil
}7.2 准入控制
RuntimeClass 的验证逻辑通过准入控制器实现:
// pkg/kubeapiserver/admission/runtimeclass/runtimeclass.go
func (c *runtimeClass) Admit(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
// 验证 RuntimeClass 是否存在
if _, err := c.lister.Get(rcName); err != nil {
return admission.NewForbidden(attr, fmt.Errorf("runtimeclass %q not found", rcName))
}
// 验证 Pod 资源请求是否满足 overhead 要求
if err := c.validateOverhead(attr, rc); err != nil {
return err
}
return nil
}7.3 调度器集成
调度器需要考虑 RuntimeClass 的 overhead:
// pkg/scheduler/framework/plugins/noderesources/fit.go
func (f *Fit) PreFilter(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
// 获取 RuntimeClass
rcName := pod.Spec.RuntimeClassName
if rcName != nil {
rc, err := f.runtimeClassLister.Get(*rcName)
if err == nil && rc.Overhead != nil {
// 将 overhead 添加到 Pod 的资源需求中
pod = addOverhead(pod, rc.Overhead)
}
}
// 计算节点资源是否足够
return f.calculateResource(pod, nodeInfo)
}7.4 Kubelet 运行时选择
Kubelet 根据 RuntimeClass 选择对应的运行时:
// pkg/kubelet/kuberuntime/kuberuntime_manager.go
func (m *kubeGenericRuntimeManager) GetRuntime(pod *v1.Pod) (runtimeName string, err error) {
if pod.Spec.RuntimeClassName != nil {
// 获取 RuntimeClass 配置
rc, err := m.runtimeClassLister.Get(*pod.Spec.RuntimeClassName)
if err != nil {
return "", err
}
return rc.Handler, nil
}
// 使用默认运行时
return m.defaultRuntime, nil
}8. 总结
Kubernetes RuntimeClass 为多运行时环境提供了统一的管理接口,使得用户可以根据工作负载的特性选择最合适的容器运行时。通过合理的 RuntimeClass 配置,可以实现:
1. 安全隔离:为敏感应用提供更强的安全边界。 2. 性能优化:为特殊工作负载选择专用运行时。 3. 资源管理:准确计算运行时开销,优化资源分配。 4. 灵活调度:根据运行时需求进行智能节点选择。
在生产环境落地时,建议运维团队建立统一的运行时管理规范,明确不同 RuntimeClass 的适用场景(如 gvisor 用于不可信代码,nvidia 用于 AI 训练),并通过监控系统持续关注 Overhead 对集群资源的影响。随着云原生技术的不断发展,RuntimeClass 将在混合运行时环境中发挥越来越重要的作用,为构建安全、高效、灵活的 Kubernetes 平台提供坚实基础。
9. 参考资料
• Kubernetes:RuntimeClass 设计与用法(官方文档)https://kubernetes.io/docs/concepts/containers/runtime-class/ • Kubernetes:Pod Overhead(官方文档)https://kubernetes.io/docs/concepts/scheduling-eviction/pod-overhead/ • containerd:CRI 配置参考与示例 https://github.com/containerd/containerd/blob/main/docs/cri/config.md • NVIDIA Container Toolkit 安装指南 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html • NVIDIA Kubernetes Device Plugin https://github.com/NVIDIA/k8s-device-plugin • gVisor 文档 https://gvisor.dev/docs/ • Kata Containers 文档 https://katacontainers.io/docs/