亚马逊AWS官方博客

Karpenter : 新一代 Kubernetes auto scaling 工具

在亚马逊云科技2021 re:Invent大会期间,亚马逊云科技宣布为Kubernetes 构建的开源自动扩缩容项目Karpenter升级至0.5版本并GA,可在生产环境使用。在Kubernetes上的auto scaling始终被人关注,当前的kubernetes提供Cluster Autocaler用于制定调度和扩缩决策。那么,既然有了Kubernetes Cluster Autoscaler,为何还要重复造轮子,去开发新的auto scaling工具呢?本文将介绍Karpenter作用和原理,同时对比一下大家熟悉的Cluster Autoscaler,并演示Karpenter的基本操作。

Karpenter 是什么?

Karpenter 是一个为 Kubernetes 构建的开源自动扩缩容项目。 它提高了 Kubernetes 应用程序的可用性,而无需手动或过度配置计算资源。 Karpenter 旨在通过观察不可调度的 Pod 的聚合资源请求并做出启动和终止节点的决策,以最大限度地减少调度延迟,从而在几秒钟内(而不是几分钟)提供合适的计算资源来满足您的应用程序的需求。

Karpenter 与 Cluster Autocaler

在Kubernetes上可以实现集群自动缩放的工具,有Cluster Autoscaler、Escalator和Cerebral等。可惜有些工具已经疏于维护或终止开发。如今Cluster Autoscaler是集群自动缩放组件的首选,常见的优点包括:可以使集群根据需求的增长而自动扩容;通过有效整合利用资源和终止不必要的节点而较少基础架构带来的成本;其开发者是中立的,支持所有主流的公有云厂商;应用广泛,通过了实战的考验;支持大约1000个节点的集群。

下面我们了解一下Cluster Autoscaler 是如何工作的,如下图所示:当有Pod因为资源不足状态为pending时,就会触发Cluster Autoscaler的扩展机制,Cluster Autoscaler会增加托管节点组底层的Auto Scaling Group中的desired实例数量,从而通过Auto Scaling Group部署新的EC2实例加入节点组,作为预置新节点,pending状态的pod会被调度到新的节点进行部署。

Cluster Autoscaler 在使用中有一些问题和注意事项。Cluster Autoscaler 对节点组进行的自动扩缩容,是依赖于 launch template 和 Auto Scaling group 进行的,所以Auto Scaling group的最大值和最小值也会限制节点组的最大和最小节点数量。有时Cluster Autoscaler 为了进行一些指定的扩缩容操作,我们需要为每种实例类型或可用区单独区创建一个节点组。在节点组中如果创建节点失败,并不会立刻进行处理,因为Cluster Autoscaler 处理错误的机制是基于超时。Cluster Autocaler 官方的性能测试报告,测试中使用了1000个节点,每个节点30个pod,超过这样的规模目前还没有官方的测试反馈。Cluster Autocaler 的操作也是比较复杂的,足有78个命令行参数。并且用户不能自定义调度器。基于以上的问题,Zalando 公司基于Cluster Autocaler 做了修改,并且在github里fork出了一个分支,他们改进了节点处理代码,支持多实例类型的 Auto Scaling Group,使用了更加可靠的backoff逻辑。但这些改进可能还不够,我们是否可以拥有一个更简单、扩展更快速、支持更大集群的扩缩容工具?这就是Karpenter了。

Karpenter 取消了节点组的概念,这是它与Cluster Autoscaler 的根本区别,节点组通常是效率较低的原因之一。Karpenter 直接提供计算资源,动态的计算pod需要何种大小的EC2实例类型作为节点。从Cluster Autocaler 的静态模版到 Karpenter 的动态生成模版,不必去创建节点组来确定实例的各种属性,从而降低了配置的复杂性。Cloud Provider的API负载也会大大减少,在Cluster Autocaler 中,Auto Scaling group总会不断请求Cloud Provider来确认状态,在集群庞大以后,很可能碰到API调用限制,造成整个系统停止响应。而Karpenter只在创建和删除容量时调用API,这种设计可以支持更高的API吞吐量。没有了节点组,调度的复杂程度也被降低,因为Cluster Autoscaler 不同节点组有不同属性,需要考虑pod被调度到哪个节点组。

下面我们了解一下 Karpenter 的工作过程,如下图所示:当有一个pending状态的pod时,如果还有存在的容量,则由kube scheduler进行调度。如果没有容量造成pod不能被调度,则由Karpenter绕过kube scheduler将pod绑定到新建的预置节点。

启动EC2实例的方式很多,Cluster Autoscaler 使用 Auto Scaling group,Karpenter使用EC2 Fleets,这两种方式都可以启动EC2实例。之所以选择EC2 Fleets,是因为它更强大更灵活,举例来说,当决定为一组pod创建节点时,可以不受限于可用区、实例类型,我们可以在Cloud Providers指定实例类型、可用区等属性,对Karpenter进行约束,Karpenter 可以在指定的属性中寻找最适合的实例类型来部署pod。EC2 fleet也会选择指定实例中最便宜的实例类型。另外,使用Auto Scaling group来启动节点,大约需要30秒的时间,如果改用EC2 Fleets将远远少于30秒。

调度也非常重要,Karpenter 也优化了调度,一旦容量扩容的决定被做出,发出创建实例的请求,会立即获得实例ID,不等实例创建完成就创建节点对象,将需要调度的pod绑定到节点。这是在kube scheduler之外强制执行了一个调度决策。这样做的好处有两个,第一,为pod部署降低了5秒左右的延迟,第二,不必匹配Karpenter与kube scheduler之间的版本。

通过nodeSelector,我们可以使用kubernetes Well-Known Label:https://kubernetes.io/docs/reference/labels-annotations-taints/ 来指定属性启动实例,可以指定的属性包括可用区,实例类型,容量类型,CPU架构,操作系统等。

如何为pod选择合适的EC2实例,即Bin Packing打包问题。Karpenter 采用了First Fit Descending算法,我们将pod按照从大到小排序,先将最大的pod去适配实例,如果不行就再换小一些的pod,这个过程尝试的实例越来越小,直到将最小的pod找到合适的实例。这样做的好处是,大的pod经常会在实例上留下一些间隙,可以让后面小pod填入,可以更有效的利用资源。对于pod排序的优先级,可以按照CPU、内存或euclidean。

Karpenter 不止增加节点,也负责终止节点。有一个控制器专门负责终止节点,默认一个节点5分钟内没有pod,Karpenter 就会终止它。另外,当EC2实例由于某种原因处于unhealthy状态或spot实例即将被回收,它都会发送一个事件,Karpenter 会响应这些事件,新创建节点来重部署上面的pod。另外Karpenter也可以为节点设置一个TTL值,比如配置节点生命周期是90天,这个功能在升级时非常有用,可以确保节点一直滚动升级。

Karpenter 也对节点的启动过程做了优化,之前节点启动都有一大堆步骤,一般云中的节点启动大约需要2分钟,实际这是由于过度配置造成的,现在这个延迟被Karpenter减少到了15到50秒。

Karpenter上手指南

Karpenter 自动配置新节点以响应不可调度的 pod。 Karpenter 通过监控 Kubernetes 集群内的事件,然后向底层cloud providers发送命令来做到这一点。在此示例中,集群在亚马逊云科技的 Elastic Kubernetes Service (EKS) 上运行。 Karpenter 旨在与云提供商无关,但目前仅支持亚马逊云科技cloud providers。 欢迎加入https://github.com/awslabs/karpenter 以开发其他版本cloud providers。完成本指南所需的时间不到 1 小时,成本低于 0.25 美元。 记得按照文末方式对资源进行清理删除。

Karpenter上手之环境准备

本次实验我们会用到四个工具:

安装完必要工具后,运行shell设置以下环境变量:

export CLUSTER_NAME=$USER-karpenter-demo
export AWS_DEFAULT_REGION=us-west-2
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

使用 eksctl 创建集群。 此示例配置文件指定了一个具有一个初始节点的基本集群,并为该集群设置了一个 IAM OIDC provider,以用于后续步骤设置IAM Roles for Service Accounts(IRSA):

cat <<EOF > cluster.yaml
---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: ${CLUSTER_NAME}
  region: ${AWS_DEFAULT_REGION}
  version: "1.20"
managedNodeGroups:
  - instanceType: m5.large
    amiFamily: AmazonLinux2
    name: ${CLUSTER_NAME}-ng
    desiredCapacity: 1
    minSize: 1
    maxSize: 10
iam:
  withOIDC: true
EOF
eksctl create cluster -f cluster.yaml

 

