Make. Build. Break. Reflect.
912 subscribers
116 photos
1 video
121 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
#aws #devops #longread #longstory #msk #apigateway #cloudfront

Суббота, отличная погода и великолепное настроение - идеальный момент для меня, чтобы написать новую историю.

А читателям можно устроиться поудобнее с бокалом любимого напитка и неторопливо погрузиться в лонгрид, полный разных идей, технологий и подходов.

https://telegra.ph/My-maiden-magnum-opus-08-01
🔥18👍53
#terraform #aws

Самая нелюбимая часть работы - terraform hell dependency и AnyCloudProvider(в данном примере AWS) deprecated resources/versions.
Вишенка на торте - вкупе с публичными модулями.

То есть "что-то скоро перестанет работать - обнови версию".
Просто обнови и всё! 😀

Простой пример: прилетает письмо от AWS "скоро python9 станет депрекейтед в AWS"
Никаких возражений, я полностью согласен с такой позицией. 100% верно.
Пилим таску "обновить версию рантайма везде", иначе через 180 дней превратимся в тыкву.

Проверяем есть ли у нас лямбды 3.8/3.9.
aws lambda list-functions --region us-east-1 --output text --query "Functions[?Runtime=='python3.8'].FunctionArn"
aws lambda list-functions --region us-east-1 --output text --query "Functions[?Runtime=='python3.9'].FunctionArn"

Увы, есть. Находим один аж с 3.8. С него и начнём.

Казалось бы - в чём сложность? Версию апдейтни.
Попытка обновить версию самого модуля - получаю ошибку, что террраформ версии 1.5.3 не подходит, тут нужно минимум 1.5.7. Просто так указать ему версию рантайма не прокатывает - он не в курсе про такой параметр в этой старой версии.
Хорошо, везде обновляем терраформ на 1.5.7, но теперь ругается на провайдеры, terraform/aws. Их тоже надо обновить.
Обновляем и их повсеместно. Прогоняем план - последовательно находим 4 бага - в github issues, обновляем раз за разом разные версии, вроде прошли к какой-то, в котором багов нет. Появляются даже какие-то костыли с lifecycle.
В двадцатый раз уже terraform plan - появились обязательные поля у части ресурсов, а они не указаны.
И так по кругу. Хистори показало печаль 😭
history | tail -n 200 | grep plan | wc -l
59


И вот что тут делать?
- либо дальше повышать версию за версией, добавлять/убавлять параметры, чтобы стейт с кодом был идентичным, пока это говно не заработает
- сделать роллбек всех сделанных изменений кода с версиями, повесить техдолг

Потрачено уже немалое количество времени и принимаю решение - делаю роллбек.
Нужен рабочий и код и стейт. А роллбек ничего и не дал. 🚶‍♀
В лямбде версия рантайма уже 3.12 и откатить обратно к 3.8 не даёт - нет даже такого поля(а скоро и не станет 3.9).

Да что же такое. Значит ни первое и ни второе решение, предложенное выше.
В итоге просто иду на страницу модуля старой версии, копирую все ресурсы as is, пишу свой модуль и свои версии зависимостей. Убираю несколько лишних ресурсов и прав к ним.
Запускаю лямбду - ошибка питон кода, ведь он был написан для 3.8, а у меня 3.12.
Переписываю питон скрипт, проверка - всё работает.
Обновляю сразу все зависимости до последнего актуального, чтобы оно дольше проработало.
Выпиливаю публичный модуль, документирую свой модуль, план, фиксирую версии, терраформ апплай - всё работает.

Вот так, попытка "просто сделать апдейт версии питон рантайм лямбды" вышло в 5 часов абсолютно глупой работы. Итог плохой:
- мне он не нравится
- технический долг немного вырос
Из хорошего
- выпили ещё один сомнительный модуль, который своими зависимостями ломает вообще всё к херам.
- не поломали код и стейт
- обновили все лямбды до 3.12 (не только от старого паблик модуля, а везде)

Мне кажется, тут нет правых, виноватых. Хороших и плохих решений.
Эта ситуация возникала не раз, уж слишком у нас много зависимостей в нашем коде, инфраструктуре и работе. Особенно сторонних модулях.
Можно сколь угодно обвинять "а чо ты раньше не обновился?", "а чо ты на 1.5.3 сидишь как сыч?" и многое другое.
Не хватает ресурсов, чтобы всё держать в обновлённом состоянии. Увы.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍16😁1
#aws #smallindiecompany #ui #🐒

ALRIGHT AWS, ALRIGHT, I GET IT
Please open Telegram to view this post
VIEW IN TELEGRAM
🤣11
#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 #aws #ssh #retool #paranoia #linux

Есть такая штука, называется retool https://retool.com/
Какая-то платформа для менеджеров, я ей не пользовался никогда в бизнес уровне.
В UI интерфейсе мышкой подключаются resource и на их базе могут строятся какие-то таблички, панели и прочая не интересная мне информация для менеджеров и сейлзов(наверное).
Resources чаще всего это база данных. Например MySQL или PostgreSQL. У retool тысячи их для любых интеграций.
Это всё, что надо знать для начала, идём дальше.

В основном Retool из интернета подключается к вашим ресурсам либо напрямую по hostname, либо к AWS через IAM credentials, либо через SSH bastion host.
Первый случай я не знаю для кого, у меня пока не было опыта публичных БД, второй способ мне не нравится, да и не пройти с ним SOC2 аудит, так что у нас только третий вариант - через ssh tunnel.

У них есть вполне годный гайд https://docs.retool.com/data-sources/guides/connections/ssh-tunnels, который подойдёт для 99% случаев.
Создаётся пользователь на бастион хосте(например через ansible или руками), добавляется их ssh public key, раскидываются права - всё готово. В общем всё по инструкции.
При подключении ресурса вы в UI указываете ваш bastion хост, порт и прожимаете test connection.
Если всё ок- ошибки нет. Не ок - ошибка.
Ошибка чаще всего дурацкая, потому что открывается какой-то аналог developer tool и показывает длинный стактрейс.

