Kubernetes 中 Windows 容器的调度指南
Windows 应用程序构成了许多组织中运行的服务和应用程序的很大一部分。 本指南将引导你完成在 Kubernetes 中配置和部署 Windows 容器的步骤。
要在 Kubernetes 上部署 Windows 容器,你必须首先创建一个示例应用程序。 下面的示例 YAML 文件创建了一个简单的 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
Note: 端口映射也是支持的,但为简单起见,在此示例中容器端口 80 直接暴露给服务。
kubectl get nodes部署服务并观察 pod 更新:
kubectl apply -f win-webserver.yaml
kubectl get pods -o wide -w
正确部署服务后,两个 Pod 都标记为 “Ready”。要退出 watch 命令,请按 Ctrl + C。
检查部署是否成功。验证:docker ps
kubectl get pods
curl
你的 pod IPs 的端口 80,以检查 Web 服务器响应curl
虚拟服务 IP (在 kubectl get services
下可见)curl
服务名称 默认 DNS 后缀curl
NodePortNote: 由于当前平台对 Windows 网络堆栈的限制,Windows 容器主机无法访问在其上调度的服务的 IP。只有 Windows pods 才能访问服务 IP。
日志是可观测性的重要一环;使用日志用户可以获得对负载运行状况的洞察, 因而日志是故障排查的一个重要手法。 因为 Windows 容器中的 Windows 容器和负载与 Linux 容器的行为不同, 用户很难收集日志,因此运行状态的可见性很受限。 例如,Windows 工作负载通常被配置为将日志输出到 Windows 事件跟踪 (Event Tracing for Windows,ETW),或者将日志条目推送到应用的事件日志中。 LogMonitor 是 Microsoft 提供的一个开源工具,是监视 Windows 容器中所配置的日志源 的推荐方式。 LogMonitor 支持监视时间日志、ETW 提供者模块以及自定义的应用日志, 并使用管道的方式将其输出到标准输出(stdout),以便 kubectl logs <pod>
这类命令能够读取这些数据。
请遵照 LogMonitor GitHub 页面上的指令,将其可执行文件和配置文件复制到 你的所有容器中,并为其添加必要的入口点(Entrypoint),以便 LogMonitor 能够将你的日志输出推送到标准输出(stdout)。
从 Kubernetes v1.16 开始,可以为 Windows 容器配置与其镜像默认值不同的用户名 来运行其入口点和进程。 此能力的实现方式和 Linux 容器有些不同。
从 Kubernetes v1.14 开始,可以将 Windows 容器工作负载配置为使用组托管服务帐户(GMSA)。 组托管服务帐户是 Active Directory 帐户的一种特定类型,它提供自动密码管理, 简化的服务主体名称(SPN)管理以及将管理委派给跨多台服务器的其他管理员的功能。 配置了 GMSA 的容器可以访问外部 Active Directory 域资源,同时携带通过 GMSA 配置的身份。
目前,用户需要将 Linux 和 Windows 工作负载运行在各自特定的操作系统的节点上, 因而需要结合使用污点和节点选择算符。这可能仅给 Windows 用户造成不便。 推荐的方法概述如下,其主要目标之一是该方法不应破坏与现有 Linux 工作负载的兼容性。
如果 IdentifyPodOS
特性门控是启用的, 你可以(并且应该)为 Pod 设置 .spec.os.name
以表明该 Pod 中的容器所针对的操作系统。对于运行 Linux 容器的 Pod,设置 .spec.os.name
为 linux
。对于运行 Windows 容器的 Pod,设置 .spec.os.name
为 Windows
。
Note: 从 1.24 开始,IdentifyPodOS
功能处于 Beta 阶段,默认启用。
在将 Pod 分配给节点时,调度程序不使用 .spec.os.name
的值。你应该使用正常的 Kubernetes 机制将 Pod 分配给节点, 确保集群的控制平面将 Pod 放置到适合运行的操作系统。 .spec.os.name
值对 Windows Pod 的调度没有影响,因此仍然需要污点、容忍度以及节点选择器, 以确保 Windows Pod 调度至合适的 Windows 节点。
用户可以使用污点和容忍度确保 Windows 容器可以调度在适当的主机上。目前所有 Kubernetes 节点都具有以下默认标签:
如果 Pod 规范未指定诸如 "kubernetes.io/os": windows
之类的 nodeSelector,则该 Pod 可能会被调度到任何主机(Windows 或 Linux)上。 这是有问题的,因为 Windows 容器只能在 Windows 上运行,而 Linux 容器只能在 Linux 上运行。 最佳实践是使用 nodeSelector。
但是,我们了解到,在许多情况下,用户都有既存的大量的 Linux 容器部署,以及一个现成的配置生态系统, 例如社区 Helm charts,以及程序化 Pod 生成案例,例如 Operators。 在这些情况下,你可能会不愿意更改配置添加 nodeSelector。替代方法是使用污点。 由于 kubelet 可以在注册期间设置污点,因此可以轻松修改它,使其仅在 Windows 上运行时自动添加污点。
例如:--register-with-taints='os=windows:NoSchedule'
向所有 Windows 节点添加污点后,Kubernetes 将不会在它们上调度任何负载(包括现有的 Linux Pod)。 为了使某 Windows Pod 调度到 Windows 节点上,该 Pod 需要 nodeSelector 和合适的匹配的容忍度设置来选择 Windows。
nodeSelector:
kubernetes.io/os: windows
node.kubernetes.io/windows-build: '10.0.17763'
tolerations:
- key: "os"
operator: "Equal"
value: "windows"
effect: "NoSchedule"
每个 Pod 使用的 Windows Server 版本必须与该节点的 Windows Server 版本相匹配。 如果要在同一集群中使用多个 Windows Server 版本,则应该设置其他节点标签和 nodeSelector。
Kubernetes 1.17 自动添加了一个新标签 node.kubernetes.io/windows-build
来简化此操作。 如果你运行的是旧版本,则建议手动将此标签添加到 Windows 节点。
此标签反映了需要兼容的 Windows 主要、次要和内部版本号。以下是当前每个 Windows Server 版本使用的值。
产品名称 | 内部编号 |
---|---|
Windows Server 2019 | 10.0.17763 |
Windows Server version 1809 | 10.0.17763 |
Windows Server version 1903 | 10.0.18362 |
RuntimeClass 可用于 简化使用污点和容忍度的过程。 集群管理员可以创建 RuntimeClass
对象,用于封装这些污点和容忍度。
runtimeClasses.yml
文件。 它包括适用于 Windows 操作系统、体系结构和版本的 nodeSelector
。apiVersion: node.k8s.io/v1集群管理员执行
kind: RuntimeClass
metadata:
name: windows-2019
handler: 'docker'
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