默认情况下,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"
来选择不驱逐 PodNode Consolidation(节点合并)
:可以通过在节点上设置注释 karpenter.sh/do-not-consolidate: "true"
来选择不使用Cosolidation
。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所在节点被回收:
kubectl delete deployment inflate
kubectl delete provisioners.karpenter.sh default
kubectl delete awsnodetemplates.karpenter.k8s.aws default
kubectl delete pdb inflate-pdb
我们设置了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
会发现这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
事件触发。我们会发现新启动一台节点:
原来的节点被干掉:
查看 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
等事件: