Using Karpenter with vCluster Platform
vCluster is compatible with Karpenter and vCluster's features can be used in conjuntion with Karpenter to further reduce infrastructure costs.
This article explains how to create a designated node pool for a vCluster's workload pods, create a designated node pool for a project, and target node pool based on vCluster pod labels. All three of which can be powerful cost saving tools.
This article assumes you are using a control plane cluster provisioned on one of the following managed Kubernetes services: EKS (Elastic Kubernetes Service), AKS (Azure Kubernetes Service), or GKE (Google Kubernetes Engine).
The pattern <value> repeatedly occurs in this documentation and is meant to imply that the user should replace the value, angular brackets included, with their own.
Much of this article borrows and builds upon the Getting Started with Karpenter documentation, the Karpenter Provider Azure README.md, and the Karpenter Provider GCP documentation. Most if not all scripts and yaml manifests were written by the authors of those articles or articles of other AKS, Azure, and GCP documentation. Some of those works were then modified while others remain the same.
The Karpenter core API version and NodeClass kind differ by cloud provider. The EKS and AKS examples in this article use karpenter.sh/v1beta1. The GCP provider (cloudpilot-ai/karpenter-provider-gcp) requires the current karpenter.sh/v1 API and its own GCENodeClass kind. Follow only the tab that matches your control plane cluster's cloud provider, and don't mix fields between tabs.
Pre-requisites​
- Either an EKS Kubernetes cluster, an AKS Kubernetes cluster, or a GKE Standard cluster.
- vCluster Platform installed on control plane cluster. Read Deploy the vCluster Platform docs.
- EKS specific requirements for vCluster Platform compatibility:
- EBS driver installed. Get the Amazon EBS CSI driver add-on.
- Delete the gp2 StorageClass with
kubectl delete storageclass gp2and replace with gp3 by applying following yaml:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
allowVolumeExpansion: true
- GKE specific requirements: enable the Compute Engine, GKE, and IAM Service Account Credentials APIs used by the Karpenter GCP provider:
gcloud services enable compute.googleapis.com container.googleapis.com iamcredentials.googleapis.com
Install Karpenter​
- EKS RBAC Requirements
- AKS RBAC Requirements
- GKE RBAC Requirements
- Run helm install for karpenter:
Set KARPENTER_VERSION per the Karpenter getting started guide before running this command.
export KARPENTER_NAMESPACE=kube-system
helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter --version "${KARPENTER_VERSION}" --namespace "${KARPENTER_NAMESPACE}" --create-namespace \
--set "settings.clusterName=my-cluster" \
--set "settings.interruptionQueue=my-cluster" \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--set controller.resources.limits.cpu=1 \
--set controller.resources.limits.memory=1Gi \
--wait
- Set your environment variables:
export RESOURCE_GROUP=my-resource-group
export CLUSTER_NAME=my-cluster
export AZURE_LOCATION=eastus
export IDENTITY_NAME=karpentermsi
export KARPENTER_NAMESPACE=kube-system
export KARPENTER_VERSION=0-f83fadf2c99ffc2b7429cb40a316fcefc0c4752a
- Enable OIDC issuer for your cluster if it is not enabled yet.
az aks update \
--resource-group "${RESOURCE_GROUP}" \
--name "${CLUSTER_NAME}" \
--enable-oidc-issuer
- Enable OIDC workload identity for your cluster if it is not enabled yet.
az aks update \
--resource-group "${RESOURCE_GROUP}" \
--name "${CLUSTER_NAME}" \
--enable-workload-identity
- Set local AKS credentials.
az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}" --overwrite-existing
AKS_JSON=$(az aks show --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}" --output json)
- Create managed identity for karpenter.
KMSI_JSON=$(az identity create --name "${IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --location "${AZURE_LOCATION}")
- Get the OIDC issuer URL.
export ISSUER_URL=$(az aks show --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}" --query "oidcIssuerProfile.issuerUrl" -o tsv)
- You will then need to create a credential linked to the identity and a service account using the OIDC issuer URL.
az identity federated-credential create --name KARPENTER_FID --identity-name "${IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" \
--issuer "${ISSUER_URL}" \
--subject system:serviceaccount:kube-system:karpenter-sa \
--audience api://AzureADTokenExchange
- Create the role assignments that Karpenter needs.
KARPENTER_USER_ASSIGNED_CLIENT_ID=$(jq -r '.principalId' <<< "$KMSI_JSON")
RG_MC=$(jq -r ".nodeResourceGroup" <<< "$AKS_JSON")
RG_MC_RES=$(az group show --name "${RG_MC}" --query "id" -otsv)
for role in "Virtual Machine Contributor" "Network Contributor" "Managed Identity Operator"; do
az role assignment create --assignee "${KARPENTER_USER_ASSIGNED_CLIENT_ID}" --scope "${RG_MC_RES}" --role "$role"
done
- Generate values file needed to deploy Karpenter in AKS cluster.
curl -sO https://raw.githubusercontent.com/Azure/karpenter-provider-azure/main/hack/deploy/configure-values.sh
chmod +x ./configure-values.sh && ./configure-values.sh "${CLUSTER_NAME}" "${RESOURCE_GROUP}" karpenter-sa "${IDENTITY_NAME}"
- Finally, deploy Karpenter in your AKS cluster using helm upgrade:
helm upgrade --install karpenter oci://ksnap.azurecr.io/karpenter/snapshot/karpenter \
--version "${KARPENTER_VERSION}" \
--namespace "${KARPENTER_NAMESPACE}" --create-namespace \
--values configure-values.yaml \
--set controller.resources.requests.cpu=1 \
--set controller.resources.requests.memory=1Gi \
--set controller.resources.limits.cpu=1 \
--set controller.resources.limits.memory=1Gi \
--wait
kubectl logs -f -n "kube-system" -l app.kubernetes.io/name=karpenter -c controller
- Set your environment variables, then enable Workload Identity Federation for GKE on the cluster and on the node pool that will run Karpenter. Standard clusters don't enable this by default (Autopilot clusters always do), and it must be enabled at the cluster level before it can be enabled on a node pool:
export PROJECT_ID=my-project-id
export CLUSTER_NAME=my-cluster
export REGION=us-central1
export NODE_POOL_NAME=default-pool
gcloud container clusters update "${CLUSTER_NAME}" \
--location="${REGION}" \
--workload-pool="${PROJECT_ID}.svc.id.goog"
gcloud container node-pools update "${NODE_POOL_NAME}" \
--cluster="${CLUSTER_NAME}" \
--location="${REGION}" \
--workload-metadata=GKE_METADATA
Skip this step if the cluster was created with --workload-pool and its node pools already use --workload-metadata=GKE_METADATA. See Google's Workload Identity Federation for GKE guide for details.
- Create a GCP service account for the Karpenter controller and grant it the minimal IAM role from the provider's canonical permission list:
export GSA_NAME=karpenter-gsa
gcloud iam service-accounts create "${GSA_NAME}" --project="${PROJECT_ID}"
curl -fsSL https://raw.githubusercontent.com/cloudpilot-ai/karpenter-provider-gcp/main/deploy/iam/karpenter-controller-role.yaml \
-o karpenter-controller-role.yaml
gcloud iam roles create karpenter_controller \
--project="${PROJECT_ID}" \
--file=karpenter-controller-role.yaml
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="projects/${PROJECT_ID}/roles/karpenter_controller"
- Create a dedicated node service account. This replaces the Compute Engine default service account, which has broad
roles/editor-equivalent access:
export NODE_SA_NAME=karpenter-node
export NODE_SA_EMAIL="${NODE_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts create "${NODE_SA_NAME}" --project="${PROJECT_ID}"
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${NODE_SA_EMAIL}" \
--role="roles/container.nodeServiceAccount"
gcloud iam service-accounts add-iam-policy-binding "${NODE_SA_EMAIL}" \
--role roles/iam.serviceAccountUser \
--member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
--project "${PROJECT_ID}"
- Install Karpenter with Helm, pointing new nodes at the dedicated node service account:
helm repo add karpenter-provider-gcp https://cloudpilot-ai.github.io/karpenter-provider-gcp
helm repo update
helm upgrade --install karpenter-crd karpenter-provider-gcp/karpenter-crd
helm upgrade --install karpenter karpenter-provider-gcp/karpenter \
--namespace karpenter-system --create-namespace \
--set "controller.settings.projectID=${PROJECT_ID}" \
--set "controller.settings.clusterLocation=${REGION}" \
--set "controller.settings.clusterName=${CLUSTER_NAME}" \
--set "controller.settings.defaultNodepoolServiceAccount=${NODE_SA_EMAIL}" \
--set "credentials.enabled=false" \
--set "serviceAccount.annotations.iam\.gke\.io/gcp-service-account=${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
--wait
- Bind Workload Identity so the Karpenter Kubernetes service account can impersonate the GCP service account:
gcloud iam service-accounts add-iam-policy-binding \
"${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[karpenter-system/karpenter]"
- Verify the Karpenter controller pod reaches
Runningstate:
kubectl get pods -n karpenter-system
If Workload Identity isn't available in your environment, see the service account key alternative in the provider's installation guide.
Configure control plane cluster Kubernetes RBAC permissions​
To deploy the manifests presented in the remainder of this article you will need certain permissions in the Kubernetes control plane cluster. The following Kubernetes permissions will be required in the control plane cluster for the user:
- EKS RBAC Requirements
- AKS RBAC Requirements
- GKE RBAC Requirements
- Create
NodePools.karpenter.sh/v1beta1in control plane cluster. - Create
EC2NodeClass.karpenter.sh/v1beta1in control plane cluster.
- Create
NodePools.karpenter.sh/v1beta1in control plane cluster. - Create
AKSNodeClass.karpenter.sh/v1beta1in control plane cluster.
- Create
NodePools.karpenter.sh/v1in control plane cluster. - Create
GCENodeClass.karpenter.k8s.gcp/v1alpha1in control plane cluster.
Create node pools​
Create designated node pool for a vCluster's workload pods​
Required permissions
The following permissions will be required in vCluster Platform:
- Create clusters
Create node pool
This section utilizes taints and tolerations to guide cluster pods to the desired node. This works best if every node pool or node utilizes a taint. Otherwise, the pods may be scheduled on non-tainted nodes.
- Select "Create Cluster"
- Select "Configure" and add a toleration to cluster pods using the yaml editor. If the below yaml is out of date then see the applying tolerations docs.
sync:
toHost:
pods:
enforceTolerations:
- vclusterID=<vcluster-name>:NoSchedule
- Create a node pool with a taint matching the toleration and create node class for it to use.
- Provision Node Pool for EKS
- Provision Node Pool for AKS
- Provision Node Pool for GKE
cat <<EOF | kubectl apply -f -
---
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: <name-of-your-choosing>
spec:
template:
spec:
taints:
- key: vclusterID
value: <vcluster-id>
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["2"]
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: <node-class-name>
spec:
amiFamily: AL2 # Amazon Linux 2
role: "KarpenterNodeRole-<cluster-name>"
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "<cluster-name>"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "<cluster-name>"
amiSelectorTerms:
- id: "<arm-ami-id>"
- id: "<amd-ami-id>"
EOF
cat <<EOF | kubectl apply -f -
---
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: <name-of-your-choosing>
spec:
template:
spec:
taints:
- key: vclusterID
value: <cluster-id>
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: karpenter.azure.com/sku-family
operator: In
values: [D]
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: AKSNodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
---
apiVersion: karpenter.azure.com/v1alpha2
kind: AKSNodeClass
metadata:
name: <node-class-name>
spec:
imageFamily: Ubuntu2204
EOF
cat <<EOF | kubectl apply -f -
---
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: <name-of-your-choosing>
spec:
template:
spec:
taints:
- key: vclusterID
value: <vcluster-id>
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.gcp/instance-family
operator: In
values: ["n4", "n2", "e2"]
nodeClassRef:
group: karpenter.k8s.gcp
kind: GCENodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 0s
---
apiVersion: karpenter.k8s.gcp/v1alpha1
kind: GCENodeClass
metadata:
name: <node-class-name>
spec:
serviceAccount: "<node-service-account-email>"
imageSelectorTerms:
- family: ContainerOptimizedOS
channel: cluster
disks:
- sizeGiB: 60
boot: true
EOF
- Now we will confirm the node pool is working. Run
kubectl get nodes --watch. Open a separate terminal tab. Connect to your vCluster by runningvcluster connect <vcluster-id>. Then run:
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:
terminationGracePeriodSeconds: 0
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
EOF
kubectl scale deployment inflate --replicas 5
You should see nodes provision for the deployment. If you do the above on a vCluster without the tolerations then the nodes should not be provisioned on the node pool.
- Tear down the deployment used above.
kubectl delete deployment inflate
Create designated node pool for a project​
We will designate a node pool for a project by creating templates and enforcing the template for that project.
Required permissions
The following permissions will be required in vCluster Platform:
- Create vCluster Templates
- Create Projects
Create node pool
- Using the vCluster Platform add a template: Templates -> Clusters -> Add Cluster Template.
- In the yaml editor box, which contains the vcluster.yaml, add the following:
sync:
toHost:
pods:
enforceTolerations:
- vclusterID=<project-name>:NoSchedule
- Create a node pool with the taint corresponding to the toleration in step 2, e.g.:
- Provision NodePool for EKS
- Provision NodePool for AKS
- Provision NodePool for GKE
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: <name-of-your-choosing>
spec:
template:
spec:
taints:
- key: <project-name>
value: "true"
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["2"]
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: <node-class-name>
spec:
amiFamily: AL2 # Amazon Linux 2
role: "<karpenter-node-role>" # replace with your cluster name
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "<cluster-name>"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "<cluster-name>"
amiSelectorTerms:
- id: "<arm-ami-id>"
- id: "<amd-ami-id>"
EOF
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: <name-of-your-choosing>
spec:
template:
spec:
taints:
- key: <project-name>
value: "true"
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: karpenter.azure.com/sku-family
operator: In
values: [D]
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: AKSNodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
---
apiVersion: karpenter.azure.com/v1alpha2
kind: AKSNodeClass
metadata:
name: <node-class-name>
spec:
imageFamily: Ubuntu2204
EOF
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: <name-of-your-choosing>
spec:
template:
spec:
taints:
- key: <project-name>
value: "true"
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.gcp/instance-family
operator: In
values: ["n4", "n2", "e2"]
nodeClassRef:
group: karpenter.k8s.gcp
kind: GCENodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 0s
---
apiVersion: karpenter.k8s.gcp/v1alpha1
kind: GCENodeClass
metadata:
name: <node-class-name>
spec:
serviceAccount: "<node-service-account-email>"
imageSelectorTerms:
- family: ContainerOptimizedOS
channel: cluster
disks:
- sizeGiB: 60
boot: true
EOF
- Create a new project that only allows templates with set tolerations. Go to project dropdown and create a new project. In the Allowed Templates section, remove the All Templates option. Select the templates that include tolerations for the node pool created in step 3. Now, all pods created in clusters belonging to this project will deploy to the designated node pools.
Target node pool based on vCluster pod labels​
Required permissions
The following permissions will be required in vCluster Platform:
- Create clusters
Create node pool
- Create a node pool with custom labels, for example
my.org/environ.
- Provision NodePool for EKS
- Provision NodePool for AKS
- Provision NodePool for GKE
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["2"]
- key: my.org/environ
operator: Exists
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: <node-class-name>
spec:
amiFamily: AL2 # Amazon Linux 2
role: "<karpenter-node-role>" # replace with your cluster name
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: "<cluster-name>"
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: "<cluster-name>"
amiSelectorTerms:
- id: "<arm-ami-id>"
- id: "<amd-ami-id>"
EOF
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: karpenter.azure.com/sku-family
operator: In
values: [D]
- key: my.org/environ
operator: Exists
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: AKSNodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
---
apiVersion: karpenter.azure.com/v1alpha2
kind: AKSNodeClass
metadata:
name: <node-class-name>
spec:
imageFamily: Ubuntu2204
EOF
cat <<EOF | kubectl apply -f -
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: karpenter.k8s.gcp/instance-family
operator: In
values: ["n4", "n2", "e2"]
- key: my.org/environ
operator: Exists
nodeClassRef:
group: karpenter.k8s.gcp
kind: GCENodeClass
name: <node-class-name>
limits:
cpu: 100
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 0s
---
apiVersion: karpenter.k8s.gcp/v1alpha1
kind: GCENodeClass
metadata:
name: <node-class-name>
spec:
serviceAccount: "<node-service-account-email>"
imageSelectorTerms:
- family: ContainerOptimizedOS
channel: cluster
disks:
- sizeGiB: 60
boot: true
EOF
-
Create a cluster.
-
Add nodeSelector to deployments in your cluster using a nodeSelector that targets the label from step 1 with the desired value.
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:
nodeSelector:
my.org/environ: <any key, "prod", "dev", "test" make sense here>
terminationGracePeriodSeconds: 0
containers:
- name: inflate
image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
resources:
requests:
cpu: 1
EOF
kubectl scale deployment inflate --replicas 5
If you make a deployment with a key/value pair that matches an existing node it can be scheduled to that node. Otherwise, a new node will be provisioned. Since you are adding the key to the node pool but not the value, using different values necessitates different nodes. You can read more about user-defined labels in the Karpenter's User-Defined Labels docs.