DevITWay | Павел Волков
173 subscribers
81 photos
6 videos
136 links
Системное мышление → DevOps-практика → карьерный рост.
Production-кейсы, разборы инцидентов, новые подходы.
Бесплатные мини-курсы: Git | Linux | Docker
Веду лично. Основатель DevIT Academy.
devitacademy.com | devopsway.ru | devitacademy.com/kmb
Download Telegram
⚡️ DevOps Digest #33 | 21.09.2026

🔥 Главное:

1. Kubernetes 1.37 разрешает задавать noexec, nosuid, nodev прямо для bind-mount томов и режимы прав для emptyDir. Теперь запрет на запуск бинарников из записываемых томов и на удаление чужих файлов делается штатно, без костылей с initContainer – включайте на чувствительных подах уже на этой неделе.
🔗 https://kubernetes.io/blog/2026/09/16/kubernetes-v1-37-hardening-container-storage/

2. Ваш конвейер CI/CD – самая незащищённая система в инфраструктуре: он выполняет произвольный код сторонних действий с привилегированными кредами и доступом внутрь контура. В статье – чек-лист защищённого конвейера: изоляция раннеров, минимальные права токенов, запрет секретов в логах, пиннинг зависимостей.
🔗 https://devops.com/why-your-ci-cd-pipeline-is-your-most-unprotected-attack-surface/

4. Классическая боль: уязвимость починили в базовом образе, а через неделю прод всё ещё на дырявом – никто не пересобрал. Buildpacks дают единый путь сборки, где платформенная команда централизованно управляет базовыми образами, SBOM и пересборкой вместо зоопарка индивидуальных Dockerfile.
🔗 https://thenewstack.io/buildpacks-container-security-scale/

5. Cloudflare сэкономил 100 ТБ памяти, оптимизировав кэш DNS в 1.1.1.1 – на их масштабе каждый срезанный с записи байт даёт 250 ГБ по флоту. Наглядный разбор о том, как охота за размером структуры кэша окупается, когда записей миллиарды.
🔗 https://sreweekly.com/sre-weekly-issue-535/

🔍 eBPF стал стандартной инфраструктурой, а не экзотикой ядра
По данным CNCF за 2026 год, 67% команд уже гоняют в проде хотя бы один инструмент на eBPF, а Cilium де-факто стал стандартным CNI. Практический смысл: eBPF даёт наблюдаемость и сетевые политики почти без накладных расходов, потому что программы проходят проверку верификатором ядра и работают в его пространстве без модулей. Если вы всё ещё на CNI поверх iptables и ставите отдельные агенты сбора трафика – пора смотреть на Cilium/Hubble как на замену сразу обоим.

🛠 Команда дня:
findmnt -rno TARGET,OPTIONS | grep -vE 'noexec' | grep -E '/tmp|/dev/shm|/var/tmp'

Находит записываемые каталоги без флага noexec – потенциальные точки запуска чужих бинарников; закройте их noexec,nosuid.

#дайджест #kubernetes #security

💬 Переслал коллегам? Пусть подпишутся: @DevITWay
🔥2❤1
Записки сумасшедшего девопса. Двадцать тысяч чужих паролей

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

Bitrix, MySQL, вся авторизация внутри. Уводим на новый стек: PostgreSQL, логин на GoTrue – движок авторизации Supabase. Двадцать с лишним тысяч живых аккаунтов. Условие одно: люди входят как раньше.
Полез смотреть, кто эти двадцать тысяч. Картина была пёстрая. Половина заходит вообще без пароля, кнопкой: тысяч семь через Яндекс, тысяч пять через Google. Остальные – телефон плюс пароль.

Пароль с телефоном переедет тихо. Казалось бы. С кнопками сложнее. Яндекс поднял через Zitadel – кнопка живёт, человек входит как раньше. Google убираем совсем. Кто заходил только им – на почту с паролем, сброс по ссылке. А у кого и почты нет, только соцвход, – звонить некуда. Несколько сотен таких. Выцепили заранее, пока Bitrix живой, вытянули почту руками. Иначе на переключении они бы просто исчезли.

Теперь пароли. Скопировал хэши из MySQL в Postgres. Думал, вечер работы.

Bitrix держит их в $6$. Это sha512crypt, тот самый crypt из /etc/shadow. GoTrue проверяет bcrypt, argon2, firebase scrypt. Всё. Что не из этих трёх – молча уходит в bcrypt-ветку и не сходится. Родные хэши не подходят вообще. Ни один. Все, кто входит паролем, – мимо.

pgcrypto? Не знает $6$, только bcrypt да остальную труху. И тупик тут по другой причине: GoTrue сверяет пароль не в базе, а у себя, в Go. В исходнике форматы прошиты константами – sha512crypt там нет. Плагин к Postgres ни при чём.

Ставлю перед GoTrue прослойку: знает старый формат, сама сверяет пароль со старым хэшем, на первом успешном входе перехеширует в bcrypt. Дальше ведёт сам GoTrue. Старый формат вытекает по мере входов, до нуля. Кому пароль не вернуть – ссылка на почту.

Написал, прогнал тесты, зелено. Утром вешаю заглушку с обратным отсчётом – замораживаю записи. Чтобы за время переключения базы не разъехались и не пришлось потом разгребать дубли. Режу ленточку: меняю домены местами, юзеры идут через Zitadel и GoTrue.

