Make. Build. Break. Reflect.
1.18K subscribers
132 photos
1 video
1 file
140 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
#kubernetes

Пока я спал там взломали все инторнеты и все кубернетисы.

Источник на английском с глубоким разбором.
- https://grahamhelton.com/blog/nodes-proxy-rce

Хорошие ребята, кого не жалко репостнуть:
- https://xn--r1a.website/tech_b0lt_Genona/6101
- https://labs.iximiuz.com/tutorials/nodes-proxy-rce-c9e436a9

Хорошо, что все кубернетисы (кроме моих личных лабораторий) на предыдущих и нынешней работах это приватные сетки (что спасает от внешних негодяев, но не спасает от внутренних, но таких у нас нет).

Как проверить, подвержен ли ваш кластер/SA? А легко!
Копи-паст скрипта в своем кластере:
kubectl get serviceaccounts --all-namespaces -o json | \
jq -r '.items[] | "\(.metadata.namespace)\t\(.metadata.name)"' | \
while IFS=$'\t' read -r ns sa; do
if kubectl auth can-i get nodes/proxy --as="system:serviceaccount:${ns}:${sa}" -n "$ns" >/dev/null 2>&1; then
echo -e "${ns}\t${sa}\tyes"
fi
done

Да, такие дела. Ну и порт 10250 должен быть доступен.
🔥2
#kubernetes #golang #troubleshooting

Снова про операторы.

У ресурса Secret в Kubernetes есть два поля для данных:
- Data map[string][]byte - бинарные данные, в YAML выглядят как base64
- StringData map[string]string - строковые данные, plain text в YAML

Как это работает на самом деле:
StringData - write-only поле для удобства.
Когда ты создаёшь или обновляешь секрет, API-сервер мержит StringData в Data (ключи из StringData перезаписывают Data при совпадении).
В etcd хранилке и в ответе API сохраняется только Data.
При чтении секрета из кластера поле StringData всегда пустое - его там просто нет.
Поэтому StringData - для людей и YAML-манифестов, а Data - для кода операторов.


А теперь типичнейшая ошибка, на которую я снова попался. 😭

Код оператора с ошибкой:
func BuildCredentialsSecret(instance *v1alpha1.MyApp) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-credentials",
Namespace: instance.Namespace,
},
Type: corev1.SecretTypeOpaque,
StringData: map[string]string{
"API_HOST": instance.Spec.Config.API.Host,
"API_TOKEN": instance.Spec.Config.API.Token,
},
}
}

В реконсайлере проверка "изменился ли секрет?":
func secretDataEqual(a, b map[string][]byte) bool {
if len(a) != len(b) {
return false
}
for k, v := range a {
if !bytes.Equal(v, b[k]) {
return false
}
}
return true
}

// в reconcile:
if !secretDataEqual(found.Data, secret.Data) {
// обновляем секрет
found.Data = secret.Data // ВОТ ТУТ ПРОБЛЕМА
return r.Update(ctx, found)
}

Видите проблему?

При создании desired-секрета мы используем StringData.
Но в Go-структуре этого объекта поле Data равно nil, потому что мы его не заполняли!!!

Что происходит:
- оператор создаёт секрет с StringData > API сервер конвертирует в Data, в кластере всё ок
- при следующем reconcile оператор читает секрет - там Data заполнен
- оператор строит desired-секрет - там Data = nil, StringData заполнен
- сравнение видит разницу:
len(found.Data) != 0, а len(secret.Data) == 0 

(потому что nil)
- оператор решает обновить секрет и делает
found.Data = secret.Data

записывая nil в поле данных!
- теперь в кластере секрет с
data: {}

(0 ключей)
- в следующем reconcile сравнение
secretDataEqual(found.Data, nil)

оба пустые? Да!
(len(nil) == 0, len(map{}) == 0)

- оператор думает: "секрет актуален", и никогда не восстанавливает данные 🤡

Результат: секрет живёт, ownerReference на месте, лейблы есть, а данных - ноль.
Поды падают с:
MountVolume.SetUp failed for volume "credentials": 
references non-existent secret key: API_TOKEN

Фикс очевиден: использовать Data в коде оператора:
func BuildCredentialsSecret(instance *v1alpha1.MyApp) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-credentials",
Namespace: instance.Namespace,
},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{
"API_HOST": []byte(instance.Spec.Config.API.Host),
"API_TOKEN": []byte(instance.Spec.Config.API.Token),
},
}
}


