Kubernetes 中调度 Windows 容器的指南

Windows 应用程序构成了许多组织中运行的服务和应用程序的很大一部分。本指南将引导您完成在 Kubernetes 中配置和部署 Windows 容器的步骤。

目标

  • 配置一个示例 deployment 以在 Windows 节点上运行 Windows 容器
  • (可选)使用组托管服务帐户(GMSA)为您的 Pod 配置 Active Directory 身份

在你开始之前

  • 创建一个 Kubernetes 集群,其中包括一个运行 Windows Server 的主节点和工作节点
  • 重要的是要注意,对于 Linux 和 Windows 容器,在 Kubernetes 上创建和部署服务和工作负载的行为几乎相同。与集群接口的 Kubectl 命令相同。提供以下部分中的示例只是为了快速启动 Windows 容器的使用体验。

入门:部署 Windows 容器

要在 Kubernetes 上部署 Windows 容器,您必须首先创建一个示例应用程序。下面的示例 YAML 文件创建了一个简单的 Web 服务器应用程序。创建一个名为 win-webserver.yaml 的服务规约,其内容如下:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: win-webserver
  5. labels:
  6. app: win-webserver
  7. spec:
  8. ports:
  9. # the port that this service should serve on
  10. - port: 80
  11. targetPort: 80
  12. selector:
  13. app: win-webserver
  14. type: NodePort
  15. ---
  16. apiVersion: apps/v1
  17. kind: Deployment
  18. metadata:
  19. labels:
  20. app: win-webserver
  21. name: win-webserver
  22. spec:
  23. replicas: 2
  24. selector:
  25. matchLabels:
  26. app: win-webserver
  27. template:
  28. metadata:
  29. labels:
  30. app: win-webserver
  31. name: win-webserver
  32. spec:
  33. containers:
  34. - name: windowswebserver
  35. image: mcr.microsoft.com/windows/servercore:ltsc2019
  36. command:
  37. - powershell.exe
  38. - -command
  39. - "<#code used from https://gist.github.com/wagnerandrade/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) } ; "
  40. nodeSelector:
  41. beta.kubernetes.io/os: windows

注意:

端口映射也是支持的,但为简单起见,在此示例中容器端口 80 直接暴露给服务。

  1. 检查所有节点是否健康:

    1. kubectl get nodes
  2. 部署服务并观察 pod 更新:

    1. kubectl apply -f win-webserver.yaml
    2. kubectl get pods -o wide -w

    正确部署服务后,两个 Pod 都标记为“Ready”。要退出 watch 命令,请按 Ctrl + C。

  3. 检查部署是否成功。验证:

    • Windows 节点上每个 Pod 有两个容器,使用 docker ps
    • Linux 主机列出两个 Pod,使用 kubectl get pods
    • 跨网络的节点到 Pod 通信,从 Linux 主服务器 curl 您的 pod IPs 的端口80,以检查 Web 服务器响应
    • Pod 到 Pod 的通信,使用 docker exec 或 kubectl exec 在 pod 之间(以及跨主机,如果您有多个 Windows 节点)进行 ping 操作
    • 服务到 Pod 的通信,从 Linux 主服务器和各个 Pod 中 curl 虚拟服务 IP(在 kubectl get services 下可见)
    • 服务发现,使用 Kubernetes curl 服务名称默认 DNS 后缀
    • 入站连接,从 Linux 主服务器或集群外部的计算机 curl NodePort
    • 出站连接,使用 kubectl exec 从 Pod 内部 curl 外部 IP

注意:

由于当前平台对 Windows 网络堆栈的限制,Windows 容器主机无法访问在其上调度的服务的 IP。只有 Windows pods 才能访问服务 IP。

使用可配置的容器用户名

从 Kubernetes v1.16 开始,可以为 Windows 容器配置与其镜像默认值不同的用户名来运行其入口点和进程。此能力的实现方式和 Linux 容器有些不同。在此处可了解更多信息。

使用组托管服务帐户管理工作负载身份