Убираю заглушку, смотрю в логи. Пошли входы. Работает? Ну работает же?!

А потом – то тут, то там – закапали входы с ошибками. Из тех, что указывают на самое неприятное: часть людей не пускает в их собственные аккаунты. Прослойка на моке считала $6$ верно, на живых – мимо: готовая библиотека sha512crypt врала на реальных хэшах, верно считал только системный crypt. Кого-то движок вообще роняет в 500 ещё до пароля – миграция оставила пустые технические поля, а он читает их как непустые строки. Сверху публичные страницы местами отдают permission denied: рестор снёс табличные гранты.

Пытался чинить на ходу, прямо на проде. Ну, такое. Каждая правка вслепую, под капающими юзерами.

План Б: откат на старые настройки. Флип DNS назад, Bitrix живой, ловит всех. Выдохнуть. Заглушка тут и оправдалась – запись была заморожена, базы не разъехались, дедупликации ноль. Никто ничего не потерял, кроме моего вечера.

Сутки пересматриваю алгоритм. Прослойку переписываю на системный crypt. Добиваю пустые поля. Восстанавливаю гранты по эталону стенда. Второй заход – взлетело!

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

Пишите в комментах, у кого такое было приключение?

#devops #человек_оркестр
🔥6❤1
⚡️ DevOps Digest #34 | 28.09.2026

🔥 Главное:


1. Сертификаты истекают и при установленном cert-manager. Он продлевает сертификат на 2/3 срока, поэтому 90-дневный сертификат Let's Encrypt попадает в окно продления за 30 дней до истечения, и весь этот месяц продление может падать молча. Автор разбирает пять классов сбоев и собирает проверку из метрик cert-manager, разбора всех Secret типа kubernetes.io/tls (включая те, которыми cert-manager не управляет) и сверки с сертификатом, который отдаёт Ingress. ИИ-обвязка в статье необязательна: детерминированную часть можно забрать отдельно.
🔗 https://dev.to/devtocash/build-a-tls-certificate-expiry-agent-catch-cert-manager-renewal-failures-unmanaged-secrets-and-1ifd

2. В Kubernetes 1.37 функция PersistentVolumeClaimUnusedSinceTime перешла в бету и включена по умолчанию. Контроллер защиты PVC теперь ставит на каждый PVC условие Unused, которое показывает, монтирует ли его хоть один запущенный под. Сводить поды и PVC самописными скриптами больше не нужно: после обновления достаточно выбрать PVC с Unused=True и по времени перехода условия решить, какие забытые тома чистить.
🔗 https://kubernetes.io/blog/2026/09/21/kubernetes-v1-37-pvc-last-used-time/

3. Coroot – открытая платформа наблюдаемости с непрерывным профилированием без правки кода. eBPF снимает CPU-профили всех процессов на ноде, для Go и Java добавляются память и блокировки, а флеймграф показывает нагрузку вплоть до строки кода. В статье – установка Community Edition через coroot-operator и четыре намеренно сломанных приложения на Node.js, Python, Go и Java: можно повторить у себя и посмотреть, что Coroot найдёт.
🔗 https://habr.com/ru/articles/1087316/

4. Вышел M-Commander 6.1.0 – форк Midnight Commander с панельными плагинами. В панелях открываются контейнеры Docker, ресурсы Kubernetes, Git-репозитории, SFTP, S3 и SQLite, а subshell заменён встроенным PTY-терминалом. Пакеты есть для Debian 13, Ubuntu 24.04/26.04 и Fedora 44, так что его можно поставить на бастион и ходить по кластеру привычными F-клавишами.
🔗 https://www.opennet.ru/opennews/art.shtml?num=66348

🔍 «Установлено» не значит «работает»
Метрики cert-manager показывают, что происходит внутри кластера: заявка создана, Secret обновлён. Клиенту важно другое – какой сертификат он получает на порту 443. Между этими точками стоят Ingress-контроллер, балансировщик и CDN, и на любом из них может остаться старый сертификат. Минимум на эту неделю – проба blackbox_exporter по каждому хосту Ingress с алертом на probe_ssl_earliest_cert_expiry. Как сверить руками – в команде дня.

🛠 Команда дня:
H=app.example.com
echo | openssl s_client -connect $H:443 -servername $H 2>/dev/null | openssl x509 -noout -serial -enddate
kubectl get secret app-tls -n prod -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -serial -enddate

Сравнивает сертификат на порту 443 с тем, что лежит в Secret. Если серийные номера совпадают, Ingress отдаёт содержимое Secret, и остаётся посмотреть на дату истечения. Если различаются, cert-manager продлил сертификат, но до клиентов он не дошёл. Обычно причин три:
- Ingress-контроллер не перечитал Secret;
- в Ingress указано другое имя Secret или неверный namespace;
- TLS завершается раньше, на балансировщике или CDN, и там свой сертификат.

#дайджест #kubernetes #monitoring
🔥4❤1
Записки сумасшедшего девопса. Пара новых серверов

В Анатомии супостата проскочило наивное: обойдусь парой новых серверов к уже развёрнутому. Вот во что эта пара выросла.

