作者:半身风雪
上一节:K8S 进阶 容器和 Pod 分配内存资源
内容简介:上一节主要学习ConfigMap 环境变量的配置及使用、ConfigMap volume中的数据处理,那么本节内容主要容器和 Pod 分配内存资源。


Windows HostProcess 运行容器化负载

  • 简介
  • 一、创建 Windows HostProcess
    • 1.1、HostProcess 的使用限制
    • 1.2、HostProcess Pod 配置
    • 1.3、配置清单
    • 1.4、内存资源
  • 二、配置 GMSA
    • 2.1、创建 GMSA 管理资源
    • 2.2、配置集群启用 GMSA 管理的 RBAC
    • 2.3、分配 GMSA 管理服务账号
    • 2.4、配置 GMSA 管理引用
    • 2.5、使用主机名或 FQDN 对网络共享进行身份验证
    • 2.6、故障排查
  • 三、为 Windows 的 Pod 和容器配置 RunAsUserName
    • 3.1、设置 Username
    • 3.2、Windows Username 的局限性
  • 总结

简介

Windows HostProcess 容器让你能够在 Windows 主机上运行容器化负载。 这类容器以普通的进程形式运行,但能够在具有合适用户特权的情况下, 访问主机网络名字空间、存储和设备。

HostProcess 容器可用来在 Windows 节点上部署网络插件、存储配置、设备插件、kube-proxy 以及其他组件, 同时不需要配置专用的代理或者直接安装主机服务。


一、创建 Windows HostProcess

我何时该使用 Windows HostProcess 容器?

  • 当你准备执行需要访问主机上网络名字空间的任务时,HostProcess 容器能够访问主机上的网络接口和 IP 地址。
  • 当你需要访问主机上的资源,如文件系统、事件日志等等。
  • 需要安装特定的设备驱动或者 Windows 服务时。
  • 需要对管理任务和安全策略进行整合时。使用 HostProcess 容器能够缩小 Windows 节点上所需要的特权范围。

HostProcess 容器功能特性默认是启用的。 kubelet 会直接与 containerd 通信,通过 CRI 将主机进程标志传递过去。 你可以使用 containerd 来运行 HostProcess 容器。

想要禁用 HostProcess 容器特性,可以执行下面的命令:

$ –feature-gates=WindowsHostProcessContainers=false

1.1、HostProcess 的使用限制

  • HostProcess 容器需要 containerd 1.6 或更高版本的 容器运行时。
  • HostProcess Pods 只能包含 HostProcess 容器。这是在 Windows 操作系统上的约束; 非特权的 Windows 容器不能与主机 IP 名字空间共享虚拟网卡(vNIC)。
  • HostProcess 在主机上以一个进程的形式运行,除了通过 HostProcess 用户账号所实施的资源约束外,不提供任何形式的隔离。HostProcess 容器不支持文件系统或 Hyper-V 隔离。
  • volume 挂载是被支持的,并且要花在到容器卷下。
  • 默认情况下有一组主机用户账户可供 HostProcess 容器使用。
  • 对资源约束(磁盘、内存、CPU 个数)的支持与主机上进程相同。
  • 不支持命名管道或者 UNIX 域套接字形式的挂载,需要使用主机上的路径名来访问 。

1.2、HostProcess Pod 配置

在 Pod 安全标准中所定义的策略中, HostProcess Pod 默认是不被 basline 和 restricted 策略支持的。因此建议 HostProcess 运行在与 privileged 模式相看齐的规则下。

当运行在 privileged 规则下时,下面是要启用 HostProcess Pod 创建所需要设置的选项:

控制策略可选值
securityContext.windowsOptions.hostProcessWindows Pods 提供运行 HostProcess 容器的能力,这类容器能够具有对 Windows 节点的特权访问权限。true
hostNetwork初始时将默认位于主机网络中。在未来可能会希望将网络设置到不同的隔离环境中。true
securityContext.windowsOptions.runAsUsername关于 HostProcess 容器所要使用的用户的规约,需要设置在 Pod 的规约中。NT AUTHORITY\SYSTEM 、NT AUTHORITY\Local service 、NT AUTHORITY\NetworkService
runAsNonRoot因为 HostProcess 容器有访问主机的特权,runAsNonRoot 字段不可以设置为 true。未定义/Nil 、false

