在专用节点池中隔离工作负载

本页面介绍了如何在 Google Distributed Cloud (GDC) 网闸隔离配置中通过专用节点池隔离容器工作负载,以便更好地控制 Pod。工作负载隔离具有以下优势:

  • 降低 Kubernetes 集群中发生权限升级攻击的风险。
  • 更好地控制需要额外资源的 pod。

对于这些情况,请考虑隔离容器工作负载,以便更好地控制和优化。

为什么要隔离工作负载?

虽然不是必需的,但将工作负载隔离在专用节点池中是一种明智的做法,可避免潜在问题。不过请注意,管理专用节点池需要更多监督,而且很多时候是不必要的。

Kubernetes 集群使用特权 GDC 管理的工作负载来启用特定集群功能和特性,例如指标收集。这些工作负载有一些特殊权限,以便它们在集群中正确运行。

部署到节点的工作负载可能会被恶意实体破解。将这些工作负载与特权 GDC 管理工作负载一起运行意味着攻破遭破解容器的攻击者可以使用节点上的特权工作负载的凭据来提升集群中的权限。

如果您要调度的 Pod 比其他 Pod 需要更多的资源(例如更多内存或更多本地磁盘空间),专用节点池也会非常有用。

您可以使用以下机制在专用节点池中调度工作负载:

节点污点会告知 Kubernetes 集群避免在这些节点上调度没有相应容忍设置的工作负载(例如 GDC 管理的工作负载)。您自己的工作负载上的节点亲和性会告知集群在专用节点上调度您的 Pod。

节点隔离的限制

  • 攻击者仍然可以从遭破解的节点发起拒绝服务 (DoS) 攻击。

  • 遭破解的节点仍然可以读取许多资源,包括集群中的所有 Pod 和命名空间。

  • 遭破解的节点可以访问在该节点上运行的每个 pod 使用的 Secret 和凭据。

  • 使用单独的节点池隔离工作负载可能会影响费用效率、自动扩缩和资源利用率。

  • 遭破解的节点仍然可以绕过出站网络政策。

  • 某些由 GDC 管理的工作负载必须在集群的每个节点上运行,并配置为容忍所有污点。

  • 如果您部署的 DaemonSet 资源具有提升的权限并且可以容忍任何污点,这些 Pod 可能会成为从遭破解节点提权的一个途径。

节点隔离的工作原理

如需为工作负载实现节点隔离,您必须执行以下操作:

  1. 为工作负载添加节点池污点和标签。

  2. 使用相应的容忍和节点亲和性规则更新工作负载。

本指南假设您从集群中的一个节点池开始。使用节点亲和性以及节点污点并非强制性要求,但我们建议您使用,因为您可以更好地控制调度。

准备工作

在开始之前,请确保您已执行以下任务:

  • 为想要用于专用节点池的节点污点和节点标签选择特定名称。例如 workloadType=untrusted

  • 如有必要,请让组织 IAM 管理员为您授予未绑定到命名空间的 User Cluster Developer 角色 (user-cluster-developer)。

为新节点池添加污点和标签

向新节点池应用污点或标签时,所有节点(包括稍后添加的任何节点)都将自动获取指定的污点和标签。

如需向新节点池添加污点和标签,请完成以下步骤:

  1. 创建节点池时,直接修改 Cluster 自定义资源的 nodePools 部分:

    nodePools:
      ...
      - machineTypeName: n2-standard-2-gdc
        name: nodepool-1
        nodeCount: 3
        taints: TAINT_KEY=TAINT_VALUE:TAINT_EFFECT
        labels: LABEL_KEY=LABEL_VALUE
    

    替换以下内容:

    • TAINT_KEY=TAINT_VALUE:与调度 TAINT_EFFECT 关联的键值对。例如 workloadType=untrusted
    • TAINT_EFFECT:以下效果值之一:
      • NoSchedule:不能容忍此污点的 pod 不会被调度到节点上;现有 pod 不会从节点中逐出。
      • PreferNoSchedule:Kubernetes 会尽量避免将不能容忍此污点的 Pod 调度到节点上。
      • NoExecute:如果 Pod 已在节点上运行,则该 Pod 会从节点中被逐出;如果尚未在节点上运行,则不会被调度到节点上。
    • LABEL_KEY=LABEL_VALUE:节点标签的键值对,与您在工作负载清单中指定的选择器对应。
  2. 应用 Cluster 资源以创建新的节点池:

    kubectl apply -f cluster.yaml \
        --kubeconfig MANAGEMENT_API_SERVER
    

    MANAGEMENT_API_SERVER 替换为托管 Kubernetes 集群的区域 API 服务器的 kubeconfig 路径。如果您尚未为目标可用区中的 API 服务器生成 kubeconfig 文件,请参阅登录了解详情。

为现有节点池添加污点和标签

如需将污点或标签应用于现有节点池,您必须将更改应用于每个现有节点。您无法动态更新节点池配置。

