Make. Build. Break. Reflect.
908 subscribers
115 photos
1 video
119 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
#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
#delimiter #separator

Заметка для самых-самых маленьких.

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

Итак, Delimiter. Separators. Разделители.
Наверняка вы замечали, что в консоли/редакторе можно выделить по разному слова:
- выделив мышкой от начала слова до конца
- двойной клик (или хоткеи клавишами в редакторе)

Но при двойном клике в разных случаях и разных слов поведение разное.
Даже тут, в Telegram, с ПК, при двойном клике будет разное выделение.
Попробуйте!
asdf-qwer
asd—1234 (а вот тут уже выделяется всё)
asdf/fdsa
1234.4321
asdf_1234 (да, и тут будет выделено всё)
as 1234a
as␣2345 (и снова выделяется всё)

Всё зависит какой есть delimiter в том месте, где вы это выделяете.

Он имеет право быть разным везде:
- в editor VSCode
- в console VSCode
- в terminal Windows 11
- в WSL terminal
- в SQL console
Да где угодно на самом деле. И настройки у всех могут быть разными, не зависящими от других.

В случае, если ваc не устраивает то, что выделяется в вашем рабочем пространстве, можно просто поискать в гугле или документации про delimiter или separator.

Например в Terminal Windows 11 дефолтный делиметр
 /\-()"'.,:;<>~!@#$%^&*|+=[]{}~?│

и он находится в пункте
Windows Terminal → Settings → Interaction → Word delimiters
Можно поменять, удалив символ дефиса (-), на
 /\()"'.,:;<>~!@#$%^&*|+=[]{}~?│

И после этого в вашем терминале, например в WSL Ubuntu, будут выделяться слова типа
eks-cluster-one  # тут, в телеграм, конечно же, не сработает, это ж пример для WSL


В VSCode, если мне не изменяет память, два пункта.
- один для editor
`~!@#$%^&*()-=+[{]}\|;:'",.<>/?

- второй для terminal
 ()[]{}',"`─‘’“”|


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

Для чего важно знать, что такое delimiter?
Потому что это напрямую влияет на скорость и удобство редактирования текста и кода.
Если изменить границы слова через настройку разделителей, то двойной клик мышью или сочетания клавиш вроде Ctrl + Shift + ←/→ начинают работать именно так, как удобно вам.
👍205🔥3👀3
#DNS #cloudflare

Подстава, откуда не ждал.
Наверное все знали, и лишь я не знал.

Надо было для одной задачи проверить кое-что.
Я в своём личном аккаунте CloudFlare купил домен, арендовал VPS на linode, создал рутовую A запись, поднял Nginx на 80 порту - всё ок.
Задача усложнилась, мне надо было 443 порт, а это сертификаты.

Очевидно нужно сгенерить, на ум приходит cert-bot, acme.sh.
Сперва я промучался с одним, потом с вторым, эти падлы не хотели генерировать мне серты.
Просидев с логами я понял, что конфликт в TXT записи _acme-challenge.<domain>.
Всмыыысле, подумал я, какие TXT, у меня ничего нет в DNS записях, только A.
Иду в UI, в DNS там только A record.
Но логи говорят об обратном.

Ладно, идём в dig.
dig _acme-challenge.domain.com +answer TXT 8.8.8.8

А там, бл, я правда есть TXT записи.
;; ANSWER SECTION:
_acme-challenge.domain.com. 297 IN TXT "blabla"

Гугл, изучение админки CloudFlare приводят меня в и к документации.

❗️Дефолтное поведение Cloudflare при покупке домена включает Universal SSL/Edge Certificate.
Cloudflare автоматически выписывает вайлдкард сертификат на 3 месяца.
Для подтверждения владения Cloudflare сам автоматически создает собственные TXT-записи с префиксом _acme-challenge.*. Эти TXT записи не отображаются в стандартном интерфейсе DNS-записей Cloudflare.
Их наличие можно подтвердить только через внешний DNS-запрос, например dig.
Айдишник от Cloudflare не совпадает с айдишником другой утилиты, вываливается ошибка, конфликт, нет серта.