С retool я уже работал, подключал на проект много лет назад.
В то время в качестве бастион хоста была убунту 18 вроде и мне пришлось промучить весь стакоферфлоу, чтобы найти решение для себя при ошибке, мне не хватало
PubkeyAcceptedKeyTypes +ssh-rsa

Ровно после этого в официальной документации и появился этот совет, если коннекта нет.
Написал им в поддержку, чтобы поправили мануал для глупеньких я.
Тогда подключили какие-то бд, я сидел дальше ковырялся с конфигами, безопасностью, со временем обложил всё секьюрити группами типа
resource "aws_security_group" "bastion" {
...
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
# https://docs.retool.com/data-sources/reference/ip-allowlist-cloud-orgs
# retool ip ranges only
# tfsec:ignore:aws-ec2-no-public-ingress-sgr
cidr_blocks = [
"35.90.103.132/30",
"44.208.168.68/30",
"3.77.79.248/30"
]
description = "SSH access from allowed IP ranges"
}
...

Потом добавились секюрити группы по MySQL/PostgreSQL типа
resource "aws_security_group_rule" "this" {
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["*********"]
security_group_id = aws_security_group.rds.id
description = "Allow PostgreSQL access from ***** VPC"
}

Так же сразу сделал отдельного пользователя в БД с чётко обрезанными правами.
Делал что-то ещё, и в целом был удовлетворён, что тварь какая-то ходит в мои сети.

Прошло много лет.

Ко мне приходят ребята инженеры, говорят хотят добавить новую БД для retool.
Уже подключена монга, mysql и всякое, а теперь надо и PostgreSQL.
Ок, говорю, ну там всё уже настроено - все ssh ключи, секьюрити группы и прочее - всё должно работать - другие то ресурсы(БД) работает до сих пор.
Берите креды и сами настраиваете, мы же договорились, что не должно быть боттлнека -девопса.
Они высылают скрин, что ошибка подключения. Я само собой не верю (бл, когда я научусь думать, что другие люди не глупее меня 🐒, баран самоуверенный).
Иду сам в retool, копирую все креды, создаю ресурс, но у меня тоже ошибка.
Извиняюсь перед ребятами, пилю тикет, начинаю разбираться.😭
Please open Telegram to view this post
VIEW IN TELEGRAM
😱3
#troubleshooting #aws #ssh #retool #paranoia #linux

Ок, что же говорит ошибка. Текст типа
Test connection failed (120.327s):(SSH) Channel open failure: open failed
{query: "Connect Request", error: Object}
query: "Connect Request"
error: Object
message: "(SSH) Channel open failure: open failed"
data: Object
reason: 1
name: "Error"
message: "(SSH) Channel open failure: open failed"

и дальше огромная простыня трейса.
А ничего она не говорит, и так понятно, ясно понятно, нет подключения.
Прохожу снова по инструкции - всё ок.
Проверяю не менялся ли паблик ключ? Не менялся.
Не менялся ли пул публичных адресов? Не менялся.
Блин, ну я же не дурак.
Добавляю руками свой IP на секьюрити группу, подключаюсь на бастион хост и оттуда делаю телнет и подключение через cli к PostgreSQL.
Сетевая связность есть, коннект.
Проверяю ещё раз все секьюрити группы, поиск по коммитам, всё отлично.
Так, стоп, а предыдущие то ресурсы работают?
Захожу в retool, в давно созданные ресурсы - тест проходит ок.

Чешу репу, гуглю решения, закидываю в 3 нейронки - решения нет.
Общие предположения, да и только.
В общем в траблшутинге и логах я просидел долго.
Чтобы не было стыдно, не буду говорить сколько, но много.

В итоге я просто начал смотреть все журналы, все конфиги и даже трейсы, мне было очень интересно.
Очевидно же, что у меня что-то не так, иначе бы ранее добавленные ресурсы не работали.

Спустя время дошёл до конца файла /etc/ssh/sshd_config перебирая по одному все включённые параметры, а там.... а там парам-пам-пам!
А там паранойя, ну прям рили паранойя.
# bastion host 1.2.3.4

cat /etc/ssh/sshd_config |grep -v "^#"
...
Match User retool
PermitTTY no
AllowTcpForwarding yes
PermitOpen mysql.amazonaws.com:3306 docdb.amazonaws.com:27017
ForceCommand /bin/echo "Shell disabled!"

Я даже сам забыл про эту паранойю!

В общем добавляю очевидное
...
PermitOpen mysql.amazonaws.com:3306 docdb.amazonaws.com:27017 postgresql.amazonaws.com:5432
...

Перезапускаю
sudo systemctl restart ssh

и Voilà! - коннект от retool есть.

Описываюсь ребятам, они подтверждают, закрываю задачу.

Какая же мораль? Как обычно:
- не думай, что остальные глупее тебя, если пишут ошибка есть - значит она есть!
- вспоминай о своих параноидальных привычках, прежде, чем тратить время
- держи конфиг sshd в IaC (ansible например), а не руками заполняй, баран. Поиском по iac я быстро увидел бы причину

* адреса endpoints к БД само собой должны быть полными и длинными, короткие URL лишь для примера
2🔥10
#aws #azure #gcp #kubernetes

Допустим вы сменили место работы.
На новом месте GCP, Azure, AWS. Миллионы куберов.
Разные тенанты, разные сабскрипшны, разные профили, разные аккаунты, разные регионы.

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

Первым делом ставим asdf.
https://github.com/asdf-vm/asdf/releases
Затем добавляем тулзы.
cat ~/.tool-versions

kubelogin 0.1.4
helm 3.17.0
dive 0.12.0
sops 3.9.4
terragrunt 0.54.15
terraform 1.5.3
kubectl 1.29.0
awscli 2.31.6
aws-sso-cli 2.0.3
kubectx 0.9.5
eksctl 0.215.0
azure-cli 2.77.0
jq 1.8.1

Апплаим (у меня это алиас asdfu)
cut -d' ' -f1 ~/.tool-versions | xargs -I{} asdf plugin add {} && asdf install

Теперь у нас есть все необходимые утилиты

Затем конфигурируем один раз профили(если есть).
aws configure sso 
# или
aws configure sso --profile profilename
az login


Теперь нам надо один раз залогиниться в ажур и амазон.
Где-то достаточно
az login и aws sso login
где-то разово надо попрыгать по профилям
aws sso login --profile profilename

Мы залогинились во все тенанты, все подписки, все аккаунты.
Как теперь в автоматике собрать все кубконфиги?
На помощь нам приходит любимый bash ❤️.

AWS (пример)
account_id=$(aws sts get-caller-identity --query Account --output text)
eksctl get cluster --all-regions -o json | jq -r '.[] | [.Name, .Region] | @tsv' | while read -r name region; do
aws eks update-kubeconfig \
--name "$name" \
--region "$region" \
--alias "${account_id}_${name}-${region}"
done

Azure (пример)
origin_context=$(kubectl config current-context)
original_subscription_info=$(az account show --query '{id:id, name:name}' --output json)
original_subscription_id=$(echo "$original_subscription_info" | jq -r '.id')

subscriptions=$(az account list --query "[].id" --output tsv)
for subscription in $subscriptions; do
az account set --subscription "$subscription"
clusters=$(az aks list --query "[].{name:name, resourceGroup:resourceGroup}" --output json)
for cluster in $(echo "$clusters" | jq -c '.[]'); do
cluster_name=$(echo "$cluster" | jq -r '.name')
resource_group=$(echo "$cluster" | jq -r '.resourceGroup')
az aks get-credentials \
--resource-group "$resource_group" \
--name "$cluster_name" \
--overwrite-existing \
--context "${subscription}_${cluster_name}"
kubelogin convert-kubeconfig -l azurecli
done
done
kubectl config use-context "$origin_context"
az account set --subscription "$original_subscription_id"

Это пример логики скриптов.
Можно изменять под любые цели, мне в целом удобнее формат алиасов кластеров
# AWS
<account_id>_<cluster_name>-<region>
# Azure
<subscription_id>_<cluster_name>-<region>

Алиасы каждый пилит сам под себя - как кому удобнее.
Можно даже добавлять суффикс, что за кластера
# AWS
eks-<account_id>_<cluster_name>-<region>
# Azure
aks-<subscription_id>_<cluster_name>-<region>

Очевидно, что если аккаунт один, подписка одна и регион один - сложный нейминг не нужен.

Теперь наше рабочее место настроено и в любой момент времени можно обновить актуальные кластера всех провайдеров, всех тенантов, подписок, аккаунтов и даже всех регионов.

Вводим kubectx, видим огромный список всего.
> kubectx

aks-98765-98765-98765_stage-fake-centralus
aks-54321-54321-54321_dev-fake-eastus2
aks-12345-12345-12345_uat-fake-germanywestcentral
eks-123456789_dev-project1-us-east-1
eks-123456789_stage-project2-us-east-2
...

Cвичимся в нужное и поехали работать.

* в условиях разных SSO и корпоративных политик некоторые способы могут отличаться. Это лишь общий паттерн.

А доступ в GCP вам так и не дали 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍16🔥4
#aws #opensearch #elasticsearch

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

Было три классических окружений(dev, stage, prod), все одинаковые по конфигу терраформ модуля, но отличающиеся по размеру стораджа, типу инстансов, шардам и количеству инстансов, на деве вроде был всего один.

Ну задача-то не сложная:
- обновил поля в индекс паттерне если есть конфликты (опционально, будет не у всех).
Если есть конфликты, то обновление на какую-то из версий опенсёрч не пройдёт
- обновить руками в UI на последнюю версию (раньше не даёт сразу обновить на опенсёрч) эластиксерч
- обновить руками в UI с эластик на опенсёрч
- обновить руками в UI одну или две версии опенсёрча до последней
UI‑апгрейт AWS OpenSearch происходит пошагово, без возможности пропуска промежуточных версий.
- поменять ресурс терраформа с
resource "aws_elasticsearch_domain" "elasticsearch"

на
resource "aws_opensearch_domain" "opensearch"

- выполнить удаление старого ресурса
terraform state rm aws_elasticsearch_domain.elasticsearch

- добавить новый ресурс импортом
terraform import aws_opensearch_domain.opensearch %domainname%

- прогнать терраформ план и поправить, где чего упущено (ну там новые названия переменных для .tfvars файла и типа того, не помню уже).
- проверить все дашборды в графане(есть ли метрики и не поменялись ли они), поправить если надо
- проверить все метрики для алертов и алерты, поправить если надо
- коммит и отправить MR

Не сложно, не правда ли?
Пока без каких проблем.

