默认情况下,Karpenter 会自动发现可Disruption的节点,这意味着不再需要的节点(例如,因为资源需求降低)会自动从集群中删除。但是,可能会出现我们想要禁用 Karpenter 中断的场景。如果我们在 Karpenter 配置的节点上运行关键应用程序,例如长时间运行的批处理作业或有状态应用程序,并且节点的 TTL 已过期,则在实例终止时应用程序将被中断。
通过在 pod 上添加 karpenter.sh/do-not-disrupt: "true"
注解,以指示 Karpenter 保留节点,直到 Pod 终止或删除 do-not-disrupt 注解。
以下是可能需要禁用Disruption的一些原因:
需要特定节点配置或硬件的工作负载: 如果我们有需要特定节点配置或硬件(GPU)的工作负载,即使节点未充分利用,我们也可能希望保留这些节点。
配置新节点成本较高的用例:在某些情况下,配置新节点的成本可能高于保留未充分利用的节点的成本。例如,如果我们正在运行需要专用硬件或大量节点的高性能计算(HPC)工作负载,创建新节点的成本可能很高。
合规性或运营要求: 在某些行业,可能存在要求在任何时候都保持一定数量节点可用的合规性或运营要求。
需要注意的是,禁用Disruption可能会影响集群的成本和效率,因此只有在我们的特定用例中确实需要时才应该这样做。在决定在 Karpenter 中禁用Disruption之前,我们应该仔细评估我们的要求和工作负载特征。
以下是禁用Disruption的不同方式:
karpenter.sh/do-not-disrupt: "true"
来选择不驱逐pod。karpenter.sh/do-not-disrupt: "true"
来选择退出Consolidation。.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所在节点被回收:
清理上一节的资源:
kubectl delete pdb inflate-pdb
kubectl delete deployment inflate
kubectl delete nodepools.karpenter.sh default
kubectl delete ec2nodeclasses.karpenter.k8s.aws default
我们设置了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
会发现这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
事件触发。我们会发现新启动了一台节点:
原来的节点被干掉:
查看 karpenter日志:
kubectl -n karpenter logs -l app.kubernetes.io/name=karpenter
会发现triggering termination for expired node after
, deprovisioning via expiration replace, terminating 1 nodes
和 launched new instance
等事件: