Kubernetes 中的 Windows 容器为运行 Windows 应用程序提供了一种方便、安全的解决方案。Windows 容器技术可以将 Windows 应用程序封装在一个独立的容器中,包括所有的依赖项和配置参数,使得应用程序可以在任何支持 Windows 容器的环境中进行部署和运行。
对于那些同时运行基于 Windows 和 Linux 应用程序的组织来说,Kubernetes 提供了一个统一的编排系统,它可以轻松地管理这些不同类型的工作负载,并提供了与操作系统无关的一致性运营体验。开发人员和运维人员可以使用相同的工具和流程来管理和部署不同类型的应用程序,从而大幅提高运营效率。
一、Windows节点
若要在 Kubernetes 中启用对 Windows 容器的编排,可以在现有的 Linux 集群中包含 Windows 节点。 在 Kubernetes 上调度 Pod 中的 Windows 容器与调度基于 Linux 的容器类似。
为了运行 Windows 容器, Kubernetes 集群必须包含多个操作系统。 尽管只能在 Linux 上运行控制平面, 可以部署运行 Windows 或 Linux 的工作节点。
支持 Windows 节点的前提是操作系统为 Windows Server 2019。本文使用术语 Windows 容器表示具有进程隔离能力的 Windows 容器。 Kubernetes 不支持使用 Hyper-V 隔离能力来运行 Windows 容器。
二、兼容性与局限性
某些节点层面的功能特性仅在使用特定容器运行时时才可用; 另外一些特性则在 Windows 节点上不可用,包括:
- 巨页(HugePages):Windows 容器当前不支持;
- 特权容器:Windows 容器当前不支持。 HostProcess 容器提供类似功能;
- TerminationGracePeriod:需要 containerD。
1、与Linux比较
Kubernetes 关键组件在 Windows 上的工作方式与在 Linux 上相同。 本节介绍几个关键的工作负载抽象及其如何映射到 Windows。
Pod:
Pod 是 Kubernetes 的基本构建块,是可以创建或部署的最小和最简单的单元。 不可以在同一个 Pod 中部署 Windows 和 Linux 容器。 Pod 中的所有容器都调度到同一 Node 上,每个 Node 代表一个特定的平台和体系结构。 Windows 容器支持以下 Pod 能力、属性和事件:
- 每个 Pod 有一个或多个容器,具有进程隔离和卷共享能力;
- Pod status 字段;
- 就绪、存活和启动探针;
- postStart 和 preStop 容器生命周期回调;
- ConfigMap 和 Secret:作为环境变量或卷;
- emptyDir 卷;
- 命名管道形式的主机挂载;
- 资源限制;
- 操作系统字段:
.spec.os.name 字段应设置为 windows 以表明当前 Pod 使用 Windows 容器。如果将 `.spec.os.name` 字段设置为 `windows`,则不能在对应 Pod 的 .spec 中设置以下字段:
- spec.hostPID
- spec.hostIPC
- spec.securityContext.seLinuxOptions
- spec.securityContext.seccompProfile
- spec.securityContext.fsGroup
- spec.securityContext.fsGroupChangePolicy
- spec.securityContext.sysctls
- spec.shareProcessNamespace
- spec.securityContext.runAsUser
- spec.securityContext.runAsGroup
- spec.securityContext.supplementalGroups
- spec.containers[*].securityContext.seLinuxOptions
- spec.containers[*].securityContext.seccompProfile
- spec.containers[*].securityContext.capabilities
- spec.containers[*].securityContext.readOnlyRootFilesystem
- spec.containers[*].securityContext.privileged
- spec.containers[*].securityContext.allowPrivilegeEscalation
- spec.containers[*].securityContext.procMount
- spec.containers[*].securityContext.runAsUser
- spec.containers[*].securityContext.runAsGroup
在上述列表中,通配符(`*`)表示列表中的所有项。
例如,spec.containers[*].securityContext 指代所有容器的 SecurityContext 对象。 如果指定了这些字段中的任意一个,则 API 服务器不会接受此 Pod。
工作负载资源包括:
- ReplicaSet
- Deployment
- StatefulSet
- DaemonSet
- Job
- CronJob
- ReplicationController
Pod、工作负载资源和 Service 是在 Kubernetes 上管理 Windows 工作负载的关键元素。 然而,它们本身还不足以在动态的云原生环境中对 Windows 工作负载进行恰当的生命周期管理。
- kubectl exec;
- Pod 和容器度量指标;
- Pod 水平自动扩缩容;
- 资源配额;
- 调度器抢占。
2、kubelet的命令行选项
某些 kubelet 命令行选项在 Windows 上的行为不同,如下所述:
- –windows-priorityclass 允许设置 kubelet 进程的调度优先级 ;
- –kube-reserved、–system-reserved 和 –eviction-hard 标志更新 NodeAllocatable;
- 未实现使用 –enforce-node-allocable 驱逐;
- 未实现使用 –eviction-hard 和 –eviction-soft 驱逐;
- 在 Windows 节点上运行时,kubelet 没有内存或 CPU 限制。 –kube-reserved 和 –system-reserved 仅从 NodeAllocatable 中减去,并且不保证为工作负载提供的资源;
- 未实现 MemoryPressure 条件;
- kubelet 不会执行 OOM 驱逐操作。
3、API兼容性
由于操作系统和容器运行时的缘故,Kubernetes API 在 Windows 上的工作方式存在细微差异。 某些工作负载属性是为 Linux 设计的,无法在 Windows 上运行。
从较高的层面来看,以下操作系统概念是不同的:
(1)身份 – Linux 使用 userID(UID)和 groupID(GID),表示为整数类型。 用户名和组名是不规范的,它们只是 /etc/groups 或 /etc/passwd 中的别名, 作为 UID+GID 的后备标识。 Windows 使用更大的二进制安全标识符(SID), 存放在 Windows 安全访问管理器(Security Access Manager,SAM)数据库中。 此数据库在主机和容器之间或容器之间不共享;
(2)文件权限 – Windows 使用基于 SID 的访问控制列表, 而像 Linux 使用基于对象权限和 UID+GID 的位掩码(POSIX 系统)以及可选的访问控制列表;
(3)文件路径 – Windows 上的约定是使用 \ 而不是 /。 Go IO 库通常接受两者,能让其正常工作,但当设置要在容器内解读的路径或命令行时, 可能需要用 \;
(4)信号 – Windows 交互式应用处理终止的方式不同,可以实现以下一种或多种:
- UI 线程处理包括 WM_CLOSE 在内准确定义的消息;
- 控制台应用使用控制处理程序(Control Handler)处理 Ctrl-C 或 Ctrl-Break;
- 服务会注册可接受 SERVICE_CONTROL_STOP 控制码的服务控制处理程序(Service Control Handler)函数。
容器退出码遵循相同的约定,其中 0 表示成功,非零表示失败。 具体的错误码在 Windows 和 Linux 中可能不同。 但是,从 Kubernetes 组件(kubelet、kube-proxy)传递的退出码保持不变。
4、容器规约的字段兼容性
以下列表记录了 Pod 容器规约在 Windows 和 Linux 之间的工作方式差异:
- 巨页(Huge page)在 Windows 容器运行时中未实现,且不可用。 巨页需要不可为容器配置的用户特权生效;
- requests.cpu 和 requests.memory – 从节点可用资源中减去请求,因此请求可用于避免一个节点过量供应。 但是,请求不能用于保证已过量供应的节点中的资源。 如果运营商想要完全避免过量供应,则应将设置请求作为最佳实践应用到所有容器;
- securityContext.allowPrivilegeEscalation – 不能在 Windows 上使用;所有权能字都无法生效;
- securityContext.capabilities – POSIX 权能未在 Windows 上实现;
- securityContext.privileged – Windows 不支持特权容器, 可使用 HostProcess 容器代替;
- securityContext.procMount – Windows 没有 /proc 文件系统;
- securityContext.readOnlyRootFilesystem – 不能在 Windows 上使用;对于容器内运行的注册表和系统进程,写入权限是必需的;
- securityContext.runAsGroup – 不能在 Windows 上使用,因为不支持 GID;
- securityContext.runAsNonRoot – 此设置将阻止以 ContainerAdministrator 身份运行容器,这是 Windows 上与 root 用户最接近的身份;
- securityContext.runAsUser – 改用 runAsUserName;
- securityContext.seLinuxOptions – 不能在 Windows 上使用,因为 SELinux 特定于 Linux;
- terminationMessagePath – 这个字段有一些限制,因为 Windows 不支持映射单个文件。 默认值为 /dev/termination-log,因为默认情况下它在 Windows 上不存在,所以能生效。
5、Pod 规约的字段兼容性
以下列表记录了 Pod 规约在 Windows 和 Linux 之间的工作方式差异:
- hostIPC 和 hostpid – 不能在 Windows 上共享主机命名空间;
- hostNetwork ;
- dnsPolicy – Windows 不支持将 Pod dnsPolicy 设为 ClusterFirstWithHostNet, 因为未提供主机网络。Pod 始终用容器网络运行;
- podSecurityContext;
- shareProcessNamespace – 这是一个 beta 版功能特性,依赖于 Windows 上未实现的 Linux 命名空间。 Windows 无法共享进程命名空间或容器的根文件系统(root filesystem)。 只能共享网络;
- terminationGracePeriodSeconds – 这在 Windows 上的 Docker 中没有完全实现。目前的行为是通过 CTRL_SHUTDOWN_EVENT 发送 ENTRYPOINT 进程,然后 Windows 默认等待 5 秒, 最后使用正常的 Windows 关机行为终止所有进程。 5 秒默认值实际上位于容器内的 Windows 注册表中, 因此在构建容器时可以覆盖这个值;
- volumeDevices – 这是一个 beta 版功能特性,未在 Windows 上实现。 Windows 无法将原始块设备挂接到 Pod;
- volumes:如果定义一个 emptyDir 卷,则无法将卷源设为 memory;
- 无法为卷挂载启用 mountPropagation,因为这在 Windows 上不支持。
6、hostNetwork的字段兼容性
现在,kubelet 可以请求在 Windows 节点上运行的 Pod 使用主机的网络命名空间,而不是创建新的 Pod 网络命名空间。 要启用此功能,请将 –feature-gates=WindowsHostNetwork=true 传递给 kubelet。
说明:此功能需要支持该功能的容器运行时。
7、Pod 安全上下文的字段兼容性
Pod 的 securityContext 中只有 securityContext.runAsNonRoot 和securityContext.windowsOptions 字段在 Windows 上生效。
三、Pause容器
在 Kubernetes Pod 中,首先创建一个基础容器或 “pause” 容器来承载容器。 在 Linux 中,构成 Pod 的 cgroup 和命名空间维持持续存在需要一个进程; 而 pause 进程就提供了这个功能。 属于同一 Pod 的容器(包括基础容器和工作容器)共享一个公共网络端点 (相同的 IPv4 和/或 IPv6 地址,相同的网络端口空间)。 Kubernetes 使用 pause 容器以允许工作容器崩溃或重启,而不会丢失任何网络配置。
Kubernetes 维护一个多体系结构的镜像,包括对 Windows 的支持。 对于 Kubernetes v1.28.4,推荐的 pause 镜像为 registry.k8s.io/pause:3.6。 可在 GitHub 上获得源代码。
Microsoft 维护一个不同的多体系结构镜像,支持 Linux 和 Windows amd64, 可以找到的镜像类似 mcr.microsoft.com/oss/kubernetes/pause:3.6。 此镜像的构建与 Kubernetes 维护的镜像同源,但所有 Windows 可执行文件均由 Microsoft 进行了验证码签名。 如果正部署到一个需要签名可执行文件的生产或类生产环境, Kubernetes 项目建议使用 Microsoft 维护的镜像。
四、容器运行时
需要将容器运行时安装到集群中的每个节点, 这样 Pod 才能在这些节点上运行。以下容器运行时适用于 Windows:
1、ContainerD
对于运行 Windows 的 Kubernetes 节点,可以使用 ContainerD 1.4.0+ 作为容器运行时。
将 GMSA 和 containerd 一起用于访问 Windows 网络共享时存在已知限制, 这需要一个内核补丁。
2、Mirantis容器运行时
Mirantis 容器运行时(MCR) 可作为所有 Windows Server 2019 和更高版本的容器运行时。
五、版本兼容性
在 Windows 节点上,如果主机操作系统版本必须与容器基础镜像操作系统版本匹配, 则会应用严格的兼容性规则。 仅 Windows Server 2019 作为容器操作系统时,才能完全支持 Windows 容器。
对于 Kubernetes v1.28,Windows 节点(和 Pod)的操作系统兼容性如下:
Windows Server LTSC release
- Windows Server 2019
- Windows Server 2022
Windows Server SAC release
- Windows Server version 20H2
也适用 Kubernetes 版本偏差策略。
六、获取帮助
对 Kubernetes 集群进行故障排查的主要帮助来源应始于故障排查页面。
本节包括了一些其他特定于 Windows 的故障排查帮助。 日志是解决 Kubernetes 中问题的重要元素。 确保在任何时候向其他贡献者寻求故障排查协助时随附了日志信息。
报告问题和功能请求:
如果发现疑似 bug,或者想提出功能请求,请按照 SIG Windows 贡献指南新建一个 Issue。 应该先搜索 issue 列表,以防之前报告过这个问题,凭对该问题的经验添加评论, 并随附日志信息。 Kubernetes Slack 上的 SIG Windows 频道也是一个很好的途径, 可以在创建工单之前获得一些初始支持和故障排查思路。