#victorialogs #victoriametrics #grafana #troubleshooting
Когда простая задача превращается в тупик:
Самобичевание, часть 1 из 2.
Вот уже много лет я работаю.
За плечами - десятки пройденных боёв с Kubernetes, облаками, мониторингом и коллегами.
Поел не одну ложку коричневой субстанции.
Казалось, что в области observability я видывал всё.
Но одна простая задача недавно поставила меня в тупик настолько основательно, что я до сих пор негодую на себя.
Как всё начиналось
У нас на работе есть собственная система observability - довольно сложная, с десятками компонентов, которые менялись со временем.
Сначала были одни инструменты, потом другие, затем поменялась концепция хранения, добавился мульти-тенантный доступ и так далее.
И я попал на задачу в самый неподходящий момент: когда нужно было переделать дашборды и для клиентов (с их интерфейсом и логами), и для внутренней техподдержки SRE.
Задача звучала тривиально:
- поменять дата-сорс в Grafana с Loki на VictoriaLogs
- параллельно часть метрик из VictoriaMetrics тоже переехала в VictoriaLogs - веб-серверный компонент стало удобнее хранить именно там
- переделать существующие панели, сохранив при этом изоляцию данных: чтобы клиенты не видели чужие логи, а права доступа работали корректно
Казалось, пара дней - и готово.
Когда тупик оказался глубже, чем я думал
Я провозился с этой задачей больше недели.
Почему?
Потому что оказалось, что я не знал многого.
Да нихера я не знал, начиная с базовой архитектуры:
- как работает индексация полей в VictoriaLogs (спойлер: индексируются все поля, не только лейблы как в Loki, плюс есть отдельные _stream_fields для ускорения типичных фильтров)
- как агенты отправляют данные и что такое _stream_fields
- что такое тенанты (AccountID, ProjectID) и как они задаются через HTTP-заголовки
- какие эндпоинты существуют (/select/logsql/query для запросов, /insert/loki/api/v1/push для записи)
Самое сложное - это понимание метрик в логах.
Когда берёшь запрос, не ясно: эта метрика уже распарсена из JSON на этапе ingestion, или её нужно парсить прямо в запросе?
Оказалось, что VictoriaLogs по умолчанию автоматически парсит JSON при записи данных (начиная с версии v1.16.0).
При Loki-совместимом ingestion VictoriaLogs пытается распарсить JSON из поля сообщения (message / _msg) и разложить его ключи в отдельные поля, которые затем индексируются.
Это можно отключить флагом -loki.disableMessageParsing или параметром disable_message_parsing=1 в URL.
А для парсинга в запросах есть свои операторы LogsQL:
- unpack_json - распаковка JSON
- unpack_logfmt - распаковка logfmt
- extract и extract_regexp - извлечение по паттерну
- fields pipe - работа с полями
Но вот сюрприз номер два: оказалось, что в Grafana можно вообще не парсить через LogsQL.
Есть Grafana Transformations - extractFields с format: "json" - и он достаёт поля из JSON прямо в браузере. 🤡
Это работает, но жрёт ресурсы на клиенте и ломает голову: где парсить - в запросе или в трансформации?
Примеры запросов, с которыми пришлось работать:
Тут видно:
- фильтрация по stream полям (stream:"stderr")
- поиск по тексту ("[crit]", "[emerg]")
- stats pipe с count() as total
- переменные Grafana (${tenant_id}, ${region})
И каждый раз для новой панели нужно было решать:
парсить в LogsQL через unpack_json или в Grafana через transformations?
Использовать stats pipe или range запрос?
Какой подход быстрее, а какой уложит VictoriaLogs ошибками 502 и 504?
Спойлер: я раз 30 уложил на лопатки виктория сторадж.
Когда простая задача превращается в тупик:
миграция дашбордов с Loki на VictoriaLogs
Самобичевание, часть 1 из 2.
Вот уже много лет я работаю.
За плечами - десятки пройденных боёв с Kubernetes, облаками, мониторингом и коллегами.
Поел не одну ложку коричневой субстанции.
Казалось, что в области observability я видывал всё.
Но одна простая задача недавно поставила меня в тупик настолько основательно, что я до сих пор негодую на себя.
Как всё начиналось
У нас на работе есть собственная система observability - довольно сложная, с десятками компонентов, которые менялись со временем.
Сначала были одни инструменты, потом другие, затем поменялась концепция хранения, добавился мульти-тенантный доступ и так далее.
И я попал на задачу в самый неподходящий момент: когда нужно было переделать дашборды и для клиентов (с их интерфейсом и логами), и для внутренней техподдержки SRE.
Задача звучала тривиально:
- поменять дата-сорс в Grafana с Loki на VictoriaLogs
- параллельно часть метрик из VictoriaMetrics тоже переехала в VictoriaLogs - веб-серверный компонент стало удобнее хранить именно там
- переделать существующие панели, сохранив при этом изоляцию данных: чтобы клиенты не видели чужие логи, а права доступа работали корректно
Казалось, пара дней - и готово.
Когда тупик оказался глубже, чем я думал
Я провозился с этой задачей больше недели.
Почему?
Потому что оказалось, что я не знал многого.
Да нихера я не знал, начиная с базовой архитектуры:
- как работает индексация полей в VictoriaLogs (спойлер: индексируются все поля, не только лейблы как в Loki, плюс есть отдельные _stream_fields для ускорения типичных фильтров)
- как агенты отправляют данные и что такое _stream_fields
- что такое тенанты (AccountID, ProjectID) и как они задаются через HTTP-заголовки
- какие эндпоинты существуют (/select/logsql/query для запросов, /insert/loki/api/v1/push для записи)
Самое сложное - это понимание метрик в логах.
Когда берёшь запрос, не ясно: эта метрика уже распарсена из JSON на этапе ingestion, или её нужно парсить прямо в запросе?
Оказалось, что VictoriaLogs по умолчанию автоматически парсит JSON при записи данных (начиная с версии v1.16.0).
При Loki-совместимом ingestion VictoriaLogs пытается распарсить JSON из поля сообщения (message / _msg) и разложить его ключи в отдельные поля, которые затем индексируются.
Это можно отключить флагом -loki.disableMessageParsing или параметром disable_message_parsing=1 в URL.
А для парсинга в запросах есть свои операторы LogsQL:
- unpack_json - распаковка JSON
- unpack_logfmt - распаковка logfmt
- extract и extract_regexp - извлечение по паттерну
- fields pipe - работа с полями
Но вот сюрприз номер два: оказалось, что в Grafana можно вообще не парсить через LogsQL.
Есть Grafana Transformations - extractFields с format: "json" - и он достаёт поля из JSON прямо в браузере. 🤡
Это работает, но жрёт ресурсы на клиенте и ломает голову: где парсить - в запросе или в трансформации?
Примеры запросов, с которыми пришлось работать:
{tenant_id="${tenant_id}"} stream:"stderr" "[crit]" | stats count() as total
{tenant_id="${tenant_id}", region=~"${region}"} eventType:kubernetes-eventТут видно:
- фильтрация по stream полям (stream:"stderr")
- поиск по тексту ("[crit]", "[emerg]")
- stats pipe с count() as total
- переменные Grafana (${tenant_id}, ${region})
И каждый раз для новой панели нужно было решать:
парсить в LogsQL через unpack_json или в Grafana через transformations?
Использовать stats pipe или range запрос?
Какой подход быстрее, а какой уложит VictoriaLogs ошибками 502 и 504?
Спойлер: я раз 30 уложил на лопатки виктория сторадж.
👍6
#victorialogs #victoriametrics #grafana #troubleshooting
Самобичевание, часть 2 из 2.
Особенности Grafana, о которых я забыл
Потом всплыли нюансы самой Grafana.
В плагине VictoriaLogs для Grafana (victoriametrics-logs-datasource) есть несколько queryType:
- Raw Logs - для логовой панели (лента логов, tailing)
- Range - для time series и графиков по времени, под капотом ходит в /select/logsql/stats_query_range
- Stats - для агрегатов без временной развёртки (одно число/набор чисел по фильтру)
И вот тут началась жесть с выбором правильного queryType:
- queryType: "range" - для time series графиков
- queryType: "stats" - для агрегаций через | stats count() as total
- queryType: "logs" - для сырых логов
Где-то нужен range-запрос, где-то stats.
Где-то метрика уже доступна как число в отдельном поле, а где-то её нужно извлекать из JSON.
И если ошибёшься с queryType - панель просто пустая, без ошибок.
Сидишь и гадаешь: запрос кривой или тип панели неправильный?😭
Разница между _stream_fields (лейблы потока, быстрая фильтрация) и обычными полями лога оказалась критичной:
- фильтровать по stream field - быстро и дёшево
- по log field - медленнее, особенно если JSON-парсинг выполняется в запросе
Выводы и самоанализ
Когда я осмотрел свои коммиты, увидел десятки итераций:
- сначала делал парсинг в запросах через unpack_json, потом переключился на Grafana transformations, потом понял - лучше хранить поля структурированно при ingestion
- менял queryType с range на stats и обратно, потому что панели то пустые, то не те данные
- возился с мульти-тенантностью: настраивал лейблы типа tenant_id в запросах и флаги изоляции
- оптимизировал запросы со stats count() as total чтобы не гонять по всей базе полнотекстовый поиск
- добавлял фильтры по stream:"stderr" вместо поиска по всем логам
Полная фигня, если честно. 🤡
Это сильно меня опечалило:
- либо я отстал от технологий и мои знания Prometheus/Grafana устарели и надо срочно учить весь VM стек
- либо я переоценил себя при оценке задачи
- либо это действительно сложная задача, которую я недооценил
Скорее всего, правда посередине.
VictoriaLogs - не просто замена Loki, это другой инструмент с другой философией:
- Loki индексирует только лейблы потока, текст сообщений не индексируется - regex по message сканирует содержимое
- VictoriaLogs индексирует все поля логов, так что по ним можно быстро фильтровать и искать. При этом стрим-поля через _stream_fields дополнительно помогают с компрессией и ускоряют типичные фильтры
- LogsQL ≠ LogQL - это разные языки запросов, хоть и похожие
- Высокая кардинальность (trace_id, user_id, ip) в VictoriaLogs гораздо менее болезненна, чем в Loki, если хранить такие вещи как обычные поля, а не stream fields. Loki сильно страдает, если тащить user_id/trace_id в лейблы
А миграция дашбордов в мульти-тенантной среде - это не рутина, а полноценный проект.
Во всяком случае так вышло для меня в этот раз.
И это я ещё пропускаю часть про VMrules и recording rules, которые пришлось добавить для некоторых панелей....😭
Задача - теперь для меня не сложная. Ну я максимум часа 2-3 на подобное потрачу.
Проблема в том, что я переоценил свои силы и знания (которых и не было).
В общем надо учиться и читать документацию, прежде, чем приступать к задаче.
Самобичевание, часть 2 из 2.
Особенности Grafana, о которых я забыл
Потом всплыли нюансы самой Grafana.
В плагине VictoriaLogs для Grafana (victoriametrics-logs-datasource) есть несколько queryType:
- Raw Logs - для логовой панели (лента логов, tailing)
- Range - для time series и графиков по времени, под капотом ходит в /select/logsql/stats_query_range
- Stats - для агрегатов без временной развёртки (одно число/набор чисел по фильтру)
И вот тут началась жесть с выбором правильного queryType:
- queryType: "range" - для time series графиков
- queryType: "stats" - для агрегаций через | stats count() as total
- queryType: "logs" - для сырых логов
Где-то нужен range-запрос, где-то stats.
Где-то метрика уже доступна как число в отдельном поле, а где-то её нужно извлекать из JSON.
И если ошибёшься с queryType - панель просто пустая, без ошибок.
Сидишь и гадаешь: запрос кривой или тип панели неправильный?
Разница между _stream_fields (лейблы потока, быстрая фильтрация) и обычными полями лога оказалась критичной:
- фильтровать по stream field - быстро и дёшево
- по log field - медленнее, особенно если JSON-парсинг выполняется в запросе
Выводы и самоанализ
Когда я осмотрел свои коммиты, увидел десятки итераций:
- сначала делал парсинг в запросах через unpack_json, потом переключился на Grafana transformations, потом понял - лучше хранить поля структурированно при ingestion
- менял queryType с range на stats и обратно, потому что панели то пустые, то не те данные
- возился с мульти-тенантностью: настраивал лейблы типа tenant_id в запросах и флаги изоляции
- оптимизировал запросы со stats count() as total чтобы не гонять по всей базе полнотекстовый поиск
- добавлял фильтры по stream:"stderr" вместо поиска по всем логам
Полная фигня, если честно. 🤡
Это сильно меня опечалило:
- либо я отстал от технологий и мои знания Prometheus/Grafana устарели и надо срочно учить весь VM стек
- либо я переоценил себя при оценке задачи
- либо это действительно сложная задача, которую я недооценил
Скорее всего, правда посередине.
VictoriaLogs - не просто замена Loki, это другой инструмент с другой философией:
- Loki индексирует только лейблы потока, текст сообщений не индексируется - regex по message сканирует содержимое
- VictoriaLogs индексирует все поля логов, так что по ним можно быстро фильтровать и искать. При этом стрим-поля через _stream_fields дополнительно помогают с компрессией и ускоряют типичные фильтры
- LogsQL ≠ LogQL - это разные языки запросов, хоть и похожие
- Высокая кардинальность (trace_id, user_id, ip) в VictoriaLogs гораздо менее болезненна, чем в Loki, если хранить такие вещи как обычные поля, а не stream fields. Loki сильно страдает, если тащить user_id/trace_id в лейблы
А миграция дашбордов в мульти-тенантной среде - это не рутина, а полноценный проект.
Во всяком случае так вышло для меня в этот раз.
И это я ещё пропускаю часть про VMrules и recording rules, которые пришлось добавить для некоторых панелей....
Задача - теперь для меня не сложная. Ну я максимум часа 2-3 на подобное потрачу.
Проблема в том, что я переоценил свои силы и знания (которых и не было).
В общем надо учиться и читать документацию, прежде, чем приступать к задаче.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥1
#kubernetes #мысли
Сижу, разбираюсь с
- https://docs.crossplane.io/latest/
- https://marketplace.upbound.io/providers (да, мне понравились upbound)
Надо было кое-что найти, а обычный лист выдаёт сотни ответов без грепа.
Глянул сколько у нас всего кастом кайндов, а там..
Ах этот безумный-безумный мир.
513
498
В наших кластерах CRD станет скоро больше, чем базовых сущностей кубернетиса.
Безумие, абсолютное безумие.
В целом можно на собеседовании/в баре с коллегами по девопс-цеху меряться:
- У нас в кластере 498 CRD, а сколько у вас?😁
Скоро это будет показывать насколько зрелый проект, мощная команда и глубокое погружение в экосистему кубера(надеюсь нет).
Очередной раз не завидую всем молодым специалистам, кому надо будет учить этот кубер.
Сижу, разбираюсь с
Crossplane и провайдерами для Azure.- https://docs.crossplane.io/latest/
- https://marketplace.upbound.io/providers (да, мне понравились upbound)
Надо было кое-что найти, а обычный лист выдаёт сотни ответов без грепа.
Глянул сколько у нас всего кастом кайндов, а там..
Ах этот безумный-безумный мир.
kubectl api-resources --verbs=list | grep -v customresourcedefinition | wc -l
513
kubectl get crd -o name | wc -l
498
В наших кластерах CRD станет скоро больше, чем базовых сущностей кубернетиса.
Безумие, абсолютное безумие.
В целом можно на собеседовании/в баре с коллегами по девопс-цеху меряться:
- У нас в кластере 498 CRD, а сколько у вас?
Скоро это будет показывать насколько зрелый проект, мощная команда и глубокое погружение в экосистему кубера
Очередной раз не завидую всем молодым специалистам, кому надо будет учить этот кубер.
Please open Telegram to view this post
VIEW IN TELEGRAM
💯12😱6😁1
1.png
431.8 KB
#devops #git
Никакого рокетсайнса или, прости господи, лайфхаков, просто делюсь наблюдением.
Последние месяцы для работы со схемами/диаграммами вместо привычных ресурсов (draw.io excalidraw.com diagrams.mingrammer.com) перешёл на связку:
- любой AI ассистент, любая модель
- markdown файл https://www.markdownguide.org/
- mermaid фреймворк https://mermaid.js.org/
Поддержка mermaid в MD файлах уже достаточно давно, а основные инструменты - VSCode, GitHub, GitLab, Cursor умеют это отображать для человека и в IDE, и в веб-браузере.
В некоторых случаях надо ставить плагин, но в некоторых уже работает и так.
Как это работает:
- я прошу ассистента нарисовать некую схему. Схема может быть обычная flowchart, может быть state, sequence или что-то другое
- в основном README.md или CLAUDE.md появляется блок кода, который отлично лежит в md файле и при этом
- - схема видна в IDE через preview режим
- - схема видна в браузере на страницах GitHub/GitLab
- - схема понятна любой нейронке
- самое главное, это прозрачно и для меня и нейронка понимает что хочет, чтобы изменение кода было корректным
- ну и всё это в git репозитории
Рандомный пример промпта:
Рандомный пример кода (в файлах формата
Результат на скриншоте (GitHub + Cursor IDE).
Сами промпт/схема просто рандомно-мусорная, лишь для примера.
Можно рисовать что угодно:
- схема инфры
- как работает хэндшейки бэкенда
- логика распределения подов по нод группам в кубере
- как работает самописный оператор
- sequence diagram, как микросервисы общаются при отказе базы
и так далее.
Связка мне нравится, я думаю многие это используют.
Если ещё не пробовали - начните.
Мы итак многие инженерные вещи переусложнили.
Хочется хотя бы с диаграммами и схемами сделать всё проще.
Никакого рокетсайнса или, прости господи, лайфхаков, просто делюсь наблюдением.
Последние месяцы для работы со схемами/диаграммами вместо привычных ресурсов (draw.io excalidraw.com diagrams.mingrammer.com) перешёл на связку:
- любой AI ассистент, любая модель
- markdown файл https://www.markdownguide.org/
- mermaid фреймворк https://mermaid.js.org/
Поддержка mermaid в MD файлах уже достаточно давно, а основные инструменты - VSCode, GitHub, GitLab, Cursor умеют это отображать для человека и в IDE, и в веб-браузере.
В некоторых случаях надо ставить плагин, но в некоторых уже работает и так.
Как это работает:
- я прошу ассистента нарисовать некую схему. Схема может быть обычная flowchart, может быть state, sequence или что-то другое
- в основном README.md или CLAUDE.md появляется блок кода, который отлично лежит в md файле и при этом
- - схема видна в IDE через preview режим
- - схема видна в браузере на страницах GitHub/GitLab
- - схема понятна любой нейронке
- самое главное, это прозрачно и для меня и нейронка понимает что хочет, чтобы изменение кода было корректным
- ну и всё это в git репозитории
Рандомный пример промпта:
Нарисуй flowchart в mermaid, который показывает, как Pod с toleration scheduling'ится на tainted node в Kubernetes
Рандомный пример кода (в файлах формата
*.md)graph TB
subgraph "Node Pool"
N1[Node 1<br/>No Taint]
N2[Node 2<br/>Taint: dedicated=app:NoSchedule]
N3[Node 3<br/>Taint: dedicated=app:NoSchedule]
end
subgraph "Pods"
P1[Pod A<br/>No Toleration]
P2[Pod B<br/>Toleration: dedicated=app]
P3[Pod C<br/>No Toleration]
end
P1 -->|Can Schedule| N1
P1 -.->|Cannot Schedule| N2
P1 -.->|Cannot Schedule| N3
P2 -->|Can Schedule| N1
P2 -->|Can Schedule| N2
P2 -->|Can Schedule| N3
P3 -->|Can Schedule| N1
P3 -.->|Cannot Schedule| N2
P3 -.->|Cannot Schedule| N3
style N1 fill:#E8F5E9,stroke:#4CAF50,stroke-width:2px,color:#1B5E20
style N2 fill:#FCE4EC,stroke:#E91E63,stroke-width:2px,color:#880E4F
style N3 fill:#FCE4EC,stroke:#E91E63,stroke-width:2px,color:#880E4F
style P1 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px,color:#0D47A1
style P2 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px,color:#0D47A1
style P3 fill:#E3F2FD,stroke:#2196F3,stroke-width:2px,color:#0D47A1
Результат на скриншоте (GitHub + Cursor IDE).
Сами промпт/схема просто рандомно-мусорная, лишь для примера.
Можно рисовать что угодно:
- схема инфры
- как работает хэндшейки бэкенда
- логика распределения подов по нод группам в кубере
- как работает самописный оператор
- sequence diagram, как микросервисы общаются при отказе базы
и так далее.
Связка мне нравится, я думаю многие это используют.
Если ещё не пробовали - начните.
Мы итак многие инженерные вещи переусложнили.
Хочется хотя бы с диаграммами и схемами сделать всё проще.
5👍28❤6
#longread #grafana #kubernetes #troubleshooting #одинденьизжизни
А давненько не было лонгридов.
Три дашборда на границе Grafana
Часть 1 из 3.
У нас был дашборд
Пришло время мигрировать на VictoriaLogs.
Чтобы не ломать продакшен, я сделал всё по уму: создал копию дашборда с новым UID
Пару недель работал над ним параллельно с основным.
Переписывал запросы, проверял, что данные сходятся.
Всё работало.
Старый дашборд на Loki, новый на VictoriaLogs - оба живут рядом, никому не мешают.
Когда draft был готов, убрал из названия "Draft", поменял UID на
Ещё через неделю решили, что суффикс
Меняю в JSON:
на
И UID с
Коммит, пуш, ArgoCD синкается.
Зелёненькое. Красота.
Иду пить чай.
А, нет, не иду, коллеги пишут, что многое не работает, ссылки ведут на старую/поломанную борду.
Иду в Grafana проверить.
А там старый дашборд. С "Draft" в названии.
И рядом ещё какие-то СТАРЫЕ версии.
И переменная
Эээ, а я вообще смеержил в main ветку?
Да, смержил, вижу новый код в git.
Первая мысль - может ArgoCD не синканул?
Всё синхронизировано.
Статус: Healthy, Synced.
Странно.
Может ConfigMap в кубернетисе не обновился?
Смотрю - там новый JSON.
Без "Draft". Всё верно.
Ничего старого.
Может в поде Grafana старый файл закэшировался?
Нет, файл тоже новый. UID правильный. Title без "Draft".
Может другие файлы рядом лежат и он их синкает?
Все файлы внутри пода проверил - не, только новые тут.
Проверил во второй реплике POD Grafana -так же только новые дашборды.
Бред.
Чего имеем:
- в гите есть изменения, старых дашбордов нет
- ConfigMap правильный
- файл в поде правильный
а в UI - старое.
Что-то не сходится.
Что я упускаю?
А давненько не было лонгридов.
Три дашборда на границе Grafana
Часть 1 из 3.
У нас был дашборд
service-metrics-overview, который работал с Loki. Пришло время мигрировать на VictoriaLogs.
Чтобы не ломать продакшен, я сделал всё по уму: создал копию дашборда с новым UID
service-metrics-overview-vl-draft и пометкой "[VictoriaLogs Draft]" в названии.Пару недель работал над ним параллельно с основным.
Переписывал запросы, проверял, что данные сходятся.
Всё работало.
Старый дашборд на Loki, новый на VictoriaLogs - оба живут рядом, никому не мешают.
Когда draft был готов, убрал из названия "Draft", поменял UID на
service-metrics-overview-vl. Ещё через неделю решили, что суффикс
-vl не нужен - пусть будет просто service-metrics-overview, как раньше. Старый Loki-дашборд к тому моменту уже удалили.Меняю в JSON:
"noscript": "Service Metrics Overview [VictoriaLogs Draft]"
на
"noscript": "Service Metrics Overview [VictoriaLogs]"
И UID с
service-metrics-overview-vl-draft на service-metrics-overview.Коммит, пуш, ArgoCD синкается.
Зелёненькое. Красота.
Иду пить чай.
А, нет, не иду, коллеги пишут, что многое не работает, ссылки ведут на старую/поломанную борду.
Иду в Grafana проверить.
А там старый дашборд. С "Draft" в названии.
И рядом ещё какие-то СТАРЫЕ версии.
И переменная
loki висит, хотя я её точно убирал.Эээ, а я вообще смеержил в main ветку?
Да, смержил, вижу новый код в git.
Первая мысль - может ArgoCD не синканул?
argocd app get dashboards --refresh
Всё синхронизировано.
Статус: Healthy, Synced.
Странно.
Может ConfigMap в кубернетисе не обновился?
kubectl get configmap -n grafana dashboards-myapp-service-metrics-common -o yaml | tail -20
Смотрю - там новый JSON.
Без "Draft". Всё верно.
Ничего старого.
Может в поде Grafana старый файл закэшировался?
kubectl exec -n grafana deployment/prod-region1-grafana -c grafana -- tail -5 /tmp/dashboards/MyApp/service-metrics-overview.json
Нет, файл тоже новый. UID правильный. Title без "Draft".
Может другие файлы рядом лежат и он их синкает?
Все файлы внутри пода проверил - не, только новые тут.
Проверил во второй реплике POD Grafana -так же только новые дашборды.
Бред.
Чего имеем:
- в гите есть изменения, старых дашбордов нет
- ConfigMap правильный
- файл в поде правильный
а в UI - старое.
Что-то не сходится.
Что я упускаю?
👍6🔥1
#longread #grafana #kubernetes #troubleshooting #одинденьизжизни
Три дашборда на границе Grafana
Часть 2 из 3.
Смотрю логи Grafana:
И вот оно:
Grafana provisioning видит файл, пытается его применить, но не может - в базе уже есть дашборд с таким UID.
Кстати в некоторых версиях Grafana логи могут не содержать сам UID.
В таком случае придётся идти в API или базу данных напрямую.
У нас графана с авторизацией по SSO, у меня права, как у девелопера.
Мне лень писать заявки на админский доступ и ковырять UI интерфейс, думаю мне хватит и curl + localhost для анализа.
Ведь я знаю, что рут пароль лежит в секретах кубера.😁
Я сам себе админ😀
Проверяю через Grafana API:
И вижу:
-
-
-
Сука, да откуда вы берётесь.
Три версии одного дашборда.
А файл у меня один (гит, конфигмап, файл внутри пода).
Тут до меня дошло: каждый раз, когда я менял UID, provisioning создавал новую запись в базе.
А старые записи никуда не девались - они просто висели мёртвым грузом.
Теперь, когда я хочу вернуть оригинальный UID
Grafana хранит дашборды в PostgreSQL. Provisioning работает так:
- видит файл с UID
- проверяет - есть ли в БД запись с таким UID?
- если есть и привязана к этому файлу - обновляет
- если есть, но привязана к другому источнику - ошибка
Это не баг, а задуманное поведение: provisioning не перезаписывает дашборды, созданные из других источников.
Лезу в базу:
Смотрю что там:
Три записи от разных этапов миграции.
Файл один.
Вот и причина.
Варианты:
1. Удалить через UI - не выйдет (я ж попробовал), "provisioned dashboard cannot be deleted"
2. Ждать - не рассосётся само
3. Рестарт пода/подов - не вариант, мне по шапке за это дадут, графана как бы на всех.
4. Принудительный reload provisioning через API:
Не помогло - ошибка та же, записи в БД мешают.
5. Удалить записи из базы напрямую🤡
Выбираю последний вариант. Сначала бэкап:
Затем удаление:
Три дашборда на границе Grafana
Часть 2 из 3.
Смотрю логи Grafana:
kubectl logs -n grafana deployment/prod-region1-grafana -c grafana --since=1h | grep -iE "provision|dashboard"
И вот оно:
{"error":"A dashboard with the same uid already exists","file":"/tmp/dashboards/MyApp/service-metrics-overview.json"}Grafana provisioning видит файл, пытается его применить, но не может - в базе уже есть дашборд с таким UID.
Кстати в некоторых версиях Grafana логи могут не содержать сам UID.
В таком случае придётся идти в API или базу данных напрямую.
У нас графана с авторизацией по SSO, у меня права, как у девелопера.
Мне лень писать заявки на админский доступ и ковырять UI интерфейс, думаю мне хватит и curl + localhost для анализа.
Ведь я знаю, что рут пароль лежит в секретах кубера.
Я сам себе админ
Проверяю через Grafana API:
GRAFANA_PASS=$(kubectl get secret -n grafana creds-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d)
kubectl exec -n grafana deployment/prod-region1-grafana -c grafana -- curl -s -u "admin:${GRAFANA_PASS}" "http://localhost:3000/api/search?query=service%20metrics"И вижу:
-
service-metrics-overview -
service-metrics-overview-vl-
service-metrics-overview-vl-draftСука, да откуда вы берётесь.
Три версии одного дашборда.
А файл у меня один (гит, конфигмап, файл внутри пода).
Тут до меня дошло: каждый раз, когда я менял UID, provisioning создавал новую запись в базе.
А старые записи никуда не девались - они просто висели мёртвым грузом.
Теперь, когда я хочу вернуть оригинальный UID
service-metrics-overview, он уже занят старой записью.Grafana хранит дашборды в PostgreSQL. Provisioning работает так:
- видит файл с UID
- проверяет - есть ли в БД запись с таким UID?
- если есть и привязана к этому файлу - обновляет
- если есть, но привязана к другому источнику - ошибка
Это не баг, а задуманное поведение: provisioning не перезаписывает дашборды, созданные из других источников.
Лезу в базу:
# ищу primary ноду (рид реплика не даст писать)
kubectl exec -n grafana pg-monitoring-1 -c postgres -- psql -U postgres -d grafana -c "SELECT pg_is_in_recovery();"
# true = replica
kubectl exec -n grafana pg-monitoring-2 -c postgres -- psql -U postgres -d grafana -c "SELECT pg_is_in_recovery();"
# false = primary
Смотрю что там:
kubectl exec -n grafana pg-monitoring-2 -c postgres -- psql -U postgres -d grafana -c "SELECT id, uid, noscript, version FROM dashboard WHERE uid LIKE '%service-metrics%' ORDER BY uid;"
id | uid | noscript | version
------+-------------------------------------+--------------------------------------------------+---------
3781 | service-metrics-overview | Service Metrics Overview | 1
3830 | service-metrics-overview-vl | Service Metrics Overview [VictoriaLogs] | 1
3825 | service-metrics-overview-vl-draft | Service Metrics Overview [VictoriaLogs Draft] | 1
Три записи от разных этапов миграции.
Файл один.
Вот и причина.
Варианты:
1. Удалить через UI - не выйдет (я ж попробовал), "provisioned dashboard cannot be deleted"
2. Ждать - не рассосётся само
3. Рестарт пода/подов - не вариант, мне по шапке за это дадут, графана как бы на всех.
4. Принудительный reload provisioning через API:
kubectl exec -n grafana deployment/prod-region1-grafana -c grafana -- curl -s -X POST -u "admin:${GRAFANA_PASS}" "http://localhost:3000/api/admin/provisioning/dashboards/reload"Не помогло - ошибка та же, записи в БД мешают.
5. Удалить записи из базы напрямую
Выбираю последний вариант. Сначала бэкап:
kubectl exec -n grafana pg-monitoring-2 -c postgres -- pg_dump -U postgres grafana > grafana_backup_$(date +%Y%m%d_%H%M%S).sql
Затем удаление:
kubectl exec -n grafana pg-monitoring-2 -c postgres -- psql -U postgres -d grafana -c "DELETE FROM dashboard WHERE uid IN ('service-metrics-overview', 'service-metrics-overview-vl', 'service-metrics-overview-vl-draft');"DELETE 3
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5
#longread #grafana #kubernetes #troubleshooting #одинденьизжизни
Три дашборда на границе Grafana
Часть 3 из 3.
Жду 30 секунд (updateIntervalSeconds в provisioning конфиге).
Проверяю:
Одна запись. Правильный noscript. Правильный UID.
Иду в UI - работает. Никаких дублей, никаких "Draft", никакого Loki.
Победа! 🎉
Если provisioning не срабатывает автоматически, можно вызвать reload через API (выше был пример курлом) или перезапустить pod Grafana (не мой вариант).
Подробнее про provisioning: https://grafana.com/docs/grafana/latest/administration/provisioning/
На всё ушло часа два.
Могло бы - минут пятнадцать, если бы сразу глянул в логи.
Выводы, а куда без них.
- Grafana provisioning не перезаписывает существующие записи в БД, если они созданы из другого источника. Он кидает ошибку в лог и оставляет старую версию.
- ошибка "A dashboard with the same uid already exists" - это про запись в PostgreSQL, не про файл. Файл может быть идеальным, но если в базе мусор - ничего не заработает.
- логи первым делом. Там обычно всё написано. Но не во всех версиях Grafana логи достаточно информативны.
- удаление из БД работает, но это неофициальный подход. Я не нашёл другого способа. Для provisioned dashboards безопасно - они автоматически пересоздаются из файлов. Главное - делать на primary ноде PostgreSQL и иметь бэкап.
- миграции дашбордов - это не "поменять пару строк". Особенно если UID менялся несколько раз. Особенно, если я баран.
Как избежать этой проблемы (предполагаю):
- не меняйте UID после первого деплоя. Если нужна миграция - создайте новый дашборд с новым UID, а старый удалите полностью (включая запись в БД).
- используйте стабильные UID с префиксами:
- не используйте один UID для дашбордов в разных папках - это создаёт race condition.
- документируйте миграции в changelog или README.
- используйте
Пример конфигурации провижининга
Альтернативный подход, без прямого доступа к БД - когда его нет или страшно (предполагаю, что это правильный путь, но я не проверял):
- временно переименовать ConfigMap (например добавить суффикс
- дождаться, пока Grafana удалит дашборды (если
- вернуть ConfigMap с правильным именем и новым UID
- provisioning создаст дашборды заново
Этот способ медленнее, но безопаснее.
Для зануд:
- - -
- https://github.com/grafana/grafana/issues/12411
- https://github.com/grafana/grafana/issues/41085
- https://github.com/grafana/grafana/issues/73043
Три дашборда на границе Grafana
Часть 3 из 3.
Жду 30 секунд (updateIntervalSeconds в provisioning конфиге).
Проверяю:
kubectl exec -n grafana pg-monitoring-2 -c postgres -- psql -U postgres -d grafana -c "SELECT id, uid, noscript FROM dashboard WHERE uid LIKE '%service-metrics%';"
id | uid | noscript
------+-----------------------------+--------------------------------------------
3838 | service-metrics-overview | Service Metrics Overview [VictoriaLogs]
Одна запись. Правильный noscript. Правильный UID.
Иду в UI - работает. Никаких дублей, никаких "Draft", никакого Loki.
Победа! 🎉
Если provisioning не срабатывает автоматически, можно вызвать reload через API (выше был пример курлом) или перезапустить pod Grafana (не мой вариант).
Подробнее про provisioning: https://grafana.com/docs/grafana/latest/administration/provisioning/
На всё ушло часа два.
Могло бы - минут пятнадцать, если бы сразу глянул в логи.
Выводы, а куда без них.
- Grafana provisioning не перезаписывает существующие записи в БД, если они созданы из другого источника. Он кидает ошибку в лог и оставляет старую версию.
- ошибка "A dashboard with the same uid already exists" - это про запись в PostgreSQL, не про файл. Файл может быть идеальным, но если в базе мусор - ничего не заработает.
- логи первым делом. Там обычно всё написано. Но не во всех версиях Grafana логи достаточно информативны.
- удаление из БД работает, но это неофициальный подход. Я не нашёл другого способа. Для provisioned dashboards безопасно - они автоматически пересоздаются из файлов. Главное - делать на primary ноде PostgreSQL и иметь бэкап.
- миграции дашбордов - это не "поменять пару строк". Особенно если UID менялся несколько раз. Особенно, если я баран.
Как избежать этой проблемы (предполагаю):
- не меняйте UID после первого деплоя. Если нужна миграция - создайте новый дашборд с новым UID, а старый удалите полностью (включая запись в БД).
- используйте стабильные UID с префиксами:
myteam-service-metrics-overview
myteam-app-logs-dashboard
- не используйте один UID для дашбордов в разных папках - это создаёт race condition.
- документируйте миграции в changelog или README.
- используйте
allowUiUpdates: false в provisioning конфиге - это предотвращает дрейф конфигурации.Пример конфигурации провижининга
apiVersion: 1
providers:
- name: 'sidecarProvider'
orgId: 1
type: file
disableDeletion: false
allowUiUpdates: false # запрет редактирования в UI
updateIntervalSeconds: 30 # интервал сканирования файлов
options:
foldersFromFilesStructure: true
path: /tmp/dashboards
Альтернативный подход, без прямого доступа к БД - когда его нет или страшно (предполагаю, что это правильный путь, но я не проверял):
- временно переименовать ConfigMap (например добавить суффикс
-old)- дождаться, пока Grafana удалит дашборды (если
disableDeletion: false)- вернуть ConfigMap с правильным именем и новым UID
- provisioning создаст дашборды заново
Этот способ медленнее, но безопаснее.
Для зануд:
Прямое изменение базы данных Grafana - неофициальная практика!
Это не является моим советом или рекомендацией!
Просто поделился своей историей.
- - -
- https://github.com/grafana/grafana/issues/12411
- https://github.com/grafana/grafana/issues/41085
- https://github.com/grafana/grafana/issues/73043
🔥8❤1
#kubernetes
Пока я спал там взломали все инторнеты и все кубернетисы.
Источник на английском с глубоким разбором.
- https://grahamhelton.com/blog/nodes-proxy-rce
Хорошие ребята, кого не жалко репостнуть:
- https://news.1rj.ru/str/tech_b0lt_Genona/6101
- https://labs.iximiuz.com/tutorials/nodes-proxy-rce-c9e436a9
Хорошо, что все кубернетисы (кроме моих личных лабораторий) на предыдущих и нынешней работах это приватные сетки (что спасает от внешних негодяев, но не спасает от внутренних, но таких у нас нет).
Как проверить, подвержен ли ваш кластер/SA? А легко!
Копи-паст скрипта в своем кластере:
Да, такие дела. Ну и порт 10250 должен быть доступен.
Пока я спал там взломали все инторнеты и все кубернетисы.
Источник на английском с глубоким разбором.
- https://grahamhelton.com/blog/nodes-proxy-rce
Хорошие ребята, кого не жалко репостнуть:
- https://news.1rj.ru/str/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
Снова про операторы.
У ресурса
-
-
Как это работает на самом деле:
Когда ты создаёшь или обновляешь секрет, API-сервер мержит
В etcd хранилке и в ответе API сохраняется только
При чтении секрета из кластера поле
А теперь типичнейшая ошибка, на которую я снова попался.😭
Код оператора с ошибкой:
В реконсайлере проверка "изменился ли секрет?":
Видите проблему?
При создании desired-секрета мы используем
Но в Go-структуре этого объекта поле
Что происходит:
- оператор создаёт секрет с
- при следующем reconcile оператор читает секрет - там
- оператор строит desired-секрет - там
- сравнение видит разницу:
(потому что nil)
- оператор решает обновить секрет и делает
записывая nil в поле данных!
- теперь в кластере секрет с
(0 ключей)
- в следующем reconcile сравнение
оба пустые? Да!
- оператор думает: "секрет актуален", и никогда не восстанавливает данные🤡
Результат: секрет живёт,
Поды падают с:
Фикс очевиден: использовать
Теперь
🎉🎉🎉
Мораль:
-
-
- в client-go структуре
- если используешь
- сравнивай и применяй то, что реально используешь
Потерял на этом баге часа два, пока не дошло. Сука.
Секрет создавался, лейблы были,
Классика - симптомы в одном месте (падающие поды), причина в другом (nil вместо мапы в билдере).
Снова про операторы.
У ресурса
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, особенно с ингресс-контроллерами, которые держат весь трафик.
Тихо и незаметно вышла мажорная версия 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
Там
Прям с графиками, картиночками, наглядно.
В целом годно, как и многие его другие ресурсы/статьи. Никакого рокетсайнса и кишочков - просто база-дополнение к официальной документации.
Рекомендую как для общего развития начинающим специалистам, так и в качестве подготовки к собеседованиям на тему кубернетиса.
https://learnkube.com/kubernetes-api-explained
Daniele Polencic обновил статью про k8s API.Прям с графиками, картиночками, наглядно.
В целом годно, как и многие его другие ресурсы/статьи. Никакого рокетсайнса и кишочков - просто база-дополнение к официальной документации.
Рекомендую как для общего развития начинающим специалистам, так и в качестве подготовки к собеседованиям на тему кубернетиса.
https://learnkube.com/kubernetes-api-explained
🔥12👍2
#gitlab #troubleshooting #одинденьизжизни
На новом проекте дали задачу: небольшие изменения в гошном операторе Kubernetes.
Секреты, cluster-scoped объекты, ничего космического.
Покрутил код, разобрался в архитектуре, поднял локальный kind-кластер, погонял тесты.
Makefile на месте, всё зелёное. Красота.
Коммичу, пушу MR.
Тесты падают. 🙃
Ну ладно, думаю. Я серьёзно менял логику, наверное поломал что-то. Бывает.
Возвращаюсь, перепроверяю. Локально гоняю - всё проходит.
Коммичу, пушу - снова падает.
Ок, уже интересно.
Открываю
Повторяю ровно то же самое локально - make test, всё зелёное.
В GitLab - красное.
Смотрю логи джобы внимательнее.
Один из тестов падает на скачивании image из container registry соседнего репозитория. Не хватает авторизации. Ага.
Проверяю:
- мой GitLab токен - валидный✅
- доступ в соседние репозитории (сайдкар-образы для тестов) - есть✅
- образы в registry - на месте✅
- пайплайн до моих изменений - зелёный, тесты проходили✅
Всё на месте, а не работает.🤡
Перепроверяю ещё тысячу раз.
Локально - 100% то же самое - работает.
В GitLab - нет.
Тут приходит мысль: а может дело не в коде, а в правах?
Пайплайн-то запускается от моего имени.
А у тех, кто запускал до меня, может быть что-то другое.
Иду смотреть настройки репозитория.
Settings - нет доступа.
Access Tokens - нет доступа.
Deploy Tokens - нет доступа.
У меня Developer, без права заглянуть хоть куда-то в настройки.
Пишу в личку коллеге, который точно Admin:
- Слушай, можешь по этой ссылке просто нажать Retry на джобе тестов?
Он жмёт.
Тесты проходят. ✅Сука.
Я жму Retry.
Тесты падают. ❌Сука.
Пишу главной команде DevOps (да, оксюморон - девопс пишет девопсам "спасити-памагити").
Объясняю ситуацию: так и так, тесты падают только когда пайплайн запускает мой юзер, предполагаю, что
Ну, после некоторых усилий удалось донести мысль. Ребята берут таймаут.
Через несколько дней/часов:
- Ретрай.
Ретраю.
Зелёное. 🎉
- А что было?
- Мы тоже долго дебажили, скормили все токены в 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". Просто
- вы не увидите эту галочку сами - она в Admin Area, доступ только у администраторов GitLab.
- при онбординге на новый проект спросите: "А мой аккаунт точно не external?" Серьёзно. Одна галочка - полдня жизни.
- локальные тесты != CI тесты. Даже если команды идентичны. Контекст авторизации разный.
- и как обычно: во всём виноваты девопсы. Даже когда девопс - это ты сам.😢
Тихий убийца времени.
На новом проекте дали задачу: небольшие изменения в гошном операторе 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
👍23🤡2💔1
#longread #troubleshooting #operators #kubectl #kubernetes #одинденьизжизни
Внезапный и незапланированный лонгрид.
Разбирал по работе задачу, узнал новое про фичу
Теперь делюсь историей и знаниями с вами.
Приятного прочтения.
https://teletype.in/@kruchkov_alexandr/wWa-INvQvJH
Внезапный и незапланированный лонгрид.
Разбирал по работе задачу, узнал новое про фичу
kubectl cache discovery.Теперь делюсь историей и знаниями с вами.
Приятного прочтения.
https://teletype.in/@kruchkov_alexandr/wWa-INvQvJH
Teletype
Когда kubectl врёт
Сегодня мне для нашего кубернетис оператора надо было поменять Namespaced scope на Cluster scope у одного CRD.
15👍27🔥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.
Было ещё много прочитанного, изученного, но в общем и целом это, скорее, белый шум, чем что-то, чем хочется поделится.
Просто делюсь последними находками.
- 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
- игнорирует рут сандбокс (может легко выходить из песочницы)
- при моих запретах средствами операционной системы начинает писать питон скрипты, чтобы обойти запреты доступа к файловой системе и просить аппрув на выполнение🤨
Каждый раз без зазрения совести использует опасные команды без аппрува, если ему захотелось
-
-
-
-
и многое другое. Печаль.
Модели:
- Claude-4.5-sonnet
- 4.6 Opus
Четырёх сильных ошибок мне хватило.
Благо git всё помнит и можно ревертнуть руками, а косяк был без персистент даты.
Мне нравится этот новый поезд LLM и агентов.
Однако слишком быстро стремящийся поезд.
Предпочитаю быть за рулем, или хотя бы быть в вагоне с кабиной управления, а не ехать в заднем вагоне с закрытыми глазами.
- - -
На скрине часть моей драмы-переписки.
В итоге у меня сейчас 0 MCP серверов, пустые allow list, пару костыль-ограничений средствами операционки и Ask every time.
Печаль.
Устал разгребать дерьмо от агента.
Прям утомился и устал.
Реалии февраля 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
😁19❤5👍5🥴2😢1
Ямлоделие
О, дивный мир, на работе я узнал об ограничении GitLab на парсинг YAML файла с пайплайном.
Проскочила ошибка, уронила всё.
Документация привела к
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 мегабайтных файлов и какие причины такого ограничения.
Самое интересное -
То есть 2 MB YAML может жрать больше 2 MB RAM в зависимости от структуры.
Зачем вообще лимит?
Погуглил, почитал документацию, посмотрел код.
- DoS защита
один огромный YAML может положить весь Sidekiq (классная архитектура😀 )
- OOM превенция
память пре-аллоцируется, условно 100 пайплайнов по 100 MB =изда RIP серверу
- производительность
парсинг гигантских YAML тормозит всех (чужие проекты, пайплайны)
- code smell детектор (это я/мы)
если у вас 2+ MB YAML, что-то пошло не так в архитектуре
Ну штош, значит пора оптимизировать содержимое пайплайнов.😭
О, дивный мир, на работе я узнал об ограничении 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
Затем спросил нейронку сколько памяти/ресурсов экономится при парсинге 2 или 4 мегабайтных файлов и какие причины такого ограничения.
Самое интересное -
max_yaml_size_bytes это не физический размер файла, а память, выделяемая на объекты при парсинге. То есть 2 MB YAML может жрать больше 2 MB RAM в зависимости от структуры.
Зачем вообще лимит?
Погуглил, почитал документацию, посмотрел код.
- DoS защита
один огромный YAML может положить весь Sidekiq (классная архитектура
- OOM превенция
память пре-аллоцируется, условно 100 пайплайнов по 100 MB =
- производительность
парсинг гигантских YAML тормозит всех (чужие проекты, пайплайны)
- code smell детектор (это я/мы)
если у вас 2+ MB YAML, что-то пошло не так в архитектуре
Ну штош, значит пора оптимизировать содержимое пайплайнов.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5👌2❤1
#aws #rds #troubleshooting
Приходят как-то коллеги, говорят - нет прав включить Enhanced Logging на RDS Proxy.
Для дебага очень надо на пару часов.
Ок, смотрю политику, а там
Говорю - всё есть, всё должно работать.
Хоть дропайте всё к херам (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, фильтрую по
Нахожу событие. И там:
Вот оно.
AWS при вызове
Для этого нужно право передать (
В политике группы
Про
Добавляем в политику:
Применяем, перелогиниваемся.
Галочка встаёт и остаётся стоять.
Ивенты пишутся.
Итоги:
- "Successfully modified" в AWS консоли не гарантирует, что изменение применилось🥲
-
- Консоль эту ошибку не показывает совсем. Молча глотает. Никакого красного баннера. Никаких ошибок. Никаких ивентов.😎
- CloudTrail - первое место, куда надо смотреть, когда "всё работает, но не работает"
-
- нехер снова кривить свою морду, коллеги всё же солнышки-зайчики и правы, даже если пояснение проблемы тупорылое
Приходят как-то коллеги, говорят - нет прав включить 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
🤯16❤2😭1