Make. Build. Break. Reflect.
909 subscribers
115 photos
1 video
119 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
#одинденьизжизни #longread #kubernetes

Импровизация.
Лонгрид.

Да-да, часть моей работы это импровизация.
Зачастую бывает ситуация, что мне надо что-то решить, а я ну воообще не понимаю что это такое.

Сложилась такая ситуация, что у нас перестал работать стабильно некий сервис.
Пользователи/коллеги жалуются: "при работе с фронтенд приложением видим ошибку ****, помогает рестарт нескольких подов в кубере".
Речь идёт про DEV, но потенциально может аффектить и PROD часть.
Продакшн нам нельзя ломать, иначе меня уволят и мне нечем будет хвастать в интернетах успехами своих успехов.

Идём разбираться.
Какая ошибка? Ошибка в вебе звучит примерно так
{
“errorCode”: “ERR_DIRECT_INVOKE”,
“message”: “failed to invoke, id: REPLACEMESERVICENAME, err: failed to resolve address for ‘REPLACEMESERVICENAME-dapr.REPLACEMENAMESPACE.svc.cluster.local’: lookup REPLACEMESERVICENAME-dapr.REPLACEMENAMESPACE.svc.cluster.local on 10.0.12.10:53: no such host”
}

Я честно говоря хер знает что такое инвоук, что такое дапр и уж тем более не знаю почему такая ошибка возникает.
И почему тут вообще ресолв, с*ка. Причем тут внутренний DNS. Вы поехавшие что-ли?

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

Ок, смотрим что такое DAPR.
Читаем официальную документацию. https://dapr.io/
На основании документации выносим для себя определение что это.
DAPR (Distributed Application Runtime) - это фреймворк для упрощения разработки микросервисов, предоставляющий готовые решения для обмена сообщениями, управления состоянием и взаимодействия сервисов. Используется для создания масштабируемых и устойчивых распределённых приложений.
Ага, нихера не понятно. Очередная всратость для кубернетеса.
Что-то должна упростить, осталось понять кому она упрощает и как.
Смотрим, а что же у нас есть.

У нас есть отдельный неймспейс, в котором стоит
- dapr оператор
- dapr dashboard
- dapr-sidecar-injector
- dapr-scheduler
и всякое подобное.
Почитал логи, порестартил поды, посмотрел ивенты. Ничего не понятно.
Не понимаю чем это должно мне помочь, читаю дальше и разбираюсь по коду.

А как деплоится этот проблемный сервис?
>ky pods REPLACEMESERVICENAME-7766b4d5c-w66vc
apiVersion: v1
kind: Pod
metadata:
annotations:
dapr.io/app-id: REPLACEMESERVICENAME
...
dapr.io/enabled: "true"
...
labels:
app: REPLACEMESERVICENAME
...
dapr.io/app-id: REPLACEMESERVICENAME
dapr.io/metrics-enabled: "true"
dapr.io/sidecar-injected: "true"
...
metrics_scrape_enabled: "true"
metrics_scrape_from_pods: "true"
pod-template-hash: 7766b4d5c
release: REPLACEMESERVICENAME
tier: web
track: stable

По аннотациям видно, что генерация манифеста идёт через хелмчарт.
Ага, дошёл до helm чарта, у нас есть параметр-аннотация для дапра.
Понимаю, что при добавлении
dapr:
enabled: true

Для каждого сервиса, где включен этот параметр, добавляется сайдкар.
Круто, хоть куда то сдвинулся в понимании что есть что.
👍51
#одинденьизжизни #longread #kubernetes

Промежуточное мнение: у нас есть N сервисов, между собой они используют dapr сайдкар контейнеры, который инжектится при помощи оператора и сайдкар инжектора при добавлении параметра в values файле.
Ага, ну хоть что-то.
Смотрим а как вообще роутинг устроен:
- в пределах кубера
- за пределами кластера кубера
Внутри кубера через DAPR (ну то есть через SVC адрес), снаружи через кастомный ингресс контроллер с кучей стрёмных аннотаций и хидеров.
Давай, Алекс, решать, куда будем копать.
Логика самой первой ошибки подсказывает, что ошибка не на уровне ингресса.
И как бы мне не хотелось с кровью выкорчевать весь этот кастомный ингресс контроллер из левой репы и сделать "по нормальному", мне сейчас надо чинить сервис и траблшутить ,а не правда матку рубить и настраивать как по книжке.
ПлАчу, засовываю своё негодование в известное место и забиваю на ингресс, иду в сторону service сущностей кубера.

Что мы имеет на данный момент? В валуес файл добавили параметр, оператор добавляет сайдкар, другие сервисы обращаются по адресу service к падающему сервису и получают иногда(с*ка, да как так то??!) ошибку.
Причём рестарт пода помогает.