Однако в процессе миграции кто-то решил "а давайте ещё и SSO включим! Ну всё равно же апдейт!" 🤩.
SSO так SSO, правила диктуют боссы и стейкхолдеры.
К текущему плану добавляем новый ресурс нечто типа (могут быть опечатки, пишу на память и примерам)
resource "aws_opensearch_domain" "opensearch"
...
advanced_security_options {
enabled = true
internal_user_database_enabled = true
master_user_options {
...
}

и
resource "aws_opensearch_domain_saml_options" "saml"
...

- ну ещё там добавились всякие политики, роли и так далее. Я думаю это и так очевидно. Перечислять не буду.
- со стороны коллектора логов (Vector/VMlogs/fluentbit/Fluentd etc - выбор у каждого свой) добавилась ещё и авторизация - если УЖЕ включили SAML/SSO на OpenSearch, то для банальной отправки логов в хранилище будет авторизация.
- добавить 3 Azure enterprise application - на каждый из энвов, ведь Reply URL (Assertion Consumer Service URL) везде разный. Если был бы один опенсёрч на все энвы - был бы один аппликейшн.
- коммит и отправить MR, получить аппрув

Внесли в код, начали апплаить - а всё работает!
Коллектор логов шуршит, собирает логи, асьюмит роль, монтирует токен, цепляется к опенсёрчу, проходит авторизацию, аутентификацию и успешно делает PUT логов.
Пользователи отлично заходят на OpenSearch UI через SSO и всё работает.
На всё ушло около 4 часов чтения доков и вроде полутора-двух часов для апплая и отладки.

Ок, идём к Stage окружение, там повторяем примерно всё, что написано выше, правим мелкие недочёты если есть.
Почти не было кода, лишь просто выполнять команды в порядке, описанном в тикете.
На всё ушло вроде меньше 2 часов.
Всё так же работает.
Please open Telegram to view this post
VIEW IN TELEGRAM
15👍3
#aws #opensearch #elasticsearch

Поехали к проду 🍷
Объявили время на апдейт, утвердили даты на выходные, ну а когда же.
Время апдейта? А время апдейта я написал, что "да за 6 часа управимся!"🐒🐒🐒
О, как же я ошибался в сроках.😭

Короче из-за того, что на проде очень жирные инстансы, многократно больше даты в индексах, больше инстансов, сроки апгрейда вышли из берегов благоразумия и границ моральной устойчивости.

Пристегните ремни, а теперь объявляем реальное время апгрейда😁:
- первый этап обновления до последней версии эластиксёрча - 12 часов.
- обновление до опенсёрча - 19 часов.
- обновление до последней на тот момент времени версии опенсёрча - 15 часов.
- включение SAML и авторизации - 18 часов.
- переиндексация (а потому что смотри первый пункт) - 49 часов.
На стейдже и деве не было конфликтов, а на проде не хватало полей и после апдейта просто рефреш не помогал, помогла только реиндексация всего. Поиндексно.

Подводим итоги:
вместо "да тут приключения на 20 минут", миграция заняла больше недели.
Я же не могу постоянно быть у ПК, а значит процесс запущенный днём, мог завершиться в 2 ночи, но я у ПК только в 10.00 утра, на этом тоже потеря времени.
Всю неделю логи были то доступны, то нет, во время обновления SAML вообще не было ничего доступно - доступ без пароля УЖЕ не работал, но доступ по SSO ЕЩЁ не работал.

Написание плана и кода вообще - не сложно.
Адекватно оценить сроки апгрейда прода - сложно.
П - планирование. 🚬

- - - - - - -

"К чему эта заметка, Алекс?" - спросите вы.
А всё просто: календарь напомнил, что через месяц заканчивается стандартная поддержка - самое время обновляться.
Ссылка на детали тут: https://aws.amazon.com/blogs/big-data/amazon-opensearch-service-announces-standard-and-extended-support-dates-for-elasticsearch-and-opensearch-versions/
Там расписано всё и по OpenSearch, и по Elasticsearch.

У меня тех старых кластеров уже нет, но если у вас ещё остались, то у вас примерно месяц, чтобы успеть с апдейтом и миграцией.

РЕБЯТА, У ВАС ОСТАЛСЯ ВСЕГО МЕСЯЦ НА ТО, ЧТОБЫ ЗАПРЫГНУТЬ В ЭТО УВЛЕКАТЕЛЬНОЕ ПУТЕШЕСТВИЕ МИГРАЦИИ ВСЕГО НА 6 ЧАСОВ! 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
1😁14🔥51🫡1
#azure #aws

AWS:
- У нас авария, ничего не понятно, только не пишите в статус пейдже, в самом начале инцидента, что это DNS, пока пишите про что угодно, но не про DNS, ну не знаю, пусть будет свой собственный мониторинг и DynamoDB.

Azure:
- У нас авария, ничего не понятно, там у AWS недавно был DNS, давайте напишем в статус пейдже, что у нас тоже DNS, а потом поменяем на реальную причину, вдруг там AFD.
🤣26👍1😨1
#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
#aws #ecr #kubernetes

Представим себе, что у нас есть некий софт.
Он разрабатывается внутри компании и версионируется.
Результат сборки это готовый имадж в реджистри провайдера.
Затем мы этот софт предоставляете клиентам.
У софта есть версии и он разворачивается на одном регионе AWS.
Прям буквально клиенты тыкают в веб-интерфейсе, выбирают версию софта и регион(ы), в котором хотят видеть и дальше ваша, ну допустим назовём её "платформа" раскатывает софт нужной версии на нужных регионах.

Всё очень просто:
- собрали новый софт с новой версией
- готовый имадж положили в реджистри
- кастомер может задеплоить в доступный(е) регион(ы)

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

Katastrofa!

Варианты, которые сходу приходят в голову:
1) ECR Replication - автоматическая репликация имаджей между регионами.
Классная шутка, работает, но не покрывает кейс со "старыми имаджами", работает только для новых имаджей и всех последующих.
Да и она уже включена.
2) Pull-Through Cache - великолепная штука, работает для всех внешних реджистри (ghcr.io, dockerhub, quay.io и тп).
Фича так же уже включена.
3) скрипт на питоне, которые будет синкать старые/имаджи при подключении нового региона
Только не в Слизерин! Лишь бы не скрипт, лишь бы не скрипт 🎩
4
) не разрешать кастомеру деплоить на новые регионы старые версии
5) AWS event bridge + Lambda (не сработает, лямбда вроде не умеет работать с PTC)
6) схемы типа "ECR-to-ECR PTC"
https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache-private.html (именно приват)
Pull-Through Cache для private-ECR-to-ECR-private работает (с 2023 года) с IAM аутентификацией.
Там есть пара ограничений, но для нашей задачи это подходит.
7) уход от парадигмы "один регион = имаджи только из регионального ECR", пуллить все из us-east-1
Смотрю
- как часто происходят релизы софта
- размер этих внутренних имаджа(ей)
- биллинг хранения и трафика ECR
- как часто будут разворачиваться новые регионы и как часто в новых регионах захотят старый софт
Дополнительно ресёрч:
- политики/доступы из других регионов в дефолт ECR из разных EKS
- kyverno policy (не будет ли перетирать путь)
- пулл секрет в новом регионе содержит правильный путь?

Минусы последнего решения понятны:
- цена: хранение/трафик (спойлер - увеличение на 13 баксов в месяц)
- latency на первый пулл

Мне нравятся оба варианта:
- Private Registry Upstream
- дефолт ECR для разных EKS

