Skip to content

Commit cfcaf27

Browse files
feat: Add support for Pod Identity assocation on Karpenter sub-module (#3031)
Co-authored-by: Bryant Biggs <[email protected]>
1 parent f3299cc commit cfcaf27

File tree

6 files changed

+127
-63
lines changed

6 files changed

+127
-63
lines changed

examples/karpenter/README.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Karpenter Example
22

3-
Configuration in this directory creates an AWS EKS cluster with [Karpenter](https://karpenter.sh/) provisioned for managing compute resource scaling. In the example provided, Karpenter is running on EKS Fargate yet Karpenter is providing compute in the form of EC2 instances.
3+
Configuration in this directory creates an AWS EKS cluster with [Karpenter](https://karpenter.sh/) provisioned for managing compute resource scaling. In the example provided, Karpenter is provisioned on top of an EKS Managed Node Group.
44

55
## Usage
66

@@ -22,10 +22,47 @@ aws eks --region eu-west-1 update-kubeconfig --name ex-karpenter
2222
kubectl scale deployment inflate --replicas 5
2323

2424
# You can watch Karpenter's controller logs with
25-
kubectl logs -f -n karpenter -l app.kubernetes.io/name=karpenter -c controller
25+
kubectl logs -f -n kube-system -l app.kubernetes.io/name=karpenter -c controller
2626
```
2727

28-
You should see a new node named `karpenter.sh/provisioner-name/default` eventually come up in the console; this was provisioned by Karpenter in response to the scaled deployment above.
28+
Validate if the Amazon EKS Addons Pods are running in the Managed Node Group and the `inflate` application Pods are running on Karpenter provisioned Nodes.
29+
30+
```bash
31+
kubectl get nodes -L karpenter.sh/registered
32+
```
33+
34+
```text
35+
NAME STATUS ROLES AGE VERSION REGISTERED
36+
ip-10-0-16-155.eu-west-1.compute.internal Ready <none> 100s v1.29.3-eks-ae9a62a true
37+
ip-10-0-3-23.eu-west-1.compute.internal Ready <none> 6m1s v1.29.3-eks-ae9a62a
38+
ip-10-0-41-2.eu-west-1.compute.internal Ready <none> 6m3s v1.29.3-eks-ae9a62a
39+
```
40+
41+
```sh
42+
kubectl get pods -A -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName
43+
```
44+
45+
```text
46+
NAME NODE
47+
inflate-75d744d4c6-nqwz8 ip-10-0-16-155.eu-west-1.compute.internal
48+
inflate-75d744d4c6-nrqnn ip-10-0-16-155.eu-west-1.compute.internal
49+
inflate-75d744d4c6-sp4dx ip-10-0-16-155.eu-west-1.compute.internal
50+
inflate-75d744d4c6-xqzd9 ip-10-0-16-155.eu-west-1.compute.internal
51+
inflate-75d744d4c6-xr6p5 ip-10-0-16-155.eu-west-1.compute.internal
52+
aws-node-mnn7r ip-10-0-3-23.eu-west-1.compute.internal
53+
aws-node-rkmvm ip-10-0-16-155.eu-west-1.compute.internal
54+
aws-node-s4slh ip-10-0-41-2.eu-west-1.compute.internal
55+
coredns-68bd859788-7rcfq ip-10-0-3-23.eu-west-1.compute.internal
56+
coredns-68bd859788-l78hw ip-10-0-41-2.eu-west-1.compute.internal
57+
eks-pod-identity-agent-gbx8l ip-10-0-41-2.eu-west-1.compute.internal
58+
eks-pod-identity-agent-s7vt7 ip-10-0-16-155.eu-west-1.compute.internal
59+
eks-pod-identity-agent-xwgqw ip-10-0-3-23.eu-west-1.compute.internal
60+
karpenter-79f59bdfdc-9q5ff ip-10-0-41-2.eu-west-1.compute.internal
61+
karpenter-79f59bdfdc-cxvhr ip-10-0-3-23.eu-west-1.compute.internal
62+
kube-proxy-7crbl ip-10-0-41-2.eu-west-1.compute.internal
63+
kube-proxy-jtzds ip-10-0-16-155.eu-west-1.compute.internal
64+
kube-proxy-sm42c ip-10-0-3-23.eu-west-1.compute.internal
65+
```
2966

3067
### Tear Down & Clean-Up
3168

examples/karpenter/main.tf

Lines changed: 29 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ data "aws_ecrpublic_authorization_token" "token" {
4141
}
4242

4343
locals {
44-
name = "ex-${replace(basename(path.cwd), "_", "-")}"
45-
cluster_version = "1.29"
46-
region = "eu-west-1"
44+
name = "ex-${basename(path.cwd)}"
45+
region = "eu-west-1"
4746

4847
vpc_cidr = "10.0.0.0/16"
4948
azs = slice(data.aws_availability_zones.available.names, 0, 3)
@@ -62,62 +61,42 @@ locals {
6261
module "eks" {
6362
source = "../.."
6463

65-
cluster_name = local.name
66-
cluster_version = local.cluster_version
67-
cluster_endpoint_public_access = true
64+
cluster_name = local.name
65+
cluster_version = "1.29"
6866

6967
# Gives Terraform identity admin access to cluster which will
7068
# allow deploying resources (Karpenter) into the cluster
7169
enable_cluster_creator_admin_permissions = true
70+
cluster_endpoint_public_access = true
7271

7372
cluster_addons = {
74-
coredns = {
75-
configuration_values = jsonencode({
76-
computeType = "Fargate"
77-
# Ensure that we fully utilize the minimum amount of resources that are supplied by
78-
# Fargate https://docs.aws.amazon.com/eks/latest/userguide/fargate-pod-configuration.html
79-
# Fargate adds 256 MB to each pod's memory reservation for the required Kubernetes
80-
# components (kubelet, kube-proxy, and containerd). Fargate rounds up to the following
81-
# compute configuration that most closely matches the sum of vCPU and memory requests in
82-
# order to ensure pods always have the resources that they need to run.
83-
resources = {
84-
limits = {
85-
cpu = "0.25"
86-
# We are targeting the smallest Task size of 512Mb, so we subtract 256Mb from the
87-
# request/limit to ensure we can fit within that task
88-
memory = "256M"
89-
}
90-
requests = {
91-
cpu = "0.25"
92-
# We are targeting the smallest Task size of 512Mb, so we subtract 256Mb from the
93-
# request/limit to ensure we can fit within that task
94-
memory = "256M"
95-
}
96-
}
97-
})
98-
}
99-
kube-proxy = {}
100-
vpc-cni = {}
73+
coredns = {}
74+
eks-pod-identity-agent = {}
75+
kube-proxy = {}
76+
vpc-cni = {}
10177
}
10278

10379
vpc_id = module.vpc.vpc_id
10480
subnet_ids = module.vpc.private_subnets
10581
control_plane_subnet_ids = module.vpc.intra_subnets
10682

107-
# Fargate profiles use the cluster primary security group so these are not utilized
108-
create_cluster_security_group = false
109-
create_node_security_group = false
110-
111-
fargate_profiles = {
83+
eks_managed_node_groups = {
11284
karpenter = {
113-
selectors = [
114-
{ namespace = "karpenter" }
115-
]
116-
}
117-
kube-system = {
118-
selectors = [
119-
{ namespace = "kube-system" }
120-
]
85+
instance_types = ["m5.large"]
86+
87+
min_size = 2
88+
max_size = 3
89+
desired_size = 2
90+
91+
taints = {
92+
# This Taint aims to keep just EKS Addons and Karpenter running on this MNG
93+
# The pods that do not tolerate this taint should run on nodes created by Karpenter
94+
addons = {
95+
key = "CriticalAddonsOnly"
96+
value = "true"
97+
effect = "NO_SCHEDULE"
98+
},
99+
}
121100
}
122101
}
123102

@@ -138,9 +117,8 @@ module "karpenter" {
138117

139118
cluster_name = module.eks.cluster_name
140119

141-
# EKS Fargate currently does not support Pod Identity
142-
enable_irsa = true
143-
irsa_oidc_provider_arn = module.eks.oidc_provider_arn
120+
enable_pod_identity = true
121+
create_pod_identity_association = true
144122

145123
# Used to attach additional IAM policies to the Karpenter node IAM role
146124
node_iam_role_additional_policies = {
@@ -162,14 +140,13 @@ module "karpenter_disabled" {
162140
################################################################################
163141

164142
resource "helm_release" "karpenter" {
165-
namespace = "karpenter"
166-
create_namespace = true
143+
namespace = "kube-system"
167144
name = "karpenter"
168145
repository = "oci://public.ecr.aws/karpenter"
169146
repository_username = data.aws_ecrpublic_authorization_token.token.user_name
170147
repository_password = data.aws_ecrpublic_authorization_token.token.password
171148
chart = "karpenter"
172-
version = "0.35.1"
149+
version = "0.36.1"
173150
wait = false
174151

175152
values = [
@@ -178,14 +155,6 @@ resource "helm_release" "karpenter" {
178155
clusterName: ${module.eks.cluster_name}
179156
clusterEndpoint: ${module.eks.cluster_endpoint}
180157
interruptionQueue: ${module.karpenter.queue_name}
181-
serviceAccount:
182-
annotations:
183-
eks.amazonaws.com/role-arn: ${module.karpenter.iam_role_arn}
184-
tolerations:
185-
- key: 'eks.amazonaws.com/compute-type'
186-
operator: Equal
187-
value: fargate
188-
effect: "NoSchedule"
189158
EOT
190159
]
191160
}

modules/karpenter/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Configuration in this directory creates the AWS resources required by Karpenter
88

99
In the following example, the Karpenter module will create:
1010
- An IAM role for use with Pod Identity and a scoped IAM policy for the Karpenter controller
11+
- A Pod Identity association to grant Karpenter controller access provided by the IAM Role
1112
- A Node IAM role that Karpenter will use to create an Instance Profile for the nodes to receive IAM permissions
1213
- An access entry for the Node IAM role to allow nodes to join the cluster
1314
- SQS queue and EventBridge event rules for Karpenter to utilize for spot termination handling, capacity re-balancing, etc.
@@ -104,6 +105,7 @@ No modules.
104105
| [aws_cloudwatch_event_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
105106
| [aws_cloudwatch_event_target.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
106107
| [aws_eks_access_entry.node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_access_entry) | resource |
108+
| [aws_eks_pod_identity_association.karpenter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_pod_identity_association) | resource |
107109
| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
108110
| [aws_iam_policy.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
109111
| [aws_iam_role.controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
@@ -135,6 +137,7 @@ No modules.
135137
| <a name="input_create_iam_role"></a> [create\_iam\_role](#input\_create\_iam\_role) | Determines whether an IAM role is created | `bool` | `true` | no |
136138
| <a name="input_create_instance_profile"></a> [create\_instance\_profile](#input\_create\_instance\_profile) | Whether to create an IAM instance profile | `bool` | `false` | no |
137139
| <a name="input_create_node_iam_role"></a> [create\_node\_iam\_role](#input\_create\_node\_iam\_role) | Determines whether an IAM role is created or to use an existing IAM role | `bool` | `true` | no |
140+
| <a name="input_create_pod_identity_association"></a> [create\_pod\_identity\_association](#input\_create\_pod\_identity\_association) | Determines whether to create pod identity association | `bool` | `false` | no |
138141
| <a name="input_enable_irsa"></a> [enable\_irsa](#input\_enable\_irsa) | Determines whether to enable support for IAM role for service accounts | `bool` | `false` | no |
139142
| <a name="input_enable_pod_identity"></a> [enable\_pod\_identity](#input\_enable\_pod\_identity) | Determines whether to enable support for EKS pod identity | `bool` | `true` | no |
140143
| <a name="input_enable_spot_termination"></a> [enable\_spot\_termination](#input\_enable\_spot\_termination) | Determines whether to enable native spot termination handling | `bool` | `true` | no |
@@ -153,6 +156,7 @@ No modules.
153156
| <a name="input_irsa_assume_role_condition_test"></a> [irsa\_assume\_role\_condition\_test](#input\_irsa\_assume\_role\_condition\_test) | Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role | `string` | `"StringEquals"` | no |
154157
| <a name="input_irsa_namespace_service_accounts"></a> [irsa\_namespace\_service\_accounts](#input\_irsa\_namespace\_service\_accounts) | List of `namespace:serviceaccount`pairs to use in trust policy for IAM role for service accounts | `list(string)` | <pre>[<br> "karpenter:karpenter"<br>]</pre> | no |
155158
| <a name="input_irsa_oidc_provider_arn"></a> [irsa\_oidc\_provider\_arn](#input\_irsa\_oidc\_provider\_arn) | OIDC provider arn used in trust policy for IAM role for service accounts | `string` | `""` | no |
159+
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Namespace to associate with the Karpenter Pod Identity | `string` | `"kube-system"` | no |
156160
| <a name="input_node_iam_role_additional_policies"></a> [node\_iam\_role\_additional\_policies](#input\_node\_iam\_role\_additional\_policies) | Additional policies to be added to the IAM role | `map(string)` | `{}` | no |
157161
| <a name="input_node_iam_role_arn"></a> [node\_iam\_role\_arn](#input\_node\_iam\_role\_arn) | Existing IAM role ARN for the IAM instance profile. Required if `create_iam_role` is set to `false` | `string` | `null` | no |
158162
| <a name="input_node_iam_role_attach_cni_policy"></a> [node\_iam\_role\_attach\_cni\_policy](#input\_node\_iam\_role\_attach\_cni\_policy) | Whether to attach the `AmazonEKS_CNI_Policy`/`AmazonEKS_CNI_IPv6_Policy` IAM policy to the IAM IAM role. WARNING: If set `false` the permissions must be assigned to the `aws-node` DaemonSet pods via another method or nodes will not be able to join the cluster | `bool` | `true` | no |
@@ -168,6 +172,7 @@ No modules.
168172
| <a name="input_queue_managed_sse_enabled"></a> [queue\_managed\_sse\_enabled](#input\_queue\_managed\_sse\_enabled) | Boolean to enable server-side encryption (SSE) of message content with SQS-owned encryption keys | `bool` | `true` | no |
169173
| <a name="input_queue_name"></a> [queue\_name](#input\_queue\_name) | Name of the SQS queue | `string` | `null` | no |
170174
| <a name="input_rule_name_prefix"></a> [rule\_name\_prefix](#input\_rule\_name\_prefix) | Prefix used for all event bridge rules | `string` | `"Karpenter"` | no |
175+
| <a name="input_service_account"></a> [service\_account](#input\_service\_account) | Service account to associate with the Karpenter Pod Identity | `string` | `"karpenter"` | no |
171176
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
172177

173178
## Outputs
@@ -182,11 +187,13 @@ No modules.
182187
| <a name="output_instance_profile_id"></a> [instance\_profile\_id](#output\_instance\_profile\_id) | Instance profile's ID |
183188
| <a name="output_instance_profile_name"></a> [instance\_profile\_name](#output\_instance\_profile\_name) | Name of the instance profile |
184189
| <a name="output_instance_profile_unique"></a> [instance\_profile\_unique](#output\_instance\_profile\_unique) | Stable and unique string identifying the IAM instance profile |
190+
| <a name="output_namespace"></a> [namespace](#output\_namespace) | Namespace associated with the Karpenter Pod Identity |
185191
| <a name="output_node_access_entry_arn"></a> [node\_access\_entry\_arn](#output\_node\_access\_entry\_arn) | Amazon Resource Name (ARN) of the node Access Entry |
186192
| <a name="output_node_iam_role_arn"></a> [node\_iam\_role\_arn](#output\_node\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the node IAM role |
187193
| <a name="output_node_iam_role_name"></a> [node\_iam\_role\_name](#output\_node\_iam\_role\_name) | The name of the node IAM role |
188194
| <a name="output_node_iam_role_unique_id"></a> [node\_iam\_role\_unique\_id](#output\_node\_iam\_role\_unique\_id) | Stable and unique string identifying the node IAM role |
189195
| <a name="output_queue_arn"></a> [queue\_arn](#output\_queue\_arn) | The ARN of the SQS queue |
190196
| <a name="output_queue_name"></a> [queue\_name](#output\_queue\_name) | The name of the created Amazon SQS queue |
191197
| <a name="output_queue_url"></a> [queue\_url](#output\_queue\_url) | The URL for the created Amazon SQS queue |
198+
| <a name="output_service_account"></a> [service\_account](#output\_service\_account) | Service Account associated with the Karpenter Pod Identity |
192199
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

modules/karpenter/main.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,21 @@ resource "aws_iam_role_policy_attachment" "controller_additional" {
411411
policy_arn = each.value
412412
}
413413

414+
################################################################################
415+
# Pod Identity Association
416+
################################################################################
417+
418+
resource "aws_eks_pod_identity_association" "karpenter" {
419+
count = local.create_iam_role && var.enable_pod_identity && var.create_pod_identity_association ? 1 : 0
420+
421+
cluster_name = var.cluster_name
422+
namespace = var.namespace
423+
service_account = var.service_account
424+
role_arn = aws_iam_role.controller[0].arn
425+
426+
tags = var.tags
427+
}
428+
414429
################################################################################
415430
# Node Termination Queue
416431
################################################################################

modules/karpenter/outputs.tf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,17 @@ output "instance_profile_unique" {
9696
description = "Stable and unique string identifying the IAM instance profile"
9797
value = try(aws_iam_instance_profile.this[0].unique_id, null)
9898
}
99+
100+
################################################################################
101+
# Pod Identity
102+
################################################################################
103+
104+
output "namespace" {
105+
description = "Namespace associated with the Karpenter Pod Identity"
106+
value = var.namespace
107+
}
108+
109+
output "service_account" {
110+
description = "Service Account associated with the Karpenter Pod Identity"
111+
value = var.service_account
112+
}

modules/karpenter/variables.tf

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,28 @@ variable "irsa_assume_role_condition_test" {
138138
default = "StringEquals"
139139
}
140140

141+
################################################################################
142+
# Pod Identity Association
143+
################################################################################
144+
# TODO - Change default to `true` at next breaking change
145+
variable "create_pod_identity_association" {
146+
description = "Determines whether to create pod identity association"
147+
type = bool
148+
default = false
149+
}
150+
151+
variable "namespace" {
152+
description = "Namespace to associate with the Karpenter Pod Identity"
153+
type = string
154+
default = "kube-system"
155+
}
156+
157+
variable "service_account" {
158+
description = "Service account to associate with the Karpenter Pod Identity"
159+
type = string
160+
default = "karpenter"
161+
}
162+
141163
################################################################################
142164
# Node Termination Queue
143165
################################################################################

0 commit comments

Comments
 (0)