Методом наблюдательности глаз вижу, что у нас на самом деле ДВЕ проблемы.
Первая проблема - у некоторых подов, где параметр стоит, нет сайдкара.
Я совершенно не знаю как это случилось и почему и как такое детектировать.
Делаем рестарт пода - появляется сайдкар с dapr.
Чтобы "поиграться" в будущем с такими ситуациями, мне нужен алёрт, который будет это отслеживать и рапортовать мне в слак. Я увижу проблемную ситуацию и буду иметь возможность в реалтайме траблшутить.
Пишем алерт
- alert: DAPR_ibanni_1
expr: |-
(
sum(kube_pod_annotations{annotation_dapr_io_enabled="true"}) by (cluster, exported_namespace, pod)
) unless (
sum(kube_pod_container_info{container="daprd"}) by (cluster, exported_namespace, pod)
)
for: 5m
annotations:
summary: "DAPR sidecar is not running on `{{ $labels.cluster }}`/`{{ $labels.exported_namespace }}`/`{{ $labels.pod }}`"

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

Вторая проблема, которую вижу - "что-то" "где-то" "как-то" происходит и перестаёт ресолвится.
Как такое детектировать? Оно же не всегда.
Идём в документацию кубернетиса. Читаем что же такое может случится, что адрес service есть, но ресолва нет.
Лейблы? Селекторы? Херекторы? Я не знаю.
Документация сходу ничего не подсказывает и вот тут я иду в нейронку и спрашиваю напрямую "какие бывают случаи, когда у меня дапр сайдкары и в какой-то момент времени service есть но ресолва нет". Помучав вопросами я понимаю, что проблема в kubernetes endpoints.
Получив ключевые слова от нейроники я снова ухожу в документацию кубернетис.
Оказывается если ПОД не реди, то из endpoints пропадает IP адрес POD и service в кубере перестаёт ресолвится.
Ура! Зацепка! Под нот реди.
👍6
Лезем в конфигурацию, тут есть и стартап пробы и лайвнесс и рединес.
Наша формула "что-то" "где-то" "как-то" приобретает очертания.
ПОД не проходит проверку лайвнес/рединесс пробы, меняет состояние на нод реди, его IP адрес удаляется из endpoints и ресолв перестаёт работать для ДРУГИХ сервисов по его имени service.
Пилим второй алёрт, чтобы детектировать (ну не буду же я постоянно сидеть с этой проблемой или ждать гневного письма босса).
- alert: DAPR_ibanni_2
expr: |-
kube_pod_status_ready{condition!="true", exported_namespace!="velero"} > 0
and on (pod, exported_namespace, cluster)
kube_pod_annotations{annotation_dapr_io_enabled="true"}
for: 30s
labels:
severity: critical
annotations:
summary: "DAPR. No route to `{{ $labels.cluster }}`/`{{ $labels.exported_namespace }}`/`{{ $labels.pod }}`"

