Make. Build. Break. Reflect.
912 subscribers
116 photos
1 video
120 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
Для последнего проекта для AWS EKS использовал AMI на базе bottlerocket.
https://aws.amazon.com/bottlerocket/
Название амишки и версию в голове и коде не держу, смелости и дурости у меня много, так что у меня идёт latest (на самом деле там почти хардкод, просто выглядит пугающе). Сбоев не было никогда из-за последнего в ssm.
С точки зрения кода там просто:
locals {
aws_ami_name = "bottlerocket"
aws_ami_architecture = "x86_64"
aws_ami_type = "${upper(local.aws_ami_name)}_${local.aws_ami_architecture}"
}

data "aws_ssm_parameter" "eks_ami_image_version" {
name = "/aws/service/${local.aws_ami_name}/aws-k8s-${aws_eks_cluster.eks.version}/${local.aws_ami_architecture}/latest/image_version"
}

resource "aws_eks_node_group" "blue-green-group" {
...
ami_type = local.aws_ami_type
release_version = data.aws_ssm_parameter.eks_ami_image_version.value
...

Если пугает такое обновление версий, то можно залепить такое и обновлять раз в месяц/квартал вручную.
  lifecycle {
ignore_changes = [release_version]
...
}


Стратегий у меня несколько:
- event-driven
- load-driven

Ноды готовы очень быстро. При тюнинге в основном секунд 15.
В самых худших кейсах 2 минут 24 секунды. Такое было раз 5 по наблюдениям.
Зависит от типа инстанса, региона и спот ли.

Например типичный сценарий. Есть сервис в десятках подов, кто читает SQS. Они разные и цели у них разные.
- всего 2 ноды, 0 подов с без рабочей нагрузки.
- прилетели тонны мессаджей в AWS SQS, секунд за 5-15 на бурст в 100000+ сообщений
- KEDA через 5 секунд после threshold поднимается пару десятков подов
- кластер-автоскейлер/карпентер (зависит от проекта) поднимает одну или несколько нод в нод группу.
* Кластер автоскейлер мгновенно триггерит, если видит, что не хватает ресурсов и хоть что-то в Pending статусе из POD-ов
- сами ноды на ботлрокете поднимаются секунд за 12-15, каждая из нод. то есть через 15 секунд в основном уже ready.
- поды на новых нодах шедулятся, проходят 2 пробы(startup, readiness), ещё 8-15 секунд.
- приложение готово к работе и разбирает мессаджи в sqs (+20 секунд к таймингам, если на этой SQS long polling)
- после разбора sqs всё тушится к нулям через KEDA, чтобы не платить

Ради теста повторил, лог такой:
ip-10-1-33-89.ec2.internal    NotReady   <none>   0s      v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 1s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 1s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 1s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 1s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal NotReady <none> 10s v1.30.5-eks-baa6d11
ip-10-1-33-89.ec2.internal Ready <none> 13s v1.30.5-eks-baa6d11

ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 0s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 5s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal NotReady <none> 10s v1.30.5-eks-baa6d11
ip-10-1-34-52.ec2.internal Ready <none> 14s v1.30.5-eks-baa6d11

Как видно ноды очень быстро стартуют. Это самый главный плюс.

Почему bottlerocket:
- крайне быстрый старт нод, основная причина
- чуть более удобная(для меня) кастомизация kubelet, хоть там и не любимый мной TOML
- ну а как же без пункта best-CV-project, надо и новые вещи изучать
#AWS #EKS #bottlerocket
🆒31
Мне уже двое написали про bottlerocket, так что не теряя времени, пока смелые духом не запустили это в прод, продолжу.

Немного теории:
В Kubernetes существует два типа эвикции (выселения) подов:
Soft Eviction (Мягкое выселение):
- Kubernetes сначала посылает сигнал SIGTERM поду, давая ему время корректно завершить работу
- Pod получает grace period (период отсрочки) для завершения работы
- По умолчанию grace period составляет 30 секунд
Можно настроить через параметр --eviction-soft-grace-period /--eviction-soft

Hard Eviction (Жёсткое выселение):
- Происходит немедленное удаление пода
- Pod получает сигнал SIGKILL
- Используется когда нужно срочно освободить ресурсы
Настраивается через параметр --eviction-hard

Основные триггеры эвикции:
- memory.available: доступная память
- nodefs.available: место на диске ноды
- nodefs.inodesFree: свободные иноды
- imagefs.available: место для образов контейнеров
Пороговые значения можно задавать как в процентах, так и в абсолютных величинах (например, 100Mi для памяти).

Дефолтные значения ванильного k8s можете посмотреть тут:
https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/
Для AL2023 вот такие дефолт значения:
memory.available: "100Mi" 
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"


Выше была общая теория, ниже практика.

#AWS #EKS #bottlerocket
🤔1
У bottlerocket нет дефолтных значений.
Вы это можете проверить командой на ноде
apiclient -u /settings | jq

Ну или же посмотреть в конфиге:
https://github.com/bottlerocket-os/bottlerocket/blob/1.20.x/packages/kubernetes-1.29/kubelet-kubeconfig
Там пусто.

Что же это значит?
Это значит, что по дефолту если у вас:
- под(ы) с эфемерным хранилищем сожрёт все место на ноде
- под(ы) без лимитов цпу сожрёт всё процессорное время на ноде
- под(ы) без лимитов памяти сожрёт всю память на ноде
- диск заполнится имаджами под(а/ов) и гарбадж коллектор не успеет отработать
Ваша нода умрёт. Системные компоненты перестанут работать, кублету не хватит памяти и нода умрёт.
Нода не защищена от неконтролируемых действий со стороны подов.

Давайте это исправим:
- создадим темплейт/конфиг bottlerocket.tpl в нашем репозитории
- внутри добавим нечто типа такого
[settings.kubernetes]
api-server = "${endpoint}"
cluster-certificate = "${cluster_auth_base64}"
cluster-name = "${cluster_name}"
pod-pids-limit = 1024
image-gc-high-threshold-percent = 85
image-gc-low-threshold-percent = 80
container-log-max-files = 50
container-log-max-size = "100Mi"
[settings.kubernetes.system-reserved]
cpu = "200m"
ephemeral-storage = "1Gi"
memory = "1Gi"
[settings.kubernetes.kube-reserved]
memory = "893Mi"
[settings.kernel]
lockdown = "integrity"
[settings.host-containers.admin]
enabled = ${enable_admin_container}
[settings.host-containers.control]
enabled = ${enable_control_container}
[settings.kubernetes.eviction-hard]
"memory.available" = "1Gi"
"nodefs.available" = "20%"
"nodefs.inodesFree" = "5%"
"imagefs.available" = "15%"
[settings.kubernetes.eviction-soft]
"memory.available" = "10%"
[settings.kubernetes.eviction-soft-grace-period]
"memory.available" = "30s"

- добавить этот конфиг через юзердату
resource "aws_launch_template" "node-launch-template" {
...
user_data = base64encode(templatefile("${path.module}/../../templates/bottlerocket.tpl",
{
"cluster_name" = aws_eks_cluster.eks.name #обязательный параметр!!!
"endpoint" = aws_eks_cluster.eks.endpoint #обязательный параметр!!!
"cluster_auth_base64" = aws_eks_cluster.eks.certificate_authority[0].data #обязательный параметр!!!
"aws_region" = var.AWS_DEFAULT_REGION #обязательный параметр!!!
"enable_admin_container" = false
"enable_control_container" = true
}
))
}


Не буду описывать все параметры, они описаны тут:
https://bottlerocket.dev/en/os/1.20.x/api/settings/kubernetes/#eviction-hard
https://github.com/bottlerocket-os/bottlerocket/blob/develop/README.md#description-of-settings
Цифры можете брать из дефолта(выше писал), в моём примере цифры ровно под те требования, которые есть у меня в моим кластерам и приложениям. Не обязательно использовать все параметры, что есть в моём примере, можете только 4 параметра использовать.

* если вы хотите выдать доступ к вашим нодам, то в моем конфиге надо false поменять на true и дальше по инструкции
https://github.com/bottlerocket-os/bottlerocket?tab=readme-ov-file#control-container

#AWS #EKS #bottlerocket
🤩2
Bottlerocket, storage.

Ну а куда без боттлрокета.
Допустим вы решили перейти на боттлрокет после моих постов - https://xn--r1a.website/makebreakreflect/35
Сразу споткнувшись про пустой дефолтный конфиг кублета читаете и тут
https://xn--r1a.website/makebreakreflect/38

Готово.
У вас и кластер AWS EKS и нод группа(ы) и лаунчтемплейт.
Всё работает, всё круто, но спустя время вы замечаете, что у вас участились алёрты DiskPressure.
Так же в ивентах у вас нечто типа
The node had condition: [DiskPressure].
The node was low on resource: ephemeral-storage. Threshold quantity: 4281126156, available: 3438556Ki

"Какого х*ра рожна" думаете вы, ведь у меня диск как был 200 гигабайт, так и остался.
Смотрите свой код, видите там нечто типа
resource "aws_launch_template" "node-launch-template" {
...
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = 200
volume_type = "gp3"
}
}
...

