上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器上用 RHEL + Kata Containers 把容器安全性拉满

发布人:Minchunlin 发布时间:2025-08-30 09:15 阅读量:810


香港葵涌机房线上的一个多租户业务准备扩容,合规审计又刚把“容器逃逸风险”用红笔画了三遍。运维群里很安静,大家都盯着我的终端窗口——我决定在 RHEL 上把 Kata Containers 上进去,给关键 Pod 换一层 “轻虚机” 的硬隔离。
这不是第一次折腾 Kata,但每次到机房落地都像首秀: BIOS 里 VT-x/AMD-V 会不会被托管商关了?RHEL 的 cgroup v2 和 SELinux 会不会给我来点“惊喜”?直到那一刻,我按下回车,风扇声里混入了 QEMU 启动的轻微噪音——这是我要的隔离墙正在立起来的声音。

我们到底要解决什么问题(威胁模型与目标)

威胁模型(简化版)

  • 多租户/混部:不同团队、不同敏感级别的容器同 Node 混跑。
  • 内核共享风险:传统 runc 共享宿主机内核,内核级漏洞或容器逃逸带来高影响面。
  • 合规要求:高敏感工作负载需要“硬隔离”证据链(KVM/轻虚机级别)。

目标

  • 用 Kata Containers(轻量虚拟机) 作为指定工作负载的运行时,把关键 Pod 与宿主机内核隔离开。
  • 对普通业务维持 runc 的性能与密度,对高敏业务使用 Kata 的安全隔离,做到安全-性能两条线并行。
  • 能灰度、可回滚、可观测,现场问题能 30 分钟内定位。

现场环境与硬件清单(香港节点)

组件 参数/型号 备注
机房 香港(葵涌),单可用区 + 双路由上联 机柜 10GbE 汇聚
服务器 2× Intel Xeon Gold 6330(40C/80T) 支持 VT-x/VT-d
内存 512GB DDR4 频繁启动轻虚机内存足够
系统盘 2× NVMe SSD(RAID1,PCIe 3.0 x4) 用作系统 + 容器镜像缓存
数据盘 2× NVMe SSD(RAID0,PCIe 4.0 x4) Kata guest rootfs/overlay、冷热分层
网卡 2× 25GbE(Bond LACP) MTU 9000 内网、对外 1500
OS RHEL 9.x(实测 9.3/9.4) SELinux enforcing,cgroup v2
容器引擎 containerd 1.7.x runc + kata 双栈
Kata 2.6+/v3(以 v2 shim 为例) 运行时 io.containerd.kata.v2

注:OpenShift/CRI-O 也可用 Kata,这里主线用 containerd,文末给 CRI-O 参考配置。

方案总览(架构图解成文字版)

同一台 RHEL 宿主,同时支持两类运行时:

  • runc:常规工作负载,高密度。
  • Kata(KVM+QEMU 轻虚机):高敏工作负载,强隔离。
  • Kubernetes 用 RuntimeClass 精准选择运行时,团队只需在 YAML 里加一行 runtimeClassName: kata 即可“置身轻虚机”。
  • 文件系统用 virtio-fs(优先)替代 9p,提高性能;网络默认 tcfilter,必要时配 macvtap/SR-IOV。
  • 观测用 kata-monitor + journalctl -u kata* + QEMU 进程可见性,搭配 Node 级 Exporter。

前置检查与准备(现场一步一确认)

1) CPU 虚拟化与内核模块

# CPU 是否支持虚拟化
egrep -i 'vmx|svm' /proc/cpuinfo | wc -l

# KVM 模块是否加载(Intel/AMD 取其一)
lsmod | egrep 'kvm|kvm_intel|kvm_amd'

# 若未加载(RHEL 9)
sudo modprobe kvm
sudo modprobe kvm_intel   # 或 kvm_amd

坑点 1:托管商 BIOS 里 VT-x/AMD-V 被关了,KVM 模块加载失败。现场处理:远程约 NOC,BIOS 打开 Intel VT-x/VT-d,AMD 对应 AMD-V/IOMMU。

2) SELinux、cgroup、内核版本

getenforce           # 期望:Enforcing
stat -fc %T /sys/fs/cgroup  # 期望:cgroup2fs(RHEL9 默认)
uname -r             # 确认内核 ≥ 5.x,便于 virtio-fs、cgroupv2 兼容

3) RHEL 订阅与基本包

sudo subscription-manager status
sudo dnf -y install git wget jq vim htop \
  qemu-kvm libvirt virt-install \
  containerd runc