如需向现有节点池添加污点和标签,请完成以下步骤:

  1. 列出专用节点池中的节点:

    kubectl get node --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG \
        -l baremetal.cluster.gke.io/node-pool=NODE_POOL_NAME
    

    执行以下变量替换操作:

    • KUBERNETES_CLUSTER_KUBECONFIG:Kubernetes 集群的 kubeconfig 路径。
    • NODE_POOL_NAME:专用节点池的名称。

    记下输出中节点池中所有节点的每个节点 ID。

  2. 对于节点池中的每个节点,应用以下污点:

    kubectl taint nodes NODE_ID \
        TAINT_KEY=TAINT_VALUE:TAINT_EFFECT \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    执行以下变量替换操作:

    • NODE_ID:专用节点池中工作器节点的 ID。
    • TAINT_KEY=TAINT_VALUE:与调度 TAINT_EFFECT 关联的键值对。例如 workloadType=untrusted
    • TAINT_EFFECT:以下效果值之一:
      • NoSchedule:不能容忍此污点的 pod 不会被调度到节点上;现有 pod 不会从节点中逐出。
      • PreferNoSchedule:Kubernetes 会尽量避免将不能容忍此污点的 Pod 调度到节点上。
      • NoExecute:如果 Pod 已在节点上运行,则该 Pod 会从节点中被逐出;如果尚未在节点上运行,则不会被调度到节点上。
    • KUBERNETES_CLUSTER_KUBECONFIG:Kubernetes 集群的 kubeconfig 路径。
  3. 对于节点池中的每个节点,应用与您将在容器工作负载中定义的节点选择器对应的标签:

    kubectl label NODE_ID \
        LABEL_KEY:LABEL_VALUE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    执行以下变量替换操作:

    • NODE_ID:专用节点池中工作器节点的 ID。
    • LABEL_KEY:LABEL_VALUE:节点标签的键值对,与您在工作负载清单中指定的选择器对应。
    • KUBERNETES_CLUSTER_KUBECONFIG:Kubernetes 集群的 kubeconfig 路径。

添加容忍和节点亲和性规则

污染专用节点池后,任何工作负载都无法在其上调度,除非它们具有与您添加的污染相对应的容忍度。将容忍度添加到您的工作负载规范中,让这些 Pod 在您的受污染节点池调度。

如果您已为专用节点池添加标签,则还可以添加节点亲和性规则,以指示 GDC 仅在该节点池上调度工作负载。

如需配置容器工作负载以在专用节点池中运行,请完成以下步骤:

  1. 将以下部分添加到容器工作负载的 .spec.template.spec 部分:

    kind: Deployment
    apiVersion: apps/v1
        ...
        spec:
        ...
          template:
            spec:
              tolerations:
              - key: TAINT_KEY
                operator: Equal
                value: TAINT_VALUE
                effect: TAINT_EFFECT
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: LABEL_KEY
                        operator: In
                        values:
                        - "LABEL_VALUE"
              ...
    

    替换以下内容:

    • TAINT_KEY:您应用于专用节点池的污点键。
    • TAINT_VALUE:您应用于专用节点池的污点值。
    • TAINT_EFFECT:以下效果值之一:
      • NoSchedule:不能容忍此污点的 pod 不会被调度到节点上;现有 pod 不会从节点中逐出。
      • PreferNoSchedule:Kubernetes 会尽量避免将不能容忍此污点的 Pod 调度到节点上。
      • NoExecute:如果 Pod 已在节点上运行,则该 Pod 会从节点中被逐出;如果尚未在节点上运行,则不会被调度到节点上。
    • LABEL_KEY:您应用于专用节点池的节点标签键。
    • LABEL_VALUE:您应用于专用节点池的节点标签值。

    例如,以下 Deployment 资源为 workloadType=untrusted:NoExecute 污点添加容忍设置,并为 workloadType=untrusted 节点标签添加节点亲和性规则:

    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: my-app
      namespace: default
      labels:
        app: my-app
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: my-app
      template:
        metadata:
          labels:
            app: my-app
        spec:
          tolerations:
          - key: workloadType
            operator: Equal
            value: untrusted
            effect: NoExecute
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: workloadType
                    operator: In
                    values:
                    - "untrusted"
          containers:
          - name: my-app
            image: harbor-1.org-1.zone1.google.gdc.test/harborproject/my-app
            ports:
            - containerPort: 80
          imagePullSecrets:
          - name: SECRET
    
  2. 更新部署:

    kubectl apply -f deployment.yaml -n NAMESPACE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    执行以下变量替换操作:

    • NAMESPACE:容器工作负载的项目命名空间。
    • KUBERNETES_CLUSTER_KUBECONFIG:Kubernetes 集群的 kubeconfig 路径。

GDC 会重新创建受影响的 pod。节点亲和性规则强制 Pod 推送到您创建的专用节点池。容忍设置仅允许将这些 Pod 放置在节点上。

验证分离是否有效

要验证调度是否正常运行,请运行以下命令并检查您的工作负载是否在专用节点池上:

kubectl get pods -o=wide -n NAMESPACE \
    --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG

建议和最佳实践

设置节点隔离后,我们建议您执行以下操作:

  • 创建新的节点池时,您可以通过向这些节点池添加自己的污点,阻止大多数由 GDC 管理的工作负载在这些节点上运行。
  • 每当您将新工作负载部署到集群时(例如安装第三方工具时),请审核 Pod 所需的权限。如果可能,请避免将使用提升权限的工作负载部署到共享节点。