"ну да, двести гигов" говорите вы вслух самому себе и идёте проверять терраформ/террагрант план.
Всё ок. Никаких
  lifecycle {
ignore_changes

Лезете в кубер, смотрите а чо там рили.
kubectl get nodes -o yaml | grep -i -A6 ephemeral-storage

Внезапно там ну вообще ни 200 гигов, там 20.
Начинаете перепроверять код, лаунчемплейт, ласт вершн и issue github.
А нет ничего.
У вас желаемое 200 гигов, а реально видно 20.


И тут подстава, откуда вы не ждали.
У bottlerocket не совсем типичный подход к монтированию томов.

Чтобы ноде под поды выделить больше места дефолта с 20 гигами, вам надо выдать отдельный маунт на побольше.
resource "aws_launch_template" "node-launch-template" {
...
# !!! вот тут много не надо, это для самой ноды, операционка, можно хоть 10
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = 20
volume_type = "gp3"
}
}
# !!! вот это добавляем, для POD-ов
block_device_mappings {
device_name = "/dev/xvdb"
ebs {
volume_size = 200
volume_type = "gp3"
}
}

Терраформ план, терраформ апплай, проверяем
kubectl get nodes -o yaml | grep -i -A6 ephemeral-storage

Отлично, теперь под эфемерку у нас 200 гигов, а не дефолтные 20(или 30, я не помню если честно) гигабайт.
Разуемся, алёрты пропадают, эфемерки всем хватает, кластер снова сладкий как яблочки с огорода у бабушки.