安装 Kata Containers(稳妥两法)

我选“官方安装脚本 + 审计”:速度快、版本关系处理好。若公司不允许“curl | bash”,就先下载脚本审计后再执行。

方法 A:官方安装脚本(推荐)

# 下载并校验(示例)
curl -fsSLO https://raw.githubusercontent.com/kata-containers/kata-containers/main/tools/install/install_kata.sh
sha256sum install_kata.sh  # 与官方校验和比对
sudo bash install_kata.sh  # 安装 Kata runtime/guest kernel/rootfs 等

脚本完成后通常会把 io.containerd.kata.v2 shim、guest kernel、镜像、配置文件一并放好(路径视版本而定,如 /usr/share/kata-containers/、/etc/kata-containers/)。

方法 B:RPM 包仓(适合离线/自建镜像)

在内网 YUM 仓或离线介质准备 kata-containers、kata-runtime、qemu-virtiofsd 等包。

注意 RHEL 的 QEMU 路径 多为 /usr/libexec/qemu-kvm,而不是 /usr/bin/qemu-system-x86_64。配置要对应。

关键配置:让 containerd 同时认识 runc 与 kata

1) 生成 containerd 配置并编辑

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

在 /etc/containerd/config.toml 里,确保:

[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.kata]
  runtime_type = "io.containerd.kata.v2"
  privileged_without_host_devices = false

# cgroup v2 兼容
[plugins."io.containerd.grpc.v1.cri"]
  systemd_cgroup = true

坑点 2:把 runtime_type 写成 kata 或者路径写错,Kubelet 起不来 Pod。处理:用 io.containerd.kata.v2,并 journalctl -u containerd -f 看报错。

2) Kata 主配置确认(可选优化)

/etc/kata-containers/configuration.toml(不同版本可能是 configuration-qemu.toml)重点位:

[hypervisor.qemu]
path = "/usr/libexec/qemu-kvm"           # RHEL 路径
machine_type = "q35"
enable_iommu = true                      # DMA 保护
default_vcpus = 1
default_memory = 1024                    # 初始内存,后续可调
block_device_driver = "virtio-scsi"

启用 virtio-fs(性能更好):

[hypervisor.qemu]
# ...
shared_fs = "virtio-fs"
virtio_fs_daemon = "/usr/libexec/virtiofsd"   # RHEL 9 常见路径(或 /usr/lib/qemu/virtiofsd)

3) 重启并校验

sudo systemctl enable --now containerd
sudo systemctl status containerd
kata-runtime --version     # 或 kata-collect-data.sh 查看更详尽信息

单机验证(不进 K8s 先热手)
# 用 crictl 直接跑(确保已安装 crictl 并指向 containerd 的 CRI)
sudo crictl info | jq '.config.containerd.runtimes'

# 运行一个 kata 容器(示例)
sudo crictl runp --runtime=kata <<EOF
{
  "metadata": {"name":"sandbox-kata"},
  "linux": {"security_context": {}}
}
EOF

更简单,我通常直接用 ctr:

sudo ctr images pull docker.io/library/nginx:1.25
sudo ctr run --rm --runtime io.containerd.kata.v2 \
  docker.io/library/nginx:1.25 kata-nginx

在另一端看 QEMU:

ps -ef | egrep 'qemu|kata'

能看到 qemu-kvm 进程就对了。ss -lpn | grep vhost/vsock 能看到 Kata 的通信通道。

接入 Kubernetes(RuntimeClass 一把梭)

前提:Kubelet 指向 containerd(/var/lib/kubelet/kubeadm-flags.env 里有 --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock)。

1) 定义 RuntimeClass

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
handler: kata
overhead:
  podFixed:
    memory: "256Mi"
    cpu: "100m"
scheduling:
  nodeSelector:
    node.kubernetes.io/instance-type: hk-biz-kata    # 也可用 taints/labels 控制调度

handler: kata 要和 containerd 里 runtimes.kata 一致。

2) 测试 Pod(和 runc 形成对照)

apiVersion: v1
kind: Pod
metadata:
  name: web-kata
spec:
  runtimeClassName: kata
  containers:
  - name: nginx
    image: nginx:1.25
    ports:
    - containerPort: 80

部署后看 Pod 描述:

kubectl describe pod web-kata | egrep 'Runtime|kata'
kubectl exec -it web-kata -- uname -a   # 注意看到的是 guest kernel,而非宿主机内核

我们实测的开销与收益(POC 报告摘录)

