Отже, Karpenter зробив ще один великий шаг до релізу, і у версії 0.32 вийшов з етапу Alpha до Beta.
Давайте кратко подивимось на зміни – а вони досить суттєві, а потім виконаємо апгрейд на EKS з Karpneter Terraform module та Karpenter Helm chart.
Сам процес установки Karpenter описував у пості Terraform: створення EKS, частина 3 – установка Karpenter, і нижче буде трохи відсилок на нього, типу імені файла karpenter.tf
та деяких variables.
Основна документація:
Зміст
Що нового в v0.32.1?
Починаючи з 0.32, API-ресурси Provisioner
, AWSNodeTemplate
та Machine
будуть deprecated, а з версії 0.33 їх приберуть взагалі.
Замість них додані:
Provisioner
=>NodePool
AWSNodeTemplate
=>EC2NodeClass
Machine
=>NodeClaim
v1alpha5/Provisioner => v1beta1/NodePool
NodePool являється наступником Provisioner, і має параметри для:
- налаштування запуску Pods та WorkerNodes – requirements подів до нод, taints, labels
- налаштування того, як Karpenter буде розміщувати поди на нодах, та як буде виконувати deprovisioning зайвих нод
“Під капотом” NodePool буде створювати ресурси v1beta1/NodeClaims (які прийшли за зміну v1alpha5/Machine), і взагалі тут ідея приблизно як з Deployments та Pods: в Deployment (NodePool) ми описуємо template того, як буде створюватись Pod (NodeClaims). А v1beta1/NodeClaims в свою чергу являється наступником ресурсу v1alpha5/Machine.
Також була додана новая секція disruption
– сюди були перенесені всі параметри, які відносяться до видалення зайвих нод та управління подами, див. Disruption.
v1alpha5/AWSNodeTemplate => v1beta1/EC2NodeClass
EC2NodeClass являється наступником AWSNodeTemplate, і має параметри для:
- налаштування AMI
- SecurityGroups
- subnets
- EBS
- IMDS
- User Data
Було видалене поле spec.instanceProfile
– тепер Karpenter буде створювати Instance Profile для ЕС2 на основі IAM Role, яку буде передано в spec.role
.
Також остаточно було видалене поле spec.launchTemplateName
.
Див. документацію у NodeClasses.
Зміни в Labels
Лейбли karpenter.sh/do-not-evict
та karpenter.sh/do-not-consolidate
були об’єднані у нову лейблу karpenter.sh/do-not-disrupt
, яку можна використовувати як для Pods, щоб заборонити Karpenetr виконувати Pod eviction, так і для WorkerNodes, щоб заборонити видалення цієї ноди.
Міграція 0.30.0 на v0.32.1
Далі я описую все досить детально і розглядаю різні варіанти, тож може скластися враження, що процес апгрейду досить геморний, бо буде трохи багато тексту, але насправді ні – все досить просто.
Що ми маємо зараз?
- AWS EKS Cluster, створений з Terraform
- за допомогою модулю
terraform-aws-modules/eks/aws//modules/karpenter
версії 0.30.0 в AWS створюються необхідні ресурси – IAM, SQS, etc - сам Karpenter встановлено з Helm-чарту
karpenter
- який приймає параметр
version
, який ми передаємо зі змінноїvar.helm_release_versions.karpenter
- який приймає параметр
- і маємо два ресурси
kubectl_manifest
– дляkarpenter_provisioner
іkarpenter_node_template
Процес міграції включає в себе:
- оновлення IAM Role, яка використовується подами контролера Karpenter для управління EC2 в AWS:
- замінити тег
karpenter.sh/provisioner-name
наkarpenter.sh/nodepool
(див. chore: Release v0.32.0) – стосується тільки ролі, яка була створена з Cloudformation, бо в Terraform модулі використовується іншийCondition
- додати IAM Policy
iam:CreateInstanceProfile
,iam:AddRoleToInstanceProfile
,iam:RemoveRoleFromInstanceProfile
,iam:DeleteInstanceProfile
таiam:GetInstanceProfile
- замінити тег
- додавання нових CRD
v1beta1
, після чого Karpenter сам оновить ресурси Machine на NodeClaim- для міграції AWSNodeTemplate => EC2NodeClass та Provisioner => NodePool можна використати утіліту karpenter-convert
Після чого виконати заміну WorkerNodes:
- з використання фічі drift:
- додати taint
karpenter.sh/legacy=true:NoSchedule
до існуючого Provisioner - Karpneter помітить всі ноди цього Provisioner як drifted
- Karpenter запустить нові ЕС2, використовуючи новий NodePool
- додати taint
- видалення нод:
- створити NodePool аналогічний існуючому Provisioner
- видалити існуючий Provisioner командою
kubectl delete provisioner <provisioner-name> --cascade=foreground
, в результаті чого Karpenter видалить всі його ноди виконавши node drain для всіх відразу, і поди, які перейдуть в стан Pending, запустить на нодах, які були створені з NodePool
- ручна заміна:
- створити NodePool аналогічний існуючому Provisioner
- додати
taint karpenter.sh/legacy=true:NoSchedule
до старого Provisioner - по черзі вручну видалити всі його WorkerNopes з
kubectl delete node
Що це значить для нас?
- IAM має оновитись з апдейтом версії модуля
terraform-aws-modules/eks/aws//modules/karpenter
- див git diff у feat: Add Karpenter v1beta1 compatibility
- CRD маємо додати руками або з новим чартом
- і оновимо Helm-чарт:
- див. git diff у chore: Release v0.32.0 та feat: v1beta1
- також змінились values – блок
aws
вsettings
тепер deprecated, див.values.yaml
Всі зміни начебто backward compatible (перевірив – відкатував версії), тобто можемо спокійно оновлювати існуючі ресурси один за одним – поламатись нічого не повинно.
Тож, що зробимо:
- оновимо модуль Terraform
- додамо CRD
- оновимо Helm-чарт з Karpenter
- задеплоїмо, перевіримо – старі ноди від старого Provisioner продовжать працювати, поки ми їх не вб’ємо
- додамо нові NodePool та EC2NodeClass
- перестворимо WorkerNodes
Поїхали.
Step 1: оновлення Terraform Karpenter module
Виконуємо terraform apply
до всіх змін, щоб мати задеплоєну останню версію нашого коду.
У файлі karpenter.tf
маємо виклик модуля і його версію:
module "karpenter" { source = "terraform-aws-modules/eks/aws//modules/karpenter" version = "19.16.0" ...
Karpenter Instance Profile
В самому модулі була додана нова змінна enable_karpenter_instance_profile_creation
, яка визначає хто буде менеджити IAM Roles для WorkerNodes – Terraform, як було раніше, чи використати нову фічу від Karpenter. Якщо enable_karpenter_instance_profile_creation
задати в true, то модуль просто додає ще один блок прав в IAM, див. main.tf
.
Але тут “є нюанс” (с) в залежностях модулю Karpneter, і Helm-чарту: якщо enable_karpenter_instance_profile_creation
включити в true (дефолтне значення false) – то модуль не створить resource "aws_iam_instance_profile"
, який далі використовується в чарті для Karpenter – параметр settings.aws.defaultInstanceProfile
.
Тож тут два варіанти:
- спочатку оновити тільки версії – модуля і чарта, але використати старі параметри і Provisioner
- після апдейту – створити NodePool and EC2NodeClass, замінити параметри, і перестворити WorkerNodes
- або обновити відразу все – і модуль/чарт, і параметри, і Provisioner замінити на NodePool, і задеплоїти все разом
Спочатку можна зробити пошагово, десь на Dev-кластері, щоб подивитись, як воно все пройде, а потім на Prod викатувати вже весь апдейт відразу.
Почнемо, звісно, з Dev.
Міняємо версію на v19.18.0 – див. Releases, і додаємо enable_karpenter_instance_profile_creation = true
– але поки закоментимо:
module "karpenter" { source = "terraform-aws-modules/eks/aws//modules/karpenter" #version = "19.16.0" version = "19.18.0" cluster_name = module.eks.cluster_name irsa_oidc_provider_arn = module.eks.oidc_provider_arn irsa_namespace_service_accounts = ["karpenter:karpenter"] create_iam_role = false iam_role_arn = module.eks.eks_managed_node_groups["${local.env_name_short}-default"].iam_role_arn irsa_use_name_prefix = false # In v0.32.0/v1beta1, Karpenter now creates the IAM instance profile # so we disable the Terraform creation and add the necessary permissions for Karpenter IRSA #enable_karpenter_instance_profile_creation = true } ...
Поки не деплоїмо, йдемо до чарту.
Step 2: оновлення Karpenter Helm chart
Версії чартів в мене зібрані в одній змінній – оновлюємо тут karpneter
v0.30.0 на v0.32.1:
... helm_release_versions = { #karpenter = "v0.30.0" karpenter = "v0.32.1" external_dns = "1.13.1" aws_load_balancer_controller = "1.6.1" secrets_store_csi_driver = "1.3.4" secrets_store_csi_driver_provider_aws = "0.3.4" vpa = "2.5.1" } ...
Далі у нас два основних апдейти – це CRD та values чарту.
Karpenter CRD Upgrade
CRD є в основному чарті Karpneter, але:
Helm does not manage the lifecycle of CRDs using this method, the tool will only install the CRD during the first installation of the helm chart. Subsequent chart upgrades will not add or remove CRDs, even if the CRDs have changed. When CRDs are changed, we will make a note in the version’s upgrade guide.
Тобто наступні запуски helm install
не оновлять CRD, які були встановлені при першій інсталяції.
Тож варіантів тут (знову!) два – або просто вручну їх додати з kubectl
, або встановити з додаткового чарту karpenter-crd, див. CRD Upgrades.
При чому чарт встановить і старі CRD v1alpha5
, і нові v1beta1
, тобто ми будемо мати такий собі “backward compatible mode” – зможемо використовувати і старий Provisioner, і одночасно додати новий NodePool.
З helm template
можна перевірити що саме чарт karpenter-crd буде робити:
$ helm template karpenter-crd oci://public.ecr.aws/karpenter/karpenter-crd --version v0.32.1 ...
Але тут є нюанс: для встановлення нових CRD з чарту потрібно буде видалити вже існуючі CRD, а це призведе до того, що і існуючі Provisioner та Machine і відповідні WorkdeNodes будуть видалені.
Тож якщо це вже давно існуючий кластер, і ви хочете все зробити без downtime – то CRD встановлюємо руками.
Якщо кілька хвилин простою вам ОК – то краще вже робити з додатковим Helm-чартом, як й надалі буде все автоматично менеджити.
Ще можна спробувати заімпортити існуючі CRD в реліз нового чарту, див. Import Existing k8s Resources in Helm 3 – особисто я не пробував, але має працювати.
Отже в моєму випадку робимо апдейт CRD з чартом – додаємо його в наш Terraform:
... resource "helm_release" "karpenter_crd" { namespace = "karpenter" create_namespace = true name = "karpenter-crd" repository = "oci://public.ecr.aws/karpenter" repository_username = data.aws_ecrpublic_authorization_token.token.user_name repository_password = data.aws_ecrpublic_authorization_token.token.password chart = "karpenter-crd" version = var.helm_release_versions.karpenter } ...
Переходимо до основного чарту.
Karpenter Chart values
Далі в resource "helm_release" "karpenter"
прописуємо нові вальюси та додамо depends_on
на чарт з CRD.
Додаємо параметр settings.aws.defaultInstanceProfile
– потім його приберемо:
... resource "helm_release" "karpenter" { namespace = "karpenter" create_namespace = true name = "karpenter" repository = "oci://public.ecr.aws/karpenter" repository_username = data.aws_ecrpublic_authorization_token.token.user_name repository_password = data.aws_ecrpublic_authorization_token.token.password chart = "karpenter" version = var.helm_release_versions.karpenter values = [ <<-EOT settings: clusterName: ${module.eks.cluster_name} clusterEndpoint: ${module.eks.cluster_endpoint} interruptionQueueName: ${module.karpenter.queue_name} aws: defaultInstanceProfile: ${module.karpenter.instance_profile_name} serviceAccount: annotations: eks.amazonaws.com/role-arn: ${module.karpenter.irsa_arn} EOT ] depends_on = [ helm_release.karpenter_crd ] /* set { name = "settings.aws.clusterName" value = local.env_name } set { name = "settings.aws.clusterEndpoint" value = module.eks.cluster_endpoint } set { name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn" value = module.karpenter.irsa_arn } set { name = "serviceAccount.annotations.eks\\.amazonaws\\.com/sts-regional-endpoints" value = "true" type = "string" } set { name = "settings.aws.defaultInstanceProfile" value = module.karpenter.instance_profile_name } set { name = "settings.aws.interruptionQueueName" value = module.karpenter.queue_name } */ } ...
Виконуємо terraform init щоб завантажити нову версію модулю.
Зараз у нас вже є CRD, які були створені при першій інсталяції Karnepnter:
$ kk get crd | grep karpenter awsnodetemplates.karpenter.k8s.aws 2023-10-03T08:30:58Z machines.karpenter.sh 2023-10-03T08:30:59Z provisioners.karpenter.sh 2023-10-03T08:30:59Z
Видаляємо їх:
$ kk -n karpenter delete crd awsnodetemplates.karpenter.k8s.aws machines.karpenter.sh provisioners.karpenter.sh customresourcedefinition.apiextensions.k8s.io "awsnodetemplates.karpenter.k8s.aws" deleted customresourcedefinition.apiextensions.k8s.io "machines.karpenter.sh" deleted customresourcedefinition.apiextensions.k8s.io "provisioners.karpenter.sh" deleted
І запускаємо terraform apply
– зараз у нас має оновитись тільки сам чарт – в IAM поки змін не буде, бо маємо enable_karpenter_instance_profile_creation == false
.
Після деплою перевіряємо CRD:
$ kk get crd | grep karpenter awsnodetemplates.karpenter.k8s.aws 2023-11-02T15:33:26Z ec2nodeclasses.karpenter.k8s.aws 2023-11-03T11:20:07Z machines.karpenter.sh 2023-11-02T15:33:26Z nodeclaims.karpenter.sh 2023-11-03T11:20:08Z nodepools.karpenter.sh 2023-11-03T11:20:08Z provisioners.karpenter.sh 2023-11-02T15:33:26Z
Перевіряємо поди і ноди – все має залишитись, як було – той самий ресурс Machine, щоб був створений зі старого Provisiner, і та сама WokrderNode:
$ kk get machine NAME TYPE ZONE NODE READY AGE default-b6hdr t3.large us-east-1a ip-10-1-35-97.ec2.internal True 30d
Якщо все ОК – то переходимо до створення NodePool та EC2NodeClass.
Step 3: створення NodePool та EC2NodeClass
Спочатку давайте розберемося з IAM ролями 🙂 Але це стосується конкретного мого сетапу, бо якщо ви всі ноди створюєте з Karpenter, то цю частину можна скіпнути.
В Terraform модулі EKS у нас створюється Managed Node Group, в якій створюється IAM Role, яка потім використовується в InstanceProfile для всіх нод кластера.
Далі ця роль передається в модуль karpenter
, і тому create_iam_role
в модулі Карпентер стоїть в false – бо роль вже є:
... module "karpenter" { ... # disable create as doing in EKS NodeGroup resource create_iam_role = false iam_role_arn = module.eks.eks_managed_node_groups["${local.env_name_short}-default"].iam_role_arn irsa_use_name_prefix = false ... } ...
Потім, коли Karpenter запускав нові EC2-інстанси, їм підключалась ця роль.
Але з новою версією Karpenter він сам створює instanceProfile
з spec.role
.
Щоб в новому маніфесті з EC2NodeClass передати в поле spec.role
ім’я замість iam_role_arn
– шукаємо його в outputs.tf
:
... output "iam_role_name" { description = "The name of the IAM role" value = try(aws_iam_role.this[0].name, null) } output "iam_role_arn" { description = "The Amazon Resource Name (ARN) specifying the IAM role" value = try(aws_iam_role.this[0].arn, var.iam_role_arn) } ...
Тепер можна додавати решту ресурсів.
Додавання EC2NodeClass
Див. доку в NodeClasses.
Тут робимо прямо в коді файлу karpenter.tf
, як було і для AWSNodeTemplate.
Лишаємо і старий маніфест, і поруч додаємо новий:
... resource "kubectl_manifest" "karpenter_node_template" { yaml_body = <<-YAML apiVersion: karpenter.k8s.aws/v1alpha1 kind: AWSNodeTemplate metadata: name: default spec: subnetSelector: karpenter.sh/discovery: "atlas-vpc-${var.environment}-private" securityGroupSelector: karpenter.sh/discovery: ${local.env_name} tags: Name: ${local.env_name_short}-karpenter environment: ${var.environment} created-by: "karpneter" karpenter.sh/discovery: ${local.env_name} YAML depends_on = [ helm_release.karpenter ] } resource "kubectl_manifest" "karpenter_node_class" { yaml_body = <<-YAML apiVersion: karpenter.k8s.aws/v1beta1 kind: EC2NodeClass metadata: name: default spec: amiFamily: AL2 role: ${module.eks.eks_managed_node_groups["${local.env_name_short}-default"].iam_role_name} subnetSelectorTerms: - tags: karpenter.sh/discovery: "atlas-vpc-${var.environment}-private" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: ${local.env_name} tags: Name: ${local.env_name_short}-karpenter environment: ${var.environment} created-by: "karpneter" karpenter.sh/discovery: ${local.env_name} YAML depends_on = [ helm_release.karpenter ] } ...
В spec.amiFamily
передаємо AmazonLinux v2, в spec.role
– IAM Role для InstanceProfile – вона ж додана і до aws-auth
ConfigMap нашого кластера (в модулі eks
).
Додавання NodePool
Так як Provisioner/NodePools планувалось мати не один, то їхні параметри задані в variables – копіюємо саму змінну:
... variable "karpenter_provisioner" { type = map(object({ instance-family = list(string) instance-size = list(string) topology = list(string) labels = optional(map(string)) taints = optional(object({ key = string value = string effect = string })) })) } variable "karpenter_nodepool" { type = map(object({ instance-family = list(string) instance-size = list(string) topology = list(string) labels = optional(map(string)) taints = optional(object({ key = string value = string effect = string })) })) } ...
І значення:
... karpenter_provisioner = { default = { instance-family = ["t3"] instance-size = ["small", "medium", "large"] topology = ["us-east-1a", "us-east-1b"] labels = { created-by = "karpenter" } } } karpenter_nodepool = { default = { instance-family = ["t3"] instance-size = ["small", "medium", "large"] topology = ["us-east-1a", "us-east-1b"] labels = { created-by = "karpenter" } } } ...
Як і з Provisioner – додаємо файл шаблону configs/karpenter-nodepool.yaml.tmpl
– тут формат теж трохи змінився, наприклад labels
тепер в блоці spec.template.metadata.labels
а не spec.labels
, як було в Provisioner, див. NodePools.
Тож тепер шаблон виглядає так:
apiVersion: karpenter.sh/v1beta1 kind: NodePool metadata: name: ${name} spec: template: metadata: %{ if labels != null ~} labels: %{ for k, v in labels ~} ${k}: ${v} %{ endfor ~} %{ endif ~} spec: %{ if taints != null ~} taints: - key: ${taints.key} value: ${taints.value} effect: ${taints.effect} %{ endif ~} nodeClassRef: name: default requirements: - key: karpenter.k8s.aws/instance-family operator: In values: ${jsonencode(instance-family)} - key: karpenter.k8s.aws/instance-size operator: In values: ${jsonencode(instance-size)} - key: topology.kubernetes.io/zone operator: In values: ${jsonencode(topology)} # total cluster limits limits: cpu: 1000 memory: 1000Gi disruption: consolidationPolicy: WhenEmpty consolidateAfter: 30s
Важливо: якщо ви в AWS не використовуєте Spot Instances, то додайте karpenter.sh/capacity-type == "on-demand"
, див. причину нижче у Помилка: The provided credentials do not have permission to create the service-linked role for EC2 Spot Instances.
І додаємо новий ресурс kubectl_manifest
в karpenter.tf
, поруч зі старим Provisioner:
... resource "kubectl_manifest" "karpenter_provisioner" { for_each = var.karpenter_provisioner yaml_body = templatefile("${path.module}/configs/karpenter-provisioner.yaml.tmpl", { name = each.key instance-family = each.value.instance-family instance-size = each.value.instance-size topology = each.value.topology taints = each.value.taints labels = merge( each.value.labels, { component = var.component environment = var.environment } ) }) depends_on = [ helm_release.karpenter ] } resource "kubectl_manifest" "karpenter_nodepool" { for_each = var.karpenter_nodepool yaml_body = templatefile("${path.module}/configs/karpenter-nodepool.yaml.tmpl", { name = each.key instance-family = each.value.instance-family instance-size = each.value.instance-size topology = each.value.topology taints = each.value.taints labels = merge( each.value.labels, { component = var.component environment = var.environment } ) }) depends_on = [ helm_release.karpenter ] } ...
Далі:
- в
resource "helm_release" "karpenter"
видаляємо з valuesaws.defaultInstanceProfile
- в
module "karpenter"
включаємоenable_karpenter_instance_profile_creation
в true
Тепер Terraform має:
- додати права до ролі KarpenterIRSA
- якщо для модулю
karpenter
не передавався параметрcreate_instance_profile == false
– то видалитьсяmodule.karpenter.aws_iam_instance_profile
, але в моєму випадку він все одно не використовувався - і додати
kubectl_manifest.karpenter_nodepool["default"]
таkubectl_manifest.karpenter_node_class
Деплоїмо, перевіряємо:
$ kk get nodepool NAME NODECLASS default default $ kk get ec2nodeclass NAME AGE default 40s
І все ще маємо нашу стару Machine:
$ kk get machine NAME TYPE ZONE NODE READY AGE default-b6hdr t3.large us-east-1a ip-10-1-35-97.ec2.internal True 30d
Все – нам лишилось перестворити WorkderNodes, перемістити Поди, і після деплою на Staging та Production прибратись в коді – видалити все, що лишилось від версії 0.30.0.
The provided credentials do not have permission to create the service-linked role for EC2 Spot Instances
В якийсь момент в логах пішла помилка такого плана:
karpenter-5dcf76df9-l58zq:controller {“level”:”ERROR”,”time”:”2023-11-03T14:59:41.072Z”,”logger”:”controller”,”message”:”Reconciler error”,”commit”:”1072d3b”,”controller”:”nodeclaim.lifecycle”,”controllerGroup”:”karpenter.sh”,”controllerKind”:”NodeClaim”,”NodeClaim”:{“name”:”default-ttx86″},”namespace”:””,”name”:”default-ttx86″,”reconcileID”:”6d17cadf-a6ca-47e3-9789-2c3491bf419f”,”error”:”launching nodeclaim, creating instance, with fleet error(s), AuthFailure.ServiceLinkedRoleCreationNotPermitted: The provided credentials do not have permission to create the service-linked role for EC2 Spot Instances.”}
Але, по-перше – чому Spot? Звідки це?
Якщо глянути NodeClaim, який був створений для ціїє ноди, то там бачимо "karpenter.sh/capacity-type == spot"
:
$ kk get nodeclaim -o yaml ... spec: ... - key: karpenter.sh/nodepool operator: In values: - default - key: karpenter.sh/capacity-type operator: In values: - spot ...
Хоча в документації сказано, що по-дефолту capacity-type
має бути on-demand:
... - key: "karpenter.sh/capacity-type" # If not included, the webhook for the AWS cloud provider will default to on-demand operator: In values: ["spot", "on-demand"] ...
А ми в NodePool його не вказували.
Якщо ж в NodePool вказати karpenter.sh/capacity-type
явно:
... requirements: - key: karpenter.k8s.aws/instance-family operator: In values: ${jsonencode(instance-family)} - key: karpenter.k8s.aws/instance-size operator: In values: ${jsonencode(instance-size)} - key: topology.kubernetes.io/zone operator: In values: ${jsonencode(topology)} - key: karpenter.sh/capacity-type operator: In values: ["on-demand"] ...
То все працює, як треба.
І по-друге – яких саме пермішенів йому не вистачає? Що за помилка “ServiceLinkedRoleCreationNotPermitted
“?
Я спотами в AWS не користувався, тому довелось трохи погуглити, і відповідь знайшлась в документації Work with Spot Instances та гайді Using AWS Spot instances, де мова йде про IAM Role AWSServiceRoleForEC2Spot, яка має бути створена в AWS Account, щоб мати змогу створювати Spot-інстанси.
Трохи дивне рішення по-дефолту створювати Spot, тим більш в документації говориться навпаки. Крім того – в 0.30 все працювало і без явного налаштування karpenter.sh/capacity-type
.
Окей, будемо мати на увазі – якщо користуємось виключно On Demand – то треба додавати в конфіг NodePool.
Step 3: оновлення WorkerNodes
Що нам лишилося – це переселити наші поди на нові ноди.
Насправді всі поди переїхали на нові ноди ще під час апдейту, але давайте зробимо, бо нам ще апдейтити інші кластери.
Тут маємо три варіанти, про які говорили на початку. Давайте пробувати робити це без даунтайму – з використанням drift
(але без даунтайму – це якщо маєте мінімум по 2 поди на сервіс, і на додачу PodDisruptionBudget
).
Що нам треба зробити – це додати taint
до існуючого Provisioner, задеплоїти зміни, щоб taint додався до Nodes, і тоді Karpenter виконає Node Drain та створить нові ноди, щоб перемістити наші workloads.
Додаємо в наш шаблон configs/karpenter-provisioner.yaml.tmpl
:
apiVersion: karpenter.sh/v1alpha5 kind: Provisioner metadata: name: ${name} spec: taints: - key: karpenter.sh/legacy value: "true" effect: NoSchedule ...
В чарті версії 0.32.1 параметр drift
досі в false, тому включаємо в values нашого resource "helm_release" "karpenter"
:
... values = [ <<-EOT settings: clusterName: ${module.eks.cluster_name} clusterEndpoint: ${module.eks.cluster_endpoint} interruptionQueueName: ${module.karpenter.queue_name} featureGates: drift: true ...
І вже всі ці зміни разом можна викатувати на інші Kubernetes кластери – тільки не забудьте оновити tfvars для цих кластерів (якщо маєте щось типу окремих envs/dev/dev-1-28.tfvars
, envs/staging/staging-1-28.tfvars
, envs/prod/prod-1-28.tfvars
).
Rolling back the upgrade
Навряд чи це знадобиться, бо в принці особливих проблема не має бути, але я робив під час ре-тесту апгрейду, тож запишу:
- міняємо версію
module "karpenter"
з нової 19.18.0 на стару 19.16.0 - в
module "karpenter"
коментуємо опціюenable_karpenter_instance_profile_creation
- в
tfvars
дляhelm_release_versions
міняємо версію чарту Karpenter з нової v0.32.1 на стару v0.30.0 - лишаємо
resource "helm_release" "karpenter_crd"
- в
resource "helm_release" "karpenter"
коментуюємо новий блок values, розкоментуємо старі values черезset
- коментуємо ресурси
resource "kubectl_manifest" "karpenter_nodepool"
таresource "kubectl_manifest" "karpenter_node_class"
- у файлі
configs/karpenter-provisioner.yaml.tmpl
прибираємо Taint