Кому интересно могут почитать на официальном сайте детальнейшее описание причин, но там на самом деле скукота.

#aws #eks #bottlerocket
👍6
#aws #eks #kubernetes #bottlerocket #troubleshooting

Case:
AWS EKS, Bottlerocket AMI. Случайно уронили коллектор логов, сбора не было несколько часов, но логи нужны прям сейчас. Если коллектор починить, то ждать долго(кстати какого фига долго я хз).
Нужных логов в поде нет, в ArgoCD их тоже нет(логично же).
Как быть?

Можно слить с EKS node(s), но этот путь не очевидный, ведь у нас Bottlerocket.

Для начала нужно быть уверен, что вы включали админ доступ в TOML конфиге.*
# Доступ к системным логам, получению root-оболочки (sheltie), запуску journalctl, logdog и пр.
[settings.host-containers.admin]
enabled = true

# Доступ к Bottlerocket API для настроек и диагностики, но без глубокого доступа к логам и файловой системе ноды.
[settings.host-containers.control]
enabled = true

Ок, админ доступ включён.
Смотрим на какой ноде запущен наш под.
kubectl get pods -o wide | grep podname
... ip-10-1-114-226.ec2.internal ...

Узнаем айди инстанса.
kubectl get node -o yaml ip-10-1-114-226.ec2.internal | grep -A4 -i "nodeid"
... "i-09cd72826bee50209" ...

Подключаемся к инстансу через SSM
aws ssm start-session --target i-09cd72826bee50209

Сейчас нет никаких прав, переходим в админа.
enter-admin-container

Ок, мы стали админами(через контейнер), теперь нам нужен шелл - шелти.
[root@admin]# sudo sheltie

Дальше просто смотрим в /var/log/
ls /var/log/containers/
argo-cd-argocd-application-controller-0_argo-cd_application-controller-dfb4d12f601e71ac373b30bfaad8d02b56141218e7bcc5f1358f3a7d8f7df7f7.log
...

Ну и смотрим логи
cat argo-cd-argocd-application-controller-0_argo-cd_application-controller-dfb4d12f601e71ac373b30bfaad8d02b56141218e7bcc5f1358f3a7d8f7df7f7.log


"но Алекс, нам нужны все логи, как их слить к себе локально?"

Ок, запускаем сборку логов в шелти
bash-5.1# logdog

После коллекции у нас логи хранятся на ноде в
logs are at: /var/log/support/bottlerocket-logs.tar.gz

Теперь со своей локальной машины дёргаем их к себе через k8s API (да-да, я тоже не знал)
kubectl get --raw "/api/v1/nodes/ip-10-1-114-226.ec2.internal/proxy/logs/support/bottlerocket-logs.tar.gz" > ./bottlerocket-logs.tar.gz

У нас локально теперь все логи в bottlerocket-logs.tar.gz
Задача решена.**


* Если изначально админ доступ не включён, то всё не ок. По идее если его включаешь в момент задачи, то ноды пересоздаются и старые логи с нод исчезают в пучине небытия и бездны. Тогда уж лучше ждать окончания работы починенного коллектора логов.

** При необходимости повторить для других нод, если исторически старый под был запущен на других нодах.
👍85🔥3
#troubleshooting #kubernetes #bottlerocket #aws # linux #база