На входе всё собиралось на арендованном GitLab на reg ru. Почти все пайплайны на основной ветке красные, а изменение вливалось в среднем через шесть минут после открытия – поверх красного и без ревью. Фронт уезжал на прод rsync'ом рабочей папки раннера, зависимости докачивались прямо на проде в момент деплоя. В вики предыдущая команда написала первой строкой "работаем по Trunk Based Development", то есть мелкими изменениями в одну основную ветку. Ниже был описан GitFlow: долгие ветки и релизы пачками через релиз-менеджера, а в git – сотня веток и основная, мёртвая больше двух месяцев.

Свой контур начинался по принципу "не до жиру, быть бы живу". Агенты писали код, и собирать его нужно было уже вчера. Поэтому самое простое: свой GitLab в облаке и раннер рядом. Даже это завелось не сразу: раннер ходил к своему GitLab через публичный адрес, и пайплайны висели, пока не докрутил внутреннюю DNS-зону.

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

Дальше шаг за шагом. Лёгкие проверки отдельно от тяжёлых сборок. Андроид на своей машине, потому что он тупо кладёт хост вместе со всем остальным. Общий кэш в S3 вместо холодного на каждом раннере. Стадии, которые зря ждали друг друга, развязаны, и путь до деплоя сократился с двадцати пяти минут до двенадцати. Сборка теперь идёт до слияния, а в main изменения попадают через поезд слияний (merge train): встают в очередь, и каждое проверяется вместе с теми, что едут перед ним. Так ловятся два изменения, зелёные по отдельности и красные вместе.

С экономией так же. Простое решение – гасить стенды кроном на ночь. Первая же ночь показала, что крон в убунте не понимает, в каком часовом поясе ему велели жить, и живёт по поясу сервера. А сервер жил по Нью-Йорку. Через неделю парковщик погасил заодно и сам GitLab, и мониторинг. Сейчас машины под сборку просыпаются, когда в очереди есть задачи, и засыпают после пятнадцати минут тишины. То, на чём стоит всё остальное, не спит никогда: однажды припарковали зеркало NORA, и краснело всё подряд, хотя код был ни при чём.

Отдельная история – доверие к индикаторам. Строгий линтер числился зелёным и при этом ни разу не отработал: падал на кривых флагах раньше, чем доходил до кода, а allow_failure в настройках разрешал ему падать, не краснея. GitLab показывал раннер online, хотя виртуалка под ним была выключена. Проверка, которая не умеет покраснеть, ничего не проверяет, и смотреть приходится в источник. Лампочке верить нельзя.

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

И про TBD. Изменение и сейчас живёт меньше часа от открытия до слияния. Только вливает его поезд и только зелёным. За три месяца так прошло под тысячу восемьсот изменений в одну живую основную ветку. Та самая первая строка из вики, только теперь её видно в git.

Расти есть куда: чтобы GitLab сам не давал влить мимо поезда, а зеркало (NORA), на котором держится весь CI, было не в единственном экземпляре.

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

Пара новых серверов. Одних под сборку теперь пять, и большую часть суток они спят.

Почему база, функции и фронт катятся по отдельности – в следующий раз.

#devops #человек_оркестр
👍4🔥2❤1
⚡️ DevOps Digest #35 | 05.10.2026

🔥 Главное за прошедшую неделю:

1. OPA Gatekeeper в продакшене: 18 месяцев, 47 политик, ни одного обхода. Больше всего нарушений ловят четыре правила, и первые два – запрет privileged: true и образы только из разрешённого реестра. Автор приводит готовые ConstraintTemplate на Rego. Режим "запрещено всё, что не разрешено" с исключениями только для kube-system и gatekeeper-system – хорошая отправная точка для своего кластера.
🔗 https://dev.to/scsoi/opa-gatekeeper-in-production-50-policies-that-caught-real-misconfigurations-4d0p

2. Crossplane CLI v2.3 добавил понятие Project – единую структуру каталогов для платформенного API. Команды CLI создают заготовку проекта, генерируют код, тестируют его и запускают локально. Платформа на Crossplane теперь собирается и тестируется как обычное ПО, а не как набор YAML-файлов. Если у вас самописная обвязка для сборки Configuration-пакетов, пора сравнить её со штатной.
🔗 https://blog.crossplane.io/introducing-the-new-crossplane-cli-developer-experience/

3. Git 2.56: git branch научился удалять уже слитые ветки, git history получил команду drop, а через git refs теперь можно изменять ссылки. Главное в выпуске – определился состав Git 3.0: SHA-256 станет форматом хешей по умолчанию, reftable – хранилищем ссылок, а Rust – обязательной зависимостью.
🔗 https://about.gitlab.com/blog/whats-new-in-git-2-56-0/

4. SRE Weekly #537 собрал два сильных разбора. Cloudflare сэкономил ещё 100 ТБ оперативной памяти за счёт математики и Rust и показал, как поменять схему хеширования в распределённом кеше, не обнулив его. Netflix рассказал об автоматическом подборе ресурсов для контейнеров с ограничителями и откатом при ошибке. Оба приёма переносятся на ваш кластер: менять схему кеша постепенно, а лимиты подбирать автоматически, но с запасом и защитой.
🔗 https://sreweekly.com/sre-weekly-issue-537/