Тут я отсеял велеро, который за каким-то рожном сюда попадал и я просто отфильтровал его, даже не хочу и 0.0001мс тратить на траблшутинг ещё и велеро.
Теперь у нас есть два алёрта, которые как минимум мне просигнализируют раньше пользователей и коллег, что у нас беда.
Уходим на обед.
.
..
...
"Тревога, тревога, волк унес зайчат!" - кричит мне слак и я бегу с кухни в кабинет.
Попался, дапр тебя подери.
Вижу ситуацию, пишу коллегам в слак "всё под контролём, Алекс всё знает и чинит уже".
А проба не проходит, вот да, видимо по ивентам, по дескрайбу, по стейту.
В некоторых случаях есть даже рестарты, exit code НЕ 137/OOM - для ДРУГИХ подов с похожей проблемой.
А что в логах? А в логах у нас ничего.
Лезем к разработчикам, смотрим, что там dotnet и включаем debug левел прям для dev стенда, а что вы мне сделаете.
Уходим допивать чай.
.
..
...
Спустя время снова алёрт.
Методом метрик понимаем, что в целом проблема с 1-2 приложениями в кубере, они как бы core часть и к ним по servicename лезут другие приложения и если этот падает или недоступен - лежит весь продукт.
Уже есть логи и мы видим, что в логах у нас outofmemory.
То есть не на уровне kubelet/кубера, а на уровне приложения внутри.
Смотрим в графане CPU/Memory usage, фильтруем по имени деплоймента и видим исторически, что все без исключения ПОДы текут по памяти как барышни на концерте Энрике Иглесиаса.
Пишем разработке "мы вроде нашли проблему, у нас память течёт".
Они снимают дамп(а я не умею(((( и научится не успел, они сняли раньше) и уходят на перекур.
Спустя пару часов выкатывают фикс, несколько часов тестирования, смотрим по метрикам - всё ок.

Проходит ещё 1-2 дня и жалобы заканчиваются, алёртов тоже нет.

Проблема, при которой не работал ресолв, решилась.
Что было: приложение текло по памяти, ничего не писало в логи, его не убивали по ООМ, он просто переставал работать. Некорректные настройки liveness probes позволяли ему 503-ть при пробах, он переходил в not ready, его IP адрес пропадал из Endpoint k8s, переставал ресолвится по service имени, весь продукт падал.
Сейчас всё ок.
Количество реплик больше 1 на конкретно данном приложении сделать нельзя, оно не масштабируется, и даже стратегия там ReCreate. Ну вот такое приложение. Это не ко мне вопрос. Только 1 реплика.

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


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

Из плюсов:
- узнал про эту технологию
- написал лонгрид о чём я думаю в такие моменты и как работает моя голова
- очередной раз доказал самому себе, что стартовать траблшутинг с нейронки не стоит, она бы 10000% завела бы меня не туда. Потом пользоваться можно, но не со старта.
- починили приложение и всё начало работать стабильно
Минусы:
- опять потратил куча времени, никто не оценит человеко-часы таких погружений
👍12🔥6🤔2
#aws #rds
Короткий материал на уровне senior+

Нырни на самое дно. Без страховки.

Иногда, когда проект достиг своего максимума на данный момент, приходится оптимизировать уже не софт и не пайплайны, а саму инфраструктуру.
Например параметры движка или базу.
Так и вышло в этот раз.

Традиционные параметры типа CPU/memory usage, connections, memory usage, latency, IOPS, lags, cache, CRUD throughput, slow logs и прочее мы научились мониторить и алёртить, что заставило нас оптимизировать запросы к бд и серьёзно снизило нагрузку.

Совсем недавно мы нырнули глубже и поменяли пару параметров в ущерб потери 1 секунды данных в случае аутейджа со стороны Амазон.
Данный проект не использует критически важные операции для транзакций типа финансовых операций, так что мы имеем право это включить. Если у вас финансовые операции или любые критичные операции вам НЕЛЬЗЯ это включать. Нельзя и запомните это.

innodb_flush_log_at_trx_commit
Значение 2 означает, что журнал транзакций (redo log) записывается в буфер операционной системы при каждой фиксации (commit), но сброс на диск происходит только раз в секунду
- Улучшает производительность, так как уменьшает операции ввода-вывода диска
- Снижает надежность: возможна потеря транзакций последней секунды при сбое сервера/ОС
- Компромисс между производительностью (выше) и надежностью (ниже) по сравнению со значением 1

innodb_trx_commit_allow_data_loss
Значение 1 означает, что система разрешает возможную потерю данных для увеличения производительности
- Значительно ускоряет операции фиксации транзакций
- Снижает гарантии сохранности данных
- Может привести к потере транзакций при сбоях

Результат дал о себе знать почти сразу, но нам явно придётся ещё несколько раз нырять для более точного тюнинга. Будем наблюдать и изменять под себя.

DevOps это не всегда общение, ямлоделие, пайплайны и кубер.
Иногда это тюнинг БД, когда все остальные элементы уже исчерпаны или нет на это бюджета(выше тип инстанса, gravitron v4, больше read реплик, RDS proxy, queries tuning etc).

Ну и код для примера:
resource "aws_rds_cluster_parameter_group" "this" {
...
parameter {
apply_method = "immediate"
name = "innodb_flush_log_at_trx_commit"
value = "2"
}
parameter {
apply_method = "immediate"
name = "innodb_trx_commit_allow_data_loss"
value = "1"
}
}
👍6🔥3
#AI #мысли

Больше года активно использую AI в работе.
Не скрываю, пробовал почти все, от локальных до платных(писал выше и не раз).
Генерация документации, кодогенерация, написание тестов всех типов, авторевью, просто чат и поиск решений.

Все новости "нас заменят AI" читаю пока со скепсисом.
Умные работодатели об AI знают не хуже нашего и они просто начинают оплачивать подписку на AI-tools(CodeRabbit, Cursor IDE etc). Руководители знают, что AI умеют.

Нас, инженеров, не скоро заменят.
У нас появится больше работы, так как привычную/простую работу мы выполняем теперь заметно быстрее.
💯13👍2👏1
#пятница #мысли #AI

Самоё ужасное, что могло произойти с появлением AI, это мириады статей, сгенерированных нейросетями, содержавших в себе непроверенный "мусор" и сбивающие с толку людей, кому приходится это читать.
Вероятно мы должны прийти к тому, что читать технические статьи нужно на популярных площадках с модерацией текста.
👍19💯2
#aws #aurora #eks #troubleshooting #longread

Лонгрид "история одного бага".
Часть 1 из 3.

Веселье.
Жил был стартап. Сперва на пару человек, но уже в облаке.
Пару сервисов в ECS и база MySQL.
Всем было весело и интересно.
Со временем продукт вырос, у него появилось масса нового: новые микросервисы, postgresql(а потом была удалена), Mongo и так далее. Обычный жизненный цикл любого продукта.
Стартап перешёл из этапа pre-seed в seed.
Уже есть реальные живые пользователи, кто реально платит живые деньги за продукт.
Это привело к тому, что на инфраструктуре появилась нагрузка.
Из одноинстансовой базы был совершён переход на cluster Aurora(mySQL compatible), был добавлен RDS proxy(а я не знаю зачем изначально), а ECS заменён на EKS.
На самом деле много всего было сделано, но выходе в проекте был макросервис(наполовину распиленный монолит) монорепы и десятки микросервсов.
Потом пришла series a и клиентов появилось сильно больше.
Пришла НАГРУЗКА.

Масштабирование
Продукт, не смотря на то, что это макросервис и монорепозиторий, научили в масштабирование.
Сперва было тупое через HPA и ресурсы, затем появилась KEDA и продукт научился в масштабирование по количеству сообщений в SQS или лагу в кафка топике. Стало не хватать нод, добавили автоскейлер (впоследствии заменив на карпентер).
Да и в целом было сделано на 99% масштабирование и кластеризация всего и вся.
Где-то на этом моменте продукт начал получать весьма негативные фидбеки "ЧОТА ТОРМОЗИТ ИНОГДА" и команда впервые задумалась о мониторинге.

Observability, v1.
Была добавлена графана для инфры, прометиус в куб и опенсёрч для логов(вообще не связан с историей).
Помимо всей инфры в кубах, начали мониторить и алертить стандартные метрики всех ресурсов Амзаона.
невероятно обширная работа, от 60 дашбордов, больше 400 панелей в графане по всем ресурсам стандартных графиков.
И.. и это ничего не дало.

Не верю.
Команда ничего не понимала.
Да как так-то? Все метрики есть, дашборды украдены из интернетов, но любой случай "ЧОТА ТОРМОЗИТ ИНОГДА" по метрикам и графикам не видно.
Начали копать дальше, читать про базы данных, движки, прокси и многое другое.
Стартап вырос из своих штанишек, надо было пересилить себя и разобраться.
Сложность была в том, что никто не понимал что не так то. Откуда вообще копать.
Так же всё усложнялось тем, что нет времени на раскачку и траблшутинг, проблема возникает редко, а надо пилить новые фичи, люди ждут продукт, как и инвесторы.
Шли недели и месяцы. Многие думали, что это не проблема софта, а проблема Амазон. Не верю, картинно возражали они и шли дальше пилить код.
А затем пришёл series b и пришла даже не нагрузка, а НАГРУЗИЩЕ.
х800 трафик и количество от клиентов от того, что было месяц назад.
Проблема стала так же в сотни раз чаще и пришло время её фиксировать.

Observability, v2.
Команда научилась отслеживать slow logs и реагировать на них.
https://xn--r1a.website/makebreakreflect/33
Есть запросы больше 10 секунд? Изучаем query и катим фикс.
И так до тех пор, пока слоу логи не прекратились.
И.. это не решило проблему.

Кубер/хелм/девопс во всём виноват.
Обратили внимание, что частично проблема повторятся, когда идёт деплой или скейлинг.
Проверили всё, пробы, макссёрж, количество реплик, стратеджи, перешли на KEDA с детальнейшим конфигом, пофиксили пару серёезных багов со стороны не совсем корректной настойки проб.
https://xn--r1a.website/makebreakreflect/9
И.. это не решило проблему.
2
#aws #aurora #eks #troubleshooting #longread

Лонгрид "история одного бага".
Часть 2 из 3.

Observability, v3
Было принято решение мониторить поэтапно, каждый элемент инфры отдельно, а не комплексно, как это было ранее.
Например не просто "базу", а "базу поэтапно.
Что приходит на приложении при обращении к прокси, что происходит на RDS proxy, что приходит от прокси до БД, что внутри БД. На всё метрики и новые дашборды. Где не было метрик - писали сами.
Часть в графине, часть в CloudWatch, смотря откуда source был или быстрее/удобнее.
Эээ трейсинг мониторинга?
Первая зацепка попалась впервые за много месяцев, когда появился дашборд RDS proxy и там был замечен спайк.
Честно говоря не представляю, кто первый придумал назвать это спайком, но дальше по всем post mortem данный фигурант назвался спайк. Спайк количество подключений клиента к БД. Вместо частых 300-500 спайки доходили до 40000(в зависимости от типа инстанса и от настроек, их меняли часто во время эксплуатации и траблшутинга)
И.. спайки совпали с временем жалоб "ЧОТА ТОРМОЗИТ ИНОГДА".

Радость, v1.
О чудо, кричала команда, мы нашли причину.
Однако радость быстро омрачилась осознанием того, что никто не знал ээээ а кто собственно создаёт спайки подключений и зачем? Все микросервисы, от монолита, макросервиса монорепы и других микросервисов сидели под одной учёткой на один ендпойнт.

Кастомизейшн, v1
Были созданы отдельные учётные записи для каждого сервиса.
И.. это ничего не дало.
Видно спайк, видно проблему, не видно источника.

Кастомизейшн? v2
Были созданы отдельные RDS proxy endpoint для каждого сервиса отдельно.
https://xn--r1a.website/makebreakreflect/55
То есть каждый сервис со своими подами(независимо сколько их в скейлинге сейчас) подключается через отдельный эндпойнт RDS proxy, с отдельной учётной записи.

Радость, v2.
О чудо, снова закричала команда.
Радость улетучилась раньше, чем прошло эхо от коллективного крика.
Стало понятно, что нагрузка идет только на один определённый эндпойнт, только от конкретного юзера-приложения.
Команда глянула в код и.. это ничего не дало.
Приложением из монорепозитория.
У него буквально 1-2 параметра энтрипойнта других и немного другой функционал.
Сами параметры запуска этого сервиса 100% на влияют на проблему.
Грубо говоря у одного сервиса задача "слушай вебхуки, пушь в базу", у второго "слушай sqs, пушь в базу", а у третьего "слушай кафку, пушь в базу". Всё остальное одинаковое.

Observability, v4
Ок, команда знает про спайк, про нагрузку от RDS proxy от одного сервиса, но откуда оно?
Создаётся ещё несколько новых dashboard, устанавливается Performance insight от AWS.
Команда видит, что есть нагрузка на read/write IOPS область, снова читаются книги, статьи, снова оптимизация кода и инфры.
https://xn--r1a.website/makebreakreflect/115
🤔1
#aws #aurora #eks #troubleshooting #longread

Лонгрид "история одного бага".
Часть 3 из 3.

Финал. Финал без разбивки.
Понимаю, что вы уже устали читать, но конец близок.
И вот мы приходим к состоянию, что команда видит чуть ли не каждый трейс, обращение, логи, слоу логи, мониторинг базовых сущностей и метрик, мониторинг кастом метрик, всё это обвешано алёртингом, а количество дашбордов больше 50(панелей около 450). Господи, да даже влияние запроса на IOPS видно в аналитике.
Все. Ждут. Проблему.
Создана целая вселенная, как во Black and White и теперь под микроскопом смотрим кто же из наших мелких хулиганит.

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

В саппорте сперва сперва катают как в доте новичка тупыми вопросами категории "вы пробовали выключить и включить", на что сразу получают максимально подробный и отчасти грубый ответ "дайте нам другого менеджера" и сразу переключают на очень умного инженера, явно выше первого уровня технической поддержки.
Он анализирует все десятки скриншотов, логов, аргументов, истории траблшутинга и даёт одну единственную ссылку на документацию и ссылку на логи.
первая ссылка https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy-pinning.html
где нас интересует только это
Any statement with a text size greater than 16 KB causes the proxy to pin the session.
А вторая ссылка на логи, которые упустила команда, и где есть чёткое
The proxy can't reuse this connection until the session ends. Reason: The connection ran a SQL query which exceeded the 16384 byte limit.
(это было пропущено при траблшутинге, есть косяк).
Срочный созвон, брейншторм разработчиков за 0.00000001мс, фикс на 2 строчки(ограничение payload).
И.. и проблема ушла.

Итог.
Вот так, в поисках одного бага, который возникал на 1-3 секунды, аффектил всех клиентов, который появился только при нагрузке при росте продукта, позволили потратить больше 11.000 долларов(дебаг, включение фичей, доп услуги, хранение и обогащение метрик, борды, разделение по rdx proxy endpoints и многоие другое), десятки часов траблшутинга, научив всю команду обсервабилити и пониманию КАЖДОГО этапа всех элементов продукта.
Всего-лишь большой payload в базу через прокси.. Всего-лишь большой payload..

*В этой истории упущены ещё два десятка незначительных модификаций, которые либо вели команду не тудла, либо ну совсем ни на что не влияли, а текста отняли бы ещё на пару постов.
🤯24🔥10👍6
#devops

Дотфайлы в репозитории: что, сколько?

В 2025 репо без дотфайлов - как микросервис без контейнера: не взлетает.
Они решают всё: от фильтра мусора до CI/CD и AI IDE.
Сколько их сейчас нужно для стандартного проекта?
5-10 - стандарт для микросервисов.
Меньше - тухло, больше 15 - безусловно перегиб.
Пробежимся по категориям.

1. Игнорирование: убираем мусор
.gitignore - Очевидно, без вопросов. Чистит node_modules, логи, .env. Все знают, что тут.
.dockerignore - Почти все проекты - контейнеры. Фильтрует контекст Docker, чтобы не тащить лишнего. Писал тут https://xn--r1a.website/makebreakreflect/84
.helmignore - Как .dockerignore, но для Helm-чартов. Нужен для репо с чартами или гетерогенных пайплайнов.
.npmignore - Чистит лишнее при публикации npm-пакетов. Только для пакетов.
2. Окружение: переменные
.env - Хранит DATABASE_URL, порты. В .gitignore, без вариантов.
.env.example/development/production - Шаблон(ы) .env для разных окружений.
3. Инструменты: линтеры, форматтеры, хуки
.eslintrc - Линтинг JS/TS. Без него в JS/TS никуда.
.yamllint - Линтинг YAML - K8s, CI/CD. Если YAML много, надо.
.tflint - Линтинг Terraform. Для Terraform-проектов.
.prettierrc - Форматирует код. Почти везде.
.nvmrc - Фиксирует версию Node.js. Для Node.
.huskyrc - Преккоммит-хуки. Автопроверки перед коммитом.
.lintstagedrc - Линтинг только изменённых файлов. Работает с Husky.
.pre-commit-config.yaml - Конфиг для pre-commit. Например запускает линтеры (tflint, ESLint, chehov) перед коммитом. Для автопроверок. Must have как по мне для всех.
5. CI/CD: автоматизация
.github/workflows/*.yml - Пайплайны GitHub Actions. Для GitHub CI/CD.
.gitlab-ci.yml - Пайплайны GitLab. Для GitLab.
6. Тесты и сборка
.jest.config.js - Конфиг Jest для тестов JS/TS.
.tsconfig.json - Конфиг TypeScript. Для TS.
.vite.config.js - Сборка фронта/бэка. Для сложных сборок.
7. IDE/AI IDE: редакторы
.editorconfig - Единые настройки редакторов. Для команд.
.cursorignore - Исключает файлы для AI в Cursor. Для Cursor. Например с локальным .env и паролями.
.cursorrules - правила Cursor IDE
.cursor/rules/*.mdc - Правила для AI Cursor - стиль кода, ограничения. Для Cursor.
.windsurfrules - Правила Windsurf, стиль, ограничения.
.idea/ - Конфиги IntelliJ. Для JetBrains

Так сколько же? Для бизнес аппликейшн среднее значение 6-8:
.gitignore, .dockerignore, .CICD.yaml, .editorconfig, .pre-commit-config.yaml, .IDE.yaml + для стека
Для helm chart 3+:
.gitignore, .helmignore, .pre-commit-config.yaml, .yamllint.yaml
Для terra*/инфры 5+:
.gitignore, .editorconfig, .pre-commit-config.yaml, .IDE.yaml, .tflint, yamllint.yaml

Бежать и пилить все недостающие файлы? Не, конечно не стоит.
Каждый сам должен для себя решить когда и что ему нужно в проектах и постепенно добавлять то, чего не хватает команде и бизнесу. Взросление не происходит мгновенно.
Если не знаете с чего начать, то спросите любую нейронку какие дотфайлы нужны вам для вашего проекта и какой пример содержимого.
В конце концов есть даже генераторы и готовые сборки, например:
https://github.com/PatrickJS/awesome-cursorrules
https://github.com/sanjeed5/awesome-cursor-rules-mdc/
https://github.com/github/gitignore
https://github.com/toptal/gitignore
(искать через "awesome .DOTFILENAME")

У нас уже почти стандарт 7 файлов по минималке везде, кроме хелм чарта.
В монолит монорепе аж 14 дотфайлов. Все в работе.

* Безусловно я мог пропустить часть дотфайлов, либо забыл, либо не работал с ними.
** .CICD.yaml = ямл для того CICD, который на проекте, от трависа до гитхаба/гитлаба.

Update: конечно я забыл про один из главных файлов .tool-versions - просто мастхэв.
Подробно писал про asdf тут https://xn--r1a.website/makebreakreflect/69
👍101😢1🤡1
#aws #azure #finops

Не стыдно.

А вот совершенно не стыдно за каждый чих клянчить/требовать деньги обратно у клауд операторов, если они не правы.
У меня было 26 обращений за последние 3 года моего опыта с клауд(юбилееейчик), где я просил вернуть деньги за неуказанные услуги или за глюки.
Один из самых интересных, на мой взгляд, для 2024/2025 это внедрение около AI фич со стороны провайдера.
Например новое поведение у AWS cost anomaly.

Был случай, когда я по своей глупости(я об этом ещё обязательно напишу) включал пару фичей, которые быстро слили много денег на CloudWatch.
Проблему увидели через два-три дня с письмом от Cost anomaly detection, все выключили, за косяк пришлось заплатить. (На скрине первый спайк).

Прошло несколько месяцев(!) и руководитель этого проекта кое-что для теста включал и снова по CloudWatch пошли траты, но в этот момент письмо от cost anomaly detection уже не было отправлено. Траты увидели случайно только через несколько дней(на скрине второй спайк). Само собой написали в  саппорт "а чо нет уведомлений?😭".
Получили ответ типа "блабла, у нас ai везде ai. Из-за первого спайка система решила, что этот, второй спайк, это норма, будет исправлено"
Естественно тут же написали "а ведь если бы не ваш баг с неверным определением поведения, мы бы раньше увидели ишшую и погасили бы логи, пусть будет формула такая: 1 день гэп общий, 1 день я/команда слоупок, дальше реакция на письмо, а вот все последующие дни - ваша проблема. Верните деньги за эти дни. Если бы не баг, я бы раньше все узнали бы и выключили".

Получаем ответ "Considering the financial impact, we've approved and processed a billing adjustment in the form of a refund back to your credit card ending in *. Please see Refund ID *** [383.48 USD] here."
🎉 Крохотная, но победа.

Суммарно за весь мой опыт удалось вернуть около $5230 разными обращениями, где виноват был облачный провайдер. Вернуть, к слову, удавалось не всё и не всегда.

Не стыдитесь и не бойтесь требовать там, где вы уверены в своей правоте.
Возвращать свои же деньги не стыдно.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍1
#azure

Сегодня немного ненависти😡, полезной информации не будет.

Одна из самых всратых в ажуре вещей - эти две галочки, которые всегда врут.
Бесит неимоверно.
Хочется в щщи авторам навернуть, вот так бесит.
Please open Telegram to view this post
VIEW IN TELEGRAM
💯15👍2
#бытовое

Мечтал я о столе.
Рабочий стол.
Чтоб был чуть выше обычного(мне так хочется).
Не шатался как припадочный при любом касании(95% столов в масс маркете).
Полочки были выдвижные 6-7 сантиметров высотой внутри, не больше, не меньше, убрать все мелкие вещи, от часов, проводов и документов до приставки и джойстика.
Глубокая столешница 80 см(беречь глаза) и длина 160 см(постоянно приходит кот и занимает всё пространство при столе 120-140см).
Поверхность не шершавая, чтобы локти не были как у бабушки.

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

Денег ушло море, времени тоже море, но результат мне пока нравится.
Пока, безусловно, непривычно сидеть, но я надеюсь на положительный результат и одни плюсы от такого опыта.
Со временем быть может добавлю дополнительный монитор или кронштейн заменю(мне не нравится текущая низкая высота). Может и кабель менеджмент запилю.
Стул La Deshman' меня пока тоже устраивает, но присматриваюсь через пару зарплат к ErgoChair Pro
👍17🔥5💊32
#aws #azure #finops #fuckup

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

1) Datadog + все(!) ивенты AWS EKS + все(!) логи приложений в AWS EKS.
~$11600 за 4 дня из-за кинконглионов событий и метрик.

2) AWS RDS + зачем-то general log + CloudWatch + unlimited retention.
~$2600 за несколько дней(пятница-понедельник утро).

3) Нескончаемое количество раз все ML/DWH/AI сервисов.
Например использование мультирегион дисков с репликацией для кеша в Trino.
~$4300 за месяц.
Или, например ЗАЧЕМ-ТО запуск AWS Kendra раз в сутки(30 баксов в день) вместо запуска 2 раза в месяц
~$150, за 5 дней

4) CloudFront(все настройки по дефолту) + s3 бакетом с файлами дистрибутивами программы + "клиент решил массово установить наш софт по всем компьютерам своей локальной сети через планировщик задач. Однако, как оказалось, прав на установку не хватило, задача вошла в цикл и выкачала несколько десятков терабайт с нашего s3 (файл вроде около 15 мегабайт).
~$450 за 2 дня.
Зато узнали много нового про WAF + код ответа 429 после этого.