Решение очевидное: отключить Universal SSL/Edge Certificate Cloudflare:
Перейти в раздел SSL/TLS → Edge Certificates .
Найти блок Disable Universal SSL(в самом низу) и нажать кнопку Disable Universal SSL
Подождать около 15 минут. Он ревоукнет сертификат и уберёт все не редактируемые неявные TXT записи.

Теперь можно работать с любыми генераторами сертов, кто челленджит владение DNS зоной через создание _acme-challenge.domain.

Тех, кто челленджит DNS через HTTP-01 Challenge, это поведение не аффектит.

* а бектики я не поставил, потому как телеграм глючит и любой код в этой заметке превращает в мусор из спецсимволов и тайский алфавит. Как, например, превращает TXT в TXT. Fixed.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍111👎1🤡1
#linux

Самое сложное это переступить свои привычки и стереотипы.

Начал карьеру с VSCode/vim/IDEA - привыкаешь с хоткеям, интерфейсу, удобству.
С браузером так же. Пересесть на другое - неудобно, непривычно, неохота.

По Linux утилитам тоже самое, одни плюсы: часть уже есть в операционной системе по умолчанию, часть ты знаешь так, что даже без нейронки и хелпа на память помнишь синтаксис.
cat, sed, ping, du, df, grep, awk

Зачем переходить на другое?
- Его надо отдельно ставить. Фу.
- Снова учить синтаксис. Больно, да.

Однако причины перейти всё же есть.
Скорость работы, удобство и новые фичи.
Конкретно для меня это user/human-friendly.

Для себя я нашёл эти и даже успел к ним привыкнуть:
cat ~/.tool-versions
...
jq 1.8.1
direnv 2.37.1
duf 0.9.1
dust 1.2.3
bat 0.25.0
yq 4.48.1
delta 0.18.2
...

Остальное тоже пробовал, но не прижилось.
Фигня для rustаманов-зумеров 😁.
Шучу, просто не зашло по задачам
🤡.

Ниже список репозиториев, где есть несколько современных утилит - замена/альтернатива привычных всем:
- https://github.com/ibraheemdev/modern-unix
- https://github.com/johnalanwoods/maintained-modern-unix

Как бонус (не замена, а просто общий список):
- https://github.com/agarrharr/awesome-cli-apps
- https://terminaltrove.com/list/
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16
#troubleshooting #alertmanager

Заметка для самых маленьких.

Прилетает алерт "KubernetesStatefulsetDown".
Брр, ну ок, смотрю что не так.
А, понял! Судя по названию приложения statefulset, это же наш новый тестовый неймспейс test-project.
Там сейчас на пару месяцев будут тесты, нам не нужны эти алерты.
Можно добавить и сайленс, но сайленс работает лишь до первого рестарта алертменеджера и не хранит стейт.
Хочу полностью отключить все алерты на этом неймспейсе.

Иду в алертменеджер, добавляю рут в начале типа
      routes:
...
- receiver: 'null'
matchers:
- namespace = "test-project"
continue: false
....
тут дальше остальные руты на нормальные ресиверы

Применяю, всё ок.
Проходит час - снова этот же алёрт.

Может опечатка? Может конфиг поломанный?
Смотрим что у нас в логах vmalert/alertmanager.
kubectl logs vmalert-podname
...
... Rules reloaded successfully from ... yaml
...

Да не, время совпадает - применилось недавно мной.

Ладно. Идём в git к алёрту, смотрим что там у него в expr.
expr: kube_statefulset_replicas != kube_statefulset_status_replicas_ready > 0

Ну всё эээ понятно. Всё просто. Никаких вопросов.
Пока ковырялся, пришёл ещё алерт, такой же.

Не понял, иду в https://prometheus.io/webtools/alerting/routing-tree-editor/
Копирую туда часть конфига алертменеджера с рутами.
Дальше проверяю на namespace {namespace="test-project").
Матчинг, идёт в null, как и должно.

Бред, возвращаюсь в git к алерту.
Копирую expr
kube_statefulset_replicas != kube_statefulset_status_replicas_ready > 0