5. У Anthropic за полгода число CI-задач выросло в 25 раз, у Linear тестовый набор почти вчетверо – агенты пишут код быстрее, чем конвейер успевает его проверить. Anthropic начал запускать только те тесты, которые может затронуть изменение. Автор считает, что ускорять сам конвейер – решение не на том уровне. Если число MR от агентов у вас растёт, начните с выборочного запуска тестов, прежде чем докупать раннеры.
🔗 https://thenewstack.io/ci-bottleneck-agent-verification/

🔍 Git 3.0: готовиться пора уже сейчас
SHA-256 и reftable уже есть в текущих версиях Git, но поддержка в библиотеках, форжах и CI-скриптах пока неравномерная. Проверьте свой контур: на какой версии ваш GitLab, какие библиотеки (libgit2, go-git, JGit) используют пайплайны, и где в скриптах регулярные выражения рассчитаны ровно на 40-символьный хеш. Заведите тестовый репозиторий с --object-format=sha256, прогоните через него свой CI и узнайте, что сломается, до того как это случится в продакшене.

🛠 Команда дня:
git add --resolved

Индексирует только файлы, где был конфликт слияния, и отказывается это делать, если в них остались маркеры <<<<<<< (нужен Git 2.56+).

#дайджест #kubernetes #cicd
🔥4❤1
Записки сумасшедшего девопса. База, функции, фронт

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

Почему база, серверные функции (небольшие куски бэкенда рядом с базой) и фронт теперь катятся по отдельности. Цель простая: выкатывать чаще, без простоя и с меньшим числом ошибок.

Сначала в одной монорепе жило всё: приложение, миграции, полсотни функций, а заодно конфиг nginx, docker-compose, скрипты деплоя, перенос данных со старого сайта и публикация Android-сборки. Пока изменений мало, это удобно. Когда их около сотни в неделю, выясняется, что у всего этого разный ритм и разная цена ошибки. Фронт откатывается за минуту, а схему базы так уже не вернёшь.

Те самые три копии – конфиг nginx: шаблон, собранный файл и запасной вариант прямо внутри скрипта деплоя. Копии разъехались, и в августе это дало ошибку 502 на части страниц и пропавшие на проде заголовки. В конце августа инфраструктура уехала в свой репозиторий и стала кодом, в сентябре следом разъехались база и функции, у каждого свой путь до прода.

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

Отсюда главное наблюдение про разработку с агентами: если нужны и скорость, и качество, гигиеной приходится заниматься каждый день. Агенты пишут быстро и много, и любую неоднозначность они размножат. Поэтому нужны ограждения (guardrails): проверки, которые краснеют, когда что-то пошло не туда, и заметки для агентов, сверенные с живым кодом. Чистота и простота работают так же: если у конфига один источник, а у выкатки один путь, агенту не из чего выбрать неправильное.

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

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

Причина в RLS (Row-Level Security), правилах прямо в базе: кто какие строки может читать и писать. Миграция включила RLS, а правил не завела, и доступ закрылся для всех. Автор рассчитывал на служебную роль Supabase, которая обычно ходит мимо правил. Но здесь Supabase стоит на управляемом Postgres облака, роли там заводит облако, и такого права у служебной роли нет ни на одном окружении.

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

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

А у вас база и фронт едут одним релизом или порознь?

#devops #человек_оркестр
🔥4❤2
Записки сумасшедшего девопса. Три зоны и один бастион, часть 1

8 октября в 01:31 в Яндекс Облаке отключилась зона доступности b. Днём Яндекс уточнил: пожар в дата-центре в Сасово после атаки беспилотников, работа остановлена полностью, сроков нет. В зоне b у нас стояло почти всё: сайт, бэкенд, админка, стенд. И бастион, единственный вход во внутреннюю сеть. В консоли облака все эти машины при этом числились RUNNING. В голове заиграла композиция "Говорил я вам" группы Громыка.

База пережила. Ещё в июне в оценке рисков записал, что всё в зоне b (от предыдущей команды) и это единая точка отказа. Первой дешёвой мерой в той же оценке шла база на нескольких зонах, перенос приложения во вторую зону стоял в плане на полгода. Данные базы кодом не поднять, этот риск надо гасить сразу. Остальное кодом разворачивается заново, там риск можно принять. При переезде в августе описал кластер Postgres в Terraform на три хоста в трёх зонах, хотя это втрое дороже одного, обосновал бизнесу, бюджет расширили. Мастер в тот момент был в зоне a, реплика в зоне d тоже жила, поэтому выпал только третий хост, и база всё это время принимала запись.

Рядом стояли две управляемые базы на одном хосте каждая, сервис входа и аналитика, обе в зоне b. Восстановить их из бэкапа в живую зону не вышло: облако отвечало внутренней ошибкой на любую запись в управляемый Postgres. Даже на запрос с заведомо несуществующими данными, так что дело было не в нас. Холодный резерв тоже не помог бы. Включить выключенную машину в живой зоне облако отказалось, создать новую маленькую тоже: в аварию ресурсы ограничили для всех, и работало только то, что было запущено заранее.

Дальше бастион. У базы нет внешнего адреса, сетевые правила пускают к ней только машины из зоны b, а SSH на всё остальное открыт только с бастиона. Живая база в соседней зоне, а дороги к ней нет.

