在 Kubernetes 中运行 Windows 容器的指南
此页面提供了一些步骤的演练,你可以按照这些步骤使用 Kubernetes 运行 Windows 容器。 此页面还重点介绍了 Kubernetes 中一些特定于 Windows 的功能。
需要注意的是,在 Kubernetes 上创建和部署服务和工作负载对于 Linux 和 Windows 容器的行为方式基本相同。 与集群交互的 kubectl 命令 是相同的。 此页面中的示例旨在快速启动你使用 Windows 容器的体验。
目标
配置一个示例部署,以便在 Windows 节点上运行 Windows 容器。
开始之前
你应已可以访问包含运行 Windows Server 的工作节点的 Kubernetes 集群。
入门:部署 Windows 工作负载
下面的示例 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
注意
也支持端口映射,但为简单起见,此示例将容器的 80 端口直接暴露给服务。检查所有节点是否健康
kubectl get nodes
部署服务并观察 Pod 更新
kubectl apply -f win-webserver.yaml kubectl get pods -o wide -w
当服务正确部署后,两个 Pod 都标记为就绪。 要退出 watch 命令,请按 Ctrl+C。
检查部署是否成功。 要验证
- 从 Linux 控制平面节点列出的几个 Pod,使用
kubectl get pods
- 跨网络的节点到 Pod 通信,从 Linux 控制平面节点
curl
你 Pod 的 IP 的 80 端口,以检查 Web 服务器响应 - Pod 到 Pod 通信,使用
kubectl exec
在 Pod 之间(以及跨主机,如果你有多个 Windows 节点)进行 ping 操作 - 服务到 Pod 通信,从 Linux 控制平面节点和各个 Pod
curl
虚拟服务 IP(在kubectl get services
下看到) - 服务发现,使用 Kubernetes 默认 DNS 后缀
curl
服务名称 - 入站连接,从 Linux 控制平面节点或集群外部的机器
curl
NodePort - 出站连接,使用
kubectl exec
从 Pod 内部curl
外部 IP
- 从 Linux 控制平面节点列出的几个 Pod,使用
注意
由于 Windows 网络堆栈的当前平台限制,Windows 容器主机无法访问在其上调度的服务的 IP。 只有 Windows Pod 能够访问服务 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.32 的 Kubernetes 节点都具有以下默认标签
- kubernetes.io/os = [windows|linux]
- kubernetes.io/arch = [amd64|arm64|...]
如果 Pod 规范未指定 nodeSelector
,例如 "kubernetes.io/os": windows
,则 Pod 可能被调度到任何主机上,无论是 Windows 还是 Linux。 这可能会有问题,因为 Windows 容器只能在 Windows 上运行,而 Linux 容器只能在 Linux 上运行。 Kubernetes 1.32 的最佳做法是使用 nodeSelector
。
但是,在许多情况下,用户有大量预先存在的 Linux 容器部署,以及现成的配置生态系统,例如社区 Helm Chart 以及编程 Pod 生成案例,例如使用操作器。 在这些情况下,你可能会犹豫是否要进行配置更改,以将 nodeSelector
字段添加到所有 Pod 和 Pod 模板中。 另一种方法是使用污点。 由于 kubelet 可以在注册期间设置污点,因此可以很容易地修改 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.17763'
tolerations:
- key: "os"
operator: "Equal"
value: "windows"
effect: "NoSchedule"
在同一集群中处理多个 Windows 版本
每个 Pod 使用的 Windows Server 版本必须与节点版本匹配。 如果你想在同一集群中使用多个 Windows Server 版本,则应设置额外的节点标签和 nodeSelector
字段。
Kubernetes 会自动添加一个标签 node.kubernetes.io/windows-build
以简化此过程。
此标签反映了需要匹配的 Windows 主版本号、次版本号和内部版本号以确保兼容性。以下是每个 Windows Server 版本使用的值
产品名称 | 版本 |
---|---|
Windows Server 2019 | 10.0.17763 |
Windows Server 2022 | 10.0.20348 |
使用 RuntimeClass 简化
RuntimeClass 可用于简化使用污点(taints)和容忍(tolerations)的过程。集群管理员可以创建一个 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.17763' 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