иду в prometheus/VM/grafana explore, вставляю:
Да, вижу респонс метрики с лейблами.
kube_statefulset_replicas
{
container="kube-state-metrics",
endpoint="http",
instance="10.0.115.164:8080",
job="kube-state-metrics",
namespace="test-project",
pod="kube-state-metrics-6794d79b55-sf879",
service="kube-state-metrics",
statefulset="fake-app"
}

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

В третий раз, словно старик закидывает невод в море, иду к алёртам. Теперь смотрю всё и сразу.
- alert: KubernetesStatefulsetDown
expr: kube_statefulset_replicas != kube_statefulset_status_replicas_ready > 0
for: 1m
labels:
...
severity: critical
namespace: '{{ $labels.namespac }}'
statefulset: '{{ $labels.statefulset }}'
...
annotations:
summary: ...

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

Зачем-то, скорее всего копипастой, был добавлен лейбл неймспейса. Дубль. С опечаткой в названии.
Лейбла namespac у нас нет в метрике, а значит пустым значением от namespac перетирается значение namespace.
В итоге до алёртменеджера алёрт приходил с пустым значением namespace и все рулсы рутов игнорировались(ведь нет матчинга).
Я такие случаи называю "broken downstream-routing".

Поиском поискал ещё опечатки, нашёл ещё три шутки, поправил(конкретно в этом алерте я просто удалил, он совсем тут не нужен), проверил в ВМ и отправил MR.
Наступила тишина в слак канале и можно попить кофе.☕️
.
..
...
....
А не, хер мне, а не отдых.
Теперь при верном неймспейсе заработали другие три алерта и прилетела новая проблема, ранее не замеченная из-за кривого лейбла. И это уже прод.
Эх, ну значит потом кофе попью😭.
Поехали чинить..
Please open Telegram to view this post
VIEW IN TELEGRAM
14😁12
#TIL

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

Стало интересно, пришлось копнуть дальше.
Думаю все это знали, но я, как обычно, к своему стыду не знал🤡.

Есть такая функция inet_aton(). https://linux.die.net/man/3/inet_aton
Функция обработки адреса интерпретирует входные данные не только как четыре октета, но и как 1, 2, или 3 числа. Если указано меньше четырёх чисел, недостающие биты адреса "добираются" из последнего указанного числа, а не просто заполняются нулями (хотя в некоторых случаях это выглядит так, как будто они заполнились нулями).

И эта функция используется утилитой ping.
Что это значит?
- можно пинговать localhost вот так
ping 127.1
PING 127.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms

- или даже пинговать DNS от CloudFlare https://www.whois.com/whois/1.0.0.1
>ping 1.1
PING 1.1 (1.0.0.1) 56(84) bytes of data.
64 bytes from 1.0.0.1: icmp_seq=1 ttl=58 time=5.85 ms

- ну или вообще вот так
ping 0
PING 0 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.030 ms


Так же и у curl (на самом деле там собственный host парсер).
curl -i https://1.1
HTTP/2 301
date:
content-length: 0
location: https://one.one.one.one/
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=TXp9lzhnM4jO3U47uy7caHHtaOGB5P%2FiVVgdhCuuaxklboKrkVlRH0at%2BwLL5kall9an%2FdkawG%2FoU7aRBxAABe2Ux52GTldnNm%2F%2F9EpxGNvKdtyJi5BQPcE%3D"}],"group":"cf-nel","max_age":604800}
nel: {"report_to":"cf-nel","max_age":604800}
server: cloudflare
cf-ray:

и
curl -i http://0
HTTP/1.1 308 Permanent Redirect
Connection: close
Location: https://0.0.0.0/
Server: Caddy
Date:
Content-Length: 0

# и даже с портом

curl -i http://0:8081
HTTP/1.1 302 Found
Cache-Control: no-store
Content-Type: text/html; charset=utf-8
Location: /login
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-Xss-Protection: 1; mode=block
Date:
Content-Length: 29

<a href="/login">Found</a>.


Зачем это надо?
А чёрт его знает.
Прикольно, не более.
Думаю это лишь историческая особенность, а не функциональность, предназначенная для практического применения.

Кому это надо?
Ну вдруг кто-то спешит и нет времени печатать полностью адрес 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
😁143👍2👏1
17 октября 2025 года человек уходит из AWS.
https://nekrolm.github.io/blog.html