Дальше все факты, аргументы, расчёты, плюсы и минусы руководству.
Первый вариант это элегантность и оптимизация, второй это простота.
А какой уж вариант выберет руководство мы пока и не знаем. (знаем 👍)
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥62
#aws #costoptimization #devops

А иногда вообще ничего из существующих финансовых алертов не помогает.
Нужен глубокий, живой человеческий анализ.


Был случай: проект, всё на AWS, стартап.
Постепенно рос, изменялся, но изначально у всех всюду был root-доступ (а как иначе в стартапе из 4 человек?). Набирались люди, улучшались процессы, разграничивались доступы, всё заносилось в IaC.
В целом стоимость услуг AWS была сравнительно небольшой, от 2к до 5к долларов, и держалась года полтора-два.
Раунд за раундом, компания выросла, трафика и сервиса стало больше, увеличился и счет.
Затем начали оптимизировать затраты: внедрили RI, SP (Reserved Instances, Savings Plans) и другие методы.
Обвешали обычным алертингом и FinOps-инструментами вроде Cost Anomaly Detection.
Каждые 1-3 месяца проводились Cost Review meetings, на которых обсуждались траты, предстоящий рост и многое другое. Каждая, повторюсь, позиция в биллинге детально разбиралась и для каждого участника команды и руководителя была очевидна, понятна и разумна.

Всё вышенаписанное лишь для того, чтобы подчеркнуть, что ничего нестандартного тут не было, всё как у всех.

Каждый месяц счет всё рос и рос. Где-то разумно за Compute - воркеры в EKS, где увеличилось количество реплик.
Где-то за RDS, потому как и размер БД увеличивается, и инстансы примерно раз в полгода-год увеличивали, да бэкапы (snapshots) также увеличивают стоимость хранения.
Где-то CloudFront, потому как количество клиентов и трафика стало больше.

Приходили и письма от Cost Anomaly Detection: "сервис А увеличился на 20% - теперь 21 доллар", "сервис Б увеличился на 47% и теперь 11 долларов".
И такие письма приходили регулярно.
Визуально - всё разумно и понятно:
- увеличивается количество кастомеров
- увеличивается трафик и нагрузка
- немного растет стоимость услуг

Однако пришел момент, когда счет за услуги CloudFront вырос до умопомрачительной отметки в 1000 долларов в месяц.
На очередном Cost meeting поставили задачу проверить корректность настроек, везде ли правильно включено кэширование, заголовки и так далее.
Триггернулись лишь потому, что на старте компании платили порядка 30 баксов, спустя год 150, затем 400 через два года, а тут сразу $1000 - слишком большой скачок.

Задачу поручили мне, и я начал копать.
Признаюсь - я ничего на тот момент не понял.
Ну ALB, CloudFront да API Gateway.
Много ресурсов, разные.
Поверхностно изучил еще раз - да, вроде очевиден рост как клиентов, так и трафика и биллинга.
Отписался "да всё норм", закрываю таску.

Спустя месяц счет стал уже 1250 долларов, и это напрягло всех.
Руководство попросило сделать анализ: как тут можно сэкономить, ведь ожидали рост клиентов x20, а это значит, что потенциально счет будет невероятно огромным.
Требовалось исследование альтернативных архитектурных решений.

Начал я повторно изучать, уже в процессе расписывая куда какой трафик идет, спрашивая разработчиков, смотря DNS, балансировщик, все файлы веб-страницы и многое другое.
Я это изучал лишь чтобы понять, как сейчас что работает, чтобы понять, как и на что мне надо заменить, чтобы снизить косты.

В процессе анализа для переноса архитектуры мне пришел неожиданный вопрос в голову:
а счет за CloudFront это с одного Distribution или с разных?
Начал включать аналитику и овервью.
Определились, что траты лишь с двух Distribution из 25.
Вопреки тому, что все думали изначально, что с 10-15.

Ок, копаю дальше, стало интересно, ведь именно у этих двух Distribution было несколько источников (Origins) и несколько правил поведения (Behaviors).
Мне же надо их на что-то менять, надо копнуть глубже.
👍9
#aws #costoptimization #devops

Затем я включил логирование (Standard log destinations) и положил все логи в S3 бакет.
Оставил на полдня, потом написал Bash-скрипт, который сделал простую выборку, например, top 20 requests by path с сортировкой по пути.
Тут меня ждало очередное удивление - в топе были не файлы S3, а балансировщик нагрузки (ALB) и поды Kubernetes🤡.
То есть, у нас топ трафика - это НЕ кэшируемый трафик! 🤬Через клаудфронт! 🤬
Написал еще пару скриптов - да, у нас внезапно в этой схеме просто трафик шел в EKS и пару мест.

Собрали консилиум с коллегами, рассказал о своей, находке, чтобы спросить "а зачем так?".
Ответ был очевидным типа "ну так было исторически, нам надо было как-то роутить трафик по пути, мы в своё время сделали это через клаудфронт, чтобы не поднимать ещё один балансер или Nginx".
"Пздц" - подумал я и пошёл дальше изучать.
Да, действительно так и оказалось - часть трафика, и это 98% от всего в клаудфронте, шло НЕ для кеша.
Просто в поды кубернетиса.

Быстро напилил альтернативное решение, пару днс записей, проверили на dev - всё работает ок, без клаудфронта.
Дальше стейдж, прод, никаких проблем.

Сижу пишу postmortem.
Понимаю, что надо сослаться на саппорт амазона, типа они негодяи не присылали кост аномали детекшн письма - но нет, присылали.
Думаю может не было реакции - нет, обсуждали повышения цен, даже текстов в чате.

Как так мы пропустили этот момент?
Подняли исторически все данные и за последние пару лет и только тогда картина полностью стала ясна.
- Изначально был временный костыль в одном из клаудфронтов - если путь /path1 то трафик отправлять в X, а если /-path2, то временно в Y.
- этого трафика была изначально мало и он вовсе попадал во free tier.
- затем его стало больше, стали платить 40 баксов, что было допустимо, затем 80 и так далее.
- пока не вырос до 1000 и 1250 долларов на момент кост инцидента.