实验中我们使用的是EKS里的managed Node Groups的节点来部署Karpenter。Karpenter也可以运行在自建节点,fargate。

Karpenter发现子网需要有指定tag:kubernetes.io/cluster/$CLUSTER_NAME 。将此tag添加到为您的集群配置的关联子网。

SUBNET_IDS=$(aws cloudformation describe-stacks \
    --stack-name eksctl-${CLUSTER_NAME}-cluster \
    --query 'Stacks[].Outputs[?OutputKey==`SubnetsPrivate`].OutputValue' \
    --output text)
aws ec2 create-tags \
    --resources $(echo $SUBNET_IDS | tr ',' '\n') \
    --tags Key="kubernetes.io/cluster/${CLUSTER_NAME}",Value=

Karpenter 启动的EC2实例必须使用 InstanceProfile 运行,该配置文件授予运行容器和配置网络所需的权限。 Karpenter 使用名称 KarpenterNodeRole-${ClusterName} 发现 InstanceProfile。

首先,使用 AWS CloudFormation 创建 IAM 资源。

TEMPOUT=$(mktemp)
curl -fsSL https://karpenter.sh/docs/getting-started/cloudformation.yaml > $TEMPOUT \
&& aws cloudformation deploy \
  --stack-name Karpenter-${CLUSTER_NAME} \
  --template-file ${TEMPOUT} \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides ClusterName=${CLUSTER_NAME}

其次,使用配置文件授予EC2实例连接到集群的访问权限。 此命令将 Karpenter 节点角色添加到您的 aws-auth 配置映射,允许具有此角色的节点连接到集群。

eksctl create iamidentitymapping \
  --username system:node:{{EC2PrivateDNSName}} \
  --cluster  ${CLUSTER_NAME} \
  --arn arn:aws:iam::${AWS_ACCOUNT_ID}:role/KarpenterNodeRole-${CLUSTER_NAME} \
  --group system:bootstrappers \
  --group system:nodes

Karpenter本身还需要启动EC2实例的权限,与Cluster Autoscaler一样,我们通过IAM Roles for Service Accounts(IRSA)实现,用下面命令配置:

eksctl create iamserviceaccount \
  --cluster $CLUSTER_NAME --name karpenter --namespace karpenter \
  --attach-policy-arn arn:aws:iam::$AWS_ACCOUNT_ID:policy/KarpenterControllerPolicy-$CLUSTER_NAME \
  --approve

如果您之前没有运行过EC2 spot实例,请运行下面命令,如果之前运行过spot实例,下面命令会报错,请忽略。

aws iam create-service-linked-role --aws-service-name spot.amazonaws.com

Karpenter 是用Helm打包的,我们需要使用Helm来安装:

helm repo add karpenter https://charts.karpenter.sh
helm repo update
helm upgrade --install karpenter karpenter/karpenter --namespace karpenter \
  --create-namespace --set serviceAccount.create=false --version 0.4.1 \
  --wait # for the defaulting webhook to install before creating a Provisioner

打开debug日志:

kubectl patch configmap config-logging -n karpenter --patch '{"data":{"loglevel.controller":"debug"}}'

Karpenter上手之Provisioner配置

Karpenter Provisioners 是一种 Kubernetes 自定义资源(CustomResourceDefinitions),使客户能够在其集群中配置 Karpenter的约束,比如实例类型,可用区等。Karpenter Provisioner带有一组全局默认值,如果您自己定义了Provisioner,默认值会被覆盖。 一个集群中,也可以存在多个Karpenter Provisioners,默认情况下,pod 将使用名为 default 的 Provisioner 定义的规则。 如果您创建了第二个Provisioner,请使用节点选择器指定 karpenter.sh/provisioner-name:alternative-provisioner。 与此同时,使用默认Provisioner也需要使用节点选择器明确指定 karpenter.sh/provisioner-name 。

下面是一个Provisioner的示例:

cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
spec:
  requirements:
    - key: node.k8s.aws/capacity-type
      operator: In
      values: ["spot"]
  provider:
    instanceProfile: KarpenterNodeInstanceProfile-${CLUSTER_NAME}
    cluster:
      name: ${CLUSTER_NAME}
      endpoint: $(aws eks describe-cluster --name ${CLUSTER_NAME} --query "cluster.endpoint" --output json)
  ttlSecondsAfterEmpty: 30
EOF

