Kubernetes中Windows容器的调度指南
Windows应用程序构成了许多组织中运行的服务和应用程序的很大一部分。本指南将引导你完成在Kubernetes中配置和部署Windows容器的步骤。
目标 在开始之前入门:部署 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='Windows Container Web Server
' ;$callerCountsString='' ;$callerCounts.Keys | % { $callerCountsString+='IP {0} callerCount {1} ' -f $ip[1].IPAddress,$callerCounts.Item($_) } ;$footer='' ;$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。
检查部署是否成功。验证:
Note:由于当前平台对Windows网络堆栈的限制,Windows容器主机无法访问在其上调度的服务的IP。只有Windowspods才能访问服务IP。
可观测性 抓取来自工作负载的日志
日志是可观测性的重要一环;使用日志用户可以获得对负载运行状况的洞察,因而日志是故障排查的一个重要手法。因为Windows容器中的Windows容器和负载与Linux容器的行为不同,用户很难收集日志,因此运行状态的可见性很受限。例如,Windows工作负载通常被配置为将日志输出到Windows事件跟踪(EventTracingforWindows,ETW),或者将日志条目推送到应用的事件日志中。 LogMonitor是Microsoft提供的一个开源工具,是监视Windows容器中所配置的日志源的推荐方式。LogMonitor支持监视时间日志、ETW提供者模块以及自定义的应用日志,并使用管道的方式将其输出到标准输出(stdout),以便kubectllogs
这类命令能够读取这些数据。
请遵照LogMonitorGitHub页面上的指令,将其可执行文件和配置文件复制到你的所有容器中,并为其添加必要的入口点(Entrypoint),以便LogMonitor能够将你的日志输出推送到标准输出(stdout)。
使用可配置的容器用户名
从Kubernetesv1.16开始,可以为Windows容器配置与其镜像默认值不同的用户名来运行其入口点和进程。此能力的实现方式和Linux容器有些不同。
使用组托管服务帐户管理工作负载身份
从Kubernetesv1.14开始,可以将Windows容器工作负载配置为使用组托管服务帐户(GMSA)。组托管服务帐户是ActiveDirectory帐户的一种特定类型,它提供自动密码管理,简化的服务主体名称(SPN)管理以及将管理委派给跨多台服务器的其他管理员的功能。配置了GMSA的容器可以访问外部ActiveDirectory域资源,同时携带通过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值对WindowsPod的调度没有影响,因此仍然需要污点、容忍度以及节点选择器,以确保WindowsPod调度至合适的Windows节点。
确保特定操作系统的工作负载落在适当的容器主机上
用户可以使用污点和容忍度确保Windows容器可以调度在适当的主机上。目前所有Kubernetes节点都具有以下默认标签:
如果Pod规范未指定诸如"kubernetes.io/os":windows之类的nodeSelector,则该Pod可能会被调度到任何主机(Windows或Linux)上。这是有问题的,因为Windows容器只能在Windows上运行,而Linux容器只能在Linux上运行。最佳实践是使用nodeSelector。
但是,我们了解到,在许多情况下,用户都有既存的大量的Linux容器部署,以及一个现成的配置生态系统,例如社区Helmcharts,以及程序化Pod生成案例,例如Operators。在这些情况下,你可能会不愿意更改配置添加nodeSelector。替代方法是使用污点。由于kubelet可以在注册期间设置污点,因此可以轻松修改它,使其仅在Windows上运行时自动添加污点。
例如:--register-with-taints='os=windows:NoSchedule'
向所有Windows节点添加污点后,Kubernetes将不会在它们上调度任何负载(包括现有的LinuxPod)。为了使某WindowsPod调度到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"
处理同一集群中的多个Windows版本
每个Pod使用的WindowsServer版本必须与该节点的WindowsServer版本相匹配。如果要在同一集群中使用多个WindowsServer版本,则应该设置其他节点标签和nodeSelector。
Kubernetes1.17自动添加了一个新标签node.kubernetes.io/windows-build来简化此操作。如果你运行的是旧版本,则建议手动将此标签添加到Windows节点。
此标签反映了需要兼容的Windows主要、次要和内部版本号。以下是当前每个WindowsServer版本使用的值。
产品名称内部编号
Windows Server 2019
10.0.17763
Windows Server version 1809
10.0.17763
Windows Server version 1903
10.0.18362
使用RuntimeClass简化
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"
集群管理员执行kubectlcreate-fruntimeClasses.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