Теперь secret.Data заполнен, сравнение работает корректно, и ты случайно не затираешь данные nil-ом.
🎉🎉🎉

Мораль:
- stringData - для YAML и kubectl, где удобно писать plain text
- data - для кода операторов на Go
- в client-go структуре StringData и Data - разные поля; API сервер не синхронизирует их при чтении
- если используешь StringData в коде, помни: secret.Data == nil в этой структуре
- сравнивай и применяй то, что реально используешь

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

Классика - симптомы в одном месте (падающие поды), причина в другом (nil вместо мапы в билдере).
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍6
#kubernetes #aws #eks #helm

Тихо и незаметно вышла мажорная версия AWS Load Balancer Controller - 3.0.0.
https://github.com/kubernetes-sigs/aws-load-balancer-controller
Вместе с ней и Helm-чарт тоже прыгнул на 3.x.
https://github.com/aws/eks-charts/tree/master/stable/aws-load-balancer-controller

Как я понял по заявлению мейнтейнеров, breaking changes в пользовательском API (Ingress, Service, Gateway) они не предполагают.
Но issue на GitHub уже висят, так что посмотрим.

Что важного и интересного (для меня):
- Gateway API объявлен GA (production ready) - теперь можно смело использовать в бою, а не в тестовых проектах 🔥
​- Версия Helm-чарта выровнена с версией контроллера - раньше было 2.x контроллер с чартом 1.x, теперь 3.0.0 = 3.0.0. Наконец-то! ❤️
​- Новые фиксы NLB target groups, порядка сабнетов, AZ mismatch и webhook-сертификатов
​- CRD ListenerRuleConfiguration добавлен, но пока экспериментальный - не использовать в проде
- Минимальная версия Kubernetes - теперь официально требуется 1.24+ (интересно, кто ещё на таком старье сидит 😁)
​- IPv6 для internet-facing ALB/NLB - теперь нативно поддерживается без костылей, раньше были ограничения 🎉

Нюансы с апгрейдом:
- перед helm upgrade до 3.0.0 нужно вручную обновить CRD'ы. Issue #4555 прямо про это - люди натыкаются на проблемы, потому что в release notes это не очень явно прописано.
Но как по мне так это и очевидно, так как мы все знаем, что при хелм апгрейде​ CRD не обновляется никогда и ни у кого, архитектурная особенность самого хелма.
- для Gateway API нужны не только CRD самого контроллера, но и базовые Gateway API CRD (HTTPRoute, GatewayClass и т.д.), если их ещё нет в кластере

Сам, конечно же, я пока обновляться не буду.
Почитаю issues ещё недельку, потом с командой решим - стоит ли сразу или подождать патч-релиз.

Better safe than sorry, особенно с ингресс-контроллерами, которые держат весь трафик.
Please open Telegram to view this post
VIEW IN TELEGRAM
💯4🎃1
А вариантов отказаться и нет 😭
Please open Telegram to view this post
VIEW IN TELEGRAM
7🤣4🤯3💔2👍1
Там Daniele Polencic обновил статью про k8s API.
Прям с графиками, картиночками, наглядно.
В целом годно, как и многие его другие ресурсы/статьи. Никакого рокетсайнса и кишочков - просто база-дополнение к официальной документации.

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

https://learnkube.com/kubernetes-api-explained
🔥12👍2
#gitlab #troubleshooting #одинденьизжизни

Тихий убийца времени.


На новом проекте дали задачу: небольшие изменения в гошном операторе Kubernetes.
Секреты, cluster-scoped объекты, ничего космического.
Покрутил код, разобрался в архитектуре, поднял локальный kind-кластер, погонял тесты.
Makefile на месте, всё зелёное. Красота.

Коммичу, пушу MR.
Тесты падают. 🙃

Ну ладно, думаю. Я серьёзно менял логику, наверное поломал что-то. Бывает.
Возвращаюсь, перепроверяю. Локально гоняю - всё проходит.
Коммичу, пушу - снова падает.

Ок, уже интересно.

Открываю .gitlab-ci.yml, читаю джобу тестов строчка за строчкой.
Повторяю ровно то же самое локально - make test, всё зелёное.
В GitLab - красное.