1.3、配置清单

这里我们只看部分内容:

spec:securityContext:windowsOptions:hostProcess: truerunAsUserName: "NT AUTHORITY\\Local service"hostNetwork: truecontainers:- name: testimage: image1:latestcommand:- ping- -t- 127.0.0.1nodeSelector:"kubernetes.io/os": windows

HostProcess 容器支持在容器卷空间中挂载卷的能力。 在容器内运行的应用能够通过相对或者绝对路径直接访问卷挂载。 环境变量 $CONTAINER_SANDBOX_MOUNT_POINT 在容器创建时被设置为指向容器卷的绝对主机路径。 相对路径是基于 .spec.containers.volumeMounts.mountPath 配置来推导的。

1.4、内存资源

资源约束(磁盘、内存、CPU 个数)作用到任务之上,并在整个任务上起作用。 例如,如果内存限制设置为 10MB,任何 HostProcess 任务对象所分配的内存不会超过 10MB。 这一行为与其他 Windows 容器类型相同。资源限制的设置方式与编排系统或容器运行时无关。 唯一的区别是用来跟踪资源所进行的磁盘资源用量的计算,出现差异的原因是因为 HostProcess 容器启动引导的方式造成的。

二、配置 GMSA

在 Kubernetes 环境中,GMSA 凭据规约配置为 Kubernetes 集群范围的自定义资源 (Custom Resources)形式。Windows Pod 以及各 Pod 中的每个容器可以配置为 使用 GMSA 来完成基于域(Domain)的操作(例如,Kerberos 身份认证),以便 与其他 Windows 服务相交互。

安装 GMSACredentialSpec CRD

需要在集群上配置一个用于 GMSA 凭据规约资源的 CustomResourceDefinition(CRD), 以便定义类型为 GMSACredentialSpec 的自定义资源。 下载 GMSA CRD YAML 并将其保存为 gmsa-crd.yaml。然后执行 kubectl apply -f gmsa-crd.yaml命令行安装 CRD。

安装 Webhook 来验证 GMSA 用户

为 Kubernetes 集群配置两个 Webhook,在 Pod 或容器级别填充和检查 GMSA 凭据规约引用。

  1. 修改模式(Mutating)的 Webhook,将对 GMSA 的引用(在 Pod 规约中体现为名字) 展开为完整凭据规约的 JSON 形式,并保存回 Pod 规约中。
  2. 验证模式(Validating)的 Webhook,确保对 GMSA 的所有引用都是已经授权 给 Pod 的服务账号使用的。

安装以上 Webhook 及其相关联的对象需要执行以下步骤:

  1. 创建一个证书密钥对(用于允许 Webhook 容器与集群通信)
  2. 安装一个包含如上证书的 Secret
  3. 创建一个包含核心 Webhook 逻辑的 Deployment
  4. 创建引用该 Deployment 的 Validating Webhook 和 Mutating Webhook 配置

也可以使用这个脚本来部署和配置上述 GMSA Webhook 及相关联的对象。你还可以在运行脚本时设置 --dry-run=server 选项以便审查脚本将会对集群做出的变更。

脚本所使用的YAML 模板 也可用于手动部署 Webhook 及相关联的对象,不过需要对其中的参数作适当替换。

2.1、创建 GMSA 管理资源

安装 GMSACredentialSpec CRD 之后,就可以配置包含 GMSA 约束自定义资源了。GMSA 凭据规约中并不包含秘密或敏感数据。 其中包含的信息主要用于容器运行时,便于后者向 Windows 描述容器所期望的 GMSA。 GMSA 凭据规约可以使用 PowerShell 脚本 以 YAML 格式生成。

