Drift检测

Karpenter 会自动将节点标注为Drifted,并添加注释 controller.nodeclaim.disruption:"marking drifted",如果节点使用的 AMI 与 EC2NodeClass 上设置的 AMI ID 不匹配。一旦节点被标记为Drifted,Karpenter 将自动隔离、排空和终止节点,同时满足任何托管具有 Pod 中断预算 (PDB) 或 karpenter.sh/do-not-disrupt: "true" 注释的 Pod 的节点。

测试Drift

我们将首先创建一个使用 Kubernetes 版本 1.29 AMI 的 EC2NodeClass,更新一个basic NodePool 以引用该新创建的 EC2NodeClass,并部署一个 Pod 来启动一个新节点。之后,我们将把 EC2NodeClass 中的 AMI 更改为 Kubernetes 版本 1.30,这最终会使先前的节点Drift,并用 1.30 版本的 AMI 替换它。

确保在继续之前已经创建了默认的 NodepoolEC2NodeClass

kubectl get nodepool default && kubectl get ec2nodeclass default  2>/dev/null || echo "Not found"

我们应该看到类似的输出:

NAME      NODECLASS
default   default
NAME      AGE
default   113m

首先为 AMI 设置一个环境变量。我们将设置一个 Kubernetes 版本 1.29 的 AMI 作为 AMI_OLD。

export AMI_OLD=$(aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.29/amazon-linux-2/recommended/image_id --region $AWS_REGION --query "Parameter.Value" --output text)
echo 1.29=$AMI_OLD

例如,如果我们的区域是 us-west-2,这里是预期的输出:

1.29=ami-0a3a90a465f626a03

现在,让我们首先创建一个新的 EC2NodeClass。我们可以在以下 node-class 部分了解更多信息。

cd ~/environment/karpenter
cat << EoF > oldnode_class.yaml
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
  name: oldnode
spec:
  amiFamily: AL2
  amiSelectorTerms:
    - id: $AMI_OLD
  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 -f oldnode_class.yaml create

我们应该看到以下消息。

ec2nodeclass.karpenter.k8s.aws/oldnode created

让我们编辑默认的 nodepool 以使用这个新创建的 EC2NodeClass。

kubectl edit nodepool default

在 spec 下搜索 nodeClassRef,并将 name 值从 default 更改为 oldnode

....
spec:
  nodeClassRef:
      name: oldnode   <<<< 将 `default` 更改为 `oldnode`
    requirements:
    - key: karpenter.k8s.aws/instance-category
    operator: In

我们将看到默认的 nodepool 已被编辑

nodepool.karpenter.sh/default edited

现在,让我们部署一个 Pod 来通过 Karpenter 启动一个节点。

cd ~/environment/karpenter
cat <<EoF> drift-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 5
  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 drift-deploy.yaml
deployment.apps/inflate created

让我们检查新部署节点的版本。

kubectl get nodes -l eks-immersion-team=my-team

我们将在几秒钟内看到一个版本为 1.29 (v1.29.xx-eks-xxxxxxx) 的节点就绪。

image-20240803164241004

现在,让我们将 AMI 版本更改为v1.30。

首先,我们将设置一个 Kubernetes 版本 1.30 AMI 的环境变量。

export AMI_NEW=$(aws ssm get-parameter --name /aws/service/eks/optimized-ami/1.30/amazon-linux-2/recommended/image_id --region $AWS_REGION --query "Parameter.Value" --output text)
echo 1.30=$AMI_NEW

如果我们的区域是 us-west-2,这里是预期的输出:

1.30=ami-08946d4d49fc3f27b

现在,让我们创建一个新的 EC2NodeClass。

cd ~/environment/karpenter
cat << EoF > newnode_class.yaml
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
  name: newnode
spec:
  amiFamily: AL2
  amiSelectorTerms:
    - id: $AMI_NEW
  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 -f newnode_class.yaml create

我们应该看到以下消息。

ec2nodeclass.karpenter.k8s.aws/newnode created

现在,让我们使用新的 EC2NodeClass newnode , 编辑默认的 nodepool:

kubectl edit nodepool default

在 spec 下搜索 providerRef,并将 name 值从 oldnode 更改为 newnode

....
spec:
  nodeClassRef:
      name: newnode   <<<< 将 `oldnode` 更改为 `newnode`
    requirements:
    - key: karpenter.k8s.aws/instance-category
    operator: In

我们将看到默认的 nodepool 已被编辑

nodepool.karpenter.sh/default edited

让我们检查节点状态。

kubectl get nodes -l eks-immersion-team=my-team 

我们将看到 v1.29 节点状态已更改为 Ready,SchedulingDisabled,而新部署的 v1.30 节点仍处于 NotReady 状态。

ip-192-168-63-204.us-west-2.compute.internal   NotReady                   <none>   23s    v1.30.13-eks-0a21954
ip-192-168-66-103.us-west-2.compute.internal   Ready,SchedulingDisabled   <none>   6h8m   v1.29.17-eks-0a21954

再次运行以下命令几秒钟后。最终,我们将看到 v1.29 节点已被终止,新创建的 v1.30 节点现已就绪。

kubectl get nodes -l eks-immersion-team=my-team 
ip-192-168-63-204.us-west-2.compute.internal   Ready    <none>   76s   v1.30.13-eks-0a21954

我们还可以检查漂移相关的日志消息。

kubectl -n karpenter logs --tail=100 -l app.kubernetes.io/name=karpenter | grep -i drift  

image-20240803164627529


删除deployment:

kubectl delete -f drift-deploy.yaml