可以看到,示例中仅对capacity-type做了spot的限制,并且指定了ttlSecondsAfterEmpty为30。使用这个Provisioner只会创建spot类型实例,并且在实例为空后30秒后关闭。更多Provisioner配置项可以参考:https://karpenter.sh/docs/provisioner-crd/

Karpenter上手之自动扩缩容

我们使用pause image创建一个replicas为0的deployment。

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 0
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
    spec:
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.2
          resources:
            requests:
              cpu: 1
EOF

 

然后将replicas设置为5,观察karpenter日志:

kubectl scale deployment inflate --replicas 5
kubectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -o name)

根据下面输出的karpenter日志我们可以看出,16:13:32时,karpenter发现5个pod被创建。我们看到在16:13:37时,完成了节点启动以及pod与节点绑定的操作,整个过程仅用了5秒。我们看到,这次pod调度需要5个vcpu,karpenter自动选择了c5.2xlagre的示例,这体现了karpenter减少复杂度,追求速度的设计理念,会把多个pod调度合并在一个节点上。

2021-10-31T16:13:30.536Z        INFO    controller.allocation.provisioner/default       Starting provisioning loop      {"commit": "c902206"}
2021-10-31T16:13:30.536Z        INFO    controller.allocation.provisioner/default       Waiting to batch additional pods        {"commit": "c902206"}
2021-10-31T16:13:32.050Z        INFO    controller.allocation.provisioner/default       Found 5 provisionable pods      {"commit": "c902206"}
2021-10-31T16:13:32.932Z        DEBUG   controller.allocation.provisioner/default       Discovered 318 EC2 instance types       {"commit": "c902206"}
2021-10-31T16:13:32.933Z        DEBUG   controller.allocation.provisioner/default       Excluding instance type t4g.nano because there are not enough resources for kubelet and system overhead     {"commit": "c902206"}
2021-10-31T16:13:32.935Z        DEBUG   controller.allocation.provisioner/default       Excluding instance type t3.nano because there are not enough resources for kubelet and system overhead      {"commit": "c902206"}
2021-10-31T16:13:32.939Z        DEBUG   controller.allocation.provisioner/default       Excluding instance type t3a.nano because there are not enough resources for kubelet and system overhead     {"commit": "c902206"}
2021-10-31T16:13:32.968Z        INFO    controller.allocation.provisioner/default       Computed packing for 5 pod(s) with instance type option(s) [c1.xlarge c3.2xlarge c4.2xlarge c6i.2xlarge c5a.2xlarge c5d.2xlarge c6g.2xlarge c5ad.2xlarge c6gd.2xlarge a1.2xlarge c6gn.2xlarge c5.2xlarge c5n.2xlarge m3.2xlarge m6g.2xlarge m4.2xlarge m5zn.2xlarge m5dn.2xlarge m5n.2xlarge m5d.2xlarge]       {"commit": "c902206"}
2021-10-31T16:13:33.146Z        DEBUG   controller.allocation.provisioner/default       Discovered subnets: [subnet-0a538ed8c05288206 subnet-07a9d3f4dbc92164c subnet-0b14f140baa9a38cb]    {"commit": "c902206"}
2021-10-31T16:13:33.262Z        DEBUG   controller.allocation.provisioner/default       Discovered security groups: [sg-0afb56113d9feb2e8]      {"commit": "c902206"}
2021-10-31T16:13:33.265Z        DEBUG   controller.allocation.provisioner/default       Discovered kubernetes version 1.20      {"commit": "c902206"}
2021-10-31T16:13:33.317Z        DEBUG   controller.allocation.provisioner/default       Discovered ami ami-0a69abe3cea2499b7 for query /aws/service/eks/optimized-ami/1.20/amazon-linux-2-arm64/recommended/image_id        {"commit": "c902206"}
2021-10-31T16:13:33.365Z        DEBUG   controller.allocation.provisioner/default       Discovered ami ami-088105bab8bfa2db6 for query /aws/service/eks/optimized-ami/1.20/amazon-linux-2/recommended/image_id      {"commit": "c902206"}
2021-10-31T16:13:33.365Z        DEBUG   controller.allocation.provisioner/default       Discovered caBundle, length 1066        {"commit": "c902206"}
2021-10-31T16:13:33.506Z        DEBUG   controller.allocation.provisioner/default       Created launch template, Karpenter-karpenter-demo-16982985708254790476      {"commit": "c902206"}
2021-10-31T16:13:33.507Z        DEBUG   controller.allocation.provisioner/default       Discovered caBundle, length 1066        {"commit": "c902206"}
2021-10-31T16:13:33.640Z        DEBUG   controller.allocation.provisioner/default       Created launch template, Karpenter-karpenter-demo-11290710479729449633      {"commit": "c902206"}
2021-10-31T16:13:36.898Z        INFO    controller.allocation.provisioner/default       Launched instance: i-0f38cb0ade09a537c, hostname: ip-192-168-132-54.us-west-2.compute.internal, type: c5.2xlarge, zone: us-west-2a, capacityType: spot      {"commit": "c902206"}
2021-10-31T16:13:37.050Z        INFO    controller.allocation.provisioner/default       Bound 5 pod(s) to node ip-192-168-132-54.us-west-2.compute.internal{"commit": "c902206"}
2021-10-31T16:13:37.050Z        INFO    controller.allocation.provisioner/default       Starting provisioning loop      {"commit": "c902206"}
2021-10-31T16:13:37.050Z        INFO    controller.allocation.provisioner/default       Waiting to batch additional pods        {"commit": "c902206"}
2021-10-31T16:13:38.050Z        INFO    controller.allocation.provisioner/default       Found 0 provisionable pods      {"commit": "c902206"}

 

