Karpenter AWS Provider
repository·main·Indexed 27 days ago
https://github.com/aws/karpenter-provider-awsAn open-source Kubernetes node provisioning project that automates scaling based on workload requirements to optimize performance and cost. It manages the node lifecycle by evaluating pod scheduling constraints and provisioning nodes that satisfy those requirements. The project includes OCI Helm charts for installation, support for AWS Launch Templates, and tools such as allocatable-diff, kompat, and kubereplay.
What's inside karpenter-provider-aws
- Karpenter is an open-source Kubernetes autoscaler that automatically provisions new nodes in response to unschedulable pods. It functions by observing events within the Kubernetes cluster and sending commands to the underlying cloud provider to manage node lifecycle.
Overview of Karpenter node provisioning
mainKarpenter is an open-source node provisioning project for Kubernetes designed to improve cluster efficiency and cost. It automates the node lifecycle by:
- Watching for unschedulable pods marked by the Kubernetes scheduler.
- Evaluating pod scheduling constraints (including resource requests,
nodeSelector, affinities, tolerations, and topology spread constraints). - Provisioning nodes that satisfy the specific requirements of those pods.
- Removing nodes when they are no longer needed to optimize costs.
Understand the Machine CRD Object Store solution for Node ownership
mainOne proposed solution for managing node ownership and in-flight capacity is to use a Machine CRD as a representation of a scheduling decision. In this model, Karpenter creates a Machine CR instead of a Node object or launching instances directly during the provisioning loop.
Key behaviors of the Machine CRD model:
- Provisioning Flow: Karpenter creates a Machine CR $\rightarrow$ a separate controller launches capacity based on requirements $\rightarrow$ the controller resolves values from the
CreateFleetresponse into the Machine's status. - Mapping: Once the instance launches and the kubelet joins the cluster, Machines are mapped to Nodes by matching the Node's
spec.providerIDwith the Machine'sstatus.providerID. - Termination: The Node termination finalizer (
karpenter.sh/termination) is migrated to the Machine to orchestrate cordoning, draining, and deletion. For compatibility, the Node finalizer remains but checks for the presence of a Machine and triggers Machine deletion if found. - Capacity Tracking: In-flight capacity is represented by the Machine's
.metadata.labelsand.status.allocatableuntil the actual Node registers and initializes its own.status.allocatable. - Synchronization: Machine labels, taints, and annotations are mirrored onto the Node via a reconciliation mechanism after the Node joins the cluster.
- Liveness: Liveness checks are performed on Nodes; if a Node does not register or become ready within a certain TTL, the backing Machine is deleted.
- Provisioning Flow: Karpenter creates a Machine CR $\rightarrow$ a separate controller launches capacity based on requirements $\rightarrow$ the controller resolves values from the
Understand Karpenter Metric Implementation Details
mainWhen working with or extending Karpenter metrics, keep the following architectural design choices in mind:
- Cluster State vs. Polling: Capacity metrics are not populated by continuously polling the Kubernetes API. Instead, they are based on Karpenter's internal cluster state representation. This reduces computation and network load.
- Histogram vs. Summary:
- Histograms are used for fixed-range buckets and are easier to implement but poor for unbounded data.
- Summaries are used for tracking running quantiles (e.g., p99 latency) and work better for unbounded data, though they cannot be aggregated across different label values.
- API Monitoring: Measuring API calls directly is difficult. Implementation strategies include using custom HTTP clients (e.g., via the AWS SDK) or implementing a proxy through which Karpenter makes API calls.
Understand Karpenter security actors and capabilities
mainKarpenter's security model is defined by three primary actors with distinct responsibilities and permission levels:
- Cluster Operator: Responsible for installing and configuring Karpenter. They have full control over
NodePoolsandEC2NodeClasses, and manage the cloud identities (IAM) and permissions for both Karpenter and the resulting Nodes. - Cluster Developer: Users who create pods via
Deployments,ReplicaSets,StatefulSets, orJobs. They should not have permissions to modify the Karpenter deployment, pods, or service accounts. - Karpenter Controller: The application running in the cluster. It has Kubernetes API permissions to manage nodes and evict pods, and AWS IAM permissions to create and manage EC2 instances with specific IAM roles.
- Cluster Operator: Responsible for installing and configuring Karpenter. They have full control over
Understand Karpenter Node Creation and Ownership Issues
mainKarpenter's current model of creating Node objects immediately after launching VM instances leads to several operational challenges. Understanding these issues is critical for troubleshooting provisioning behavior:
Synchronization and Performance
Karpenter must create the Node object to capture in-flight details (node name, allocatable resources, capacity) for its scheduler. This creates a dependency on the cloud provider API or the kubelet's registration speed, which can slow down provisioning. If resolution fails, Karpenter may lack capacity knowledge, leading to over-provisioning.
Race Conditions and Instance Orphaning
- Node Creation Conflicts: If a kubelet registers a node before Karpenter attempts to create it (e.g., during large scale-ups), Karpenter receives a conflict error. If this conflict occurs with a stale Node object (e.g., from a previously deleted instance with the same name), Karpenter may assume the new instance is already registered. This results in the Node provider ID mapping to the old instance, causing the termination finalizer to fail and leaving the new EC2 instance orphaned.
- Provisioner Deletion: Karpenter uses Kubernetes
ownerReferenceto link nodes to their Provisioners. If a Provisioner is deleted during a provisioning loop after the VM has launched but before theownerReferenceis reconciled, the resulting nodes and VM instances will be orphaned.
Capacity Reporting Errors
Because Karpenter creates nodes without initial capacity details, Kubernetes 1.23+ control planes may report
NotReadyor unhealthy events due to insufficient image filesystem capacity. These errors typically resolve once the kubelet begins reportingephemeral-storage.Labeling and Kubelet Conflicts
When Karpenter (or any third party) registers a node, the kubelet only updates a fixed set of labels. This prevents users from applying dynamic labels via
KubeletConfigoruserDatain AWS, as the kubelet's updates may be ignored or overwritten.Understand Karpenter Node Ownership and the Machine CRD
mainKarpenter is migrating its node ownership model to use a Machine CRD to represent scheduling decisions. This CRD acts as the primary record for in-flight nodes before they register as Kubernetes Nodes.
Key Characteristics of the Machine CRD Model:
- Scheduling State Persistence: The Machine CRD stores scheduling decisions, including
labels,annotations, andtaints(includingstartupTaints). This state is reconciled onto the Kubernetes Node once the Node registers. - Observability: Users gain visibility into in-flight nodes and scheduling decisions by inspecting the
Machineresources on the cluster, rather than waiting for the Node to appear in the API server. - Decoupled Startup Scripts: Because scheduling state is managed via the Machine CRD, users can customize cloudprovider startup scripts (e.g., AWS
userData) without interfering with Karpenter's ability to reconcile labels, annotations, and taints. - Performance Impact: The impact on
kube-apiserverQPS andetcdstorage is expected to be negligible. The model performs approximately 2 write operations per created node, which is comparable to or less than the current Node-based reconciliation flow.
- Scheduling State Persistence: The Machine CRD stores scheduling decisions, including
Understand Karpenter preference handling
mainKarpenter treats pod preferences (node affinity, pod affinity, pod anti-affinity, and pod topology) as requirements when determining if a pod can schedule on a node or be shifted to a new node.
When constructing requirements, Karpenter initially treats preferred affinities as required. If these cannot be met, Karpenter relaxes the preferences one-at-a-time in ascending order of weight (lowest weight relaxed first) and retries.
Warning: Karpenter does not interpret preferred affinities as required when constructing topology requirements. If a preference is strictly necessary, you must use required affinities.
Understand the In-Memory Node Store solution for Node ownership
mainAn alternative solution for managing node ownership is to use an In-Memory Node Store that relies on the cloud provider's instance data rather than the Kubernetes API server for in-flight capacity.
Key behaviors of the In-Memory Store model:
- Capacity Tracking: In-flight capacity is tracked exclusively in Karpenter's in-memory state. The node is modeled as an "in-flight node" until the instance registers the real Node object on the API server.
- State Recovery: To handle restarts, the
ListMachines()method is added to the CloudProvider interface to re-populate Karpenter-owned instances onto the API server (similar to the Kubernetes List/Watch mechanism). - Node Management: Karpenter continues to operate directly on the Node object, including applying labels, annotations, status conditions, and finalizers.
- Metadata Management: Because
userDatadoes not support annotations, scheduling decisions (labels, taints, and annotations) must be stored in cloud provider tags.- Note: This approach is limited by cloud provider constraints on the number of allowed tags.
- Liveness & GC: Includes liveness checks to delete backing instances for nodes that fail to register within a TTL, and garbage collection for in-flight nodes that no longer have backing instances.
Understand Karpenter interaction with Kubernetes features
mainKarpenter interacts with core Kubernetes components as follows:
- Kubernetes Scheduler: Karpenter monitors for pods that the Kubernetes scheduler has marked as unschedulable and attempts to provision nodes to satisfy those requirements.
- Kubernetes Cluster Autoscaler: Karpenter can operate alongside the standard Cluster Autoscaler.
Understand Karpenter interaction with AWS node groups
mainKarpenter's
NodePoolsare designed to coexist with static capacity management solutions such as EKS Managed Node Groups and EC2 Auto Scaling Groups. Users can adopt several management models:- Full Karpenter management: Manage all capacity using
NodePools. - Mixed model: Use both dynamic (
NodePool-managed) and static (Managed Node Groups/ASGs) capacity. - Fully static approach: Rely entirely on existing static capacity management.
Most users are expected to use a mixed approach initially, transitioning to full
NodePoolmanagement over time.- Full Karpenter management: Manage all capacity using
Understand NodeClaims in Karpenter
mainNodeClaims are immutable, cluster-scoped resources used by Karpenter to manage the lifecycle of Kubernetes Nodes with the underlying cloud provider. They serve as requests for capacity and map one-to-one with a cloud provider instance and a Kubernetes Node.
Key characteristics of NodeClaims:
- Ownership: Each NodeClaim is owned by a single
NodePooland references a singleNodeClass. - Naming: Names are auto-generated by Karpenter using the
NodePoolname as a prefix. - Lifecycle: Karpenter creates and deletes NodeClaims in response to Pod demands (provisioning) or disruption needs. The lifecycle involves three main stages: launch (asking the cloud provider to create the instance), registration (linking the created node to the NodeClaim), and initialization (waiting for the node and its resources to be ready).
- Properties: Labels, annotations, and taints are propagated from the
NodePooltemplate. Karpenter also applies a termination finalizer to ensure proper cleanup.
- Ownership: Each NodeClaim is owned by a single