Версія v21.0.0 додала підтримку AWS Provider Version 6
Документація – тут>>>.
З основних змін в модулі AWS EKS – це заміна IRSA на EKS Pod Identity для Karpenter sub-module:
Native support for IAM roles for service accounts (IRSA) has been removed; EKS Pod Identity is now enabled by default
Плюс “The `aws-auth` sub-module has been removed” – але особисто я давно вже його випиляв.
Також були перейменовані деякі змінні.
Про апгрейд 19 версії на 20 писав в Terraform: EKS та Karpenter – upgrade версії модуля з 19.21 на 20.0, і цього разу підемо тим жеж шляхом – міняємо версії модулів, і дивимось, що зламається.
В мене для цього є окремий “Testing” environment, який я викатую спочатку з поточними версіями модулів/провайдерів, потім оновлюю код, деплою апгрейд, і коли все пофікшено – то вже роблю апгрейд EKS Production (бо у нас один кластер на dev/staging/prod).
В Helm-чарті самого Karpenter наче без особливих змін, хоча вже вийшла версія 1.6 – можна заодно теж оновити, але це вже іншим разом.
В цілому апгрейд пройшов без пригод, але були два моменти, де довелось подебажити – це проблема з EC2 metadata для AWS Load Balancer Controller під час апгрейду, та з EKS Add-ons при створенні нового кластеру з AWS EKS Terraform module v21.x.
Зміст
Upgrade AWS EKS Terraform module
Upgrade AWS Provider Version 6
Першим міняємо версію AWS Provider – нарешті, бо відкриті пул-реквести від Renovate муляли очі, а закрити не міг.
Тут все просто – міняємо версію на 6:
... required_providers { aws = { source = "hashicorp/aws" version = "~> 6.0" } } ...
Використовуємо pessimistic constraint operator – дозволяємо апгрейди всіх мінорних версій.
Це буде враховуватись як Renovate, так і під час виконання terraform init -upgrade
.
Upgrade terraform-aws-modules/eks/aws
Апгрейдимо версію модуля EKS – міняємо 20 на 21, теж з “~>
“:
... module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> v21.0" ...
І Karpenter теж, в мене він окремим модулем:
module "karpenter" { source = "terraform-aws-modules/eks/aws//modules/karpenter" version = "~> v21.0" ...
Робимо terraform init
і ловимо “does not match configured version constraint” – писав в Terraform: “no available releases match the given constraints:
$ terraform init ... registry.terraform.io/hashicorp/aws 5.100.0 does not match configured version constraint >= 4.0.0, >= 4.36.0, >= 4.47.0, >= 5.0.0, ~> 5.14, >= 6.0.0 ...
Бо в .terraform.lock.hcl
все ще стара версія провайдеру AWS:
$ cat envs/test-1-33/.terraform.lock.hcl | grep -A 5 5.100 version = "5.100.0" constraints = ">= 4.0.0, >= 4.33.0, >= 4.36.0, >= 4.47.0, >= 5.0.0, ~> 5.14, >= 5.95.0"
Можна дропнути файл і зробити terraform init
ще раз, можна зробити terraform init -upgrade
аби відразу підтягнути всі апгрейди:
$ terraform init -upgrade
Перевіряємо .terraform.lock.hcl
ще раз – тепер все ОК:
$ git diff .terraform.lock.hcl diff --git a/terraform/envs/test-1-33/.terraform.lock.hcl b/terraform/envs/test-1-33/.terraform.lock.hcl index bd44714..cb2eace 100644 --- a/terraform/envs/test-1-33/.terraform.lock.hcl +++ b/terraform/envs/test-1-33/.terraform.lock.hcl @@ -24,98 +24,85 @@ provider "registry.terraform.io/alekc/kubectl" { } provider "registry.terraform.io/hashicorp/aws" { - version = "5.100.0" - constraints = ">= 4.0.0, >= 4.33.0, >= 4.36.0, >= 4.47.0, >= 5.0.0, ~> 5.14, >= 5.95.0" + version = "6.7.0" + constraints = ">= 4.0.0, >= 4.36.0, >= 4.47.0, >= 5.0.0, >= 6.0.0, ~> 6.0" hashes = [ ...
Поїхали робити terraform plan
і дивитись що буде “ламатись”.
Renamed variables в terraform-aws-modules/eks/aws
Першим, очікувано, помилки про відсутні змінні, бо вони були перейменовані в модулі:
$ terraform plan -var-file=test-1-33.tfvars ... │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/eks.tf line 34, in module "eks": │ 34: cluster_name = "${var.env_name}-cluster" │ │ An argument named "cluster_name" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/eks.tf line 38, in module "eks": │ 38: cluster_version = var.eks_version │ │ An argument named "cluster_version" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/eks.tf line 42, in module "eks": │ 42: cluster_endpoint_public_access = var.eks_params.cluster_endpoint_public_access │ │ An argument named "cluster_endpoint_public_access" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/eks.tf line 46, in module "eks": │ 46: cluster_enabled_log_types = var.eks_params.cluster_enabled_log_types │ │ An argument named "cluster_enabled_log_types" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/eks.tf line 50, in module "eks": │ 50: cluster_addons = { │ │ An argument named "cluster_addons" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/eks.tf line 148, in module "eks": │ 148: cluster_security_group_name = "${var.env_name}-cluster-sg" │ │ An argument named "cluster_security_group_name" is not expected here. ...
Йдемо в документацію по апгрейду – і по одній знаходимо як тепер називаються змінні:
cluster_name
=>name
cluster_version
=>kubernetes_version
cluster_endpoint_public_access
=>endpoint_public_access
cluster_enabled_log_types
=>enabled_log_types
cluster_addons
->addons
cluster_security_group_name
->security_group_name
Хоча, як на мене – то з префіксом cluster_*
було краще, бо у нас є node_security_group_name
, і була cluster_security_group_name
– чітко видно який параметр для чого.
А тепер є node_security_group_name
і “якась” security_group_name
.
Removed variables в terraform-aws-modules/eks/aws//modules/karpenter
ОК – редагуємо імена змінних в коді основного модулю, виконуємо terraform plan
ще раз – тепер маємо помилки по змінам в модулі karpenter:
... Error: Unsupported argument │ │ on ../../modules/atlas-eks/karpenter.tf line 7, in module "karpenter": │ 7: irsa_oidc_provider_arn = module.eks.oidc_provider_arn │ │ An argument named "irsa_oidc_provider_arn" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/karpenter.tf line 8, in module "karpenter": │ 8: irsa_namespace_service_accounts = ["karpenter:karpenter"] │ │ An argument named "irsa_namespace_service_accounts" is not expected here. ╵ ╷ │ Error: Unsupported argument │ │ on ../../modules/atlas-eks/karpenter.tf line 14, in module "karpenter": │ 14: enable_irsa = true │ │ An argument named "enable_irsa" is not expected here. ...
Вони були видалені, бо більше немає IRSA – тепер для Karpenter буде створено EKS Pod Identity, див. main.tf#L92
.
Про EKS Pod Indetity писав в AWS: EKS Pod Identities – заміна IRSA? Спрощуємо менеджмент IAM доступів і в Terraform: менеджмент EKS Access Entries та EKS Pod Identities.
Прибираємо їх:
... #irsa_oidc_provider_arn = module.eks.oidc_provider_arn #irsa_namespace_service_accounts = ["karpenter:karpenter"] #enable_irsa = true ...
Запускаємо terraform plan
ще раз.
Important: Karpenter’s EKS Identity Provider Namespace
І ось тут важливий момент:
... # module.atlas_eks.module.karpenter.aws_eks_pod_identity_association.karpenter[0] will be created + resource "aws_eks_pod_identity_association" "karpenter" { ... + namespace = "kube-system" + region = "us-east-1" + role_arn = "arn:aws:iam::492***148:role/KarpenterIRSA-atlas-eks-test-1-33-cluster" + service_account = "karpenter" ...
eks_pod_identity_association
буде створено для Kubernetes Namespace "kube-system"
.
Якщо Karpenter в іншому неймспейсі – то треба вказати його явно при виклику модуля:
... module "karpenter" { source = "terraform-aws-modules/eks/aws//modules/karpenter" version = "~> v21.0" cluster_name = module.eks.cluster_name namespace = "karpenter" ...
Бо інакше Karpenter “відвалиться”, і апгрейд WorkerNode Group сфейлиться – бо нода буде чекати на под Karpenter, а він буде в CrashLoopbackoff
і апгрейд групи сфейлиться.
eks_managed_node_groups
: attribute “taints”: map of object required
Тепер помилка з тегами нод-групи:
... │ The given value is not suitable for module.atlas_eks.module.eks.var.eks_managed_node_groups declared at .terraform/modules/atlas_eks.eks/variables.tf:1205,1-35: element "test-1-33-default": attribute "taints": map of object required. ...
Чому – бо:
Variable definitions now contain detailed object types in place of the previously used any type.
Див. diff 20 vs 21:
Тобто тепер це має бути map(object)
:
... type = map(object({ key = string value = optional(string) effect = string })) ...
А в мене taints
зараз передаються зі змінної з об’єктом set(map(string))
:
... variable "eks_managed_node_group_params" { description = "EKS Managed NodeGroups setting, one item in the map() per each dedicated NodeGroup" type = map(object({ min_size = number max_size = number desired_size = number instance_types = list(string) capacity_type = string taints = set(map(string)) max_unavailable_percentage = number })) } ...
З такими значеннями:
... eks_managed_node_group_params = { default_group = { min_size = 1 max_size = 1 desired_size = 1 instance_types = ["t3.medium"] capacity_type = "ON_DEMAND" taints = [ { key = "CriticalAddonsOnly" value = "true" effect = "NO_SCHEDULE" }, { key = "CriticalAddonsOnly" value = "true" effect = "NO_EXECUTE" } ] max_unavailable_percentage = 100 } } ...
Тож що треба зробити – це змінити declaration змінної в мене:
... variable "eks_managed_node_group_params" { description = "EKS Managed NodeGroups setting, one item in the map() per each dedicated NodeGroup" type = map(object({ min_size = number max_size = number desired_size = number instance_types = list(string) capacity_type = string #taints = set(map(string)) taints = optional(map(object({ key = string value = optional(string) effect = string }))) max_unavailable_percentage = number })) } ...
І оновити значення – додати ключі для map{}
:
... eks_managed_node_group_params = { default_group = { min_size = 1 max_size = 1 desired_size = 1 instance_types = ["t3.medium"] capacity_type = "ON_DEMAND" # taints = [ # { # key = "CriticalAddonsOnly" # value = "true" # effect = "NO_SCHEDULE" # }, # { # key = "CriticalAddonsOnly" # value = "true" # effect = "NO_EXECUTE" # } # ] taints = { critical_no_sched = { key = "CriticalAddonsOnly" value = "true" effect = "NO_SCHEDULE" }, critical_no_exec = { key = "CriticalAddonsOnly" value = "true" effect = "NO_EXECUTE" } } max_unavailable_percentage = 100 } } ...
Виконуємо terraform plan
ще раз – і тепер все проходить без помилок.
Деплоїмо апдейти.
Deploying changes
Виконуємо terraform apply
, і ось де маємо новий ресурс з EKS Pod Identity Association для Karpenter – module.atlas_eks.module.karpenter.aws_eks_pod_identity_association.karpenter
:
В старому кластері цього нема.
ALB Controller error: “failed to fetch VPC ID from instance metadata”
Ще виникла проблема з AWS Load Balancer Controller, бо після апгрейду він не зміг звернутись до IMDS, мабуть через переключення на v2, див. AWS: Instance Metadata Service v1 vs IMDS v2 та робота з Kubernetes Pod і Docker контейнерів:
... {"level":"error","ts":"2025-08-06T07:25:40Z","logger":"setup","msg":"unable to initialize AWS cloud","error":"failed to get VPC ID: failed to fetch VPC ID from instance metadata: error in fetching vpc id through ec2 metadata: get mac metadata: operation error ec2imds: GetMetadata, canceled, context deadline exceeded"} ...
Власне, можна не морочити собі голову і просто передати параметри явно, див. документацію Using the Amazon EC2 instance metadata server version 2 (IMDSv2).
Зверніть увагу на --aws-vpc-tag-key
:
optional flag –aws-vpc-tag-key if you have a different key for the tag other than “Name”
Спочатку спробуємо задати параметри руками, аби перевірити що воно працює:
Все завелось.
Тепер параметри для Helm-чарту, див його values.yaml#L163 – в мене контролери встановлюються з aws-ia/eks-blueprints-addons/aws в Terraform під час створення кластеру, задаємо тут:
... values = [ <<-EOT replicaCount: 1 region: ${var.aws_region} vpcId: ${var.vpc_id} tolerations: - key: CriticalAddonsOnly operator: Exists EOT ] ...
Запускаємо деплой:
Все працює.
Node Group Status CREATE_FAILED
Тут опишу проблему, яка виникала тільки при створенні нового EKS кластеру з модулем v21 – апгрейд існуючого проходить без цих складностей.
Власне, в чому ця проблема полягає: кластер створився, все наче ОК, але довго висить на створенні Node Group, і потім падає з помилкою “unexpected state ‘CREATE_FAILED’“:
... ╷ │ Error: waiting for EKS Node Group (atlas-eks-test-1-33-cluster:test-1-33-default-20250801112636765600000014) create: unexpected state 'CREATE_FAILED', wanted target 'ACTIVE'. last error: i-03f2c73c7211880f7: NodeCreationFailure: Unhealthy nodes in the kubernetes cluster ...
Хоча EC2 Auto Scaling Group є, і EC2 в ній теж.
Чому?
Тобто проблема в тому, що WorkerNode створена, але не може приєднатись до Kubernetes.
Першим про що думається – це перевірити Security Group, але тут наче все правильно – всі правила прописані. Порівнював з поточним EKS кластером, який робився ще з AWS EKS Terraform module v20.x – все аналогічно.
Проблема з IAM? У EC2 нема пермішенів достукатись до кластеру? Аналогічно – порівнюємо зі старим кластером, все ОК.
“Check the logs, Billy!”
Тут ще прикол в тому, що SSH на всі EC2 в мене налаштований – але тільки для Nodes, які створюються з Karpenter, писав в AWS: Karpenter та SSH для Kubernetes WorkerNodes.
А проблема виникла в “дефолтній” NodeGroup, де запускаються різні контролери.
Тому підключаємось через AWS Console – вибираємо Connect:
Потім в EC2 Instance Connect вибираємо “Connect using a Private IP” і вибираємо існуючий або руками швиденько створюємо новий EC2 Instance Connect Endpoint.
Задаємо ім’я юзера – для Amazon Linux це ec2-user
:
І дивимось логи:
“Container runtime network not ready – cni plugin not initialized”
Власне:
Aug 01 13:26:04 ip-10-0-48-198.ec2.internal kubelet[1619]: E0801 13:26:04.989799 1619 kubelet.go:3126] "Container runtime network not ready" networkReady="NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized"
Вау…
Окей – а що у нас там з VPC CNI?
Йдемо подивитись EKS Add-ons, і…
Взагалі пусто.
Дивимось лог terraform apply
– і бачимо “Read complete“, але нема “Creating…“:
... module.atlas_eks.module.eks.data.aws_eks_addon_version.this["vpc-cni"]: Read complete after 0s [id=vpc-cni] ...
Давайте ще глянемо чи взагалі є контейнери на ноді – може, там якісь помилки є?
Ще раз вау…
Взагалі нічого.
Вже тоді ще раз поліз в GitHub Issues, і по запиту “addon” знайшов оцю ішью – Managed EKS Node Groups boot without CNI, but addon is added after node group.
Власне, да – проблема виникла через відсутність параметра before_compute
.
Хоча трохи дивно, бо він був доданий ще в версії v19.9, я останній раз кластер з нуля деплоїв вже з v20 – і цієї проблеми не було.
Ба більше – створення тестового кластеру з мастер-бранча, де нема описаних тут апдейтів і версія модуля v20 все ще працює без проблем.
І в diff 20 vs 21 значних змін пов’язаних з before_compute
не бачу.
Втім, так як це стосується тільки створення нового кластеру – то при просто апгрейді before_compute
можна не додавати. Але якщо все ж додавати – то адони будуть перестворені.
Сама before_compute
була додана аби дати можливість вказати які адони створювати до WorkerNodes, а які після. Див. main.tf#L797
та коменти до PR #2478.
Додаємо як в прикладах EKS Managed Node Group:
... vpc-cni = { addon_version = var.eks_addon_versions.vpc_cni before_compute = true configuration_values = jsonencode({ env = { ENABLE_PREFIX_DELEGATION = "true" WARM_PREFIX_TARGET = "1" AWS_VPC_K8S_CNI_EXTERNALSNAT = "true" } }) } aws-ebs-csi-driver = { addon_version = var.eks_addon_versions.aws_ebs_csi_driver service_account_role_arn = module.ebs_csi_irsa_role.iam_role_arn } eks-pod-identity-agent = { addon_version = var.eks_addon_versions.eks_pod_identity_agent before_compute = true } ...
Виконуємо terraform apply
знов – і ось воно:
... module.atlas_eks.module.eks.aws_eks_addon.before_compute["vpc-cni"]: Creating... ... module.atlas_eks.module.eks.aws_eks_addon.before_compute["vpc-cni"]: Creation complete after 46s [id=atlas-eks-test-1-33-cluster:vpc-cni] ...
І в AWS Console:
І NodeGroup створена без помилок:
... module.atlas_eks.module.eks.module.eks_managed_node_group["test-1-33-default"].aws_eks_node_group.this[0]: Still creating... [01m40s elapsed] module.atlas_eks.module.eks.module.eks_managed_node_group["test-1-33-default"].aws_eks_node_group.this[0]: Creation complete after 1m49s [id=atlas-eks-test-1-33-cluster:test-1-33-default-20250801142042855800000003] ...
Готово.