下面是手动以 JSON 格式生成 GMSA 凭据规约并对其进行 YAML 转换的步骤:

  1. 导入 CredentialSpec 模块: ipmo CredentialSpec.psm1
  2. 使用 New-CredentialSpec 来创建一个 JSON 格式的凭据规约。 要创建名为 WebApp1 的 GMSA 凭据规约,调用 New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer)。
  3. 使用 Get-CredentialSpec 来显示 JSON 文件的路径。
  4. 将凭据规约从 JSON 格式转换为 YAML 格式,并添加必要的头部字段 apiVersion、kind、metadata 和 credspec,使其成为一个可以在 Kubernetes 中配置的 GMSACredentialSpec 自定义资源。

下面的 YAML 配置描述的是一个名为 gmsa-WebApp1 的 GMSA 凭据规约:

apiVersion: windows.k8s.io/v1kind: GMSACredentialSpecmetadata:name: gmsa-WebApp1# 这是随意起的一个名字,将用作引用credspec:ActiveDirectoryConfig:GroupManagedServiceAccounts:- Name: WebApp1 # GMSA 账号的用户名Scope: CONTOSO# NETBIOS 域名- Name: WebApp1 # GMSA 账号的用户名Scope: contoso.com # DNS 域名CmsPlugins:- ActiveDirectoryDomainJoinConfig:DnsName: contoso.com# DNS 域名DnsTreeName: contoso.com # DNS 域名根Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a# GUIDMachineAccountName: WebApp1 # GMSA 账号的用户名NetBiosName: CONTOSO# NETBIOS 域名Sid: S-1-5-21-2126449477-2524075714-3094792973 # GMSA 的 SID

上面的凭据规约资源可以保存为 gmsa-Webapp1-credspec.yaml,之后使用 kubectl apply -f gmsa-Webapp1-credspec.yml 命令应用到集群上。

2.2、配置集群启用 GMSA 管理的 RBAC

需要为每个 GMSA 凭据规约资源定义集群角色。 该集群角色授权某主体(通常是一个服务账号)对特定的 GMSA 资源执行 use 动作。 下面的示例显示的是一个集群角色,对前文创建的凭据规约 gmsa-WebApp1 执行鉴权。 将此文件保存为 gmsa-webapp1-role.yaml 并执行 kubectl apply -f gmsa-webapp1-role.yaml

# 创建集群角色读取凭据规约apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:name: webapp1-rolerules:- apiGroups: ["windows.k8s.io"]resources: ["gmsacredentialspecs"]verbs: ["use"]resourceNames: ["gmsa-WebApp1"]

2.3、分配 GMSA 管理服务账号

需要将某个服务账号(Pod 配置所对应的那个)绑定到前文创建的集群角色上。 这一绑定操作实际上授予该服务账号使用所指定的 GMSA 凭据规约资源的访问权限。 下面显示的是一个绑定到集群角色 webapp1-role 上的 default 服务账号,使之 能够使用前面所创建的 gmsa-WebApp1 凭据规约资源。

apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: allow-default-svc-account-read-on-gmsa-WebApp1namespace: defaultsubjects:- kind: ServiceAccountname: defaultnamespace: defaultroleRef:kind: ClusterRolename: webapp1-roleapiGroup: rbac.authorization.k8s.io

2.4、配置 GMSA 管理引用

Pod 规约字段 securityContext.windowsOptions.gmsaCredentialSpecName 可用来 设置对指定 GMSA 凭据规约自定义资源的引用。 设置此引用将会配置 Pod 中的所有容器使用所给的 GMSA。 下面是一个 Pod 规约示例,其中包含了对 gmsa-WebApp1 凭据规约的引用:

apiVersion: apps/v1kind: Deploymentmetadata:labels:run: with-credsname: with-credsnamespace: defaultspec:replicas: 1selector:matchLabels:run: with-credstemplate:metadata:labels:run: with-credsspec:securityContext:windowsOptions:gmsaCredentialSpecName: gmsa-webapp1containers:- image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019imagePullPolicy: Alwaysname: iisnodeSelector:kubernetes.io/os: windows

Pod 中的各个容器也可以使用对应容器的 securityContext.windowsOptions.gmsaCredentialSpecName 字段来设置期望使用的 GMSA 凭据规约。