5) AWS MSK + огромная цифра на макс сторадж скейлере + изначально большой сторадж + отсутствие алёртов на эти события и нет аномали детекшн + анлимитед ретеншн тайм у топика. Бинго как обычно. Бинго на максималке.
Разработчики начали писать годзиллионы событий в один топик. Нечто типа уровень дебага. Дев энвайронмент.
Каждые 4 часа сторадж скейлился на 10%.
Могу соврать, то там формула будет нечто такого:
Vn = V0 × (1.1)^n

где:
- V0 = 1000 (начальный объем),
- n = 180 (число увеличений за месяц, каждое через 4 часа),
- 1.1 = коэффициент роста (10% увеличение).
Короче как бы там не было, но из 1000 гигабайт, недели через 3-4 (не уследили) уперелось в максимум 16 терабайт и кластер превратился в тыкву.
~$4100 за сторадж отдали.

6) AWS EKS + большое значение макс нод + cluster autoscaler + неверные аффинити/антиафинити. Вместо динамического увеличения нод получили неприлично большое количество нод.
ну типа
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- {{ .Release.Name }}
topologyKey: kubernetes.io/hostname

вместо
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- {{ .Release.Name }}
topologyKey: kubernetes.io/hostname

Вроде всего ~$700 за сутки за EC2 инстансы. Никаких алёртов на количество нод не было, спасло аномали детекшн.