Смотрю логи джобы внимательнее.
Один из тестов падает на скачивании image из container registry соседнего репозитория. Не хватает авторизации. Ага.

Проверяю:
- мой GitLab токен - валидный
- доступ в соседние репозитории (сайдкар-образы для тестов) - есть
- образы в registry - на месте
- пайплайн до моих изменений - зелёный, тесты проходили
Всё на месте, а не работает.🤡

Перепроверяю ещё тысячу раз.
Локально - 100% то же самое - работает.
В GitLab - нет.

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

Иду смотреть настройки репозитория.
Settings - нет доступа.
Access Tokens - нет доступа.
Deploy Tokens - нет доступа.
У меня Developer, без права заглянуть хоть куда-то в настройки.

Пишу в личку коллеге, который точно Admin:
- Слушай, можешь по этой ссылке просто нажать Retry на джобе тестов?
Он жмёт.
Тесты проходят. Сука.

Я жму Retry.
Тесты падают. Сука.

Пишу главной команде DevOps (да, оксюморон - девопс пишет девопсам "спасити-памагити").
Объясняю ситуацию: так и так, тесты падают только когда пайплайн запускает мой юзер, предполагаю, что CI_JOB_TOKEN моего профиля не имеет нужных прав, он передаётся в кубер как image pull secret, а там авторизация не проходит.

Ну, после некоторых усилий удалось донести мысль. Ребята берут таймаут.

Через несколько дней/часов:
- Ретрай.
Ретраю.
Зелёное. 🎉

- А что было?
- Мы тоже долго дебажили, скормили все токены в Claude, но нашли.
У тебя в профиле GitLab стояла галочка External User.


External. User. Сука.
Галочка. В профиле. Которую я не ставил. Которую даже не видел (у меня нет доступа к админ-панели). Которую кто-то когда-то поставил при создании моего аккаунта. Или не снял. Или поставил по дефолту в LDAP/SAML/SCIM-маппинге. Или просто потому что.

Что делает External User в GitLab:
- https://docs.gitlab.com/administration/external_users/
- внешний пользователь не имеет доступа к internal-репозиториям
- CI_JOB_TOKEN наследует ограничения профиля
- даже если у тебя есть явный Developer-доступ к проекту, некоторые internal-ресурсы (включая container registry соседних проектов) могут быть недоступны
- в логах пайплайна это выглядит как обычная 401/403 при pull image - ни слова про external user

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

Потрачено:
- ~6-7 часов моего времени на дебаг
- N часов времени DevOps-команды
- массу нервных клеток
- массу токенов в Claude

Выводы:
- если пайплайн падает только у вашего юзера, а у коллег - нет, проблема не всегда в коде.
Проблема иногда в правах. Не в правах репозитория, а в правах профиля.
- External User в GitLab - это тихий убийца. Никаких предупреждений в UI, никаких баннеров "вы external". Просто CI_JOB_TOKEN молча получает урезанные права.
- вы не увидите эту галочку сами - она в Admin Area, доступ только у администраторов GitLab.
- при онбординге на новый проект спросите: "А мой аккаунт точно не external?" Серьёзно. Одна галочка - полдня жизни.
- локальные тесты != CI тесты. Даже если команды идентичны. Контекст авторизации разный.
- и как обычно: во всём виноваты девопсы. Даже когда девопс - это ты сам. 😢
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25🤡2💔1
#longread #troubleshooting #operators #kubectl #kubernetes #одинденьизжизни

Внезапный и незапланированный лонгрид.

Разбирал по работе задачу, узнал новое про фичу kubectl cache discovery.
Теперь делюсь историей и знаниями с вами.
Приятного прочтения.

https://teletype.in/@kruchkov_alexandr/wWa-INvQvJH
15👍28🔥1
#devops

Просто делюсь последними находками.

- https://system-design.space/
Нечто типа русскоязычного справочника по system design.
Большой и объёмный. Начал почитывать, весьма годно.
За один присест всё не осилить, буду продолжать как книгу читать ближайшие месяцы.
Достойный аналог известному https://github.com/donnemartin/system-design-primer

Оцениваю как 9.5/10.