不同硬件、镜像和业务会有差异,下表是我在香港节点的“冷启动(镜像已拉取)”测得的大致范围,只用于量级参考。

指标 runc kata(virtio-fs) 备注
冷启动到就绪(nginx) 250–400 ms 1.2–2.0 s Kata 有轻虚机启动成本
常驻 RSS(单 Pod) 30–60 MiB 120–220 MiB 包含 guest kernel/agent
ab -n 5k -c 100(P99) ~3–5 ms ~3–6 ms 纯静态文件几乎无差异
CPU 密度(每 Node) ★★★★★ ★★★☆ Kata 密度略低
隔离强度 ★★ ★★★★★ Kata=KVM 级硬隔离

结论:

  • 对高敏感/多租户场景,Kata 的隔离收益远大于启动/内存开销。
  • 对高吞吐业务,数据平面性能影响可控(网络/存储路径配置得当时)。
  • 策略上采用 双轨:默认 runc,敏感和合规要求强的工作负载走 Kata。

安全增益的落地点

  • 内核隔离:Kata guest 用独立内核,宿主与容器不再共享同一内核攻击面。
  • 设备与内存隔离:VT-d/IOMMU + vCPU/vMem 边界清晰。
  • 策略复用:Pod securityContext、Seccomp、SELinux 仍可用,叠加到 Kata 轻虚机内。
  • 镜像供应链:Kata 不解决“镜像本身被植入”的问题,CI/CD 扫描与签名(cosign)照样要做。

(可选)机密计算:有条件的主机可用 TDX/SEV-SNP 搭 Kata Confidential Containers,敏感度再抬一档。

线上踩过的坑与现场处理

KVM 不起 / BIOS 关 VT

现象:modprobe kvm_intel 失败、dmesg 报不支持。

处理:托管商远程开 VT-x/VT-d;云上 VM 要求“支持嵌套虚拟化”的宿主类型。能上裸金属就上裸金属。

SELinux 拦截 virtiofsd

现象:Pod Pending,journalctl -u containerd 有 permission denied。

处理:确认 virtiofsd 路径与 Booleans;RHEL 9 官方包通常已有合规标签。必要时写本地 policy(审慎)。

containerd 配置错位

现象:Kubelet 报不识别 runtime,或 Pod 一直 CreateContainerError。

处理:runtime_type = "io.containerd.kata.v2",不要手写 runtime 二进制路径。

cgroup v2 与内核参数

现象:资源限制不生效、oom 行为异常。

处理:Kubelet、containerd、Kata 全部启用 systemd cgroup;必要时在 guest kernel 加 systemd.unified_cgroup_hierarchy=1。

网络 MTU/Overlay 抖动

现象:跨网段大包丢、gRPC 超时。

处理:Node 内网 MTU 9000、外部 1500;CNI(Calico/Flannel/Cilium)与 Kata 的 tap/bridge 保持一致,必要时把 mtu 固定为 1450/1500。

镜像拉取与共享卷

现象:容器内看不到宿主卷更新。

处理:尽量用 CSI/网络卷;hostPath 在 Kata 下是通过共享文件系统转译的,注意一致性与权限。

时间漂移

现象:日志时间错位,分布式追踪对不上。

处理:宿主启用 chronyd,Kata agent 同步宿主时间;跨可用区统一 NTP。

QEMU 路径与权限

现象:Kata 找不到 hypervisor,或缺 CAP。

处理:RHEL 下 QEMU 常在 /usr/libexec/qemu-kvm;确认文件权限与 SELinux 上下文。

生产化 Checklist(我上线前必过)

  1.  节点分池:给 Kata 专属 Node 打 nodeSelector/taints,避免所有 Pod 都走 Kata。
  2.  RuntimeClass 政策:高敏命名空间(如 ns: payment)使用 LimitRange + RuntimeClass 默认 Kata。
  3.  可观测:部署 kata-monitor 暴露 /metrics;Prometheus 抓 QEMU/Kata 进程指标。
  4.  告警:轻虚机启动失败、vsock 断开、virtiofsd 崩溃、Pod Pending 超 2 分钟报警。
  5.  日志留痕:合规要求下保留 kata-collect-data.sh 的审计快照(版本、配置、内核)。
  6.  容量规划:Kata Pod 额外内存开销(100–200Mi/Pod)纳入 Node 资源模型。
  7.  回滚预案:RuntimeClass 一键切回 runc;必要时 kubectl cordon/drain 迁走 Kata Pod。

灰度与回滚(十五分钟演习)