20 октября 2025 года сразу же крупнейший инцидент.
https://health.aws.amazon.com/health/status
😁22👍3
Make. Build. Break. Reflect.
#пятница #байки #troubleshooting Задолго до работы в айти я был простым советским инженером спутниковой связи. Тарелки, антенны, модемы, кабели, бесконечные командировки. В то время случалось масса странных историй. Мы разрабатывали спутниковое оборудование…
#пятница #байки #всратость

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


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

Однако есть и другие категории клиентов: покупка полосы, допустим 1-2 мегагерца, на короткое время - час-два.
Это актуально для телеканалов, которым нужно было выехать буквально в поле, открыть антенну на автомобиле, автоматикой арендовать полосу на спутнике, снять репортаж онлайн, свернуть антенну и поехать дальше. Не надо покупать целую полосу или транспондер, это многократно, на порядки дешевле.

В общем компания решила войти в эту новую нишу: в мир автоматизации и продажи программного обеспечения, которое позволит на время арендовать канал телевизионщикам.

Какая была основная идея:
- мы пилим некий heatmap по дате/времени/календарю/частотам/полосам/спутникам, где отображены цветами полоса занята или нет
- у компании-оператора есть эта панель
- клиенты заходят на сайт, при помощи планировщика запрашивают полосу, допустим 0.5МГц, на два часа на четверг днём
- компания-оператор даёт согласие
- нужный кусок конфигурации(частота, ширина полосы, модуляция и тп) попадает на спутниковый модем от этого планировщика
- съёмочная бригада выезжает на место, просто включает оборудование, смотрит на свою собственную панель, на которой он видит ОК или НЕ ОК, можно выходить на связь или нет, убеждается, что всё ок, снимают репортаж, сворачивают антенну
- компания-оператор выставляет счёт клиенту-телеканалу за использование полосы

Это общий план, в котором всё были бы довольны:
- дежурная съёмочная бригада не ломает голову над конфигом, просто включил оборудование, получил что надо на автомате, увидел, что всё ок, вышел в эфир, выключил всё. А если не ок, то и не выходит.
- оператор удачно продал полосу, автоматизировав всё для клиентов
- телеканал за недорого получил услугу

Задача всем понятна, выкатываем первый релиз.
Первый релиз - первая всратость. 😭

Разработчики из компании подошли к разработке интерфейса для клиентов максимально логично, с их стороны.
- Зеленый цвет - всё ок, можете выходить в эфир. Всё работает. 👍
- Красный цвет - всё не ок, полоса занята/оборудование не работает, конфиг не получен, выходить на связь нельзя. 👎
Ну всё же логично!

Логично, но не для мира телевещания.
Оказывается в мире съёмок и студий красный это ON AIR.
- Красный цвет - всё ок. Можно выходить в эфир.
- Зелёный цвет - standby. Ждите, пока можно выйти в эфир.

Ну вы поняли, да? 😁

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

Выезжает другая бригада в поле, открывает UI интерфейс, видят зелёный, сидят , ждут когда можно выйти в эфир.
Час просидели, как тюлени на солнышке, подумали, что им не дали эфир, уехали. Деньги за полосу уплачены, а эфира не было, да и полоса свободна, что обидно. Скандал. Великолепно.

И так далее.

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

Хорошо, что для медицины ничего не начали поставлять на старте 😀
Please open Telegram to view this post
VIEW IN TELEGRAM
😁17🫡10🔥7🥰2🤣1
#kubernetes #linux #база

Существует достаточно частая проблема: ошибки категории "misleading".
Когда копаешь в одно, а проблема в другом.

Инженеры Kubernetes иногда сталкиваются с крайне сбивающей с толку ошибкой при запуске пода:
MountVolume.SetUp failed... no space left on device.

Не зная причины этой ошибки, мы сперва тратим время на:
- просмотр что за PVC используются у деплоймента/пода.
- что за нода сейчас запустила этот под, нет ли нехватки места на самой ноде
- лезут в графану, в метрики и графики по нодам, подам, PV
- как разбиты разделы диска на нодах, что и сколько отдаётся эфемеркам
- чего там по айнодам (ну наконец-то пригодились эти знания! 🕺)
- и так далее
И это правильные мысли и план, это так же может быть причиной ошибки, но наш случай про другое.