- https://endoflife.date/
А где-же, а где-же я смотрю когда что протухнет?
Не руками/глазами же смотрю на сайте каждого облака/ продукта/ компонента/ девайс/ операционка/ имадж/ фремворка/ либы.
Если надо глазами глянуть, то этот сайт просто топ.
Для обычных библиотек софта есть dependabot/Renovatebot (кстати, кто-то из них с этого же сайта берёт инфо).
А для всего остального этот ресурс.
Может даже у него MCP есть, но я не искал, я быстрее глазами найду, чем заряжать на это LLM.

Оцениваю как 9/10 (есть не всё).

- https://thenewstack.io/
Невероятно захватывающие и очень интересные мне истории, случаи, опыт, рассказы, postmortems.
DevOps, k8s, cloud и вот это вот всё.
Читаю те топики, что мне интересны, часть пропускаю (есть и маркетинг буллшит).

Оцениваю в 8.5/10.

- https://awesome-llmops.inftyai.com/
- https://github.com/InftyAI/Awesome-LLMOps
Неплохая таблица как у https://landscape.cncf.io/, но по LLM/LLMOps.
Курируемый список (awesome list) инструментов для LLMOps, он охватывает инференс, оркестрацию, RAG, агентов, fine-tuning и мониторинг LLM.
​Короче очередной неплохой awesome список.
В мире AI, когда каждый день всё меняется, это неплохой источник.
А надо быть в курсе всего этого, иначе уволят будешь не в теме.

Оценка 8/10.

- https://resources.anthropic.com/hubfs/The-Complete-Guide-to-Building-Skill-for-Claude.pdf?hsLang=en
Крутой мануал по скиллам. Сразу запилил себе кучу всего полезного.
Нет смысла рассказывать, что себе сделал, так как эта инфа устареет уже завтра, когда выйдет очередная новая модель и обновление курсора с новыми фичами.
Имеет смысл ознакомится и попробовать либо сейчас, либо сразу скипнуть.

- https://agentsmd.io/agents-md-best-practices
Бест практисы с AGENTS.md файлом.
Ровно как и предыдущая ссылка будет актуально лишь короткое время.
Имеет смысл ознакомится и попробовать либо сейчас, либо сразу скипнуть.

- https://github.com/metalbear-co/mirrord
Очень интересная задумка. Конечно же у него есть аналоги, но мне понравилась эта реализация.
Потыкал на лабораторном кластере с go-шным и php стеком - прикольно!
В отличии от telepresence/gefyra/nocalhost невероятно низкий входной порог и буквально в три клика развернуть.
Простота в наше переусложнённое время дорогого стоит.
Однозначно лайк, надеюсь они не умрут через полгода.

Оценка 10/10.


Было ещё много прочитанного, изученного, но в общем и целом это, скорее, белый шум, чем что-то, чем хочется поделится.
10👍20🔥16
Перешёл на Ask every time в Cursor IDE.

Устал разгребать дерьмо от агента.
Прям утомился и устал.

Реалии февраля 2026:
- игнорирует allow list в README.md
- игнорирует allow list в настройках Cursor IDE
- игнорирует файл(ы) rules Cursor IDE
- игнорирует allow для MCP серверов
- игнорирует явные запреты в AGENTS.md
- игнорирует рут сандбокс (может легко выходить из песочницы)
- при моих запретах средствами операционной системы начинает писать питон скрипты, чтобы обойти запреты доступа к файловой системе и просить аппрув на выполнение🤨

Каждый раз без зазрения совести использует опасные команды без аппрува, если ему захотелось
- terraform apply -target -auto-approve
- kubectl delete/apply
- terragrunt run-all -auto-approve
- aws .. modify ..
и многое другое. Печаль.

Модели:
- Claude-4.5-sonnet
- 4.6 Opus

Четырёх сильных ошибок мне хватило.
Благо git всё помнит и можно ревертнуть руками, а косяк был без персистент даты.

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

- - -
На скрине часть моей драмы-переписки.
В итоге у меня сейчас 0 MCP серверов, пустые allow list, пару костыль-ограничений средствами операционки и Ask every time.
Печаль.
Please open Telegram to view this post
VIEW IN TELEGRAM
😁205👍5🥴2😢1
Ямлоделие

О, дивный мир, на работе я узнал об ограничении GitLab на парсинг YAML файла с пайплайном.

Проскочила ошибка, уронила всё.
Unable to run pipeline
`gitlab-ci.yml`: The parsed YAML is too big

