1. 前言:容器技术的架构分层
- 容器管控客户端
例如 kubectl,docker cli - 容器管控服务端
例如 k8s 的管控面(含 kubelet),dockerd - 上层(High-Level)容器运行时
- 负责管理容器的完整生命周期、镜像管理、调用底层运行时来创建和运行容器
- 支持一些 web API 、容器网络管理等高级功能
- 可以遵循 CRI,从而接入 k8s
- 代表性技术:containerd,crio,docker-ce
- 底层(Low-Level)容器运行时
- 创建并执行容器内的应用进程。遵循 OCI 标准
- 代表性技术:runc,kata-runtime
CRI:Container Runtime Interface,k8s 定义的调用容器运行时的 API
OCI:Open Container Initiative ,容器运行时和镜像的标准关于两层运行时的消歧义:
可以理解成 High-Level 是“管控层”,Low-Level 是“执行层”。二者之间通过 OCI 实现对接。
2. 安全容器
2.1. 定义
传统容器:通过 namespace 实现隔离(进程,网络,FS),通过 cgroup 实现硬件资源限制。但是各容器依然共享宿主机的内核,可以通过 syscall 直接与内核交互,因此存在安全风险,可能逃逸出容器,攻击其他容器乃至宿主机。
安全容器:为每个 pod 启动一个独立、轻量的虚拟机,有独立内核、虚拟化的硬件设备。即,在容器和宿主机内核之间插入了一个硬件虚拟化层,作为强隔离。
2.2. 关键技术组件
- 轻量虚拟机(MicroVM):极度精简的内核,启动和运行的开销远低于传统虚拟机
- 裁剪了容器场景下无需使用的硬件驱动(usb,声卡...)
- 移除 UEFI,由 VM 管控(即 Hypervisor )加载内核
- 安全容器运行时:在启动容器时,先创建一个前述的轻量虚拟机实例,再在其用户空间中运行容器。兼容 OCI 标准。
对于传统的非安全容器运行时,例如 runc,其创建容器的流程可以简化为以下步骤:
- 在宿主机上创建 cgroup 与 namespace,
- chroot 到容器的 rootfs
- 执行容器入口进程,例如 nginx
对于安全容器,则在第一步前插入了创建虚拟机的步骤,并且后继的 ns 与 cgroup 都是在虚拟机的 guest os 中操作
2.3. 为何安全容器下仍然需要 cgroup 和 ns?
可能会认为,既然安全容器已经实现了内核的隔离、有了独立的 OS,那么似乎可以不用 cgroup 和 ns 了?
但是考虑到 pod 往往是一组容器,比如主进程 + sidecar,而这些容器之间依然需要视图隔离与资源限制,因此还是要应用这两个技术。
综上,可以认为安全容器的架构存在两个层次的限制与隔离:
- VM 硬隔离(由 Hypervisor 实现)
- VM 内软隔离(由 ns + cgroup 实现)
3. ref
https://cloud.tencent.com/developer/article/1895805
https://www.cnblogs.com/daemon365/p/17884222.html
https://www.tutorialworks.com/difference-docker-containerd-runc-crio-oci/