План аварийного запуска собрал утром, пока ждал новостей. Балансировщик не помогал: живых машин прода не осталось ни в одной зоне. Поднять прод на машине GitLab в зоне d было можно, но тогда персональные данные и ключи прода легли бы рядом со всем кодом, а GitLab вдобавок получил бы нагрузку прода. Решил так: если к 15:00 Яндекс не назовёт срок, поднимаю аварийный прод в другом месте. В 15:00 обновления не было, в 15:19 пришло про пожар.

Поднимал на своём сервере у другого хостера в России, иначе закон о персональных данных не даёт. База остаётся одна, в зоне a, файлы и секреты в живых сервисах облака. Цена – два-три часа работы и два временных правила доступа. Решающий шаг – переключение DNS, всё остальное можно поднять и проверить заранее. Следующее сообщение Яндекс обещал в 19:00: назови он срок в пару часов, просто не стал бы переключать. В итоге DNS переключил сразу, с заглушкой "технические работы": старые адреса всё равно мертвы, у API TTL был час, пусть истекает, пока поднимаю остальное.

Как через GitLab добрался до базы без бастиона и что поднялось из кода – во второй части.

А у вас база в скольких зонах?

#devops #человек_оркестр
🔥4❤1
⚡️ DevOps Digest #36 | 12.10.2026

🔥 Главное за неделю:

1. С Kubernetes 1.35 параметр failCgroupV1 по умолчанию равен true, и kubelet не стартует на узле с cgroup v1. В кластерах на kubeadm это проверяется ещё раньше. До апгрейда переведите все Linux-узлы на cgroup v2: нужны ядро 5.8+ и unified-иерархия (systemd.unified_cgroup_hierarchy=1). Есть и временный вариант: failCgroupV1: false в конфиге kubelet. Но это только отсрочка, удаление поддержки v1 уже идёт по KEP-5573.
🔗 https://kubernetes.io/blog/2026/10/06/kubernetes-cgroups-v2-shift/

2. Вышел flux9s 1.0 – терминальный интерфейс для Flux в духе K9s. Он показывает живое состояние всех ресурсов Flux, включая Kustomization, HelmRelease, OCIRepository и экосистему Flux Operator. Работают vim-навигация, фильтр через / и частые операции одной клавишей. Кто до сих пор смотрит кластер через flux get all | grep, может держать flux9s открытым на втором мониторе и видеть изменения сразу.
🔗 https://fluxcd.io/blog/2026/10/flux9s-ga/

3. Приём GitOps Bridge разводит Terraform и ArgoCD: Terraform пишет факты о кластере в метки и аннотации ConfigMap/Secret, ArgoCD читает их оттуда, и ни одна сторона не импортирует чужое состояние. В ArgoCD это удобно ложится на секрет кластера и генератор кластеров в ApplicationSet, который подставляет аннотации в шаблон. Если у вас Terraform катит чарты через helm-провайдер, распутывание стоит начинать с этой связки.
🔗 https://dev.to/avneet_bansal_a65b3f31fc4/the-gitops-bridge-keep-terraform-and-argocd-out-of-each-others-way-3721

4. Исследователи GitLab нашли в PyPI пять вредоносных пакетов. Четыре из них – тайпсквоттинг Flask, Requests и NumPy: они срабатывают прямо при установке и крадут учётные данные CI/CD. GitLab Dependency Firewall пока в раннем доступе, но часть защиты доступна уже сейчас: тяните пакеты только через внутренний прокси-реестр, ставьте их с pip install --require-hashes и добавьте --only-binary :all:, чтобы при установке не выполнялся setup.py из исходного архива.
🔗 https://about.gitlab.com/blog/transcend-dependency-firewall/

5. Orion soft рассказал, как отечественная Nova Container Platform уходила от монолитного Apps Operator. Он держал семь CR для инфраструктурных модулей, включая Longhorn, и привязывал их версии к релизу платформы: чтобы обновить один модуль, приходилось обновлять весь кластер и гонять полный регресс. Вывод для своей платформы: отдельные манифесты в Git и доставка через FluxCD от монолита не спасают. Независимым должен быть цикл версий каждого модуля.
🔗 https://habr.com/ru/companies/orion_soft/articles/1091104/

6. В свежем SRE Weekly – разбор отчёта об аварии Telstra 8 июля от Лорина Хохштейна и тезис Пола Лапосты. По его мнению, изменение, которое держится на знаниях одного человека, на единственной точке отказа или только на модели ИИ, рискованнее такого же изменения, которое команда может объяснить и сопровождать сама. Добавьте в чек-лист ревью изменений вопрос: кто, кроме автора или модели, сможет ночью откатить это изменение?
🔗 https://sreweekly.com/sre-weekly-issue-538/

🔍 cgroup v2 открывает swap на узлах
Переезд на cgroup v2 обычно считают уборкой перед апгрейдом, но вместе с ним появляются две полезные возможности. Swap на узлах (GA с 1.34) работает только на cgroup v2. В замерах команды Kubernetes узел со swap на NVMe держал до трёх раз больше подов на сборках ядра в CI, изолированных браузерах и Python-средах, часто почти без роста задержек. Вторая возможность – метрики давления PSI по контейнерам, которые есть только в cgroup v2. Если раннеры GitLab в кластере простаивают с раздутой резидентной памятью, совместите миграцию узлов с пилотом LimitedSwap на отдельном пуле: работа одна, выгоды две.

