Kubernetes 中调度 Windows 容器的指南

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

目标

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

在你开始之前

  • 创建一个 Kubernetes 集群,其中包括一个 运行 Windows 服务器的主节点和工作节点
  • 重要的是要注意,对于 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/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) } ; "
  40. nodeSelector:
  41. 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/v1
    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"
  2. 集群管理员运行 kubectl create -f runtimeClasses.yml 操作

  3. 根据需要向 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