hcsshim 完全指南:Windows HCS 容器运行时接口、containerd shim 架构与 buildkit 中的实际应用
【免费下载链接】buildkitconcurrent, cache-efficient, and Dockerfile-agnostic builder toolkit项目地址: https://gitcode.com/GitHub_Trending/bu/buildkit
hcsshim(Host Compute Service Shim)是微软开源的 Go 语言库,为 Windows 容器提供对 Windows 宿主计算服务(HCS)的完整 Go 接口,涵盖容器生命周期管理、宿主网络服务(HNS)封装以及支撑 Linux Hyper-V 容器的 Guest Compute Service(GCS)客户代理。本文以仓库中随 buildkit 一并 vendor 的 hcsshim 文档(vendor/github.com/Microsoft/hcsshim/README.md)为骨架,结合 vendor 目录下的源码实现,系统讲解 hcsshim 的构建方法、containerd shim V1/V2 架构演进,以及它在 buildkit 工程中被真实引用的方式,帮助读者掌握在 Windows 环境下开发、调试和扩展容器运行时所需的完整知识。
hcsshim 是什么:Windows 容器生态的核心 Go 接口层
hcsshim 包提供了用于调用 Windows Host Compute Service(HCS) 的 Golang 接口,用于启动和管理 Windows Containers。其职责边界可以概括为三块:
- HCS 接口层:以 Go 类型化 API 封装 HCS 的底层 WMI/COM 调用,屏蔽 Windows 平台细节;
- HNS 接口层:提供 Host Network Service(HNS)的 Go 封装,用于管理 Windows 容器的网络命名空间、endpoint 与策略;
- Guest Agent(GCS):即代码库中常称的 Guest Compute Service,负责支撑运行 Linux Hyper-V 容器(LCOW),是 Linux 容器跑在 Windows 主机上的关键组件。
该库主要被 Moby 与 Containerd 项目使用,但其他项目也可以自由引用。hcsshim 本身既可以作为调用 HCS API 的库来使用,也同时产出了几个二进制程序,最主要的两个是Linux guest agent和runtime v2 containerd shim API 的实现。
从源码看 hcsshim 的核心抽象
在仓库 vendor 目录中,可以找到 hcsshim 顶层 API 的直接证据。以 vendor/github.com/Microsoft/hcsshim/interface.go 为例:
Container接口定义了容器的完整生命周期方法:Start()、Shutdown()、Terminate()、Wait()、WaitTimeout()、Pause()、Resume(),以及CreateProcess()、OpenProcess()、Modify()等操作;Process接口则抽象了容器内进程:Pid()、Kill()、Wait()、ExitCode()、ResizeConsole()、Stdio()与CloseStdin();- 配置文件类型(
ContainerConfig、ProcessConfig、Layer、MappedDir、MappedPipe、HvRuntime、MappedVirtualDisk)统一从internal/hcs/schema1导出,注释明确说明它们"既作为 CreateContainer 的输入,也用于转换成 JSON 传给 HCS"——这正是 hcsshim 与 Windows 底层服务交互的桥接方式。
而 vendor/github.com/Microsoft/hcsshim/hcsshim.go 的包注释则点明了它的定位:"Shim for the Host Compute Service (HCS) to manage Windows Server containers and Hyper-V containers",并定义了一系列用户可见的常量,如WaitErrExecFailed = 32767、TimeoutInfinite = 0xFFFFFFFF等。
在内部实现层面,vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2 目录下存放着 HCS 计算系统文档的完整 Go 结构体定义,涵盖计算系统配置(compute_system.go)、容器(container.go)、虚拟机(virtual_machine.go)、内存(memory.go)、处理器(processor.go)、存储(storage.go)、网络(networking.go)等上百个类型;internal/vmcompute、internal/hns、internal/wclayer等目录则分别封装了 HCS 调用、HNS 网络管理和 Windows 容器层文件操作。
hcsshim 在 buildkit 中的实际应用
buildkit 作为面向 Windows 平台提供容器镜像构建能力的工具链,在go.mod中以依赖形式引入了 hcsshim:
github.com/Microsoft/hcsshim v0.15.0-rc.4从仓库源码中可以找到两处真实的引用场景:
containerd worker 的运行时选项解析(cmd/buildkitd/main_containerd_worker_windows.go):buildkitd 在 Windows 上启动 containerd worker 时,通过
getRuntimeOptionsType根据运行时类型返回空的运行时选项结构体。当运行时是plugins.RuntimeRunhcsV1时,返回的是runhcsoptions.Options——这个类型正来自github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options包,其定义可在 vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto 中找到。这印证了 hcsshim 文档中"runhcs shim 用于启动和管理容器生命周期"的描述在 buildkit 工程中的落地。CNI 网络命名空间管理(util/network/cniprovider/createns_windows.go):buildkit 的 CNI provider 在 Windows 平台上通过 hcsshim 的
hcn包创建宿主计算网络命名空间。createNetNS调用hcn.NewNamespace(hcn.NamespaceTypeGuest)创建 guest 类型命名空间,deleteNetNS则通过hcn.GetNamespaceByID查询后删除,并将命名空间 ID 写入 OCI 运行规范的s.Windows.Network.NetworkNamespace字段。这是 hcsshim 中 HNS/HCN 网络接口在真实工程中的典型用法。
这两处引用说明:hcsshim 对于 buildkit 而言,既是 Windows 上容器运行时的"启动器",也是网络命名空间等宿主资源的管理工具。
构建 Linux Hyper-V Container Guest Agent(GCS)
根据 hcsshim 文档,构建 Linux guest agent 本身只需将GOOS设置为"linux",并在cmd/gcs目录下执行构建。在 Windows 上使用 PowerShell:
C:\> $env:GOOS="linux" C:\> go build .\cmd\gcs\在 Linux 机器上则直接执行:
> go build ./cmd/gcs注意:当前仓库 vendor 目录仅保留了 buildkit 实际用到的 hcsshim 子集(
cmd/containerd-shim-runhcs-v1等),cmd/gcs属于 hcsshim 上游仓库的内容,未随 vendor 打包。上述命令在 hcsshim 上游源码树中有效。
将 guest agent 打包进 rootfs
如果希望把 guest agent 与其他工具一起打包进 rootfs 以便随容器引导启动,需要先提供一个可供打包的 rootfs。一个便捷的做法是导出某个容器的 rootfs:
docker pull busybox docker run --name base_image_container busybox docker export base_image_container | gzip > base.tar.gz BASE=./base.tar.gz make all构建成功后,在./out目录下应当可以看到三个产物:
> ls ./out/ delta.tar.gz initrd.img rootfs.tar.gzinitrd.img:initramfs 镜像,用于引导 Linux Utility VM;rootfs.tar.gz:打包了 guest agent 及相关工具的根文件系统;delta.tar.gz:增量层,用于叠加到基础镜像之上。
从源码看 GCS 的支撑机制
Linux Hyper-V 容器(LCOW)的实现深度依赖 hcsshim 内部的诸多模块。虽然 guest agent 本体在cmd/gcs,但支撑它的底层能力散布在 vendor 源码中:
- internal/hcs/schema2/linux_kernel_direct.go:定义 Linux 内核直启(kernel direct boot)所需的配置结构,这是无需完整 BIOS 引导、直接加载内核镜像启动 Utility VM 的机制;
- internal/hcs/schema2/hv_socket.go 等文件:定义 HvSocket 配置,guest agent 与宿主之间正是通过 Hyper-V Socket(vsock)通信;
- internal/hcs/schema2/plan9.go:定义 Plan9 共享文件系统配置,用于宿主与 Linux VM 之间的文件共享。
containerd shim(V1):containerd-shim-runhcs-v1
与 Linux 上典型的 "shim -> runc" 架构不同,Windows 上的 runhcs shim 同时负责容器的启动与生命周期管理。构建它需要将GOOS设置为"windows":
C:\> $env:GOOS="windows" C:\> go build .\cmd\containerd-shim-runhcs-v1然后将生成的二进制文件放置到环境中 containerd 所在目录。生成一份默认的 containerd 配置文件:
.\containerd.exe config default | Out-File "C:\Program Files\containerd\config.toml" -Encoding ascii这份配置已默认将 runhcs shim 设为 CRI 交互的默认运行时。可以用ctr.exe快速试用:
C:\> ctr.exe run --runtime io.containerd.runhcs.v1 --rm mcr.microsoft.com/windows/nanoserver:2004 windows-test cmd /c "echo Hello World!"在 buildkit 中,plugins.RuntimeRunhcsV1正是上面这个运行时名称io.containerd.runhcs.v1的常量引用,buildkitd 在 Windows 上遇到该运行时类型时会构造runhcsoptions.Options,这个类型的 protobuf 定义位于 vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto,字段包括KernelDirect、Vhdx、VmProcessorCount、VmMemorySizeInMB等与 Hyper-V 隔离虚拟机配置直接相关的选项。
containerd Shim V2:按平台拆分的运行时体系
V2 shim 是对 Windows containerd shim 的重写。V1 shim(containerd-shim-runhcs-v1)是单个单体二进制,同时处理 LCOW(Windows 上的 Linux 容器)、Hyper-V WCOW(Windows 上的 Hyper-V Windows 容器)、进程隔离 WCOW 以及宿主进程容器。在 V2 模型中,这个单体被拆分为聚焦的、按平台区分的多个 shim,每个 shim 与一个 sandbox 一一对应(1:1)。
V2 shim 的使用方式与 V1 相同,但暴露的 API 面不同:
- V1 shim 只实现了 containerd 的 Task API,通过单个服务同时管理 sandbox 生命周期与容器/进程(task)生命周期;
- 每个 V2 shim 则把职责拆分到 containerd 提供的两个 API 上:Sandbox API 负责管理 sandbox,Task API 负责管理其中运行的容器和进程;
- 内部实现上,每个 V2 shim 将 sandbox 与 task 分别实现为独立服务,另附一个用于诊断的
shimdiag服务。
统一的 CRI Pod 模型
三个 V2 shim 都遵循相同的 CRI pod 模型。一个带有"io.kubernetes.cri.container-type": "sandbox"注解的 task 被当作创建 pod 的 pause/infra 容器;同级的 workload task 则设置"io.kubernetes.cri.container-type": "container",并通过"io.kubernetes.cri.sandbox-id"注解关联到各自的 pause 容器。而 "sandbox" 在物理上对应什么,取决于具体的 shim。
containerd-shim-lcow-v2(LCOW)
| 维度 | 说明 |
|---|---|
| 用途 | 在 Windows 上运行 Linux 容器(LCOW),由一个 Linux Utility VM 承载 Linux 容器 |
| Sandbox | Linux UVM。每个 shim 实例与单个 UVM 一一对应,且支持在同一个 UVM 中运行多个 pod,即单个 shim 实例可承载多个 CRI pod |
| Tasks | UVM 内运行的 Linux 容器与进程,通过上文描述的 CRI 注解标识 |
| 实现 | ./cmd/containerd-shim-lcow-v2 |
| Build Tag | lcow |
| 平台要求 | Windows Server 2025(build 26100)或更高版本 |
containerd-shim-wcow-v2(WCOW)
| 维度 | 说明 |
|---|---|
| 用途 | 运行 Hyper-V 隔离的 Windows 容器(WCOW),由一个 Windows Utility VM 承载内部的进程隔离容器与宿主进程容器 |
| Sandbox | Windows Utility VM(UVM),每个 shim 实例与单个 UVM 一一对应 |
| Tasks | UVM 内运行的 Windows 容器与进程,通过标准 CRI 注解标识 |
| Build Tag | wcow |
containerd-shim-process-v2(进程隔离)
| 维度 | 说明 |
|---|---|
| 用途 | 运行进程隔离的 Windows Server 容器与宿主进程容器——这些负载直接执行在宿主机上,没有 Utility VM |
| Sandbox | pause 容器。pause 容器是最小化、长期存活的容器,持有 pod 的共享资源(如网络命名空间),在兄弟 workload 容器被启动、停止或替换期间维持这些资源存活。这是标准 Kubernetes pod 模型:pause 容器就是 pod 其余部分所依附的 sandbox |
| Tasks | 属于 pod 的实际 workload 容器,通过io.kubernetes.cri.sandbox-id注解关联回 pause 容器 |
| Build Tag | process |
从上述对比可以看出 V2 架构的设计意图:把"沙箱生命周期"与"任务生命周期"彻底解耦,使不同隔离模型(UVM 隔离 vs 进程隔离)能够各自演进,同时维持统一的 CRI 语义。
V2 shim 的构建、单元测试与 parity 测试
V2 shim 源码由对应的 build tag 保护,因此构建时必须显式传入 tag。以 LCOW shim 为例:
C:\> $env:GOOS="windows" C:\> go build -tags lcow .\cmd\containerd-shim-lcow-v2将生成的containerd-shim-lcow-v2.exe放置到与containerd.exe相同的目录(与 V1 shim 的做法一致)。
运行单元测试同样需要指定 shim 对应的 build tag:
C:\> go test -tags lcow ./...仓库还提供了parity 测试(位于./test/parity),它们把完全相同的输入分别送入旧的 V1 与新的 V2 两条管线,并断言最终生成的 HCS ComputeSystem 文档是等价的。这些测试位于独立的testGo module 中,同样需要携带 build tag 运行:
C:\> cd test C:\> go test -tags lcow ./parity/...parity 测试的价值在于:它从"输出文档等价"这一层面保证了 V2 重构不会改变容器实际提交给 HCS 的配置语义,为 shim 迁移提供了可回归验证的机制。
工程规范:Linting、Go Generate 与依赖要求
代码质量与 Linting
所有代码必须通过使用golangci-lint的 lint 阶段。由于./test是独立的 Go module,linter 需要同时在根目录与test目录下运行;此外,linter 还分别在GOOS=windows与GOOS=linux两种环境下执行,以确保跨平台代码质量。
lint 配置存放在.golangci.yaml中。若使用 VSCode,可在 workspace 或 folder 设置中添加以下配置让编辑器自动执行 lint:
"go.lintTool": "golangci-lint", "go.lintOnSave": "package",也可以安装golangci-lint后在本地手动运行:
# 使用 . 或指定路径以只 lint 某个包 # 若需显示所有 lint 错误,加 --max-issues-per-linter=0 --max-same-issues=0 > golangci-lint run若要跨整个仓库、同时覆盖GOOS=windows与GOOS=linux运行:
> foreach ( $goos in ('windows', 'linux') ) { foreach ( $repo in ('.', 'test') ) { pwsh -Command "cd $repo && go env -w GOOS=$goos && golangci-lint.exe run --verbose" } }go generate 校验
CI 流水线还会检查通过go generate生成的代码是否保持最新。与 lint 阶段类似,go generate同样需要分别在根目录与test两个 Go module 中运行:
> go generate ./... > cd test && go generate ./...这与 hcsshim 的代码生成策略直接相关:例如 hcsshim.go 顶部的//go:generate指令会调用mkwinsyscall生成zsyscall_windows.go,Windows API 调用的封装文件(如internal/vmcompute、internal/wclayer、hcn等目录下的zsyscall_windows.go)都由此类机制自动产出。
环境依赖与安全上报
- hcsshim 要求Golang 1.18 或更高版本才能构建;
- 系统运行要求请参考微软官方的 Windows Container 系统要求文档;
- 安全问题与 bug 应通过邮件私下报告给 Microsoft Security Response Center(MSRC);
- 贡献者需要签署 CLA,并按要求使用
git commit --signoff/git rebase --signoff签名提交,CI 通过 DCO 应用自动校验。
小结:从 hcsshim 到 buildkit 的 Windows 容器构建链路
把整条链路串起来看:hcsshim 提供了访问 Windows HCS/HNS 宿主能力的 Go 语言通道,containerd-shim-runhcs-v1及其 V2 系列 shim 让 containerd 能够在 Windows 上以统一的 CRI 语义管理各种隔离形态的容器,而 buildkit 则通过runhcsoptions与hcn两个切入点,把 hcsshim 的能力接入到自己的 containerd worker 与 CNI 网络管理中,从而在 Windows 平台上提供与 Linux 对等的容器镜像构建体验。理解 hcsshim 的架构分层与构建方法,是深入掌握 buildkit Windows 支持乃至整个 Windows 容器生态的基石。
【免费下载链接】buildkitconcurrent, cache-efficient, and Dockerfile-agnostic builder toolkit项目地址: https://gitcode.com/GitHub_Trending/bu/buildkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考