🛠 Команда дня:
stat -fc %T /sys/fs/cgroup/

Выводит cgroup2fs на узле с cgroup v2 и tmpfs на v1 – такой узел kubelet 1.35+ не поднимет. По всему парку удобно прогнать через ansible -m command.

#дайджест #kubernetes #cicd
🔥3❤1
Записки сумасшедшего девопса. Три зоны и один бастион, часть 2

В первой части сгорела зона b вместе с сайтом, серверами и бастионом. Тем временем база всё ещё жила в зонах a и d, а GitLab в зоне d, но дороги к базе уже не было.

Дорога нашлась, внезапно, через GitLab. Его со сборкой ещё в июне поставил в зону d, под аварию, ясен пень, не планировал, просто музыкой навеяло, что пускай будет в зоне d. Ключ от машины был, мешало только отсутствие сетевых правил, API клауда ещё работал: на запрос с несуществующими данными честно отвечал "не найдено", нам это и надо. Добавил два временных (последних) правила с пометкой TEMP, пользователя без шелла (кого я обманываю), который умеет только пробрасывать порт к базе, и SSH-туннель службой с автоперезапуском: убил процесс – через пять секунд база снова отвечает.

И дальше окупилась инфра как код. Разворачиваю бэкенд из той же роли Ansible, что на проде, образ сайта ровно тот, что крутился в проде, по хешу из реестра NORA. Около пяти вечера сайт отвечал со старенького сервера с процессором 2012 года. Но без поломок по ходу пьесы не обошлось, конечно же сломалось там, где кода не было: боевой конфиг API-шлюза жил только на сгоревшей машине, и часть запросов получала 401, а сервис, который пускает по паролю пользователей старого сайта, нашёлся только по реестру дрейфа. В конечном итоге за первую ночь после ЧП – десятки тысяч запросов и всего три ошибки, все за десять минут, когда легло объектное хранилище облака целиком облака.

Потом продолжились приключения с самим облаком. Базы, жившие на одном хосте в зоне b, восстановить из бэкапа не дали: сервис управляемых баз отвечал внутренней ошибкой на что угодно, даже на заведомую ерунду. Через день он ожил, но квоты на выходные Яндекс выдал "с небольшим запасом сверх занятого" – хватало ровно на одну маленькую базу за раз. Ну и машины сборки, стоявшие выключенными ради экономии в зоне d, напомню, всё ещё рабочей, включить облако уже отказалось: новые ресурсы в аварию не выдавали никому. Облачные функции в тот же день легли уже отдельным инцидентом.

9 октября пришла новость про повреждённый ДЦ Яндекса под Калугой. 11 октября в четыре утра удар по ДЦ во Владимире, и легла теперь зона a, где жил мастер базы. Кластер, отряхнувшись, сам переключился на хост в зоне d, туннель смотрел на адрес мастера, а не на конкретную машину, так что аварийный прод переключения не заметил, утром люди входили как обычно. Три зоны отработали второй раз за неделю, только из трёх осталась одна. Яндекс в статусе написал прямым текстом: оставшуюся конфигурацию считаем неустойчивой, восстанавливайтесь на внешних площадках. Ну, штош.

Три зоны спасают от пожара в одном здании, от выбывания целого региона, увы и ах, нет. Решение – переезжать к другому провайдеру целиком.

Пока переезд готовится, данные перестали жить только в облаке. Копия базы уходит на своё железо каждые 15 минут, файлы каждый час, репозитории, задачи и секреты тоже, вторая копия лежит в удаленном офисе. Первое же учение подъёма из копии нашло дыру: дамп снимался без прав ролей, и восстановленная база отвечала 401 и 403. Еженедельная проверка восстановления этого не видела, она считала строки в таблицах. После правки весь стек в учении поднимается из копии за пять минут. Плюс реплика базы на резервном сервере с отставанием в секунды.

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

А ваша проверка бэкапов, что проверяет?

#devops #человек_оркестр
🔥3❤2
⚡️ DevOps Digest #37 | 19.10.2026

🔥 Главное:

1. Кластер Kubernetes упирается в масштаб по трём разным осям: число узлов, частота изменений объектов (каждый переход состояния – запись в etcd) и просто объём объектов от сотен операторов. Стандартный совет "добавьте apiserver" часто делает хуже: каждый экземпляр держит полную копию кэша наблюдения (watch cache), и вы платите памятью за пропускную способность запросов. Перед масштабированием определите, по какой оси у вас кончается запас.
🔗 https://thenewstack.io/scaling-kubernetes-control-planes/

2. Если в кластере стоит Kueue, Job может висеть приостановленным без единого пода, и это нормально: сначала спросите не "почему под в Pending", а "допущен ли Workload". Начинайте разбор с условия (condition) объекта Workload, дальше идите по цепочке LocalQueue → ClusterQueue → ResourceFlavor → Cohort. Плагин Kueue для Headlamp (0.1.0-alpha, только чтение) показывает эту цепочку ссылками, без раскопок в YAML.
🔗 https://kubernetes.io/blog/2026/10/14/tracing-kueue-admission-with-headlamp/