7) Кто-то разместил ссылку на s3 бакет напрямую на веб-портале.
Не срабатывал WAF от CloudFront и снова траты при циклическом запуске со стороны клиента.
~$180 за сутки

8) Какая-то галочка в Azure OpenAI в балансировке.
Честно не помню параметр, но при частом использовании OpenAI в аппликейшн + этот параметр в API Management service сожрал много.
~$11300 за три недели🤡

Были и более мелкие, тысяча стори🤦🏽, но там уровня "девопс инженер пошёл в бар и потратил больше".
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14😨43🔥1🤡1
#aws #vanta #ai #azure

На этом моём скриншоте прекрасно всё.
Добро пожаловать в 2025 год.

Vanta, компания, которая за огромные деньги позволяет проходить аудит от SOC2 до PCIDSS, при помощи AI генерирует рекомендации с несуществующими ресурсами терраформ.

У меня всё.
😁41👏4🤡1
#kubernetes

Настройка пула соединений для базы данных в Kubernetes.

А как же правильно настроить пул соединений при работе с приложениями в Kubernetes?
У нас же много nodes, PODs, всё динамически, везде скейлинг количества подов.
Не будет ли проблем с тем, что закончились коннекшны к базе и новые поды не смогут подключится?
Проблемы будут, надо считать.