Документация привела к
https://docs.gitlab.com/administration/instance_limits/#maximum-size-and-depth-of-cicd-configuration-yaml-files

Интереса ради спуллил их репу https://gitlab.com/gitlab-org/gitlab
Аригато, кстати, что git репозиторий весит всего 2.79 GiB, просто невероятное удобство😊.

Затем спросил нейронку сколько памяти/ресурсов экономится при парсинге 2 или 4 мегабайтных файлов и какие причины такого ограничения.

Самое интересное - max_yaml_size_bytes это не физический размер файла, а память, выделяемая на объекты при парсинге.
То есть 2 MB YAML может жрать больше 2 MB RAM в зависимости от структуры.

Зачем вообще лимит?
Погуглил, почитал документацию, посмотрел код.
- DoS защита
один огромный YAML может положить весь Sidekiq (классная архитектура 😀)
- OOM превенция
память пре-аллоцируется, условно 100 пайплайнов по 100 MB = изда RIP серверу
- производительность
парсинг гигантских YAML тормозит всех (чужие проекты, пайплайны)
- code smell детектор (это я/мы)
если у вас 2+ MB YAML, что-то пошло не так в архитектуре

Ну штош, значит пора оптимизировать содержимое пайплайнов.😭
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7👌31
#aws #rds #troubleshooting

Приходят как-то коллеги, говорят - нет прав включить Enhanced Logging на RDS Proxy.
Для дебага очень надо на пару часов.
Ок, смотрю политику, а там rds:* на Resource = "*".
Говорю - всё есть, всё должно работать.
Хоть дропайте всё к херам (stage account).

Возвращаются - нет, всё равно не работает.
Проверяю глазами ещё раз полиси.
Да вроде всё ок.
Говорю сделайте релогин и попробуйте ещё, все права есть.

Снова возвращаются, сделав релогин.
Говорят "включаем, а оно не включается".
Делаю невозмутимое лицо, хотя внутри уже кривлюсь как хурма: описание проблемы уровня "включаем, а не включается, ошибок нет" звучит слишком уныло, чтобы воспринимать его всерьёз.

Прошу объяснить подробнее.
Мне показывают по шагам:
- зашли в консоль
- зашли в RDS
- нашли нужный proxy
- нажали Edit/Modify (я не помню название кнопки)
- поставили галочку "Activate enhanced logging"
- нажали "Modify proxy"
- видят надпись "Modifying proxy ..."
- видят зелёный баннер "Successfully modified proxy ..."
- выходят, заходят снова - галочки нет

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

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

Ладно, добавляю себя временно в эту группу и начинаю воспроизводить.
Захожу под их пермишнсетом, повторяю их действия.
Та же история - "Successfully modified", а галочка не стоит.

Думаю что не так.
Выхожу из SSO, захожу снова - галочки нет.
Сбрасываю кеш браузера - галочки нет.
Пробую другой браузер - галочки нет.
Смотрю в developer tools в браузере - никаких ошибок, никаких 403, всё 200.
Смотрю в сам UI консоли - ни единого красного баннера, ни единого намёка на проблему.
Само собой инкогнито мод браузера не помог.
Ивентов нет.

Потратил на это ещё чуть времени, пока до меня наконец не дошло.
Дурачок, - думаю я, - а CloudTrail-то ты проверил?

Иду в CloudTrail, фильтрую по ModifyDBProxy.
Нахожу событие. И там:
"errorCode": "AccessDenied",
"errorMessage": "User: ... is not authorized to perform: iam:PassRole
on resource: arn:aws:iam::ACCOUNT_ID:role/rds-proxy-wanker
because no identity-based policy allows the iam:PassRole action"

Вот оно.

AWS при вызове ModifyDBProxy передаёт IAM роль прокси в Secrets Manager.
Для этого нужно право передать (PassRole) именно эту роль.
rds:* тут ни при чём - это отдельное действие на отдельный ресурс.

В политике группы iam:PassRole был разрешён только для одной конкретной роли.
Про rds-proxy-* никто не подумал.

Добавляем в политику:
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::ACCOUNT_ID:role/rds-proxy-*"
}

Применяем, перелогиниваемся.
Галочка встаёт и остаётся стоять.
Ивенты пишутся.

