Disruption控制

默认情况下,Karpenter 会自动发现可Disruption的节点,这意味着不再需要的节点(例如,因为资源需求降低)会自动从集群中删除。但是,可能会出现我们想要禁用 Karpenter 中断的场景。如果我们在 Karpenter 配置的节点上运行关键应用程序,例如长时间运行的批处理作业或有状态应用程序,并且节点的 TTL 已过期,则在实例终止时应用程序将被中断。

通过在 pod 上添加 karpenter.sh/do-not-disrupt: "true" 注解,以指示 Karpenter 保留节点,直到 Pod 终止或删除 do-not-disrupt 注解。

以下是可能需要禁用Disruption的一些原因:

  • 需要特定节点配置或硬件的工作负载: 如果我们有需要特定节点配置或硬件(GPU)的工作负载,即使节点未充分利用,我们也可能希望保留这些节点。

  • 配置新节点成本较高的用例:在某些情况下,配置新节点的成本可能高于保留未充分利用的节点的成本。例如,如果我们正在运行需要专用硬件或大量节点的高性能计算(HPC)工作负载,创建新节点的成本可能很高。

  • 合规性或运营要求: 在某些行业,可能存在要求在任何时候都保持一定数量节点可用的合规性或运营要求。

需要注意的是,禁用Disruption可能会影响集群的成本和效率,因此只有在我们的特定用例中确实需要时才应该这样做。在决定在 Karpenter 中禁用Disruption之前,我们应该仔细评估我们的要求和工作负载特征。

以下是禁用Disruption的不同方式:

  • Pod Eviction:可以通过在 pod 上设置注解 karpenter.sh/do-not-disrupt: "true" 来选择不驱逐pod。
  • 节点合并(Node Consolidation):可以通过在节点上设置注解 karpenter.sh/do-not-disrupt: "true" 来选择退出Consolidation。
  • 在 NodePool 上禁用Consolidation: NodePool .spec.template.metadata.annotations 允许我们设置将应用于使用此 NodePool 启动的所有节点的注解。

在本节我们将测试关掉Pod evction这种方式,这个方式可以保证Pod执行完任务后,Karpenter才回收节点:

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      annotations:
        karpenter.sh/do-not-disrupt: "true"

此时下面四种情况,都不会触发pod所在节点被回收:

  • Consolidation
  • Drift
  • Emptiness
  • Expiration

清理上一节的资源:

kubectl delete pdb inflate-pdb
kubectl delete deployment inflate

kubectl delete nodepools.karpenter.sh default
kubectl delete ec2nodeclasses.karpenter.k8s.aws default

创建NodePool

我们设置了expireAfter这个参数为2分钟,这样所有NodePool创建出来的节点都会在2分钟后被干掉:

mkdir -p ~/environment/karpenter
cd ~/environment/karpenter
cat <<EoF> eviction.yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    consolidateAfter: 30s
    consolidationPolicy: WhenEmpty
    expireAfter: 2m0s
  limits:
    cpu: "10"
  template:
    metadata:
      labels:
        eks-immersion-team: my-team
    spec:
      nodeClassRef:
        name: default
      requirements:
        - key: "karpenter.k8s.aws/instance-category"
          operator: In
          values: ["c", "m", "r"]
        - key: "kubernetes.io/arch"
          operator: In
          values: ["amd64"]
        - key: "karpenter.sh/capacity-type" # If not included, the webhook for the AWS cloud provider will default to on-demand
          operator: In
          values: ["on-demand"]
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiFamily: AL2
  role: "KarpenterNodeRole-${CLUSTER_NAME}"
  securityGroupSelectorTerms:
  - tags:
      alpha.eksctl.io/cluster-name: $CLUSTER_NAME
  subnetSelectorTerms:
  - tags:
      alpha.eksctl.io/cluster-name: $CLUSTER_NAME
  tags:
    intent: apps
    managed-by: karpenter
EoF

kubectl apply -f eviction.yaml

创建应用

在部署inflate应用时,设置karpenter.sh/do-not-disrupt: "true",这样pod所在的节点就不会被回收:

cd ~/environment/karpenter
cat <<EoF> eviction-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 2
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
      annotations:
        karpenter.sh/do-not-disrupt: "true"
    spec:     
      terminationGracePeriodSeconds: 0
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: 1
      nodeSelector:
        eks-immersion-team: my-team
EoF

kubectl apply -f eviction-deploy.yaml

测试Pod Evction

会发现这2个pod即使在Node过期时间到之后,还会继续存在,它的Node所以也跟着没有被回收。

等待几分钟过后,我们把这个 karpenter.sh/do-not-disrupt: "true" 注释给移掉,看这个节点在2分钟后会不会被回收:

cd ~/environment/karpenter
cat <<EoF> eviction-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 2
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
    spec:     
      terminationGracePeriodSeconds: 0
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: 1
      nodeSelector:
        eks-immersion-team: my-team
EoF

kubectl apply -f eviction-deploy.yaml

等待1分钟,等expireAfter事件触发。我们会发现新启动了一台节点:

image-20231028235402482

原来的节点被干掉:

image-20231028235424812

查看 karpenter日志:

kubectl -n karpenter logs -l app.kubernetes.io/name=karpenter

会发现triggering termination for expired node after, deprovisioning via expiration replace, terminating 1 nodeslaunched new instance等事件:

image-20240803225336603