Прилетает аномалия: клиенты(живые люди), при обращении к нашему UI, начинают видеть таймауты и ошибки (5xx), особенно при резком росте нагрузки.
Ну ничего необычного. Кроме того, что такого быть не должно.

Смотрю логи фронта - он получает пятисотки от бэкенда.
Иду к бэкендерам, показываю логи, тыкаю пальцем, говорю чините.
Проходит день, два, а ошибки всё идут.
Разработчики чего только не делали:
- включали дебаг
- смотрели логи
- добавляли метрики
- добавляли ресурсы CPU/Memory (хотя нагрузка была не высокой)
- did a barrel roll
Ничего не помогает. По всей статистике, логам и метрикам - всё отлично, но есть проблема с пятисотками.
Я бы может этим и не занимался, но на общем митинге мне ставят блокер и говорят "это ваша инфра и девопсы, чините, но мы, если честно, сами не знаем чо тут и почему".

Хлеб всему голова, как и девопс всем ишшуям затычка, поехали траблшутить.
Вспоминаю, что нечто подобное уже было, и иду читать у себя же https://xn--r1a.website/aws_ru/136623
Проверяю - не, все ок с ALB и настройками приложений.

При помощи фронта и бэка пишу скрипт нагрузочного тестирования, чтобы сымитировать это в stage (да кому он сдался этот ваш dev!). Запускаю, половину не понимая, и да, с*ка, пятисотки.

Ещё раз опускаюсь в логи, трейсы, метрики - не за что зацепиться.
Просидел дня три наверное, пока всё это ковырял как дурачок.
Может и правда мы, из отдела инфры, дурачки и что-то поломали?
Сижу ковыряю коммиты и MR в репозиториях инфраструктуры за последние 3 месяца - ничего критичного.
Ладно. Пошёл ковырять само приложение.
Есть!, Есть су*ка! Ребята меняли код, связанный немного с сетевым стеком и обработчиком на стороне сервера.
Даже спустя время не могу поделится хотя бы примерно что делало приложение и бизнес, так что опустим это.
Хорошо, допустим они что-то меняли, но в метриках и логах ведь ничего нет?
Стало интересно, переношу задачу на следующий спринт, ковыряю ещё несколько часов.
После детального ныряния есть зацепка, что у нас инфра сетевой стек говно и это уже проблема на нашей стороне.

Просидел ещё несколько часов.
Не буду ходить долгим путём как же я это выясни, где-то на грани безумства и безысходности пришла мысль, что проблема именно в том, что приложение стало создавать очень много короткоживущих TCP-сессий. Оно успевает ответить клиенту, но закрывает соединение так, что оно оставляет херню в ядре.

Первым делом иду на ноду, где размещён под, у которого проблема
Я выше писал как на ноду на ботлрокете заходить - https://xn--r1a.website/makebreakreflect/205
И ввожу команду:
dmesg | grep conntrack

И вот оно! Розовый слон в комнате:
nf_conntrack: table full, dropping packet.
nf_conntrack: table full, dropping packet.

Сразу же видна причина, можно было бы и раньше увидеть 🤡(нет)

Пакеты отбрасывает не приложение, не kube-proxy, не CNI - их отбрасывает само ядро Linux ноды, потому что его таблица сетевых состояний забита!

Далее я проверяю, насколько сильно таблица забита и каков ее лимит. Это прямое чтение файлов ядра.
bash-5.1# cat /proc/sys/net/netfilter/nf_conntrack_count
192125
bash-5.1# cat /proc/sys/net/netfilter/nf_conntrack_max
262144
bash-5.1# cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait
120

Приложение-бэкенд, после изменений, стало генерировать аномально высокую частоту короткоживущих TCP-соединений. Система просто не успевает их забыть (таймаут 120 секунд слишком велик), и таблица переполняется.

Делаю нагрузочный тест - да, каррент каунт растёт.
Пробую фикс:
меняю таймаут с 120 на 60 на самой ноде и снова запускаю нагрузочный тест.
Тишина. Снова запускаю скрипт нагрузочного тестирования. Пятисотки исчезли! Красота!

Проконсультировался с коллегами - кто должен править:
- на стороне инфры
- на стороне приложения
Пару Очень Авторитетных Коллег Архитекторов сказали, что точно на стороне приложения и мы перекидываем мячик на сторону девелоперов. Они что-то там правят, тестируют этим же скриптом - всё отлично.
Выкатываем и фикс на прод, пятисоток больше нет, кастомеры довольны.
Белиссимо!

Не дай вселенная вам такое дебажить.😭
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥26😱9👍3🤡1