Итоги:
- "Successfully modified" в AWS консоли не гарантирует, что изменение применилось 🥲
- AccessDenied может прилетать не на само действие, а на зависимое - в данном случае на iam:PassRole
- Консоль эту ошибку не показывает совсем. Молча глотает. Никакого красного баннера. Никаких ошибок. Никаких ивентов. 😎
- CloudTrail - первое место, куда надо смотреть, когда "всё работает, но не работает"
- rds:* не равно "всё для RDS". Некоторые операции тащат за собой зависимости на другие сервисы
- нехер снова кривить свою морду, коллеги всё же солнышки-зайчики и правы, даже если пояснение проблемы тупорылое
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯182😭1
#ai #kubernetes

Удивительно, как быстро развивается мир моделек.

Понадобилось, внезапно, по работе посмотреть большое количество логов.
Да-да, есть под рукой VMui/OpenSearch/Grafana и так далее, но логов много и подов много, веб не поможет в моем дотошном осмотре. Немного специфичная задача была.
Надо было прям в моменте глянуть чо там чо там на нескольких десятках подов, да ещё и с дебаг левелом.

Думаю "блин, ну я вроде сам это относительно недавно делал через node proxy, даже заметку писал, дай повторю".
https://xn--r1a.website/makebreakreflect/205
Нашёл, зашёл, а там уныло - много команд, мне лень.
Не, ну там десяток нод, десятка 4 подов, не буду же я на каждую ноду по SSH/SSM залезать, это тупо.
В общем скормил мой же текст поста нейронке, говорю дай мне универсальный скрипт/команду, чтобы выкачать все файлы по фильтру неймспейса/пода, чтобы локально это исследовать.

Та-да! Пару минут и у меня есть готовый скрипт.
На самом деле их два - выбирайте под задачу.
Мне нужен был первый вариант файлов с диска без ротации.

Вариант 1: через файловую систему ноды
Этот вариант ходит напрямую в /var/log/containers/ через Node Proxy API и выкачивает raw-файлы логов.
Полезен, если нужно получить именно файлы с диска (например, для анализа ротации или когда нужен точный формат файла).
NAMESPACE="kube-system"
POD_PATTERN="kube-proxy"

for POD in $(kubectl get pods -n $NAMESPACE | grep $POD_PATTERN | awk '{print $1}'); do
NODE=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.nodeName}')
CONTAINER_ID=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.status.containerStatuses[0].containerID}' | sed 's|containerd://||')
CONTAINER_NAME=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.containers[0].name}')
LOG_FILE="${POD}_${NAMESPACE}_${CONTAINER_NAME}-${CONTAINER_ID}.log"

echo "⬇️ Downloading: $POD → $NODE"
kubectl get --raw "/api/v1/nodes/${NODE}/proxy/logs/containers/${LOG_FILE}" > "./${POD}.log"
echo "✓ Saved: ./${POD}.log"
done


Вариант 2: Через официальный API containerLogs
Этот вариант использует документированный endpoint kubelet API.
Работает с namespace/pod/container напрямую, не требует знания внутренней структуры файловой системы ноды.
NAMESPACE="kube-system"
POD_PATTERN="kube-proxy"

for POD in $(kubectl get pods -n $NAMESPACE | grep $POD_PATTERN | awk '{print $1}'); do
NODE=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.nodeName}')

CONTAINERS=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}')

for CONTAINER in $CONTAINERS; do
OUTPUT_FILE="./${POD}_${CONTAINER}.log"

echo "⬇️ Downloading: $POD/$CONTAINER from $NODE"
kubectl get --raw "/api/v1/nodes/${NODE}/proxy/containerLogs/${NAMESPACE}/${POD}/${CONTAINER}" > "$OUTPUT_FILE"
echo "✓ Saved: $OUTPUT_FILE"
done
done

аутпут
⬇️ Downloading: kube-proxy-brpfm → aks-1
✓ Saved: ./kube-proxy-brpfm.log
⬇️ Downloading: kube-proxy-bt6fp → aks-2
✓ Saved: ./kube-proxy-bt6fp.log


Ограничения обоих вариантов
Это не стандартный документированный способ получения логов контейнеров (хотя второй вариант использует документированный endpoint kubelet, это всё равно обходной путь через nodes/proxy).
Оба скрипта только для первого (индекс [0]) контейнера в поде, но мне ок по задаче.