3. В GitLab 19.5 Secrets Manager на базе OpenBao вышел в GA, в том числе для Self-Managed (только Premium и Ultimate). Секреты привязываются к конкретным джобам, а не ко всему пайплайну, и при каждом чтении из CI/CD в аудит пишутся ID пайплайна и джобы. Повод найти ключи подписи, которые лежат открытым текстом в CI/CD-переменных без ограничения области, и перенести их.
🔗 https://about.gitlab.com/blog/secrets-manager-ga/

4. GitHub описал своё приложение Evergreen для гигиены организации. Внешних участников пересматривают раз в квартал, и если владелец не ответил, доступ отзывается: безопасный исход выбран по умолчанию. Неактивные репозитории сначала получают предупреждение, потом архивируются, но никогда не удаляются. Тот же шаблон легко собрать в GitLab по расписанию через API.
🔗 https://github.blog/engineering/platform-security/staying-evergreen-how-we-automated-secure-access-and-repo-hygiene-in-a-shared-org/

5. К2Тех разбирает Uncloud (v0.20.0) для типового запроса "уберите Kubernetes, но оставьте масштабирование и бесшовные обновления". Uncloud сохраняет Docker Compose и добавляет кластерные возможности. Это ещё одна альтернатива Swarm, Nomad и Kamal для небольших инсталляций в закрытом контуре.
🔗 https://habr.com/ru/companies/k2tech/articles/1094766/

6. У Node.js-сервиса куча V8 может выглядеть спокойно, а контейнер в это время подходит к лимиту памяти: RSS, external и ArrayBuffer в куче не видны. Экспериментальный container-memory-watch сводит метрики Node.js с данными cgroup v2 через сборщик на Rust: проверка раз в 5 секунд, порог давления 80%, счётчик OOM-kill. Даже без этой библиотеки вывод один: тревогу надо ставить на память cgroup, а не на heap.
🔗 https://dev.to/uppy19d0/i-built-container-memory-watch-monitoring-memory-pressure-in-nodejs-containers-3568

🔍 Симптом живёт не на том слое, где его ищут
Три истории выпуска об одном и том же. Job без подов – это решение Kueue о допуске, а не планировщика. Контейнер с OOM при спокойной куче – это лимит cgroup, а не утечка в V8. Распухший apiserver – это кэш наблюдения, который копируется на каждый экземпляр, а не нехватка реплик. Привычный рефлекс "добавить ресурсов" во всех трёх случаях либо не помогает, либо вредит. На практике это значит: прежде чем чинить, назовите слой, который принимает решение, и снимайте метрику оттуда, а не с соседнего.

🛠 Команда дня:
kubectl get --raw /metrics | grep '^apiserver_storage_objects' | sort -k2 -n | tail -15

Показывает, каких объектов в кластере больше всего по мнению apiserver. Так быстро видно, не раздули ли операторы и CRD кэш наблюдения.

#дайджест #kubernetes #devops
❤1🔥1
Из браузера в терминал

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

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

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

Задача старая. В 1987 году в Техническом университете Берлина написали screen: несколько программ в одном терминале, и чтобы они не умирали, когда отваливается линия. В 2007-м появился tmux, и сейчас это привычный инструмент на серверах. Принцип у обоих один: терминал программе выдаёт сервер мультиплексора, а ssh-клиент только подключается посмотреть. При обрыве отваливается зритель, программа продолжает работать.

И вот новый проект можно ведь что-то новенькое попробовать, поискав, примерив, выбрал для себя zellij. Он написан на Rust (какое совпадение 🤗) и назван в честь марокканской мозаики zellige, по-русски – зеллидж. Из коробки умеет то, что в tmux собирается конфигом и плагинами:
– вкладки и строка статуса: режимы и их клавиши подписаны внизу, наизусть учить не нужно;
– плавающие панели поверх любой вкладки и панели стопкой;
– раскладки файлом: рабочее место описано конфигом и поднимается одной командой;
– воскрешение сессий: раз в минуту zellij сохраняет вкладки, каталоги и команды. Если сервер zellij убит, сами процессы умирают вместе с ним, а zellij attach возвращает раскладку и запускает команды заново – по Enter или
сразу с ключом -f;
– плагины в WebAssembly, официально пока только на Rust;
– встроенный веб-клиент: после zellij web и токена сессию можно открыть из браузера.

Как обычно, пока гром не грянет, все откладываешь на потом, и вот отлетел снова интернет на даче и голые ssh-сессии на сервере умерли вместе с агентами. ID потом искать по кускам текста, чтобы продолжить сессии с места обрыва. Подгорело, и на следующий день появился lc – функция в шелле: заходит на главный сервер, цепляется к zellij-сессии и при обрыве переподключается сама. Первая вкладка – меню агентов со всех моих серверов, с поиском и превью, сделано поверх fzf. По Alt-s то же меню открывается плавающим окном поверх любой вкладки.

Если интересно собрать такое у себя, поставьте реакцию. Наберётся 7 – распишу в блоге пошагово: конфиг zellij, меню сессий, переподключение и как к этому подключить RAG и общую память.