我们选择一个pod来查看它的创建耗时:

kubectl get pod <pod_name> -oyaml

从下面日志可以看出,16:13:36时,pod被调度,16:14:45时pod ready。整个过程为1分9秒。考虑到是5个pod,并且这段时间是创建ec2启动模版,ec2实例启动并加入集群节点和pod向节点部署的时间,整个过程还是很快的。如果想加速这段过程,可以考虑使用placeholder pod做Over-Provisioning的方式。

……
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2021-10-31T16:14:17Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2021-10-31T16:14:45Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2021-10-31T16:14:45Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2021-10-31T16:13:36Z"
    status: "True"
type: PodScheduled
……

我们打开EC2 Dashboard,查看spot requests,可以看到karpenter是使用的spot fleet,当前我们使用的是spot实例,如果使用karpenter启动on demand实例,可以使用aws cli命令aws ec2 describe-fleets去查看。

下面我们删除刚才创建的Deployment,并观察karpenter日志:

kubectl delete deployment inflate
kubectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -o name)

从日志中我们可以看到,16:40:16时发现了空实例,ttlSecondsAfterEmpty在Provisioner中设置为30,则30秒后,才将实例终止。

2021-10-31T16:13:39.549Z        INFO    controller.allocation.provisioner/default       Watching for pod events {"commit": "c902206"}
2021-10-31T16:40:16.040Z        INFO    controller.Node Added TTL to empty node ip-192-168-132-54.us-west-2.compute.internal    {"commit": "c902206"}
2021-10-31T16:40:46.059Z        INFO    controller.Node Triggering termination after 30s for empty node ip-192-168-132-54.us-west-2.compute.internal    {"commit": "c902206"}
2021-10-31T16:40:46.103Z        INFO    controller.Termination  Cordoned node ip-192-168-132-54.us-west-2.compute.internal      {"commit": "c902206"}
2021-10-31T16:40:46.290Z        INFO    controller.Termination  Deleted node ip-192-168-132-54.us-west-2.compute.internal       {"commit": "c902206"}

除了利用provisioner去自动选择扩展节点类型,我们也可以在pod中使用nodeSelector来指定Well-Known Labes启动节点,下面是一段Deployment示例:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 0
  selector:
    matchLabels:
      app: inflate
  template:
    metadata:
      labels:
        app: inflate
    spec:
      containers:
        - name: inflate
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.2
          resources:
            requests:
              cpu: 1
      nodeSelector:
        node.kubernetes.io/instance-type: c5.xlarge
EOF

 

观察karpenter日志,本次已经有了第一次扩展创建的launch template,所以从发现pod provisionable,到创建实例绑定pod,仅用了4秒。但与第一次不同,我们这次在Deployment时利用nodeSelector指定了使用c5.xlarge实例,所以karpenter创建了2台c5.xlarge实例来部署pod,而不是第一次的一台c5.2xlarge。

kubectl logs -f -n karpenter $(kubectl get pods -n karpenter -l karpenter=controller -o name)

