Skip to content

Commit 8a040e5

Browse files
jakeskyawsjakeskyawsEC2 Default Userchrismld
authored
Updates to match the Karpenter v1.0.1 release (#16)
* up to graviton deployed and tested * userdata outstanding * key intent, intent In [userdata] not in intent In [apps] * for review * simplified user data * disruption budget - to review * Updates to the Disruption Budgets blueprint * Updates to the Update Nodes with Drift blueprint * Updates Karpenter version * Updates to the Disruption Budgetrs blueprint --------- Co-authored-by: jakeskyaws <jakesky@amazon.com> Co-authored-by: EC2 Default User <ec2-user@ip-172-31-37-217.eu-west-1.compute.internal> Co-authored-by: Christian Melendez <chrismld@amazon.com>
1 parent 5c886fa commit 8a040e5

23 files changed

Lines changed: 380 additions & 262 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ You can see that the NodePool has been deployed by running this:
9090
kubectl get nodepool
9191
```
9292

93-
You can see that the EC2NodeClass has been deployed by running this:
93+
You can see that the `EC2NodeClass` has been deployed by running this:
9494

9595
```
9696
kubectl get ec2nodeclass
@@ -120,7 +120,7 @@ terraform destroy --auto-approve
120120

121121
## Deploying a Blueprint
122122

123-
After you have a cluster up and running with Karpenter installed, you can start testing each blueprint. A blueprint might have a NodePool, EC2NodeClass and a workload example. You need to open the blueprint folder and follow the steps to deploy the resources needed to test the blueprint.
123+
After you have a cluster up and running with Karpenter installed, you can start testing each blueprint. A blueprint might have a `NodePool`, `EC2NodeClass` and a workload example. You need to open the blueprint folder and follow the steps to deploy the resources needed to test the blueprint.
124124

125125
Here's the list of blueprints we have so far:
126126

@@ -146,10 +146,10 @@ The following table describes the list of resources along with the versions wher
146146
| Resources/Tool | Version |
147147
| --------------- | ------------------- |
148148
| [Kubernetes](https://kubernetes.io/releases/) | 1.30 |
149-
| [Karpenter](https://github.com/aws/karpenter/releases) | 0.37 |
150-
| [Terraform](https://github.com/hashicorp/terraform/releases) | 1.6.3 |
151-
| [AWS EKS](https://github.com/terraform-aws-modules/terraform-aws-eks/releases) | 20.8.3 |
152-
| [EKS Blueprints Addons](https://github.com/aws-ia/terraform-aws-eks-blueprints-addons/releases) | 1.16.10 |
149+
| [Karpenter](https://github.com/aws/karpenter/releases) | v1.0.1 |
150+
| [Terraform](https://github.com/hashicorp/terraform/releases) | 1.9.3 |
151+
| [AWS EKS](https://github.com/terraform-aws-modules/terraform-aws-eks/releases) | v20.23.0 |
152+
| [EKS Blueprints Addons](https://github.com/aws-ia/terraform-aws-eks-blueprints-addons/releases) | v1.16.3 |
153153

154154
## Feedback
155155

blueprints/batch-jobs/README.md

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Karpenter Blueprint: Protecting batch jobs during the consolidation process
1+
# Karpenter Blueprint: Protecting batch jobs during the disruption (consolidation) process
22

33
## Purpose
44
Karpenter can actively reduce the cluster cost by identifying when nodes can be removed or replaced because they are empty or there are a cheaper one available after some workload change. This process is called [consolidation](https://karpenter.sh/preview/concepts/disruption/#consolidation), and it implies the disruption of pods that are running in the node, if any, as they need to be rescheduled into another node. In some cases, like when running long batch jobs, you don't want those pods to be disrupted. You want to run them from start to finish without disruption, and replace or delete the node once they finish. To achieve that, you can set the `karpenter.sh/do-not-disrupt: "true"` annotation on the pod (more information [here](https://karpenter.sh/preview/concepts/disruption/#pod-level-controls)). By opting pods out of this disruption, you are telling Karpenter that it should not voluntarily remove a node containing this pod.
@@ -50,15 +50,15 @@ Now, the total number of vCPU required by the running pods are **4 vCPU**:
5050
- NGINX server - 2 vCPU required
5151
- 5-minutes job - 2 vCPU required
5252

53-
The default behaviour is the one defined in the NodePool: `consolidationPolicy: WhenUnderutilized`. Karpenter identifies the **c6g.4xlarge** (12 vCPU) is underutilized, and performs a consolidation replacement of the node. It launches a cheaper and smaller node: a **c6g.2xlarge** (8 vCPU) instance. You can check these logs by executing the following command in another terminal:
53+
The default behaviour is the one defined in the NodePool: `consolidationPolicy: WhenEmptyOrUnderutilized`. Karpenter identifies the **c6g.4xlarge** (12 vCPU) is underutilized, and performs a consolidation replacement of the node. It launches a cheaper and smaller node: a **c6g.2xlarge** (8 vCPU) instance. You can check these logs by executing the following command in another terminal:
5454
```
5555
kubectl -n karpenter logs -l app.kubernetes.io/name=karpenter --all-containers=true -f --tail=20
5656
```
5757
You should see these logs:
5858
```
59-
{"level":"INFO","time":"2024-01-10T15:06:37.063Z","logger":"controller.disruption","message":"disrupting via consolidation replace, terminating 1 candidates ip-10-0-93-19.eu-west-1.compute.internal/c6g.4xlarge/on-demand and replacing with on-demand node from types r6gd.2xlarge, c7i.2xlarge, r5a.2xlarge, m5a.2xlarge, m6a.2xlarge and 37 other(s)","commit":"1072d3b"}
59+
{"level":"INFO","time":"2024-08-16T10:03:46.529Z","logger":"controller","message":"disrupting nodeclaim(s) via replace, terminating 1 nodes (2 pods) ip-10-0-122-231.eu-west-2.compute.internal/c6g.4xlarge/on-demand and replacing with on-demand node from types c6g.2xlarge, c7g.2xlarge, m6g.2xlarge, c6a.2xlarge, c5a.2xlarge and 32 other(s)","commit":"5bdf9c3","controller":"disruption","namespace":"","name":"","reconcileID":"857f7bb5-a482-48e8-9c52-16a10823e2e4","command-id":"25beb85a-3020-4267-a525-5273e0afc7a7","reason":"underutilized"}
6060
...
61-
{"level":"INFO","time":"2024-01-10T15:06:39.390Z","logger":"controller.nodeclaim.lifecycle","message":"launched nodeclaim","commit":"1072d3b","nodeclaim":"default-98tsh","nodepool":"default","provider-id":"aws:///eu-west-1a/i-0f329cada644371ec","instance-type":"c6g.2xlarge","zone":"eu-west-1a","capacity-type":"on-demand","allocatable":{"cpu":"7810m","ephemeral-storage":"17Gi","memory":"14003Mi","pods":"58","vpc.amazonaws.com/pod-eni":"38"}}
61+
{"level":"INFO","time":"2024-08-16T10:03:48.591Z","logger":"controller","message":"launched nodeclaim","commit":"5bdf9c3","controller":"nodeclaim.lifecycle","controllerGroup":"karpenter.sh","controllerKind":"NodeClaim","NodeClaim":{"name":"default-r9nzz"},"namespace":"","name":"default-r9nzz","reconcileID":"def551ff-c16e-4b1c-a137-f79df8724ded","provider-id":"aws:///eu-west-2a/i-0cefe0bfe63f80b39","instance-type":"c6g.2xlarge","zone":"eu-west-2a","capacity-type":"on-demand","allocatable":{"cpu":"7910m","ephemeral-storage":"17Gi","memory":"14103Mi","pods":"58","vpc.amazonaws.com/pod-eni":"38"}}
6262
```
6363
The NGINX server and the 5-min job pods are rescheduled into the new c6g.2xlarge node, so **the job is restarted**, which will cause a disruption the job might not be prepared to handle like doing a checkpoint.
6464

@@ -106,9 +106,9 @@ You should see something similar to this, where a new node just appeared:
106106

107107
```
108108
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE
109-
ip-10-0-125-209.eu-west-1.compute.internal Ready <none> 16d v1.28.3-eks-e71965b m4.large
110-
ip-10-0-46-139.eu-west-1.compute.internal Ready <none> 16d v1.28.3-eks-e71965b m4.large
111-
ip-10-0-47-60.eu-west-1.compute.internal Ready <none> 44s v1.28.3-eks-e71965b c6g.4xlarge
109+
ip-10-0-125-209.eu-west-1.compute.internal Ready <none> 16d v1.30.2-eks-1552ad0 m4.large
110+
ip-10-0-46-139.eu-west-1.compute.internal Ready <none> 16d v1.30.2-eks-1552ad0 m4.large
111+
ip-10-0-47-60.eu-west-1.compute.internal Ready <none> 44s v1.30.2-eks-1552ad0 c6g.4xlarge
112112
```
113113

114114
Check the three new pods are running by executing:
@@ -135,13 +135,13 @@ kubectl -n karpenter logs -l app.kubernetes.io/name=karpenter --all-containers=t
135135
```
136136
You should see the following events indicating that Karpenter identified the need of a new node, and that it selected an instance type and purchase option:
137137
```
138-
{"level":"INFO","time":"2024-01-08T10:42:35.190Z","logger":"controller.provisioner","message":"found provisionable pod(s)","commit":"1072d3b","pods":"default/2-min-job-ml6qj, default/nginx-8467c776-bbl8w, default/5-min-job-9jc4b","duration":"89.747487ms"}
138+
{"level":"INFO","time":"2024-08-16T10:10:47.683Z","logger":"controller","message":"found provisionable pod(s)","commit":"5bdf9c3","controller":"provisioner","namespace":"","name":"","reconcileID":"d8e8907d-5b93-46bb-893a-63520f3ec12f","Pods":"default/2-min-job-czp5x","duration":"39.859328ms"}
139139
140-
{"level":"INFO","time":"2024-01-08T10:42:35.190Z","logger":"controller.provisioner","message":"computed new nodeclaim(s) to fit pod(s)","commit":"1072d3b","nodeclaims":1,"pods":3}
140+
{"level":"INFO","time":"2024-08-16T10:10:47.683Z","logger":"controller","message":"computed new nodeclaim(s) to fit pod(s)","commit":"5bdf9c3","controller":"provisioner","namespace":"","name":"","reconcileID":"d8e8907d-5b93-46bb-893a-63520f3ec12f","nodeclaims":1,"pods":1}
141141
142-
{"level":"INFO","time":"2024-01-08T10:42:35.224Z","logger":"controller.provisioner","message":"created nodeclaim","commit":"1072d3b","nodepool":"default","nodeclaim":"default-xzkfq","requests":{"cpu":"11260m","memory":"290Mi","pods":"8"},"instance-types":"c3.4xlarge, c3.8xlarge, c4.4xlarge, c5.4xlarge, c5a.12xlarge and 95 other(s)"}
142+
{"level":"INFO","time":"2024-08-16T10:10:47.699Z","logger":"controller","message":"created nodeclaim","commit":"5bdf9c3","controller":"provisioner","namespace":"","name":"","reconcileID":"d8e8907d-5b93-46bb-893a-63520f3ec12f","NodePool":{"name":"default"},"NodeClaim":{"name":"default-g4kgp"},"requests":{"cpu":"7260m","memory":"290Mi","pods":"6"},"instance-types":"c4.2xlarge, c5.2xlarge, c5.4xlarge, c5a.2xlarge, c5a.4xlarge and 55 other(s)"}
143143
...
144-
{"level":"INFO","time":"2024-01-08T10:42:37.686Z","logger":"controller.nodeclaim.lifecycle","message":"launched nodeclaim","commit":"1072d3b","nodeclaim":"default-xzkfq","nodepool":"default","provider-id":"aws:///eu-west-1a/i-044f1f028b733d18a","instance-type":"c6g.4xlarge","zone":"eu-west-1a","capacity-type":"on-demand","allocatable":{"cpu":"15790m","ephemeral-storage":"17Gi","memory":"27222Mi","pods":"234","vpc.amazonaws.com/pod-eni":"54"}}
144+
{"level":"INFO","time":"2024-08-16T10:10:49.959Z","logger":"controller","message":"launched nodeclaim","commit":"5bdf9c3","controller":"nodeclaim.lifecycle","controllerGroup":"karpenter.sh","controllerKind":"NodeClaim","NodeClaim":{"name":"default-g4kgp"},"namespace":"","name":"default-g4kgp","reconcileID":"ff5b7f6e-c52e-495e-94b1-3a30385c3439","provider-id":"aws:///eu-west-2a/i-022a05d79bceda579","instance-type":"c6g.2xlarge","zone":"eu-west-2a","capacity-type":"on-demand","allocatable":{"cpu":"7910m","ephemeral-storage":"17Gi","memory":"14103Mi","pods":"58","vpc.amazonaws.com/pod-eni":"38"}}
145145
146146
```
147147

@@ -172,7 +172,9 @@ Now, the total number of vCPU required by the running pods are **4 vCPU**:
172172
```
173173
$> kubectl describe node <node_name>
174174
...
175-
Normal DisruptionBlocked 36s karpenter Cannot disrupt Node: Pod "default/5-min-job-9jc4b" has do not evict annotation
175+
Normal NodeReady 6m7s kubelet Node ip-10-0-97-15.eu-west-1.compute.internal status is now: NodeReady
176+
Normal DisruptionBlocked 4m12s karpenter Cannot disrupt Node: pod "default/2-min-job-2fssd" has "karpenter.sh/do-not-disrupt" annotation
177+
Normal DisruptionBlocked 2m12s karpenter Cannot disrupt Node: pod "default/5-min-job-7pqdt" has "karpenter.sh/do-not-disrupt" annotation
176178
```
177179

178180
### Consolidation Replace allowed after last job finishes
@@ -184,21 +186,21 @@ NAME COMPLETIONS DURATION AGE
184186
```
185187
Now, **it is possible to replace the node** by a cheaper and smaller instance because the the NGINX server can be disrupted as it does't contain the `karpenter.sh/do-not-disrupt: "true"` annotation. You can check this in the Karpenter logs terminal:
186188
```
187-
{"level":"INFO","time":"2024-01-08T10:48:46.480Z","logger":"controller.disruption","message":"disrupting via consolidation replace, terminating 1 candidates ip-10-0-47-60.eu-west-1.compute.internal/c6g.4xlarge/on-demand and replacing with on-demand node from types c5n.2xlarge, r6a.2xlarge, m6a.2xlarge, m6id.xlarge, m7gd.xlarge and 103 other(s)","commit":"1072d3b"}
189+
{"level":"INFO","time":"2024-08-16T10:17:21.322Z","logger":"controller","message":"created nodeclaim","commit":"5bdf9c3","controller":"disruption","namespace":"","name":"","reconcileID":"1135db0e-45ef-4529-9492-63789a9837c6","NodePool":{"name":"default"},"NodeClaim":{"name":"default-9m4bv"},"requests":{"cpu":"2260m","memory":"290Mi","pods":"6"},"instance-types":"c4.xlarge, c5.xlarge, c5a.xlarge, c5d.xlarge, c5n.xlarge and 32 other(s)"}
188190
...
189-
{"level":"INFO","time":"2024-01-08T10:48:48.675Z","logger":"controller.nodeclaim.lifecycle","message":"launched nodeclaim","commit":"1072d3b","nodeclaim":"default-7qxqp","nodepool":"default","provider-id":"aws:///eu-west-1b/i-0933fc4784ff30008","instance-type":"c6g.xlarge","zone":"eu-west-1b","capacity-type":"on-demand","allocatable":{"cpu":"3820m","ephemeral-storage":"17Gi","memory":"6425Mi","pods":"58","vpc.amazonaws.com/pod-eni":"18"}}
191+
{"level":"INFO","time":"2024-08-16T10:17:23.452Z","logger":"controller","message":"launched nodeclaim","commit":"5bdf9c3","controller":"nodeclaim.lifecycle","controllerGroup":"karpenter.sh","controllerKind":"NodeClaim","NodeClaim":{"name":"default-9m4bv"},"namespace":"","name":"default-9m4bv","reconcileID":"f0e0cc47-45a9-479c-a1c7-b5f0f0341026","provider-id":"aws:///eu-west-2a/i-0a4fa068af5550afa","instance-type":"c6g.xlarge","zone":"eu-west-2a","capacity-type":"on-demand","allocatable":{"cpu":"3920m","ephemeral-storage":"17Gi","memory":"6525Mi","pods":"58","vpc.amazonaws.com/pod-eni":"18"}}
190192
...
191-
{"level":"INFO","time":"2024-01-08T10:49:30.787Z","logger":"controller.node.termination","message":"tainted node","commit":"1072d3b","node":"ip-10-0-47-60.eu-west-1.compute.internal"}
193+
{"level":"INFO","time":"2024-08-16T10:18:07.430Z","logger":"controller","message":"tainted node","commit":"5bdf9c3","controller":"node.termination","controllerGroup":"","controllerKind":"Node","Node":{"name":"ip-10-0-42-175.eu-west-2.compute.internal"},"namespace":"","name":"ip-10-0-42-175.eu-west-2.compute.internal","reconcileID":"a57044a6-f00f-41e5-a1ab-31e4b19dd838","taint.Key":"karpenter.sh/disrupted","taint.Value":"","taint.Effect":"NoSchedule"}
192194
193-
{"level":"INFO","time":"2024-01-08T10:49:39.385Z","logger":"controller.node.termination","message":"deleted node","commit":"1072d3b","node":"ip-10-0-47-60.eu-west-1.compute.internal"}
195+
{"level":"INFO","time":"2024-08-16T10:18:50.331Z","logger":"controller","message":"deleted node","commit":"5bdf9c3","controller":"node.termination","controllerGroup":"","controllerKind":"Node","Node":{"name":"ip-10-0-42-175.eu-west-2.compute.internal"},"namespace":"","name":"ip-10-0-42-175.eu-west-2.compute.internal","reconcileID":"2a51acf8-702f-4c75-988d-92052d690b01"}
194196
```
195197
Karpenter replaces the **c6g.4xlarge** (16 vCPU, 32 GiB) with a **c6g.xlarge** node (4 vCPU, 8 GiB), enough for the NGINX server:
196198
```
197199
$> kubectl get nodes --label-columns node.kubernetes.io/instance-type
198200
NAME STATUS ROLES AGE VERSION INSTANCE-TYPE
199-
ip-10-0-125-209.eu-west-1.compute.internal Ready <none> 17d v1.28.3-eks-e71965b m4.large
200-
ip-10-0-46-139.eu-west-1.compute.internal Ready <none> 17d v1.28.3-eks-e71965b m4.large
201-
ip-10-0-85-30.eu-west-1.compute.internal Ready <none> 26s v1.28.3-eks-e71965b c6g.xlarge
201+
ip-10-0-125-209.eu-west-1.compute.internal Ready <none> 17d v1.30.2-eks-1552ad0 m4.large
202+
ip-10-0-46-139.eu-west-1.compute.internal Ready <none> 17d v1.30.2-eks-1552ad0 m4.large
203+
ip-10-0-85-30.eu-west-1.compute.internal Ready <none> 26s v1.30.2-eks-1552ad0 c6g.xlarge
202204
```
203205
Finally, you can check the NGINX server pod has been re-scheduled into the new pod:
204206
```

blueprints/custom-ami/README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,30 @@ kubectl apply -f .
3131

3232
Here's the important configuration block within the spec of an [`EC2NodeClass`](https://karpenter.sh/preview/concepts/nodeclasses/#specamiselectorterms): **spec.amiSelectorTerms**
3333

34-
`spec.amiSelectorTerms` are used to configure custom AMIs for Karpenter to use, where the AMIs are discovered through ids, owners, name, and tags. This field is optional, and Karpenter will use the latest **EKS-optimized AMIs** for the `AMIFamily` if no `amiSelectorTerms` are specified. To select an AMI by name, use the `name` field in the selector term. To select an AMI by id, use the `id` field in the selector term. To ensure that AMIs are owned by the expected owner, use the `owner` field - you can use a combination of account aliases (e.g. self amazon, your-aws-account-name) and account IDs. If this is not set, it defaults to `self,amazon`.
34+
`amiSelectorTerms` are required and are used to configure AMIs for Karpenter to use. AMIs are discovered through alias, id, owner, name, and [tags](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html).
35+
36+
If amiSelectorTerms match more than one AMI, Karpenter will automatically determine which AMI best fits the workloads on the launched worker node under the following constraints:
37+
38+
- When launching nodes, Karpenter automatically determines which architecture a custom AMI is compatible with and will use images that match an instanceType’s requirements.
39+
- Unless using an alias, Karpenter cannot detect requirements other than architecture. If you need to specify different AMIs for different kind of nodes (e.g. accelerated GPU AMIs), you should use a separate EC2NodeClass.
40+
- If multiple AMIs are found that can be used, Karpenter will choose the latest one.
41+
- If no AMIs are found that can be used, then no nodes will be provisioned.
42+
43+
To select an AMI by name, use the `name` field in the selector term. To select an AMI by id, use the `id` field in the selector term. To ensure that AMIs are owned by the expected owner, use the `owner` field - you can use a combination of account aliases (e.g. self amazon, your-aws-account-name) and account IDs. If this is not set, it defaults to `self,amazon`.
3544

3645
> **Tip**
3746
> AMIs may be specified by any AWS tag, including Name. Selecting by tag
3847
> or by name using wildcards (*) is supported.
3948
4049
```
4150
amiSelectorTerms:
42-
- name: "*amazon-eks-node-1.27-*"
51+
- name: "*amazon-eks-node-1.30-*"
4352
owner: self
44-
- name: "*amazon-eks-node-1.27-*"
53+
- name: "*amazon-eks-node-1.30-*"
4554
owner: amazon
4655
```
4756

48-
***IMPORTANT NOTE:*** With this configuration, you're saying that you need to use the latest AMI available for an EKS cluster v1.27 which is either owned by you (customized) or Amazon (official image). We're using a regular expression to have the flexibility to use AMIs for either `x86` or `Arm`, workloads that need GPUs, or a nodes with different OS like `Windows`. You're basically letting the workload (pod) to decide which type of node(s) it needs. If you don't have a custom AMI created by you in your account, Karpenter will use the official EKS AMI owned by Amazon.
57+
***IMPORTANT NOTE:*** With this configuration, you're saying that you need to use the latest AMI available for an EKS cluster v1.30 which is either owned by you (customized) or Amazon (official image). We're using a regular expression to have the flexibility to use AMIs for either `x86` or `Arm`, workloads that need GPUs, or a nodes with different OS like `Windows`. You're basically letting the workload (pod) to decide which type of node(s) it needs. If you don't have a custom AMI created by you in your account, Karpenter will use the official EKS AMI owned by Amazon.
4958

5059
## Results
5160
After waiting for about one minute, you should see a machine ready, and all pods in a `Running` state, like this:
@@ -57,8 +66,8 @@ custom-ami-bdf66b777-2g27q 1/1 Running 0 2m2s
5766
custom-ami-bdf66b777-dbkls 1/1 Running 0 2m2s
5867
custom-ami-bdf66b777-rzlsz 1/1 Running 0 2m2s
5968
❯ kubectl get nodeclaims
60-
NAME TYPE ZONE NODE READY AGE
61-
custom-ami-2ht8b m5.large eu-west-1c ip-10-0-103-91.eu-west-1.compute.internal True 1m36s
69+
NAME TYPE CAPACITY ZONE NODE READY AGE
70+
custom-ami-jhdbh c5a.large spot eu-west-2c ip-10-0-117-230.eu-west-2.compute.internal True 114s
6271
```
6372

6473
## Cleanup

0 commit comments

Comments
 (0)