А у вас долгие задачи на сервере в чём живут:
screen 🤔
tmux 😎
zellij 🔥
nohup 🤯
?


#кейс #linux
1😎6🔥2🤔2❤1🏆1
⚡️ DevOps Digest #38 | 26.10.2026

🔥 Главное:

1. Скомпрометированы ключи восьми промежуточных сертификатов eMudhra, которым доверяли браузеры и ОС: с ними можно выпустить поддельный сертификат на любой домен. Cloudflare сначала закрыл исходящие клиенты (прокси к бэкендам, вызовы внешних API), затем общие хранилища доверенных сертификатов и базовые образы. Этот порядок стоит забрать в свой регламент. На этой неделе Fedora, SUSE и Red Hat выпустили обновления ca-certificates.
🔗 https://blog.cloudflare.com/emudhra-certificate-compromise/

2. HPA, VPA, KEDA и автоскейлер нод тянут один Deployment в разные стороны. HPA добавляет реплики по CPU, а VPA меняет requests, от которых HPA и считает процент загрузки. Автоскейлер нод заказывает мощности по заявленным requests и не смотрит на фактическое потребление. Рабочий минимум: VPA в режиме рекомендаций (updateMode: "Off"), его цифры переносить в values чарта через GitOps, HPA и VPA на одной метрике CPU не держать.
🔗 https://thenewstack.io/why-kubernetes-autoscalers-fight/

3. В systemd приняли поддержку новых компонентов на Rust. Существующий код на C переписывать не будут, сборка остаётся на Meson без cargo, код пишется в режиме no_std только с core, alloc и libc. Если собираете systemd в закрытом контуре, для Rust-компонентов понадобится rustc в сборочном окружении, а зеркало crates.io не потребуется.
🔗 https://www.opennet.ru/opennews/art.shtml?num=66539

4. В SRE Weekly #540 есть статья о каталоге сервисов. Первые двадцать минут инцидента уходят на выяснение, чей это сервис и кого будить, и только потом начинается диагностика. Дешёвый старт без отдельного продукта – метки команды и канала дежурства прямо в манифестах. Там же внятный разбор паттерна outbox для записи сразу в две системы.
🔗 https://sreweekly.com/sre-weekly-issue-540/

5. SafeLine от Chaitin – WAF и обратный прокси на Go для размещения на своих серверах, на этой неделе в трендах GitHub. Подходит, чтобы прикрыть внутренние веб-приложения от типовых атак без облачного WAF. Любой WAF первую неделю держите только на журналировании, иначе ложные срабатывания отрежут легитимные запросы.
🔗 https://github.com/chaitin/SafeLine

🔍 Хранилище доверия живёт не в одном месте
История с eMudhra – повод проверить, откуда каждый сервис берёт доверенные корни. Обновление ca-certificates на ноде не доходит до контейнеров: в каждом образе свой набор сертификатов, зафиксированный при сборке, а в distroless и scratch его копируют из сборочного этапа. Свои наборы есть и у рантаймов: Python requests в virtualenv читает cacert.pem из certifi, Node.js использует набор, вшитый в бинарник, у JDK из официальных архивов свой cacerts. Пересборку базовых образов и обновление этих наборов стоит вписать в регламент реакции на компрометацию УЦ, рядом с ротацией секретов.

🛠 Команда дня:
u=https://dv-g3b-emudhra.we-have-a-problem.com/; curl -so /dev/null $u; echo "$? система"; curl -so /dev/null --cacert "$(python3 -m certifi)" $u; echo "$? certifi"

Проверка системного набора сертификатов и certifi текущего Python на тестовом хосте с цепочкой от скомпрометированного УЦ eMudhra: 60 – цепочка отвергнута, 0 – набор ей всё ещё доверяет, другие коды – проблема сети.

#дайджест #kubernetes #security
🔥2❤1
zellij пошагово: терминал на сервере, который переживает обрыв ssh

Обрыв голой ssh-сессии забирает с собой всё, что в ней было запущено: сборку, kubectl logs -f, миграцию. Мультиплексор держит процессы на сервере, ssh-клиент только подключается к нему посмотреть. Отвалилась связь – переподключился и продолжил с того же места.

📺 Продолжение поста "Из браузера в терминал". Там был обзор zellij, здесь пошаговая сборка рабочего места. Все команды прогнаны на чистой Ubuntu 22.04 с zellij 0.45.1.

В посте:
– установка бинарём из релиза и проверка контрольной суммы: файл .sha256sum посчитан от бинаря внутри архива, поэтому обычный sha256sum -c на нём падает;
– панели, вкладки, плавающие окна и таблица клавиш;
– Ctrl+q, который по умолчанию завершает все процессы сессии, и как переназначить его на отключение;
– раскладка файлом: рабочее место для Kubernetes с k9s, логами и репозиторием поднимается одной командой;
– воскрешение после падения сервера: что zellij возвращает сам и какие команды запускает заново;
– переподключение при обрыве связи и почему без ServerAliveInterval ssh висит на мёртвом соединении;
– меню сессий на fzf по Alt+s с карточкой, что открыто в каждой сессии.

Полная версия 👇
https://devopsway.ru/posts/zellij-step-by-step/

#linux #devops
🔥2