Почему не обращали внимание раньше?
Потому что рост цены был медленным:
- кост аномали детекшн ловил рост цены примерно раз в неделю, и сумма каждый раз была небольшая
При постоянном медленном росте кост аномали детекшн начал отправлять всё реже и реже письма - это же очевидное поведение стало.
- все дефолт ссылки ведут на "cost and usage report" с коротким промежутком времени типа последние 30-40 дней
- на графиках и в письмах был небольшой рост - ну кто будет заниматься, если на прошлой недели мы платили 9 баксов день, а теперь 11 долларов в день, копейки же

Лишь взглянув на графики за полгода/год/два года/три года, стало ясно, что цена увеличивались постоянно.
Временный костыль начал в итоге вырабатывать 14 терабайт данных трафика.

Косяк девопса? Да.
Косяк костменеджера? Несомненно.
Косяк "временного решения"? Конечно.

Иногда, чтобы узнать, что проблема есть, надо нырнуть чуть глубже, чем "да вроде по делу списывают да не много же".
Иногда, чтобы узнать, что проблема есть, надо расширить графики больше, чем на две недели.

Суммарные потери последних 8 месяцев до этого кост инцидента - 4400 долларов.
Этот path в клаудфронте и не нужен был, просто поленились много лет назад сделать сразу нормально.
Please open Telegram to view this post
VIEW IN TELEGRAM
🫡13👏3😢21👍1
#aws #eks #kubernetes #troubleshooting #одинденьизжизни #argocd

"Алекс, у нас что-то приложение не раскатывается, деплой завис. Кластер новый, вроде ты деплоил, посмотри плиз".

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

На самом деле самый дебильный статус, потому что в практике это может быть что угодно 🐒.
Ладно, надо чинить.

Смотрю сперва "а не может ли кто сражаться за ресурсы?"
Вдруг опять арго и оператор вцепились как два деда.
Я как-то выше писал старый пример про #dapr.

Смотрю:
kubectl get applicationsets APPSET1 \
-n NAMESPACE \
-o jsonpath='{.metadata.managedFields[*].manager}' \
| tr ' ' '\n' | sort | uniq -c

1 argocd-applicationset-controller
1 helm

А тут всё нормально:
- helm один раз создал/обновил сам ApplicationSet
- argocd-applicationset-controller дальше управляет этим ApplicationSet и генерит Application(ы)
Значит дело не в конфликте.

Иду дальше, что с самим аппликейшн
kubectl get applications -n NAMESPACE \
-o wide | grep APP1

APP1 Synced Progressing

И нет ли там конфликтов
kubectl get applications APP1 -n NAMESPACE \
-o jsonpath='{.metadata.managedFields[*].manager}' \
| tr ' ' '\n' | sort | uniq -c

1 argocd-application-controller
1 argocd-applicationset-controller

Конфликтов нет, ведь:
- argocd-applicationset-controller следит за соответствием шаблону ApplicationSet.
Он не ходит в кластерные ресурсы (Deployment, Service и т.д.), только создаёт/обновляет/удаляет сами Application CR.​
- argocd-application-controller следит за тем, чтобы ресурсы в кластере совпадали с Git, и считает health (если хоть один дочерний ресурс не равен Healthy, приложение остаётся Progressing).

Идём дальше. Так какие же ресурсы генерируются и какой у них статус?
kubectl get applications APP1 -n NAMESPACE -o json \
| jq '.status.resources[] | select(.health.status != "Healthy")'

...
{
"kind": "ConfigMap",
"name": "CM1",
"namespace": "NAMESPACE",
"status": "Synced",
"version": "v1"
}
...
{
"health": {
"status": "Progressing"
},
"kind": "Service",
"name": "SVC1",
"namespace": "NAMESPACE",
"status": "Synced",
"version": "v1"
}
...

Ага! Попался!
Какой-то service в процессинге.

Что там с тобой:
kubectl get service SVC1 -o yaml -n NAMESPACE
...
type: LoadBalancer
status:
loadBalancer: {}

Визуально вроде ок,
Стоп, нет,не ок.
Ведь это означает, что контроллер, отвечающий за provision (создание) AWS Load Balancer, не смог выполнить свою работу, и поэтому поле status.loadBalancer.ingress не заполнено IP-адресом или Hostname.
* У нас LoadBalancer создаётся контроллером AWS.

Ещё раз проверяю в дескрайб:
kubectl describe service SVC1 -n NAMESPACE
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal EnsuringLoadBalancer 55m ***** Ensuring load balancer


Ладно, иду в неймспейс, где обычно лежит AWS Load Balancer, логи смотреть.
А там нет ничего! Удивляюсь, смотрю
kubectl get pods -A | grep -i load

Нет ничего!
Спрашиваю коллег, а мне ехидно говорят "да ты же сам не задеплоил лоадбалансер в этом новом кластере".
https://github.com/kubernetes-sigs/aws-load-balancer-controller

Смотрю свои коммиты, MR - и правда, именно в этом кластере я забыл добавить AWS LB.🐒
Пилю новый MR, добавляю LB, жду, когда всё поднимется - всё, проблема решена!
У проблемного applicationsset статус стал Healthy.

Добавляю в документацию по кластеру, что "не забыть про LB и иду работать дальше".
Ну хорошо, хоть это не продуктовый кластер, без клиентского ворклоада 🤡
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18
Приветствую всех.

Поскольку все читатели здесь ради контента, а не моей биографии, сразу перейду к сути.
Этот блог - мои заметки на полях.
Почти не делаю репосты, пишу для души и лишь когда на это есть время/желание.
Обычно это 2-4 поста в неделю.

В основном делюсь:
- информацией, которую узнал только что (даже если она пятилетней давности, но я узнал её сейчас)
- лонгридами, байками или всратыми историями, без указания срока давности
- последовательным описанием моего процесса мышления на работе при решении задач