Попытка найти строку "no space left on device" в исходном коде Kubelet на GitHub также не дает результатов, потому что Kubelet не генерирует этот текст - он просто ретранслирует низкоуровневую ошибку, полученную от ядра Linux(или от рантайма, тут могу наврунькать).

Истинная причина кроется в тонком различии между единицами измерения CPU и памяти в спецификации ресурсов. Это особенно критично для Проецируемых томов (Projected Volumes), таких как Service Account Token (kube-api-access-*), которые монтируются как tmpfs (файловая система в RAM).
https://kubernetes.io/docs/concepts/storage/projected-volumes/

Когда инженер по ошибке использует суффикс CPU (m) для лимита памяти, например:
resources:
limits:
memory: "512m" # Ошибка, должно быть "512Mi"!

- Kubelet парсит "512m" для памяти как метрический милли, что равно 0.512 байта. Поскольку размер тома (tmpfs) должен быть целым числом, это значение округляется до 0 байт (или ничтожно малого значения). Kubelet использует этот 0 байт для установки максимального размера тома tmpfs.
- Kubelet использует этот лимит памяти для установки максимального размера тома tmpfs для Service Account токена
- Ядро Linux получает запрос на монтирование tmpfs с нулевым или ничтожно малым размером. Системный вызов mount завершается неудачей (из-за попытки выделить даже минимальный объём, обычно 1 страница = 4 KiB). и возвращает низкоуровневую ошибку ENOSPC ("No space left on device"), поскольку невозможно создать файловую систему (даже в RAM) с таким малым выделением ресурсов.

То есть в нашем случае ошибка "no space left on device" на самом деле означает: "Не удалось создать RAM-диск (tmpfs) требуемого объема, потому что выделенный размер (0 байт) некорректен".

Стоит понимать, что Kubernetes API сервер не считает "512m" для памяти ошибкой валидации.
Он принимает это как легальное значение (0.512 байта), что позволяет поду быть созданным, но приводит к сбою выполнения (runtime failure) при монтировании на ноде.

Решение очевидное:
всегда используйте бинарные единицы (IEC) для ресурсов памяти: Mi (мебибайты), Gi (гибибайты).
Может есть и другие, я только эти использовал всегда.

Чтобы превентивно избавляться от такой проблемы, используйте дополнительно:
- pre-commit hooks
- валидаторы cli в CI/CD процессе

Какие-то конкретные утилиты советовать не буду, оставлю ссылки для примера:
- https://github.com/instrumenta/kubeval (давно не обновлялся, но он работает ок)
- https://github.com/yannh/kubeconform
- https://github.com/kubernetes-sigs/kubectl-validate

Кстати, аналогичные ошибки встречаются при монтировании секретов, конфигмапов и других tmpfs-бэкендов, использующих лимиты памяти контейнера.
Please open Telegram to view this post
VIEW IN TELEGRAM
19👍9
#azure

Как неожиданно
😁12
#azure #aws

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

Azure:
- У нас авария, ничего не понятно, там у AWS недавно был DNS, давайте напишем в статус пейдже, что у нас тоже DNS, а потом поменяем на реальную причину, вдруг там AFD.
🤣26👍1😨1
#azure #devops #entra #powershell

Мелкая компания из трёх суетливых инженеров при удаче вырастает до большой организации в 150+ человек.
Люди уходят и приходят, а хорошего процесса оффбординга Entra ID иногда нет.
Иногда удаляют/выключают аккаунты, иногда нет. Иногда просто создают и не пользуются.
Чистка аккаунтов это крайне важный элемент девопс процессов.
Рано или поздно приходит любой аудит и очистка аккаунтов необходима.

И вот аудит пришёл.
Именно тогда прилетают странные задачки, типа "причесать каталог юзеров".
Захожу, а там 200+ аккаунтов, 85% которых я вижу впервые.