Для начала нам надо понять какая база данных у нас.
Допустим реляционная база данных MySQL.
В зависимости от железа(CPU/Memory) у нас есть разное значение параметра max_connections
Сам параметр этот динамический, значение можно рассчитать и установить самому, либо взять дефолтное(например в облачной базе данных).
Для простоты расчётов будем считать, что у нас max_connections = 1000. Вы берёте свою цифру своего сервера.
- Первым делом нам надо зарезервировать 5 коннекшнов под административные нужды.
Например в случае проблем у нас будет шанс, что мы(инженеры/SRE) сможем к базе подключение сделать.
max_connections_db = max_connections - 5
max_connections_db = 1000 - 5 = 995

- У нас так же включен скейлинг приложений-клиентов через KEDA/HPA со значениями
min PODs = 2, max PODs = 30.

- Для расчета размера пула соединений на под (pool_size_per_pod) используется формула
pool_size_per_pod = floor(max_connections_db / N)

где N текущее количество подов (2 ≤ N ≤ 30).
pool_size_per_pod = floor(995 / 30) ≈ 33 соединения на под

Можно было бы на этом остановится, но это будет ошибка.

У нас время от времени проходит деплой и поднимаются новые реплики, которые тоже занимают пул соединений.
Предположим, что у нас Rolling Update + maxSurge = 25%.
Это означает, что во время деплоя может быть создано до 25% дополнительных подов сверх текущего количества.
Максимальное количество подов с учетом maxSurge
N_max_with_surge = ceil(N × (1 + 0.25)) = ceil(N × 1.25)