从 Kubernetes v1.14 开始,可以将 Windows 容器工作负载配置为使用组托管服务帐户(GMSA)。组托管服务帐户是 Active Directory 帐户的一种特定类型,它提供自动密码管理,简化的服务主体名称(SPN)管理以及将管理委派给跨多台服务器的其他管理员的功能。配置了 GMSA 的容器可以访问外部 Active Directory 域资源,同时携带通过 GMSA 配置的身份。在此处了解有关为 Windows 容器配置和使用 GMSA 的更多信息。

污点和容忍度

目前,用户需要将 Linux 和 Windows 工作负载运行在各自特定的操作系统的节点上,因而需要结合使用污点和节点选择算符。这可能仅给 Windows 用户造成不便。推荐的方法概述如下,其主要目标之一是该方法不应破坏与现有 Linux 工作负载的兼容性。

确保特定操作系统的工作负载落在适当的容器主机上

用户可以使用污点和容忍度确保 Windows 容器可以调度在适当的主机上。目前所有 Kubernetes 节点都具有以下默认标签:

  • kubernetes.io/os = [windows|linux]
  • kubernetes.io/arch = [amd64|arm64|…]

如果 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,也需要合适的匹配的容忍度设置。

  1. nodeSelector:
  2. kubernetes.io/os: windows
  3. node.kubernetes.io/windows-build: '10.0.17763'
  4. tolerations:
  5. - key: "os"
  6. operator: "Equal"
  7. value: "windows"
  8. effect: "NoSchedule"

处理同一集群中的多个 Windows 版本

每个 Pod 使用的 Windows Server 版本必须与该节点的 Windows Server 版本相匹配。 如果要在同一集群中使用多个 Windows Server 版本,则应该设置其他节点标签和 nodeSelector。

Kubernetes 1.17 自动添加了一个新标签 node.kubernetes.io/windows-build 来简化此操作。 如果您运行的是旧版本,则建议手动将此标签添加到 Windows 节点。

此标签反映了需要兼容的 Windows 主要、次要和内部版本号。以下是当前每个 Windows Server 版本使用的值。

产品名称内部编号
Windows Server 201910.0.17763
Windows Server version 180910.0.17763
Windows Server version 190310.0.18362

使用 RuntimeClass 简化

[RuntimeClass] 可用于简化使用污点和容忍度的过程。集群管理员可以创建 RuntimeClass 对象,用于封装这些污点和容忍度。

  1. 将此文件保存到 runtimeClasses.yml 文件。它包括适用于 Windows 操作系统、体系结构和版本的 nodeSelector

    1. apiVersion: node.k8s.io/v1beta1
    2. kind: RuntimeClass
    3. metadata:
    4. name: windows-2019
    5. handler: 'docker'
    6. scheduling:
    7. nodeSelector:
    8. kubernetes.io/os: 'windows'
    9. kubernetes.io/arch: 'amd64'
    10. node.kubernetes.io/windows-build: '10.0.17763'
    11. tolerations:
    12. - effect: NoSchedule
    13. key: os
    14. operator: Equal
    15. value: "windows"
  1. 集群管理员运行 kubectl create -f runtimeClasses.yml 操作
  2. 根据需要向 Pod 规约中添加 runtimeClassName: windows-2019

例如:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: iis-2019
  5. labels:
  6. app: iis-2019
  7. spec:
  8. replicas: 1
  9. template:
  10. metadata:
  11. name: iis-2019
  12. labels:
  13. app: iis-2019
  14. spec:
  15. runtimeClassName: windows-2019
  16. containers:
  17. - name: iis
  18. image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
  19. resources:
  20. limits:
  21. cpu: 1
  22. memory: 800Mi
  23. requests:
  24. cpu: .1
  25. memory: 300Mi
  26. ports:
  27. - containerPort: 80
  28. selector:
  29. matchLabels:
  30. app: iis-2019
  31. ---
  32. apiVersion: v1
  33. kind: Service
  34. metadata:
  35. name: iis
  36. spec:
  37. type: LoadBalancer
  38. ports:
  39. - protocol: TCP
  40. port: 80
  41. selector:
  42. app: iis-2019