Интересные, на мой взгляд, сообщения я публикую с тегами:
- пример основных тем канала:
#aws #azure  #kubernetes  #troubleshooting  #costoptimization  #longread  #devops
- пример второстепенных категорий:
#terragrunt  #victoriametrics  #git #docker  #одинденьизжизни  #helm
- для того, чтобы на работе не поехать кукухой, у меня есть:
#пятница  #всратость  #байки

Сообщения без тегов это просто шутка-минутка или мысль, которая была актуальна лишь на момент написания.

Все заметки не имеют строгой последовательности, читать их можно как угодно:
- начать с самого основания канала (за год постов около 230)
- использовать интересующие теги/поиск
- ну или просто начать с новых постов, пропустив всё ранее написанное 😭
Каждый решает, как ему удобно.

Буду рад, если мои заметки помогут кому-то узнать что-то новое, избежать повтора чужих ошибок или просто улыбнуться.
На крайний случай, самоутвердиться за счёт моих факапов или незнания 🐒
Всем привет и желаю приятного чтения.
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍31👨‍💻1
#costoptimization #aws #cloudwatch #одинденьизжизни

"Алекс, у нас снова траты по амазон, глянь по биллингу, чего можно скостить".

Иду в billing, выбираю последний месяц.
Дааа, много всего опять зачарджило. Чего можно убрать?
О, в топ 10 есть клаудвоч. Начну с него. Почему так много? Почти $2000+.
Смотрю что не так.
Внезапно много идет за графу
$0.01 per 1,000 metrics requested using GetMetricData API - US East (Northern Virginia)

200.000.000+ реквестов.
Читаю что это.
https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_GetMetricData.html

Ага. Какой-то сторонний механизм/утилита, которая при помощи кредов/роли подключается к AWS API и вытягивает данные по метрикам из клаудвоч.

Из известного мне есть такие два инструмента:
- https://github.com/prometheus/cloudwatch_exporter
- https://github.com/prometheus-community/yet-another-cloudwatch-exporter

Оба эти категории инструмента подключаются к AWS API, дергают метрики, экспортируют в удобный формат, чтобы можно было в victoria metrics/prometheus/grafana видеть эти новые метрики.
Почему новые? Они берут оригинальное название метрики, добавляют префикс и получается новая метрика в нашем хранилище.

Далее поиском по всем репозиториям(гит, арго, хелм) внутри компании ищу - есть ли они у нас.
Да, есть, у нас YACE.

Как оптимизировать? Мне ничего в голову не приходит, как сделать следующее:
- подключаюсь к service через порт форвард
kubectl port-forward svc/yet-another-cloudwatch-exporter 5000:5000

- вытягиваю все метрики в файл
curl localhost:5000/metrics > metrics.txt

- пишу баш скрипт, который считает количество уникальных метрик и всякое такое

Ага, у нас 54.000+ метрик. Это много.
Иду в конфиг в гите, там нечто типа
config: |-
apiVersion: v1alpha1
discovery:
exportedTagsOnMetrics:
AWS/NetworkELB:
- tenant-id
- type
- cluster
jobs:
- type: AWS/NetworkELB
regions:
- us-east-1
...
- eu-central-1
- eu-central-2
period: 300
length: 300
metrics:
- name: ActiveFlowCount
statistics: [Average, Minimum, Maximum]
- name: ActiveFlowCount_TLS
statistics: [Average, Minimum, Maximum]
...
- name: UnHealthyHostCount
statistics: [Average, Minimum, Maximum]
- name: PortAllocationErrorCount
statistics: [Sum]
...
- type: AWS/TransitGateway
regions:
- us-east-1
...
period: 300
length: 300
metrics:
- name: BytesIn
statistics: [Average, Minimum, Maximum, Sum]
- name: BytesOut
statistics: [Average, Minimum, Maximum, Sum]
- name: PacketsIn
statistics: [Average, Minimum, Maximum, Sum]
...
- type: AWS/NATGateway
regions:
- us-east-1
...
- eu-central-2
period: 300
length: 300
metrics:
- name: ActiveConnectionCount
statistics: [Maximum, Sum]
- name: BytesInFromDestination
statistics: [Sum]
...

И такого там много.

По частоте опроса вроде ок - 300 секунд, около бест практис для клаудвоч экспортёров.
А чего подрезать-то тогда?
Нужен ли max? Min? Avg?
Следим ли мы за натгейтвеем?

А всё просто.
- при том же bash скрипте расписываем все уникальные метрики и их итоговое название.
- идём все репозитории с алертингом и мониторингом (alertmanager, grafana irm, vmalert, grafana dashboards etc)
- если метрика нигде в observability не используется - мы её просто убираем из конфига. Где-то только min, где-то max, где-то полностью всю метрику со всеми значениями

Даже если удастся убрать хотя бы 50% неиспользуемых метрик - это минус 50% от биллинга, а это $1000+
Пулл реквест, ревью, аппрув, раскатываем.
Всё, в следующем месяце ждём снижения костов на клаудвоч на 50%+
👍13👏4🎄2🤷‍♂11❤‍🔥1
#aws #aurora #rds #troubleshooting

Не уверен, что материал будет простым, но публикую как есть, как всё знал на момент этой стори.


Ко мне снова обратились с привычной проблемой: AWS, есть Aurora MySQL 3.*, большая нагрузка по кверям, увеличили инстанс, добавили CPU и памяти, даже меняли IOPS - но тормоза не ушли.
Особенно заметно на пиковых нагрузках, когда в систему летят десятки тысяч инсертов в секунду.

Знакомая история, которая всегда начинается с "мы уже всё перепробовали" и заканчивается у моего терминала.
Таска говно - впарить лоху (мне).
Не, ну ладно, зарплата сама себя не отработает, поехали разбираться.

Делаю привычное SHOW PROCESSLIST - вижу волну wait/io/redo_log_flush.
Коммиты висят по 50-100 мс, хотя дисковая подсистема вроде бы не жалуется.
Первое, что приходит в голову: а чо там, а чо, давайте смотреть на sync_binlog.
SHOW VARIABLES LIKE 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+

SHOW VARIABLES LIKE 'sync_binlog';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog | 1 |
+---------------+-------+

Сука.