и для худшего случая в режиме максимальной нагрузки N=30
N_max_with_surge = ceil(30 × 1.25) = ceil(37.5) = 38

Тогда размер пула соединений на под с учетом maxSurge:
pool_size_per_pod = floor(max_connections_db / N_max_with_surge) = floor(995 / 38) ≈ 26

Итак, мы получили цифру 26.
Теперь мы можем добавить её в переменные окружения нашего golang аппликейшна.
poolSizeStr := os.Getenv("DB_POOL_SIZE")

и в ваш yaml/values файл типа
env:
- name: DB_POOL_SIZE
value: "26"


Итого: расчет пула соединений это простая арифметика, которая не зависит напрямую от Kubernetes, но требует учета его особенностей (KEDA, maxSurge, несколько дата центров например - не рассматривал в примере выше). Настройка пула соединений обеспечивает стабильную работу системы без превышения лимита соединений даже при максимальном масштабировании и деплое.
Наш расчёт пула соединений на POD (26) учитывает maxSurge = 25%, KEDA (2–30 подов) и резерв в 5 соединений, гарантируя стабильность системы.

* традиционно я мог забыть о чём то и не учесть, могут быть специфичные случаи(например несколько разных микросервисов к одной единой для них БД), я лишь рассматриваю базовый, чтобы вам ну хоть от чего-то отталкиваться в расчётах.
🔥14👍31
#docker #devsecops #paranoia #devops

