Karpenter自动管理Node

Karpenter现在已经部署完毕,我们来探索它是如何管理node的生命周期

运行以下命令,创建一个deployement:

cat <<EOF > inflate.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 0
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
    spec:
      nodeSelector:
        intent: apps
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.2
          resources:
            requests:
              cpu: 1
              memory: 1.5Gi
EOF
kubectl apply -f inflate.yaml
  • 由于replicaset数量为0,所以当前不会触发Karpenter的扩容操作;后面我们将其改为1、改为5、再改回0,我们将观察中间过程中节点的变化
  • nodeSelector设置为intent: apps,当前集群上的节点没有提前设置对应的label,所以只要有新的pod拉起都不会部署到当前集群的节点上,迫使Karpenter创建新的node来满足对应的条件。

在进行后面的操作之前,建议先安装kube-ops-view,参考 https://www.eksworkshop.com/beginner/080_scaling/install_kube_ops_view/ 。当然也可使用纯kubectl命令来观察node/pod等状态的变化

运行以下命令,将replica数量设置为1:

kubectl scale deployment inflate --replicas 1

在这个过程中,观察karpenter-controller的日志变化:

kubectl logs -f deployment/karpenter-controller -n karpenter

由于新创建的pod,当前没有node能满足它的selector条件,所以被标记为unschedulable。Karpenter会创建新的node来满足它的条件:

image-20220530221419835

从日志中注意到,karpenter从以下机型进行了挑选:

[c5ad.large c5a.large c5.large c6a.large c3.large c5d.large t3a.medium c6i.large c4.large c6id.large t3.medium c5n.large m4.large m5d.large m5ad.large t3a.large m5zn.large m5.large m1.large m5a.large]

上面所有的机型都满足pod所需的CPU/内存资源,且为最小配置,以减少资源的浪费——在这个过程中Karpenter使用了 First Fit Decreasing (FFD) 算法。

Karpenter使用Instant mode EC2 Fleethttps://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instant-fleet.html )来创建机器:

所以,使用Karpenter,无需再提前定义多个ASG配置,减少了运维的繁琐。


执行kubectl命令,来确认replica数量达到目标值:

$ kubectl get deployment inflate 
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
inflate   1/1     1            1           1m

查看新创建的机器:

image-20220530221035032

  • node标签为intent=apps
  • karpenter.sh/provisioner-namedefault ,当然我们也可以定义其他的provisioner。

继续查看节点的信息,会发现容器运行时为containerd:

System Info:
  ...
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.4.6
  ...

Karpenter和 Cluster Autoscaler 都会检测NodeSelector, Taints,Tolerations, 如果同时部署了两者会引起冲突,因为它们同时会检测状态为unschedulable的pod。


将replicas的数量设置为6:

kubectl scale deployment inflate --replicas 6

由于当前的机器资源不足,一些pod的状态变为pending,karpenter检测到后,会将需要的资源进行聚合:

022-05-30T14:53:44.039Z        INFO    controller.allocation.provisioner/default       Starting provisioning loop      {"commit": "dc47849"}
2022-05-30T14:53:44.039Z        INFO    controller.allocation.provisioner/default       Waiting to batch additional pods        {"commit": "dc47849"}
2022-05-30T14:53:45.460Z        INFO    controller.allocation.provisioner/default       Found 5 provisionable pods      {"commit": "dc47849"}
2022-05-30T14:53:45.486Z        INFO    controller.allocation.provisioner/default       Computed packing for 5 pod(s) with instance type option(s) [c4.2xlarge c3.2xlarge c6a.2xlarge c5.2xlarge c5d.2xlarge c6id.2xlarge c5a.2xlarge c5ad.2xlarge c6i.2xlarge c5n.2xlarge m3.2xlarge m5n.2xlarge m6id.2xlarge m4.2xlarge m6i.2xlarge t3a.2xlarge m5ad.2xlarge m5zn.2xlarge m5.2xlarge m5dn.2xlarge]  {"commit": "dc47849"}
2022-05-30T14:53:47.800Z        INFO    controller.allocation.provisioner/default       Launched instance: i-03b7945d402f80522, hostname: ip-192-168-69-206.us-west-2.compute.internal, type: t3a.2xlarge, zone: us-west-2a, capacityType: on-demand   {"commit": "dc47849"}
2022-05-30T14:53:47.826Z        INFO    controller.allocation.provisioner/default       Bound 5 pod(s) to node ip-192-168-69-206.us-west-2.compute.internal    {"commit": "dc47849"}

Karpenter这次挑选的机型如下:

c4.2xlarge c3.2xlarge c6a.2xlarge c5.2xlarge c5d.2xlarge c6id.2xlarge c5a.2xlarge c5ad.2xlarge c6i.2xlarge c5n.2xlarge m3.2xlarge m5n.2xlarge m6id.2xlarge m4.2xlarge m6i.2xlarge t3a.2xlarge m5ad.2xlarge m5zn.2xlarge m5.2xlarge m5dn.2xlarge

注意到有这样一行:

2021-11-15T12:33:18.802Z        INFO    controller.allocation.provisioner/default       Bound 5 pod(s) to node ip-192-168-89-216.eu-west-1.compute.internal  {"commit": "6468992"}

和Cluster Autoscaler不同,Karpenter不会等待Kube Schedular来做出调度决策,而是直接将pod绑定在新创建的node上。


将replica的数量设置为0:

kubectl scale deployment inflate --replicas 0

并观察controller的日志:

image-20220530221229199

在上一节中,我们配置了 ttlSecondsAfterEmpty 为30s,所以实例上没有pod后,karpenter在超过这个时间后会终止实例。

总结

  • Karpenter在扩容节点时,使用了group-less的方式。和Cluster Autoscaler方式不同,Cluster Autoscaler方式会先评估所有的node group,根据pod的限制,选择出来哪一个是最适合扩容的
  • Karpenter将Pod直接绑定到新创建的node上,减少了pod上线的时间。