Хорошо, моя идея в общих чертах такая:
- найти всех пользователей
- отсортировать тех, у кого аккаунт включён (accountEnabled)
- собрать ключевые данные: Display Name, UPN/Email, дата создания и, главное, дата последнего входа
- отфильтровать по порогу неактивности (например, более 60 дней)
- сформировать таблицу (CSV-файл) с колонкой для решения ("Действие")
- отправить файл руководству (коллегам/боссу/СТО) на согласование, чтобы избежать удаления важных служебных аккаунтов или аккаунтов инвесторов
- удалить/выключить пользователей согласно утвержденному списку.

Как же это сделать? Ведь Microsoft Entra ID не предоставляет таких данных в удобном виде *.
Да и az cli такого не предоставляет.
На помощь приходит PowerShell и модули.

Поехали пошагово:
- запускаю PowerShell с правами администратора
- проверяю есть ли права на выполнение скриптов
Get-ExecutionPolicy
Restricted

- если рестриктед, то надо включить(не забыть выключить)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
Y

- теперь мне надо установить модуль
Install-Module Microsoft.Graph -Scope CurrentUser

- импортирую модуль для этой сессии
Import-Module Microsoft.Graph.Authentication

- затем надо подключиться к Microsoft Graph с такими правами
Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All"

На этом этапе откроется браузер, надо пройти авторизацию
- пилим скрипт(можно даже построчно вводить)
$InactiveDays = 60
$DateCutoff = (Get-Date).AddDays(-$InactiveDays)

$Users = Get-MgUser -All -Filter "accountEnabled eq true" -Property Id, DisplayName, UserPrincipalName, SignInActivity, AccountEnabled

$InactiveUsers = $Users | Where-Object {
$_.SignInActivity -ne $null -and
$_.SignInActivity.LastSignInDateTime -ne $null -and
$_.SignInActivity.LastSignInDateTime -lt $DateCutoff
} | Select-Object DisplayName, UserPrincipalName, @{Name="LastSignInDate"; Expression={$_.SignInActivity.LastSignInDateTime}}

#mock
$InactiveUsers | Format-Table
$NeverSignedInUsers = $Users | Where-Object {
$_.SignInActivity -eq $null -or
$_.SignInActivity.LastSignInDateTime -eq $null
} | Select-Object DisplayName, UserPrincipalName, @{Name="LastSignInDate"; Expression={"Never Signed In (or before Apr 2020)"}}

$InactiveUsers + $NeverSignedInUsers | Format-Table -AutoSize

- на выходе получаю
DisplayName              UserPrincipalName          LastSignInDate
----------- ----------------- --------------
Ivan Petrov ivan.petrov@contoso.com 2022-03-15 10:45:12
Maria Sidorova (Guest) m.sidorova_ext@contoso.com 2021-11-20 14:02:55
Alexey Smirnov a.smirnov@contoso.com 2022-04-01 08:10:30
New Folder(1) test.user@contoso.com Never Signed In (or before Apr 2020)

- копирую, вставляю в Excel, добавляю новую колонку delete/don't touch
(или сразу пилить в CSV, как кому удобнее)
($InactiveUsers + $NeverSignedInUsers) | Export-Csv -Path "C:\Users\alexk\InactiveUsers_Audit.csv" -NoTypeInformation

- отправляю руководству
- получаю ответ, выключаю/удаляю ненужных юзеров
Задача закрыта, в каталоге порядок.

Так. А почему именно я делаю эту задачу?
А нечего было всем в рабочих чатах говорить, что винда лучшая операционная система.
Вот и сиди, пиши пош-скрипты 😭
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8😁5
- - -
*Случай был 2023 году.
В августе 2025 появилась эта фича https://learn.microsoft.com/en-us/entra/identity/users/users-bulk-download, теперь почти всё можно делать в UI. Однако доступны не все поля, нельзя сделать несколько фильтров последовательно, так что подход всё ещё актуален.
#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
Да камон, по три раза в день стали апдейты.
🤣14
#helm #kubernetes

Три прыжка.

Как же идёт порядок генерации манифестов по шаблону, сортировка и применение в кластере при использовании Helm v3?  


Прыжок в воду 1.

Можно просто почитать официальную документацию на версию 3.19
Немного лукавит из-за нерегулярного обновления, но в целом 90% инженерам этого достаточно.
Мы теперь знаем, что перед деплойментом у нас есть секрет, перед ним PVC, а ещё раньше их неймспейс.
Это логично и не рождает проблем в стандартных чартах.

