在 Kubernetes 中运行 Windows 容器指南

本页提供了使用 Kubernetes 运行 Windows 容器的一些步骤指南。本页还重点介绍了 Kubernetes 中的一些 Windows 特定功能。

重要提示:在 Kubernetes 上创建和部署 Service 和工作负载对于 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
  1. 检查所有节点是否健康

    kubectl get nodes
    
  2. 部署 Service 并观察 Pod 更新

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

    当 Service 正确部署后,两个 Pod 都被标记为 Ready。要退出 watch 命令,请按 Ctrl+C。

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

    • 从 Linux 控制平面节点列出若干 Pod,使用 kubectl get pods
    • 跨网络实现节点到 Pod 的通信:从 Linux 控制平面节点对 Pod IP 的 80 端口执行 curl 命令,检查 Web 服务器响应
    • Pod 到 Pod 的通信:使用 kubectl exec 在 Pod 之间(以及跨主机,如果你有多个 Windows 节点)执行 ping 命令
    • Service 到 Pod 的通信:从 Linux 控制平面节点以及各个 Pod 中,对虚拟 Service IP(通过 kubectl get services 查看)执行 curl 命令
    • 服务发现:使用 Kubernetes 默认 DNS 后缀 对服务名执行 curl 命令
    • 入站连接:从 Linux 控制平面节点或集群外部的机器,对 NodePort 执行 curl 命令
    • 出站连接:使用 kubectl exec 从 Pod 内部对外部 IP 执行 curl 命令

可观测性

从工作负载捕获日志

日志是可观测性的重要组成部分;它们使用户能够深入了解工作负载的操作方面,并且是排查问题的关键要素。由于 Windows 容器以及 Windows 容器内部的工作负载与 Linux 容器的行为不同,用户在收集日志时遇到困难,从而限制了操作可见性。例如,Windows 工作负载通常被配置为记录到 ETW (Event Tracing for Windows) 或将条目推送到应用事件日志。LogMonitor 是 Microsoft 开发的一个开源工具,它是监视 Windows 容器内已配置的日志源的推荐方式。LogMonitor 支持监视事件日志、ETW 提供程序和自定义应用日志,并将它们通过管道输出到 STDOUT,供 kubectl logs <pod> 消费。

按照 LogMonitor GitHub 页面上的说明,将其二进制文件和配置文件复制到所有容器中,并添加必要的入口点,以便 LogMonitor 将日志推送到 STDOUT。

配置容器用户

使用可配置的容器用户名

可以将 Windows 容器配置为使用与镜像默认设置不同的用户名来运行其入口点和进程。在此处了解更多信息:此处

使用 Group Managed Service Accounts 管理工作负载身份

可以将 Windows 容器工作负载配置为使用 Group Managed Service Accounts (GMSA)。Group Managed Service Accounts 是一种特定类型的 Active Directory 账户,提供自动密码管理、简化的服务主体名称 (SPN) 管理,以及将管理委派给多个服务器上其他管理员的能力。配置了 GMSA 的容器可以访问外部 Active Directory Domain 资源,同时携带使用 GMSA 配置的身份。在此处了解更多关于为 Windows 容器配置和使用 GMSA 的信息:此处

污点和容忍度

用户需要结合使用污点和节点选择器,以便将 Linux 和 Windows 工作负载调度到各自的 OS 特定节点。下面概述了推荐的方法,其主要目标之一是该方法不应破坏现有 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 节点上。

确保 OS 特定工作负载调度到相应的容器主机

用户可以使用污点和容忍度确保 Windows 容器能够调度到适当的主机。所有运行 Kubernetes 1.33 的 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.33 的最佳实践是使用 nodeSelector

然而,在许多情况下,用户已经为 Linux 容器拥有大量现有部署,以及一个由现成配置组成的生态系统,例如社区 Helm Chart 和程序化 Pod 生成场景(例如使用 Operator)。在这些情况下,你可能不愿意更改配置以向所有 Pod 和 Pod 模板添加 nodeSelector 字段。另一种方法是使用污点。由于 kubelet 可以在注册时设置污点,因此可以轻松地修改 kubelet,使其仅在 Windows 上运行时自动添加污点。

例如:--register-with-taints='os=windows:NoSchedule'

通过向所有 Windows 节点添加污点,任何 Pod 都不会调度到它们上面(包括现有 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 201910.0.17763
Windows Server 202210.0.20348

使用 RuntimeClass 简化

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

  1. 将此文件保存为 runtimeClasses.yml。它包含了针对 Windows OS、架构和版本的适当 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"
    
  2. 以集群管理员身份运行 kubectl create -f runtimeClasses.yml

  3. 根据需要向 Pod Spec 中添加 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
    
上次修改时间:太平洋标准时间 2023 年 5 月 24 日上午 10:55:修复 Windows 用户指南中的步骤缩进 (#41283) (f0e755caae)