Не заработает, если:
- у вашего пользователя нет прав nodes/proxy
- кластер имеет ограничения на node proxy access
- используется нестандартный путь для логов контейнеров (для варианта 1)
- для варианта 1, если у вас не containerd, а cri-o, поправьте sed на нечто типа:
sed -E 's/^(containerd:\/\/|cri-o:\/\/)//'

У меня крио нет, так что я хз сработает ли эта регулярка.

Получилась заметка по заметке, но зато у меня есть под рукой оба скрипта - выбираю в зависимости от задачи.
Файловая система, когда нужно покопаться в истории логов, официальный API - когда нужно быстро выкачать актуальное состояние (скорее всего не пригодится никогда).

Забавно обучать/получать ответы от нейронки по своей же заметке. 😅
👍16👎31
#AWScommunity #aws

Какие интересные письма порой приходят.
И это отлично.

Впереди будет много интересного и, надеюсь, полезного.
Наконец-то я смогу весь свой неиссякаемый энтузиазм и энергию потратить на пользу сообщества.
10🔥50👏1021
#opensearch #kubernetes #troubleshooting #одинденьизжизни

"Алекс, у нас пропали логи.
Но только на стейдже."

Иду смотреть.
Куб живой, метрики зелёные, поды в норме, а в OpenSearch тишина.
Ни одного алёрта. Ладно.

Пошёл проверять по классике:
- не упал ли агент сбора логов
- не отвалились ли права
- не умер ли endpoint
- не сломали ли что-то свежими коммитами

Первый заход:
Никаких коммитов, которые могли это поломать нет.
Ок, идём в куб.
kubectl get ds -n kube-system

fluent-bit живой, все реплики Ready
kubectl get pods -n kube-system | grep fluent-bit

всё раннит
kubectl logs -n kube-system daemonset/fluent-bit --since=30m

И вот там уже не тишина.

В логах:
- validation_exception
- this action would add [4] total shards
- cluster currently has [1000]/[1000] maximum shards open


Красота.
Значит не "логов нет", а "логгер не может писать".

Дальше мысль идёт так:
куб живой, агенты живые, опенсёрч живой, с pvc всё ок.
но если шардов в потолок, значит retention не чистит старые индексы.🤔

И тут флешбек:
несколько месяцев назад мигрировали с filebeat на fluent-bit.
Индексный префикс поменяли.
Ну там было нечто типа eks-filebeat-* на eks-fluentbit-*, не помню уже.
А retention policy, как это обычно бывает в суровой реальности, осталась смотреть на старое имя индекса ☔️.

Итог:
- новые индексы копились
- старые правила их не трогали
- шардов стало 1000/1000
- OpenSearch начал отвечать 400 на bulk insert
- fluent-bit ретраил, потом сдавался
- в UI ощущение, что "логи пропали только на stage"

То есть виноват не новый деплой, не "вчерашний коммит", не магия.
Просто policy смотрела не туда.
И да, жаль, что эта policy была не в IaC, а когда-то сделана руками в консоли.
Именно поэтому при миграции её и проглядели.

Что проверил и что сработало:
- поправил index pattern в retention/ISM policy на актуальный префикс
- подчистил лишние старые индексы вручную (DELETE /indexname* предыдущих лет/месяцев)
- освободились шарды
- проверил повторно логи fluent-bit -> ошибки по maximum shards open ушли
- в OpenSearch новые логи снова пошли

Короткий вывод:
- миграция агента логирования = почти всегда миграция index naming
- если не обновить retention под новый pattern, инцидент это вопрос времени
- всё, что не в IaC, рано или поздно теряется в памяти любой команды
- алерт на долю открытых шардов нужен заранее (80-85%), а не в момент 1000/1000 (а алёрта и не было)

Постмортем одной строкой:
Логи не пропали. Мы сами забили OpenSearch старыми индексами, потому что retention policy жила в прошлом и смотрела на имя эпохи filebeat.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍202
Впервые запустил apple music на новом железе.
Да-да, я теперь снова владелец MacBook pro, но уже на M5, и скоро буду писать свои позитивные и негативные, никому не нужные, отзывы.

Штош, заманчивая цена после триала, но, пожалуй, воздержусь.
Пожалуй я буду и дальше слушать музыку в браузере или у сторонних приложений за 3,5 гроша в год.
🔥11😁9😱3🎉2
#devops #troubleshooting