apiVersion: apps/v1kind: Deploymentmetadata:labels:run: with-credsname: with-credsnamespace: defaultspec:replicas: 1selector:matchLabels:run: with-credstemplate:metadata:labels:run: with-credsspec:containers:- image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019imagePullPolicy: Alwaysname: iissecurityContext:windowsOptions:gmsaCredentialSpecName: gmsa-Webapp1nodeSelector:kubernetes.io/os: windows

当 Pod 规约中填充了 GMSA 相关字段(如上所述),在集群中应用 Pod 规约时会依次 发生以下事件:

  1. Mutating Webhook 解析对 GMSA 凭据规约资源的引用,并将其全部展开, 得到 GMSA 凭据规约的实际内容。
  2. Validating Webhook 确保与 Pod 相关联的服务账号有权在所给的 GMSA 凭据规约 上执行 use 动作。
  3. 容器运行时为每个 Windows 容器配置所指定的 GMSA 凭据规约,这样容器就可以以 活动目录中该 GMSA 所代表的身份来执行操作,使用该身份来访问域中的服务。

2.5、使用主机名或 FQDN 对网络共享进行身份验证

如果你在使用主机名或 FQDN 从 Pod 连接到 SMB 共享时遇到问题,但能够通过其 IPv4 地址访问共享, 请确保在 Windows 节点上设置了以下注册表项。

reg add “HKLM\SYSTEM\CurrentControlSet\Services\hns\State” /v EnableCompartmentNamespace /t REG_DWORD /d 1

2.6、故障排查

当我们配置 GMSA 环境的时候,不免会遇到一些报错之类的,那么我们该如何去排查呢?

首先,确保 credspec 已传递给 Pod。为此,你需要先运行 exec 进入到你的一个 Pod 中并检查 nltest.exe /parentdomain 命令的输出。 在下面的例子中,Pod 未能正确地获得凭据规约:

$ kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe

nltest.exe /parentdomain 导致以下错误:

Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

如果 Pod 未能正确获得凭据规约,则下一步就要检查与域之间的通信。 首先,从 Pod 内部快速执行一个 nslookup 操作,找到域根。

这一操作会告诉我们三件事情:

  1. Pod 能否访问域控制器(DC)
  2. DC 能否访问 Pod
  3. DNS 是否正常工作

如果 DNS 和通信测试通过,接下来你需要检查是否 Pod 已经与域之间建立了 安全通信通道。要执行这一检查,你需要再次通过 exec 进入到你的 Pod 中 并执行 nltest.exe /query 命令。

$ nltest.exe /query

由于某种原因,Pod 无法使用 credspec 中指定的帐户登录到域。 你可以尝试通过运行以下命令来修复安全通道:

$ nltest /sc_reset:domain.example

如果命令成功,你将看到类似以下内容的输出:

Flags: 30 HAS_IP HAS_TIMESERV
Trusted DC Name \dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

以上命令修复了错误,可以通过将以下生命周期回调添加到你的 Pod 规约中来自动执行该步骤。 如果这些操作没有修复错误,需要再次检查的 credspec 并确认它是正确和完整的。

image: registry.domain.example/iis-auth:1809v1lifecycle:postStart:exec:command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]imagePullPolicy: IfNotPresent

如果向你的 Pod 规约中添加如上所示的 lifecycle 节,则 Pod 会自动执行所 列举的命令来重启 netlogon 服务,直到 nltest.exe /query 命令返回时没有错误信息。

三、为 Windows 的 Pod 和容器配置 RunAsUserName

要指定运行 Pod 容器时所使用的用户名,必须在 Pod 声明中包含 securityContext (PodSecurityContext) 字段, 并在其内部包含 windowsOptions (WindowsSecurityContextOptions) 字段的 runAsUserName 字段。

为 Pod 指定的 Windows SecurityContext 选项适用于该 Pod 中(包括 init 容器)的所有容器。

下面看一个已经设置了 runAsUserName 字段的 Windows Pod 的配置文件windows/run-as-username-pod.yaml

