本页面提供了在 Kubernetes 中运行 Windows 容器的一些步骤指南。同时还介绍了 Kubernetes 中一些特定于 Windows 的功能。
需要注意的是,在 Kubernetes 上创建和部署服务与工作负载时,Linux 和 Windows 容器的操作方式基本相同。用于与集群交互的 kubectl 命令也是完全一致的。本页提供的示例旨在帮助您快速上手 Windows 容器。
配置一个示例部署,以在 Windows 节点上运行 Windows 容器。
您应该已经能够访问一个包含运行 Windows Server 的工作节点的 Kubernetes 集群。
下面的示例 YAML 文件部署了一个在 Windows 容器内运行的简单 Web 服务器应用程序。
创建一个名为 win-webserver.yaml 的清单文件,内容如下
---
apiVersion: v1
kind: Service
metadata:
name: win-webserver
labels:
app: win-webserver
spec:
ports:
# the port that this service should serve on
- port: 80
targetPort: 80
selector:
app: win-webserver
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: win-webserver
name: win-webserver
spec:
replicas: 2
selector:
matchLabels:
app: win-webserver
template:
metadata:
labels:
app: win-webserver
name: win-webserver
spec:
containers:
- name: windowswebserver
image: mcr.microsoft.com/windows/servercore:ltsc2019
command:
- powershell.exe
- -command
- "<#code used from https://gist.github.com/19WAS85/5424431#> ; $$listener = New-Object System.Net.HttpListener ; $$listener.Prefixes.Add('http://*:80/') ; $$listener.Start() ; $$callerCounts = @{} ; Write-Host('Listening at http://*:80/') ; while ($$listener.IsListening) { ;$$context = $$listener.GetContext() ;$$requestUrl = $$context.Request.Url ;$$clientIP = $$context.Request.RemoteEndPoint.Address ;$$response = $$context.Response ;Write-Host '' ;Write-Host('> {0}' -f $$requestUrl) ; ;$$count = 1 ;$$k=$$callerCounts.Get_Item($$clientIP) ;if ($$k -ne $$null) { $$count += $$k } ;$$callerCounts.Set_Item($$clientIP, $$count) ;$$ip=(Get-NetAdapter | Get-NetIpAddress); $$header='<html><body><H1>Windows Container Web Server</H1>' ;$$callerCountsString='' ;$$callerCounts.Keys | % { $$callerCountsString+='<p>IP {0} callerCount {1} ' -f $$ip[1].IPAddress,$$callerCounts.Item($$_) } ;$$footer='</body></html>' ;$$content='{0}{1}{2}' -f $$header,$$callerCountsString,$$footer ;Write-Output $$content ;$$buffer = [System.Text.Encoding]::UTF8.GetBytes($$content) ;$$response.ContentLength64 = $$buffer.Length ;$$response.OutputStream.Write($$buffer, 0, $$buffer.Length) ;$$response.Close() ;$$responseStatus = $$response.StatusCode ;Write-Host('< {0}' -f $$responseStatus) } ; "
nodeSelector:
kubernetes.io/os: windows
检查所有节点是否健康
kubectl get nodes
部署服务并观察 Pod 更新
kubectl apply -f win-webserver.yaml
kubectl get pods -o wide -w
当服务正确部署后,两个 Pod 都会被标记为 Ready(就绪)。要退出 watch 命令,请按 Ctrl+C。
检查部署是否成功。验证方法:
kubectl get podscurl 您 Pod IP 的 80 端口,以检查 Web 服务器响应kubectl exec 在 Pod 之间(以及跨主机,如果您有多个 Windows 节点)进行 ping 操作curl 虚拟服务 IP(在 kubectl get services 下查看)curl 服务名称curl NodePortkubectl exec 从 Pod 内部 curl 外部 IP日志是可观测性的重要组成部分;它们使用户能够洞察工作负载的运维层面,是故障排除的关键。由于 Windows 容器和 Windows 容器内部的工作负载与 Linux 容器的行为不同,用户在收集日志时会遇到困难,从而限制了运维的可视性。例如,Windows 工作负载通常配置为记录到 ETW(Windows 事件跟踪)或将条目推送到应用程序事件日志中。Microsoft 的开源工具 LogMonitor 是监视 Windows 容器内配置的日志源的推荐方式。LogMonitor 支持监控事件日志、ETW 提供程序和自定义应用程序日志,并将它们输送到 STDOUT,以便通过 kubectl logs <pod> 进行使用。
按照 LogMonitor GitHub 页面上的说明,将其二进制文件和配置文件复制到所有容器中,并添加必要的入口点,以便 LogMonitor 将您的日志推送到 STDOUT。
Windows 容器可以配置为使用与镜像默认值不同的用户名来运行其入口点和进程。点击此处了解更多信息。
Windows 容器工作负载可以配置为使用组托管服务账户(GMSA)。组托管服务账户是一种特定类型的 Active Directory 账户,它提供自动密码管理、简化的服务主体名称(SPN)管理,以及将管理权限委托给跨多个服务器的其他管理员的能力。配置了 GMSA 的容器可以在携带 GMSA 配置的身份的同时访问外部 Active Directory 域资源。点击此处了解更多关于为 Windows 容器配置和使用 GMSA 的信息。
用户需要结合使用 污点和节点选择器,以便将 Linux 和 Windows 工作负载调度到它们各自的操作系统特定节点上。推荐的方法概述如下,其主要目标之一是该方法不应破坏现有 Linux 工作负载的兼容性。
您可以(并且应该)为每个 Pod 设置 .spec.os.name,以指明该 Pod 中的容器所设计的操作系统。对于运行 Linux 容器的 Pod,将 .spec.os.name 设置为 linux。对于运行 Windows 容器的 Pod,将 .spec.os.name 设置为 windows。
调度程序在将 Pod 分配给节点时不会使用 .spec.os.name 的值。您应该使用标准的 Kubernetes 机制进行Pod 节点分配,以确保集群的控制平面将 Pod 放置在运行适当操作系统的节点上。
.spec.os.name 值对 Windows Pod 的调度没有影响,因此仍然需要污点和容忍度(或节点选择器)来确保 Windows Pod 落到合适的 Windows 节点上。
用户可以使用污点和容忍度确保 Windows 容器能够被调度到合适的主机上。所有运行 Kubernetes 1.36 的节点都有以下默认标签
如果 Pod 规范没有指定如 "kubernetes.io/os": windows 这样的 nodeSelector,Pod 可能会被调度到任何主机(Windows 或 Linux)上。这可能会带来问题,因为 Windows 容器只能在 Windows 上运行,而 Linux 容器只能在 Linux 上运行。对于 Kubernetes 1.36,最佳实践是使用 nodeSelector。
然而,在许多情况下,用户已经有大量现有的 Linux 容器部署,以及一系列现成的配置生态系统(例如社区 Helm Chart)和程序化 Pod 生成案例(例如操作员)。在这种情况下,您可能不愿意修改所有 Pod 和 Pod 模板以添加 nodeSelector 字段。替代方案是使用污点。由于 kubelet 可以在注册期间设置污点,因此可以轻松将其修改为在仅运行 Windows 时自动添加污点。
例如:--register-with-taints='os=windows:NoSchedule'
通过给所有 Windows 节点添加污点,没有任何东西会被调度到它们上面(这包括现有的 Linux Pod)。为了让 Windows Pod 调度到 Windows 节点上,它需要同时具备 nodeSelector 和匹配的容忍度以选择 Windows。
nodeSelector:
kubernetes.io/os: windows
node.kubernetes.io/windows-build: '10.0.20348'
tolerations:
- key: "os"
operator: "Equal"
value: "windows"
effect: "NoSchedule"
每个 Pod 使用的 Windows Server 版本必须与节点版本匹配。如果您想在同一个集群中使用多个 Windows Server 版本,则应设置额外的节点标签和 nodeSelector 字段。
Kubernetes 会自动添加一个标签 node.kubernetes.io/windows-build 来简化此操作。
此标签反映了兼容性所需的 Windows 主版本号、次版本号和构建号。以下是每个 Windows Server 版本使用的值
| 产品名称 | 版本 |
|---|---|
| Windows Server 2022 | 10.0.20348 |
| Windows Server 2025 | 10.0.26100 |
RuntimeClass 可用于简化使用污点和容忍度的过程。集群管理员可以创建一个 RuntimeClass 对象,用于封装这些污点和容忍度。
将此文件保存为 runtimeClasses.yml。它包含了针对 Windows 操作系统、架构和版本的适当 nodeSelector。
---
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: windows-2019
handler: example-container-runtime-handler
scheduling:
nodeSelector:
kubernetes.io/os: 'windows'
kubernetes.io/arch: 'amd64'
node.kubernetes.io/windows-build: '10.0.20348'
tolerations:
- effect: NoSchedule
key: os
operator: Equal
value: "windows"
以集群管理员身份运行 kubectl create -f runtimeClasses.yml
根据需要在 Pod 规范中添加 runtimeClassName: windows-2019
例如
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: iis-2019
labels:
app: iis-2019
spec:
replicas: 1
template:
metadata:
name: iis-2019
labels:
app: iis-2019
spec:
runtimeClassName: windows-2019
containers:
- name: iis
image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
resources:
limits:
cpu: 1
memory: 800Mi
requests:
cpu: .1
memory: 300Mi
ports:
- containerPort: 80
selector:
matchLabels:
app: iis-2019
---
apiVersion: v1
kind: Service
metadata:
name: iis
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 80
selector:
app: iis-2019