灰度

  • 给一台 Node 打 beta.kubernetes.io/instance-type=hk-biz-kata。
  • 发布 RuntimeClass: kata,仅把 canary Deployment 的 10% 副本改为 runtimeClassName: kata。
  • 观察 24 小时,重点看启动时延、内存曲线、HTTP P99、错误率。

回滚

  • 把 runtimeClassName 移除或改回 runc;
  • 若 Node 故障,kubectl cordon + drain,Deployment 自动在 runc 池子重建。
  • CRI-O 参考(给 OpenShift/CRI-O 用户)

在 /etc/crio/crio.conf.d/99-kata.conf:

[crio.runtime]
default_runtime = "runc"

[crio.runtime.runtimes.kata]
runtime_type = "vm"
runtime_path = ""                      # v2 shim 不再使用二进制路径
runtime_root = "/run/vc"
monitor_path = ""

/etc/crio/crio.conf 中 conmon_cgroup = "pod" 并与 systemd cgroup 配套。
重启:

sudo systemctl restart crio

K8s 侧同样用 RuntimeClass(handler: kata)。

运维侧的小优化(现场体感很有用)

Kata Factory(预热轻虚机):降低冷启动延迟;夜间大促前先做一次预热。

Guest 内核裁剪:对特定业务裁掉不必要模块,减少 RSS。

镜像分层优化:把大依赖放基础层,减少 Kata guest 的共享开销。

调度亲和:高敏 + 高 I/O 的 Pod 落到 NVMe RAID0 的节点上。

镜像签名:cosign/Notary 强制校验,防止“安全的轻虚机里跑不安全的镜像”。

FAQ:团队最常问的三个问题

“Kata 会不会很慢?”
控制面启动会慢 1–2 秒;数据面(网络/磁盘)调好后与 runc 接近。选对工作负载,就值。

“能 100% 替代 runc 吗?”
不建议。双轨是最优解:默认 runc 保持效率,敏感走 Kata 保证隔离。

“怎么证明它更安全?”
从攻击面角度,guest kernel 与宿主分离,KVM 形成强隔离;再配合最小权限、镜像签名与审计,能满足大多数合规条款。

结尾:风扇声后的宁静

那天清晨 5 点,香港的天还没亮,NOC 走廊尽头的咖啡机终于不再排队。Grafana 上轻虚机的启动曲线稳住了,几个高敏服务的 Pod 成功切到 Kata,审计的红色标注也被我们自己划掉了。
回到酒店前,我把终端里的 kubectl get pods -o wide 截了一张图,给同事发去:“该睡了。隔离墙已经立好。”
安全不是一次性的“补丁”,而是一套能在风扇声里稳定运行的工程。希望这篇手记,能帮你也把那面墙立起来。

附:一键校验脚本(我现场常用)

只做只读/校验,不改系统;放到 check-kata.sh,给新人跑一遍心里有底。

#!/usr/bin/env bash
set -euo pipefail

GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m'
ok(){ echo -e "${GREEN}[OK]${NC} $*"; }
err(){ echo -e "${RED}[ERR]${NC} $*"; }

echo "== CPU virtualization =="
if egrep -qi 'vmx|svm' /proc/cpuinfo; then ok "CPU supports VT"; else err "No VT"; fi

echo "== KVM modules =="
if lsmod | egrep -q 'kvm_(intel|amd)'; then ok "KVM loaded"; else err "KVM not loaded"; fi

echo "== SELinux =="
getenforce | grep -qi enforcing && ok "SELinux enforcing" || err "SELinux not enforcing"

echo "== cgroup v2 =="
stat -fc %T /sys/fs/cgroup | grep -q cgroup2fs && ok "cgroup v2" || err "not cgroup v2"

echo "== containerd runtimes =="
if command -v crictl >/dev/null; then
  crictl info | jq -r '.config.containerd.runtimes | keys[]' | grep -q kata \
    && ok "kata runtime registered" || err "kata runtime missing"
else
  err "crictl not installed"
fi

echo "== QEMU path =="
[ -x /usr/libexec/qemu-kvm ] && ok "qemu-kvm exists" || err "qemu-kvm missing"

echo "== Kata files =="
for f in /usr/share/kata-containers/vmlinuz* /usr/share/kata-containers/kata-containers.img; do
  [ -f "$f" ] && ok "found $f" || err "missing $f"
done

echo "== Done =="

如果你已经在 RHEL/香港节点上做到了这一步,剩下的就是把它纳入你的日常工程纪律:灰度、观测、回滚——和那杯凌晨 3 点的咖啡。

目录结构
全文