apiVersion: v1kind: Podmetadata:name: run-as-username-pod-demospec:securityContext:windowsOptions:runAsUserName: "ContainerUser"containers:- name: run-as-username-demoimage: mcr.microsoft.com/windows/servercore:ltsc2019command: ["ping", "-t", "localhost"]nodeSelector:kubernetes.io/os: windows
  1. 创建 Pod:

$ kubectl apply -f https://k8s.io/examples/windows/run-as-username-pod.yaml

  1. 验证 Pod 容器是否在运行:

$ kubectl get pod run-as-username-pod-demo

  1. 获取该容器的 shell:

$ kubectl exec -it run-as-username-pod-demo – powershell

  1. 检查运行 shell 的用户的用户名是否正确:

$ echo $env:USERNAME

  1. 输出结果

ContainerUser

3.1、设置 Username

要指定运行容器时所使用的用户名,请在容器清单中包含 securityContext (SecurityContext) 字段,并在其内部包含 windowsOptions (WindowsSecurityContextOptions) 字段的 runAsUserName 字段。

为容器指定的 Windows SecurityContext 选项仅适用于该容器,并且它会覆盖 Pod 级别设置。

下面看一个 Pod 的配置文件 windows/run-as-username-container.yaml,其中只有一个容器,并且在 Pod 级别和容器级别都设置了 runAsUserName:

apiVersion: v1kind: Podmetadata:name: run-as-username-container-demospec:securityContext:windowsOptions:runAsUserName: "ContainerUser"containers:- name: run-as-username-demoimage: mcr.microsoft.com/windows/servercore:ltsc2019command: ["ping", "-t", "localhost"]securityContext:windowsOptions:runAsUserName: "ContainerAdministrator"nodeSelector:kubernetes.io/os: windows
  1. 创建 Pod:

$ kubectl apply -f https://k8s.io/examples/windows/run-as-username-container.yaml

  1. 验证 Pod 容器是否在运行:

$ kubectl get pod run-as-username-container-demo

  1. 获取该容器的 shell:

$ kubectl exec -it run-as-username-container-demo – powershell

  1. 检查运行 shell 的用户的用户名是否正确(应该是容器级别设置的那个):

$ echo $env:USERNAME

  1. 输出结果:

ContainerAdministrator

3.2、Windows Username 的局限性

想要使用此功能,在 runAsUserName 字段中设置的值必须是有效的用户名。 它必须是 DOMAIN\USER 这种格式,其中 *DOMAIN* 是可选的。 Windows 用户名不区分大小写。此外,关于 DOMAIN 和 USER 还有一些限制:

  • runAsUserName 字段不能为空,并且不能包含控制字符(ASCII 值:0x00-0x1F、0x7F)
  • DOMAIN 必须是 NetBios 名称或 DNS 名称,每种名称都有各自的局限性:
    • NetBios 名称:最多 15 个字符,不能以 .(点)开头,并且不能包含以下字符:\ / : * ” /> |
    • DNS 名称:最多 255 个字符,只能包含字母、数字、点和中划线,并且不能以 .(点)或 -(中划线)开头和结尾。
      USER 最多不超过 20 个字符,不能 只 包含点或空格,并且不能包含以下字符:” / \ [ ] : ; | = , + * ? @

runAsUserName 字段接受的值的一些示例:ContainerAdministrator、ContainerUser、 NT AUTHORITY\NETWORK SERVICE、NT AUTHORITY\LOCAL SERVICE

总结

本篇内容共约一万字,内容还是比较多的,总共包含了 Windows HostProcess的创建、为 Windows Pod 和容器配置 GMSA 和 Windows 的 Pod 和容器配置 RunAsUserName三大功能模块。全篇文章主要讲解了,怎么将运行在 Windows 节点上的 Pod 和容器配置组管理的服务账号(Group Managed Service Accounts,GMSA)。 组管理的服务账号是活动目录(Active Directory)的一种特殊类型,提供自动化的 密码管理、简化的服务主体名称(Service Principal Name,SPN)管理以及跨多个 服务器将管理操作委派给其他管理员等能力。