Из всего невообразимого и бесконечного потока, любимого и одновременно ненавистного всеми, AIAIAIA со всех углов, мне больше всего понравились skills.
Вообще ничего больше не впечатлило за последние года два - как скиллы.
Ну да, ну эйая, ну вайбкодинг. Это даже все бабушки знают и слышали.

Но, господи, как же скиллы охрененно работают в траблшутинге.

Вот пишу я это и понимаю, что скиллы сейчас - топ артефакт в работе девопса и SRE.
Во всяком случае, как я это использую - прям рекомендация 1000000 из 1.
Я готов прыгать от счастья, осознавая сколько времени они мне экономят.

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

Как использую у себя в двух словах:
- создал несколько SKILLS.md файлов (на самом деле у меня их иерархия, уже 29 скиллов)
- замапил скиллы для Cursor IDE. Можно и в Claude Code, скиллы будут работать и там и там, просто путь разный
- в скиллах и основном ридми файле описал структуру проекта(ов)
- - где ArgoCD (UI, API, git repos)
- - какой нейминг у кластеров кубернетис (AKS, EKS, GCP), нейминг неймспейсов
- - где вообще все гит репозитории и иерархию
- - адрес гитлаба и зеркало, локальные склонированные репозитории
- - что контекст переключается локально через kubectl
- - есть утилиты kubectx, jq и многое другое
- указываю все ключевые адреса репозиториев и ендпойнтов (виктория метрикс, виктория логс, алертменеджер, гитлаб и многое многое другое)
- если где есть сверхтяжелая авторизация, то говорю используй port-forward на внутренние service, там авторизации нет

В общем указал все, что надо для траблшутинга.

Как происходит процесс с моей стороны:
- прилетает алерт
- я копирую текст алерта в агент-ассистент (сейчас у меня курсор)
- жду
- агент смотрит что за кластер, неймспейс, под, контейнер
- лезет в гит, смотрит таймлайн когда кто что релизил, когда коммитили и где менялись какие версии или конфиги
- лезет в нужный кластер кубернетиса, смотрит в нужном неймспейсе, под, логи, дескрайб, ивенты кубера
- при необходимости лезет в логи виктория логс, метрики виктория метрикс (прям через curl)
- если надо, то лезет в арго
- если надо, то лезет в гит репозитории бекенда и фронтенда
- строит гипотезы, проверяет, уточняет
- при сомнениях что-то сам проверяет (ну типа если ему кажется, что проблема нехватки нод - нехватка ip адресов, то иди и проверь метрики сети в AWS и EKS CNI)
- если надо, лезет с рид правами в azure, gcp, aws cli
- выводит мне удобный формат по инциденту и вариант(ы) решений

С вероятностью 90 процентов это точный результат.

Чрезвычайно убого замазанные примеры на скриншотах.

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

Это только кажется, что это сложно, непонятно, магически. Нет.
Только представьте себя на месте агента: знаешь на 99 процентов структуру всей инфры, проектов, процессов.
Ты же как-то сам логически идешь по траблшутингу? Вот так и агент.

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

Алерт - копипаст - ждешь пару минут (автоаппрув или каждую команду аппрув) - ответ и решение(гипотезы).


- - -

А вот теперь грустная правда этого позитивного отзыва по скиллам.
Честно говоря это первый артефакт моей работы, который я не хочу отдавать коллегам или боссам.
Скиллы это просто сильвербаллет в 2026 и я буду тянуть до последнего, делясь с командами.

На полном серьезе не хочу шарить на команду 🚬
Даже если кто из команды это читает.
Господи, кому я вру, если скажут - отдам уже завтра.☔️

Я ведь ровно так же понимаю, что написав свои 100500 скиллов, которые уже сейчас, на сотнях алертов и десятке инцидентов на 100 процентов точно дали ответ и удобный аутпут для команд, можно пошарить самостоятельному агенту или условному мидлу с зарплатой три копейки.
И я вылечу на помойку.
Вот такая грустная правда.

Скиллы - это прекрасно.
Скиллы - это главный мой артефакт работы в 2026.
Скиллы - это грустно, они на 10000 процентов подсократят работы нам, инженерам.☔️
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
10🔥236🤡3🫡1
Понедельник начинается с кофе прохождения AI adoption survey 🤡
Please open Telegram to view this post
VIEW IN TELEGRAM
😁23