Прыжок в воду 2.

Так же можно посмотреть в исходном коде на версию 3.19
Там видно настоящий список, а так же указаны логические операторы про анноун ресурсам и сортировке.
Всякие CRD, хуки и весы.
Чуть точнее и глубже, этого достаточно ещё 5% людей.

Прыжок в воду 3.

Побудем сегодня любопытными инженерами и узнаем всё до конца, нырнув глубже.

В общем и целом есть несколько основных категорий:
- pre-* хуки
- post-* хуки
- Standard ресурсы
- CRD
- Unknown ресурсы

При запуске helm install/helm upgrade --install у нас идёт такой порядок:
1) CRD. Всегда первым. Из директории <чарт>/crds/.
Они не шаблонизируются, а применяются как есть.
https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/

2) pre-install hooks.
Если есть. ("helm.sh/hook": *)
Внутри группы хуков сначала сортируются по весу("helm.sh/hook-weight"):
- отрицательный
- ноль (по умолчанию)
- положительный
- по имени
Внутри одного веса сортировка идёт по имени ресурса (алфавитно).
https://helm.sh/docs/topics/charts_hooks/

3) standard ресурсы, по порядку в исходном коде в kind_sorter.go
Сортировка происходит после рендеринга шаблонов, но до их применения к кластеру.
Рендеринг -> сортировка -> применение.
- PriorityClass
- Namespace
..
- ClusterRoleBinding
..
- Deployment
..

4) Unknown ресурсы
Так сказать всё остальное бесчисленное множество.
Важно понимать, что внутри группы Unknown ресурсов порядок детерминирован и следует правилу: сначала сортировка по Kind (алфавитно) (например, ApplicationSet перед PostgreSQL), а затем по имени ресурса (алфавитно).
- ApplicationsSet (ArgoCD)
- PostgreSQL (Zalando)
..
- ScaledObject (KEDA)
Этот алфавитный порядок зачастую не совпадает с требуемым логическим порядком зависимостей.

5) post-install хуки.
Если есть. ("helm.sh/hook": *)
Внутри группы хуков сначала сортируются по весу("helm.sh/hook-weight"):
- отрицательный
- ноль (по умолчанию)
- положительный
- по имени
Внутри одного веса сортировка идёт по имени ресурса (алфавитно).
https://helm.sh/docs/topics/charts_hooks/
Вот теперь картина полная и мы славные инженеры.

- - -
Для чего же мы ныряем так глубоко? Зачем нам это?

Иногда прилетают задачи о оптимизации имеющегося чарта.
Например (это. лишь. пример), у нас сложная технологическая платформа, а в чарте есть и cert-manager с выпуском сертификатов, и externalsecrets и issuer, и многое другое.
Все ресурсы категории Unknown и нам нужен строгий порядок или, что хуже, тайминг(чтобы успело создаться что-то до следующего ресурса).

Нет ишшуера/не будет выпущен серт - не будет его куда запушить/примонтировать - не будет секрета/пушсикрета - проблема платформы. Да и серт не мгновенно выпускается. Нужно время. Как быть?
Тот же issuer по алфавиту идёт после certificate, хотя должно быть и иначе, а pushsecret ничего не сможет прокинуть, потому что серт ещё не выпущен. И всё обёрнуто через ArgoCD.

Как раз тут и пригодятся нам эти глубокие знания.
Зная правила мы спокойно решаем задачу.

- Алекс, а что там с Арго?
Под капотом ArgoCD использует как раз helm template.
https://argo-cd.readthedocs.io/en/stable/user-guide/helm/
- А что там с хуками?
Argo CD автоматически конвертирует эту логику в свои Hooks и Waves, сохраняя ваш строгий контроль над порядком.
- helm.sh/hook -> Resource Hooks
- helm.sh/hook-weight -> Sync Waves
https://argo-cd.readthedocs.io/en/release-2.14/user-guide/resource_hooks/
https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/

Занимательное:
- при helm upgrade CRD из crds/ не обновляются (Helm не изменяет их содержимое, нужно делать вручную), только при helm install
1👍195🔥1👨‍💻1🆒1
#terragrunt

