关闭Deprovisoning

默认情况下,Karpenter 启用节点的“Deprovisioning”,这意味着不再需要的节点(例如因为对资源的需求减少)会自动从集群中删除。

但是,在某些情况下您可能希望在 Karpenter 中禁用Deprovisioning。 如果您在 Karpenter 配置的节点上运行关键应用程序,例如长时间运行的批处理作业有状态应用程序,并且节点的 TTL 已过期,则当实例终止时,应用程序将被中断。 通过向 Pod 添加 karpenter.sh/do-not-evict 注释,可以指示 Karpenter 保留该节点,直到 Pod 终止或删除 do-not-evict注释。

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

  • 如果您的工作负载需要特定节点配置(GPU机器),您可能希望保持这些节点运行,即使它们没有得到充分利用。

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

  • 合规性或操作要求:在某些行业中,可能存在合规性或操作要求,要求一定数量的节点始终可用。

请注意禁用Deprovisioning可能会影响集群的成本和效率,因此仅当有必要时才应执行此操作。 在决定在 Karpenter 中禁用Deprovisioning之前,应仔细评估需求和工作负载特征。


以下是禁用Deprovisoning的不同方法:

  • Pod Eviction:可以通过在 pod 上设置注释 karpenter.sh/do-not-evict: "true" 来选择不驱逐 Pod
  • Node Consolidation(节点合并):可以通过在节点上设置注释 karpenter.sh/do-not-consolidate: "true" 来选择不使用Cosolidation
  • 禁用 Provisioner 上的Consolidation:Provisioner中的 .spec.annotations 将应用于此 Provisioner 启动的所有节点的注释。

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

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

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

  • Consolidation
  • Drift
  • Emptiness
  • Expiration

清理上节的资源

kubectl delete deployment inflate
kubectl delete provisioners.karpenter.sh default
kubectl delete awsnodetemplates.karpenter.k8s.aws default
kubectl delete pdb inflate-pdb

创建Provisioner

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

mkdir -p ~/environment/karpenter
cd ~/environment/karpenter
cat <<EoF> eviction.yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
spec:
  providerRef:
    name: default

  labels:
    eks-immersion-team: my-team

  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"]
  ttlSecondsAfterEmpty: 30
  ttlSecondsUntilExpired: 120
  limits:
    resources:
      cpu: "10"

---
apiVersion: karpenter.k8s.aws/v1alpha1
kind: AWSNodeTemplate
metadata:
  name: default
spec:
  subnetSelector:
    alpha.eksctl.io/cluster-name: ${CLUSTER_NAME}
  securityGroupSelector:
    aws:eks:cluster-name: ${CLUSTER_NAME}
  tags:
    managed-by: "karpenter"
    intent: "apps"
EoF

kubectl apply -f eviction.yaml

创建应用

在部署inflate应用时,设置karpenter.sh/do-not-evict: "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-evict: "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-evict: "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分钟,等ttlSecondsUntilExpired事件触发。我们会发现新启动一台节点:

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-20231028235619610