Make. Build. Break. Reflect. – Telegram
Make. Build. Break. Reflect.
1.04K subscribers
124 photos
1 video
1 file
134 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
#пятница #всратость #javanoscript

Заходят как-то в бар арифметика с плавающей точкой и неявное приведение типов...



Баланс отпуска (PTO) на JS:
- когда "1.41" - "1" почему-то превращается в "15.92".
Обожаю этот язык, не меняйте ничего!1!!
🤣11🙈4😁2👍1
#victorialogs #victoriametrics #grafana #troubleshooting

Когда простая задача превращается в тупик:
миграция дашбордов с 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 на подобное потрачу.
Проблема в том, что я переоценил свои силы и знания (которых и не было).
В общем надо учиться и читать документацию, прежде, чем приступать к задаче.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥1
#kubernetes #мысли

Сижу, разбираюсь с 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 репозитории

Рандомный пример промпта:
Нарисуй 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👍286
#longread #grafana #kubernetes #troubleshooting #одинденьизжизни

А давненько не было лонгридов.

Три дашборда на границе 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:
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 конфиге).

Проверяю:
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
🔥81
#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? А легко!
Копи-паст скрипта в своем кластере:
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
👍23🤡2💔1
#longread #troubleshooting #operators #kubectl #kubernetes #одинденьизжизни

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

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

https://teletype.in/@kruchkov_alexandr/wWa-INvQvJH
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.


Было ещё много прочитанного, изученного, но в общем и целом это, скорее, белый шум, чем что-то, чем хочется поделится.
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
😁195👍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
👍6👌21
#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
🤯162😭1
#ai #kubernetes

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

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

Думаю "блин, ну я вроде сам это относительно недавно делал через node proxy, даже заметку писал, дай повторю".
https://news.1rj.ru/str/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 - когда нужно быстро выкачать актуальное состояние (скорее всего не пригодится никогда).

Забавно обучать/получать ответы от нейронки по своей же заметке. 😅
👍10👎2