Волею случая и нового проекта надо было обновить террагрант.
Ну типа была версия старая, по привычке используемая везде - 0.54.15.
А тут и терраформ новее, и синтаксис подсвечивает модуль, что ошибка, и форматтера нет, и пре-коммит поставил, чтобы форматировал при коммитах.
---
repos:
- repo: https://github.com/gruntwork-io/pre-commit
rev: v0.1.30
hooks:
- id: terragrunt-hcl-fmt

А пре-коммит в свою очередь хочет минимум версию 0.77.22.
В общем надо было обновиться и я сделал это.

И тут, блин, началось. Мусор какой-то в аутпуте при плане и апплае.
Даже немного попсиховал.
Словно включен инфо/дебаг левел.
09:34:36.050 STDOUT terraform:       ~ default_node_pool {
09:34:36.050 STDOUT terraform: ~ auto_scaling_enabled = false -> true
09:34:36.050 STDOUT terraform: ~ max_count = 0 -> 4
09:34:36.050 STDOUT terraform: ~ min_count = 0 -> 2
09:34:36.050 STDOUT terraform: name = "general"
09:34:36.050 STDOUT terraform: tags = {}
09:34:36.050 STDOUT terraform: # (28 unchanged attributes hidden)
09:34:36.050 STDOUT terraform: # (1 unchanged block hidden)
09:34:36.050 STDOUT terraform: }

Сперва посмотрел в env | grep -i terra / env | grep -i tf, но не вижу ничего критичного.
По диагонали залез в мануал cli - ничего не нашёл.
Попробовал классические варианты типа
TF_LOG=ERROR  TERRAGRUNT_LOG_LEVEL=error terragrunt plan

Тоже не помогает.

Херня какая, полез для скорости в AI (ох, и я попался на эту удочку снова).
Несёт хрень всякую, я гиеной заорал с советов, там и отключить это нельзя, и давай добавим local-exec с export TF* 🤣

Делать нечего - полез в документацию, там, неожиданно для меня, произошли изменения касательно аутпута.
Начиная с версии v0.77.0 (вроде бы) террагрант вчистую выводит то, что даёт ему терраформ или другие утилиты типа opentofu.
https://terragrunt.gruntwork.io/docs/reference/logging/#log-levels

Чтобы "было как раньше" и "как привык" я пока нашёл одно решение - в ~/.bashrc добавить
export TG_LOG_FORMAT=bare

Ну либо использовать флаг (мне так очевидно неудобно).
terragrunt --log-format bare plan

Только так он выводит "как раньше", без лишнего мусора.
      ~ default_node_pool {
~ auto_scaling_enabled = false -> true
~ max_count = 0 -> 4
~ min_count = 0 -> 2
name = "general"
tags = {}
# (28 unchanged attributes hidden)
# (1 unchanged block hidden)
}

Предполагаю, что новые изменения удобнее всему комьюнити, кроме меня, ну либо для тех, у кого десятки, сотни модулей и при terragrunt run-all становится очень сложно понять, какое именно сообщение относится к какому именно модулю или к какому инструменту (Terragrunt или Terraform).

Штош, ещё одна переменная в конфигах.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍121
#terragrunt

Интересную штуку подглядел у коллеги.
provider "aws" {
...
default_tags {
tags = {
environment = "${local.environment}"
managed_by = "terragrunt"
terragrunt_path = "${path_relative_to_include()}" <<<<<< this one
}
}
}

Ну с тегами окружения и менеджед бай террагрант меня не удивить.
Но путь до модуля! Это гениально!
Гениально и просто.

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

Наверняка это придумали в мезозое и уже успели заменить на что-то другое, а я узнал только сегодня, ну да и ладно 🤡
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12👏1
Не пятница, но всё же
🤣30🔥9😁3🐳3
Прям день интересных событий.
- то релиз helm 4 https://helm.sh/docs/overview/
- то nginx retirement https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/
To prioritize the safety and security of the ecosystem, Kubernetes SIG Network and the Security Response Committee are announcing the upcoming retirement of Ingress NGINX

Всё же придётся ехать на k8s Gateway API? 😭
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7😭6