……
2021-10-31T17:13:28.459Z        INFO    controller.allocation.provisioner/default       Waiting to batch additional pods        {"commit": "c902206"}
2021-10-31T17:13:29.549Z        INFO    controller.allocation.provisioner/default       Found 5 provisionable pods      {"commit": "c902206"}
2021-10-31T17:13:30.648Z        DEBUG   controller.allocation.provisioner/default       Discovered 318 EC2 instance types       {"commit": "c902206"}
2021-10-31T17:13:30.661Z        INFO    controller.allocation.provisioner/default       Computed packing for 3 pod(s) with instance type option(s) [c5.xlarge]      {"commit": "c902206"}
2021-10-31T17:13:30.675Z        INFO    controller.allocation.provisioner/default       Incremented node count to 2 on packing for 2 pod(s) with instance type option(s) [c5.xlarge]        {"commit": "c902206"}
2021-10-31T17:13:30.860Z        DEBUG   controller.allocation.provisioner/default       Discovered subnets: [subnet-0a538ed8c05288206 subnet-07a9d3f4dbc92164c subnet-0b14f140baa9a38cb]    {"commit": "c902206"}
2021-10-31T17:13:30.951Z        DEBUG   controller.allocation.provisioner/default       Discovered security groups: [sg-0afb56113d9feb2e8]      {"commit": "c902206"}
2021-10-31T17:13:30.955Z        DEBUG   controller.allocation.provisioner/default       Discovered kubernetes version 1.20      {"commit": "c902206"}
2021-10-31T17:13:31.016Z        DEBUG   controller.allocation.provisioner/default       Discovered ami ami-088105bab8bfa2db6 for query /aws/service/eks/optimized-ami/1.20/amazon-linux-2/recommended/image_id      {"commit": "c902206"}
2021-10-31T17:13:31.016Z        DEBUG   controller.allocation.provisioner/default       Discovered caBundle, length 1066        {"commit": "c902206"}
2021-10-31T17:13:31.052Z        DEBUG   controller.allocation.provisioner/default       Discovered launch template Karpenter-karpenter-demo-11290710479729449633    {"commit": "c902206"}
2021-10-31T17:13:33.150Z        INFO    controller.allocation.provisioner/default       Launched instance: i-04604513375c3dc3a, hostname: ip-192-168-156-86.us-west-2.compute.internal, type: c5.xlarge, zone: us-west-2a, capacityType: spot       {"commit": "c902206"}
2021-10-31T17:13:33.150Z        INFO    controller.allocation.provisioner/default       Launched instance: i-0e058845370c428ec, hostname: ip-192-168-154-221.us-west-2.compute.internal, type: c5.xlarge, zone: us-west-2a, capacityType: spot      {"commit": "c902206"}
2021-10-31T17:13:33.207Z        INFO    controller.allocation.provisioner/default       Bound 3 pod(s) to node ip-192-168-156-86.us-west-2.compute.internal{"commit": "c902206"}
2021-10-31T17:13:33.233Z        INFO    controller.allocation.provisioner/default       Bound 2 pod(s) to node ip-192-168-154-221.us-west-2.compute.internal{"commit": "c902206"}
……

 

打开EC2 Dashboard,查看spot request,可以看到2个spot fleet,可以看到即使同型号实例,karpenter 为了可以快速调度,也会分别创建fleet。

删除实验环境

执行下面命令,删除实验环境,避免产生额外费用。

helm uninstall karpenter --namespace karpenter
eksctl delete iamserviceaccount --cluster ${CLUSTER_NAME} --name karpenter --namespace karpenter
aws cloudformation delete-stack --stack-name Karpenter-${CLUSTER_NAME}
aws ec2 describe-launch-templates \
    | jq -r ".LaunchTemplates[].LaunchTemplateName" \
    | grep -i Karpenter-${CLUSTER_NAME} \
    | xargs -I{} aws ec2 delete-launch-template --launch-template-name {}
eksctl delete cluster --name ${CLUSTER_NAME}

 

总结

Karpenter 作为一款新的Kubernetes auto scaling工具,它有着更快速更灵活的优势。对于大规模的Kubernetes集群有着更好的支持。在具备这些优点的同时,它还大大降低的运维工作量,使auto scaling更加自动。

本篇作者

夏焱

亚马逊云科技解决方案架构师,目前专注于容器化解决方案。在加入亚马逊云科技之前,曾就职于惠普、IBM等科技公司,从事数据中心基础架构相关工作,拥有十余年技术服务经验。