Отже, 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=>NodePoolAWSNodeTemplate=>EC2NodeClassMachine=>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




