Pod Density控制

Pod 密度是每个节点的 Pod 数量,Karpenter 还提供 pod density control(Pod 密度控制),允许设置在节点上调度的 Pod 数量。 Kubernetes 默认限制每个节点 110 个 pod,在EKS上Pod 数量还受实例类型的限制。

Pod 密度控制方式

  • Static Pod Density - 在Provisioner配置 .spec.kubeletConfiguration ,指定 maxPods 。 此Provisioner生成的所有节点都将在其 kubelet 上设置此 maxPods 值,并将在调度期间考虑此值。
  • Dynamic Pod Densisty - 设置应在节点的每个 CPU 核心上调度的 pod 数量。 这是通过使用 podsPerCore参数来完成的,该参数指定每个 CPU 核心的 pod 目标数量。

Static Pod Density

默认情况下,EKS根据实例大小决定了节点能创建的最大pod数量。如果想调整这个值,可以调整spec.kubeletConfigurationmaxPods值,这个参数会由Karpenter传给kubelet,在kubelet上设置--max-pods参数。通过提高小机型的pod密度,可以达到成本优化的效果,每个节点上能跑更多的pod。

在下面的例子中,我们将为c6a.2xlarge设置Pod密度,让它最多跑6个Pod(它有8个CPU,如果pod需要1个cpu的话能跑8个)

注意设置Static Pod Density的时候很容易遇到一个坑——忽略Daemenset的数量。比如我们node上有六个DaemonSet在跑,那么应用直接启动不起来,因为一启动就会超出maxPods的值

我们将这样设置:

  kubeletConfiguration:
    maxPods: 6

先删除之前创建的资源:

部署Provisioner和Node Template,注意在spec.kubeletConfiguration部分指定了最大Pod数量:

mkdir ~/environment/karpenter
cd ~/environment/karpenter
cat <<EoF> static.yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    consolidateAfter: 30s
    consolidationPolicy: WhenEmpty
    expireAfter: Never
  template:
    metadata:
      labels:
        eks-immersion-team: my-team
    spec:
      kubelet:
        maxPods: 6
      nodeClassRef:
        name: default
      requirements:
      - key: node.kubernetes.io/instance-type
        operator: In
        values: ["c6a.2xlarge"]
      - key: kubernetes.io/arch
        operator: In
        values: ["amd64"]
      - key: karpenter.sh/capacity-type
        operator: In
        values: ["on-demand"]
      - key: kubernetes.io/os
        operator: In
        values: ["linux"]
---
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 static.yaml

创建应用:

cd ~/environment/karpenter
cat <<EoF> static-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 12  # 这里是因为我们Pod上跑了4个DaemonSet,可以继续将这个值调大
  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 static-deploy.yaml

由于我们每个节点上要跑3个DaemonSet,maxPods设置的为6,所以要创建12个replica, 也需要四台机器:

image-20240804112208990

Dynamic Pod Density

可以设置每个CPU核上运行Pod的数量,从而更好的利用CPU资源。Karpenter把这个值传给kubelet的--pods-per-core参数来完成设置。例如如果将其设置为5,Karpenter会尝试每个CPU核上运行5个pod,所以如果你有8核CPU,Karpenter将在上面部署40个Pod:

  kubelet:
    podsPerCore: 5

首先清理之前创建的资源:

kubectl delete deployment inflate
kubectl delete nodepool.karpenter.sh default
kubectl delete ec2nodeclass.karpenter.k8s.aws default

部署NodePool和NodeClass,为了设置每个CPU核上最大Pod数量,在spec.kubelet中指定了podsPerCore

mkdir -p ~/environment/karpenter
cd ~/environment/karpenter
cat <<EoF> dynamic.yaml
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: default
spec:
  disruption:
    consolidateAfter: 30s
    consolidationPolicy: WhenEmpty
    expireAfter: Never
  template:
    metadata:
      labels:
        eks-immersion-team: my-team
    spec:
      kubelet:
        podsPerCore: 5
      nodeClassRef:
        name: default
      requirements:
      - key: node.kubernetes.io/instance-type
        operator: In
        values: ["c6a.2xlarge"]
      - key: kubernetes.io/arch
        operator: In
        values: ["amd64"]
      - key: karpenter.sh/capacity-type
        operator: In
        values: ["on-demand"]
      - key: kubernetes.io/os
        operator: In
        values: ["linux"]

---
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 dynamic.yaml

部署应用

我们创建inflate应用,并让它有45个replica:

cd ~/environment/karpenter
cat <<EoF> dynamic-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: inflate
spec:
  replicas: 45
  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: 0.1
      nodeSelector:
        eks-immersion-team: my-team
EoF

kubectl apply -f dynamic-deploy.yaml

c6a.2xlarge具有8个CPU,我们将cpu request设置为0.1,理想情况它上面应该能跑80个pod。但是因为设置了podsPerCore = 5,所以它实际最多只能跑40个pod:

image-20231028221237063