Бинлог включен. Синхронный.
Каждый коммит ждёт фсинка на диск.
На Aurora MySQL 3.x, где кто-то раньше включил Enhanced Binlog (по дефолту он выключен, но у нас был исторически включён).

Вспоминаю, что на этом проекте был Дебезиум. Вроде бы.
Ладно, зацепка, иду в поиск.
Поднимаю историю по слаку, страницам RFC/PoC в Confluence.

Года три назад у нас был активный конвейер CDC на Debezium - реплицировали данные в Snowflake для аналитики.
Потом проект закрылся, команда разошлась, а инфраструктура осталась.
Кто-то выключил Debezium, кто-то отключил коннекторы, но binlog почему-то остался включённым.
"На всякий случай", "вдруг понадобится", "не трогай, оно работает".
Ну или девопс забыл 🤡 (а девопс там был я, фить-ха! )

Только вот работает оно медленно. Каждый INSERT ... VALUES (...), (...), (...) из условных 5000 строк теперь не просто пишет в таблицу, а ещё и в binlog, синхронно, с фсинком.
На пике нагрузки это добавляет 20-30 мс к каждому батчу.
А когда таких батчей 100 в секунду - получаем как раз наш bottleneck.

Перед тем как вырубать, нужно убедиться, что никто не читает этот binlog.
Вдруг там не только дебезум был.
Сперва в консоли нечто типа:
aws dms describe-replication-tasks --region us-east-1 --query "ReplicationTasks[?Status!='stopped'].ReplicationTaskIdentifier"

aws iam list-roles --region us-east-1 --query "Roles[?contains(RoleName, 'DMS')].RoleName"

aws rds describe-db-parameters --db-parameter-group-name aurora-mysql3-params --query "Parameters[?ParameterName=='binlog_format'].ParameterValue"


Затем аудит самой MySQL:
SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000123 | 1073741824 | No |
| binlog.000124 | 1073741824 | No |
| binlog.000125 | 536870912 | No |
+---------------+-----------+-----------+

Логи растут, но кто их читает?
Бегу проверять:
SHOW REPLICAS;
Empty set (0.00 sec)

SELECT * FROM mysql.ro_replica_status;
Empty set (0.00 sec)

Никаких реплик.

Проверяю в AWS Console - DMS tasks нет.
Проверяю во всех Kubernetes - нет подов с Debezium, нет коннекторов.
Проверяем в IAM - нет ролей для DMS.
Проверяем в CloudWatch - нет метрик от коннекторов.

Теперь самое главное: спрашиваем бизнес. 😁
Собираем три команды: дата-инженеров, аналитиков, девопсов.
Вопрос простой: "Кто-нибудь реплицирует данные из Aurora куда-то ещё?"

Ответы:
- "Нет, мы теперь всё через Kinesis делаем"
- "Нет, мы используем S3 snapshots"
- "Нет, у нас только internal реплики на Aurora"

Сука.

Ок, Убедился, что binlog никому не нужен.
Теперь можно всё кильнуть.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥4🥰1
#aws #aurora #rds #troubleshooting

Делаем бэкап параметр группы (привычка), потом выключаем в правильном порядке:
Сначала проверяем, включён ли Enhanced Binlog:
SHOW VARIABLES LIKE 'aurora_enhanced_binlog';

Если 1 - значит, он был активен.
Тогда выключаем его правильно:

Выключить Enhanced Binlog:
aurora_enhanced_binlog = 0
binlog_backup = 1 (возвращаем обычный бэкап)


Перезагружаем. Ждём перезагрузки.

Выключить сам binlog:
binlog_format = OFF

Ещё одна перезагрузка.

Важно (тут переуточнить в документации, если сами будете делать):
В Aurora MySQL 3.x binlog_backup = 0 используется только когда Enhanced Binlog включён. Если вы просто выставите binlog_backup = 0 без aurora_enhanced_binlog = 1 - это неправильная конфигурация. Правильный порядок: сначала гасим Enhanced Binlog через aurora_enhanced_binlog = 0, потом уже можно вырубать binlog_format = OFF.

Уже через 10-20 минут после перезапуска смотрим на графики.
А там у нас победа!!!!
Чо там чо там:
- CommitLatency снизился с 45ms до 12ms (99 перцентиль)
- WriteThroughput остался тем же, но WriteIOPS упал на 23%!!!!!!!! (только для этого проекта)
- wait/io/redo_log_flush в Performance Schema перестал быть топ-1 контрибьютором latency
- Инсерты, которые раньше тормозили на 30-50ms, теперь выполняются за 8-15ms

Профиты повсюду:
- Команда разработки радуется.
- Мониторинг перестал краснеть.
- Яблоки вторым урожаем пошли.
- AWS Bill на IOPS снизился на $??? в месяц (не осталось в записях точная сумма).

Итоги:
- Всегда аудируйте binlog.
Даже если вы думаете, что он выключен - проверьте. SHOW VARIABLES LIKE 'log_bin' - ваш лучший друг.
В Aurora MySQL 3.x по дефолту Enhanced Binlog выключен, но могут остаться включёнными legacy-настройки.
- CDC - это не навсегда.
Когда выключаете Debezium, DMS или любой другой CDC инструмент - проверьте, что выключили всё.
Включая aurora_enhanced_binlog.
Особенно на Aurora, где Enhanced Binlog не нужен для internal репликации.
- синхронный binlog на write-heavy workload - убийца performance.
sync_binlog = 1 + инсерты = гарантированная latency.
Если вам не нужен binlog - выключайте через правильную последовательность: сначала aurora_enhanced_binlog = 0, потом binlog_format = OFF.
Если нужен - используйте Enhanced Binlog, он асинхронный и влияет на производительность меньше.
- документируйте зависимости
Если три года назад кто-то включил binlog для CDC - должна быть документация.
Если её нет - придётся расследовать, как детектив.
Используйте aws rds describe-db-parameters для аудита.
- не бойтесь выключать то, что не используете.
База не обидится. Зато станет быстрее.
Но делайте это в правильном порядке и в maintenance window.
- наймите уже DBA-шника, хватит мучать девопсов 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
11🔥6😁2