Что у нас есть в пайплайнах?
Линтер питон скриптов, валидация YAML, tflint для терраформа, checkov для безопасности, git-leaks, sonarqube, скоринг кода, sast, dast и многое другое. Миллионы всего.
Всем и всё это знакомо.

А что по Dockerfile?
Есть великолепный проект для простых проверок:
https://github.com/hadolint/hadolint

Его можно добавить в pre-commit hook
https://github.com/hadolint/hadolint/blob/master/.pre-commit-hooks.yaml
Можно добавить в pipeline.
Что он умеет?
- проверка и анализ Docker на соответствие лучшим практикам
- интеграция с ShellCheck (моя любовь ❤️), но нужно не для всех
- гибкий конфиг через .hadolint.yaml
- поддержка игнора
- интеграция с CICD

Всё-всё, хватит пустых слов, что он умеет можно почитать и в документации, давайте к практике и примеру.

Добавляем его в наш основной канико(да когда ж ты умрёшь?!) имадж, используемый для сборок:
FROM gcr.io/kaniko-project/executor:v1.23.1-debug

ENV TRIVY_VERSION=0.61.0 \
CRANE_VERSION=0.19.0 \
HADOLINT=v2.12.0

RUN wget --no-verbose https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz && \
tar -zxvf trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz -C /kaniko && \
rm -f trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz

RUN wget --no-verbose https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz && \
tar -zxvf go-containerregistry_Linux_x86_64.tar.gz -C /kaniko && \
rm -f go-containerregistry_Linux_x86_64.tar.gz && \
rm -f /kaniko/LICENSE /kaniko/README.md

RUN wget --no-verbose https://github.com/hadolint/hadolint/releases/download/${HADOLINT}/hadolint-Linux-x86_64 && \
mv hadolint-Linux-x86_64 /kaniko/hadolint && \
chmod +x /kaniko/hadolint


Ок, базовый имадж есть.
Теперь добавляем его в основной общий пайплайн.
Например запускать можно так на этапе сборки имаджа.
# Lint the Dockerfile
echo "Executing hadolint check on $dockerfilePath"
/kaniko/hadolint $dockerfilePath --error DL3006 --error DL3007 --failure-threshold error

Что тут проверяем?
- чтобы всегда стоял тег бейз имаджа
https://github.com/hadolint/hadolint/wiki/DL3006
- чтобы тег бейз имаджа был НЕ latest
https://github.com/hadolint/hadolint/wiki/DL3007

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

Ещё один инструмент в нашем бесконечном списке бесконечных инструментов.
👍17