fa
Feedback
этогик // DevOps, Infrastructure, Productivity

этогик // DevOps, Infrastructure, Productivity

رفتن به کانال در Telegram

🚀Блог DevOps-инженера. Вместе растем в хард- и софт скиллах, разбираемся как жить и работать продуктивно, ищем баланс во всем 🌿 📽 yt: https://www.youtube.com/@etogeek 🌍 site: https://etogeek.dev 🌿 chat: @etogeekchat Подробнее в закрепе 🦾

نمایش بیشتر
4 359
مشترکین
اطلاعاتی وجود ندارد24 ساعت
+97 روز
+930 روز

در حال بارگیری داده...

جذب مشترکین
اوت '26
اوت '26
+71
در 0 کانال‌ها
ژوئیه '26
+57
در 0 کانال‌ها
Get PRO
ژوئن '26
+70
در 0 کانال‌ها
Get PRO
مه '26
+79
در 0 کانال‌ها
Get PRO
آوریل '26
+119
در 0 کانال‌ها
Get PRO
مارس '26
+161
در 2 کانال‌ها
Get PRO
فوریه '26
+124
در 0 کانال‌ها
Get PRO
ژانویه '26
+130
در 0 کانال‌ها
Get PRO
دسامبر '25
+121
در 0 کانال‌ها
Get PRO
نوامبر '25
+143
در 0 کانال‌ها
Get PRO
اکتبر '25
+154
در 0 کانال‌ها
Get PRO
سپتامبر '25
+143
در 0 کانال‌ها
Get PRO
اوت '25
+132
در 0 کانال‌ها
Get PRO
ژوئیه '25
+142
در 0 کانال‌ها
Get PRO
ژوئن '25
+124
در 0 کانال‌ها
Get PRO
مه '25
+171
در 0 کانال‌ها
Get PRO
آوریل '25
+138
در 0 کانال‌ها
Get PRO
مارس '25
+156
در 0 کانال‌ها
Get PRO
فوریه '25
+168
در 0 کانال‌ها
Get PRO
ژانویه '25
+168
در 0 کانال‌ها
Get PRO
دسامبر '24
+125
در 0 کانال‌ها
Get PRO
نوامبر '24
+132
در 0 کانال‌ها
Get PRO
اکتبر '24
+232
در 0 کانال‌ها
Get PRO
سپتامبر '24
+271
در 0 کانال‌ها
Get PRO
اوت '24
+163
در 0 کانال‌ها
Get PRO
ژوئیه '24
+159
در 0 کانال‌ها
Get PRO
ژوئن '24
+143
در 0 کانال‌ها
Get PRO
مه '24
+239
در 0 کانال‌ها
Get PRO
آوریل '24
+190
در 0 کانال‌ها
Get PRO
مارس '24
+282
در 0 کانال‌ها
Get PRO
فوریه '24
+766
در 1 کانال‌ها
Get PRO
ژانویه '24
+30
در 1 کانال‌ها
Get PRO
دسامبر '23
+223
در 0 کانال‌ها
تاریخ
رشد مشترکین
اشارات
کانال‌ها
31 اوت+3
30 اوت0
29 اوت0
28 اوت+5
27 اوت+1
26 اوت+9
25 اوت+4
24 اوت+3
23 اوت+1
22 اوت0
21 اوت+1
20 اوت+2
19 اوت+3
18 اوت+3
17 اوت+2
16 اوت+3
15 اوت+1
14 اوت+1
13 اوت0
12 اوت0
11 اوت+1
10 اوت+3
09 اوت+4
08 اوت0
07 اوت+2
06 اوت+3
05 اوت+6
04 اوت+3
03 اوت+2
02 اوت+3
01 اوت+2
پست‌های کانال
Чтобы немного подвести итог серии постов про бекапы (раз и два), расскажу как я ими управляю в своей инфрастркутуре. Основная цель такого поста не показать единственный способ "как правильно" (такого нет), а поделиться моим опытом, из которого вы можете подцепить какую-нибудь идею и реализовать у себя. В общем, конечно же, я делаю это через ansible. У меня есть довольно сильно параметризируемая роль backups. В директории files я складываю bash-скрипты в сыром виде. Можно их там разбить на под-директории, чтобы было больше порядка. В инвентори у нужных хостов я описываю бекап по следующей структуре:
backups: # список - можем описать несколько бекапов
  - name: billing_db_backup
    user: root # под каким юзером запускаем
    scripts:
      - script: billing_db_backup.sh # название скрипта из files
        script_dest: /etc/billing/scripts # куда положить скрипт
    schedule: { hour: 2, minute: 30 } # cron-расписание
    job: /etc/billing/scripts/billing_db_backup.sh # cron-команда
    create_dir: # если нужно создать директории
      - path: /etc/billing/scripts
        mode: '0750'
      - path: /var/lib/billing/backups
        mode: '0700'
    env:
      PGPASSWORD: >-
        {{ длинный и некрасивый lookup, чтобы забрать пароль из Infisical }}
Что роль сделает: - создаст директории - скопирует скрипт как есть - срендерит env файлик с паролем из Infisical (в скрипт надо добавить . env) - добавит расписание в cron Почему скрипты в files, а не в templates? Потому что некоторые скрипты могут конфликтовать с рендером Jinja который используется в шаблонах. Надежнее их положить в files в том виде, в котором они есть и попадут на машину.

2
Мониторинг бекапов В прошлый раз я говорил про PITR-бекапы для PostgreSQL, теперь хочу немного рассказать про мониторинг бека
Мониторинг бекапов В прошлый раз я говорил про PITR-бекапы для PostgreSQL, теперь хочу немного рассказать про мониторинг бекапов. Проблематика следующая - мы сделали бекап-скрипт, закинули его в crontab и забыли. Как будем проверять, что скрипт работает и бекапы создаются регулярно? Переключатель мертвеца - это некая система требующая постоянной активации, чтобы основная система продолжала работать. Например, педалька у высотного погрузчика, которую человек должен всегда нажимать, чтобы двигать погрузчик. Аналогичной штукой воспользуемся и с бекапами - на каждое успешное выполнение скрипта будем отправлять "пинг" в систему. Если пинг не придет в ожидаемое временное окно - получим алерт. Что может быть надежнее? Таких сервисов полно, я использую healthchecks.io. Раньше пользовался бесплатной версией, потом развернул self-hosted, чтобы обойти лимиты на хелсчеки. Интеграция простейшая: внутрь скрипта, либо через pipe в cron-команде добавляем curl-запрос типа: curl -fsS -m 10 --retry 5 -o /dev/null https://healthckecs.io/ping/efe3-da-ffdsfsd-qqsdfsf Этот сервис так же умеет замерять время выполнения скрипта (отправляем пинг в начале и в конце), а так же записывать лог ошибок (через POST запрос). Другие примеры: cronitor, sentry, тысячи их. Гуглите cronjobs monitoring.
1 654
3
Про бекапы. Хочу немного поделиться своим опытом в нескольких постах. Это больше информация для новичков, на которой я хочу и в дальнейшем фокусироваться, если честно. Синьоры-помидоры, вы итак это всё знаете (надеюсь). Что первое приходит в голову, когда мы говорим про бекапы баз данных? Ну, делать дамп раз в сутки через cron-таску и загружать его в отдельное хранилище. Есть говорить про PostgreSQL, то стандартный pg_dump достаточен для этого. А восстанавливаемся через pg_restore. Отлично подходит для небольших инстансов, всяких селф-хостед сервисов. Например, у вас развернуты self-hosted Metabase, Infisical, Grafana. База весит немного, дамп еще меньше - рестор быстрый. Проблем тут две: - мы теряем до 24 часов данных, в зависимости от того, как давно был снят последний бекап - при росте базы данных, особенно если это БД продукта, время рестора из дампа растет в прогрессии Рестор ведь можно распараллелить - запустить в несколько потоков, почему растет время рестора? Дамп базы под капотом - это инструкции как собрать базу заново - схема, COPY/INSERT-ы, CREATE INDEX, и тд. То есть при ресторе сначала все данные наливаются с нуля из SQL, а потом начинается создание индексов. На большой базе индексы могут создаваться долго, а главное - они жрут очень много ресурсов (проц+диск). Плюс дампов - можно ресторить отдельные таблицы, ресторить рядом с другой БД внутри одного инстанса, сам дамп можно "прочитать". Что делать с потерей данных, если последний дамп был давно? Надо снимать бекапы чаще :) На прошлой неделе настраивал Point in time recovery (PITR) в PostgreSQL. Что это такое: - раз в какое-то время снимаем полный бекап инстанса (ну например так же раз в сутки) - важно понимать, что мы бекапим не транзакционный дамп, а именно файлы БД. - можно фулл-бекап снимать раз в неделю, а между ними бекапить "дельты" - они меньше и быстрее бекапятся. - непрерывно и постоянно отправляем WAL-ы (write ahead log) - postgres сам запускает нужную команду, когда накапливает wal. В чем суть? - файловый бекап восстанавливается быстрее - потому что это просто копирование файлов - wal-ы позволяют восстановить инстанс на ЛЮБОЕ время. Например. Сейчас 11:00. Последний фулл-бекап был в час ночи. Евстахий получил доступ на прод и навернул таблицу в 10:45. Я могу восстановить состояние кластера на 10:44. Потеря данных (не считая даунтайма на рестор) - 16 минут. Я настраивал это с помощью софтины wal-g, которую делает Яндекс, и, кстати говоря, использует у себя в Managed Service for PostgreSQL в облаке. Сделал себе еще дашбордик с метриками и логами по бекапам, чтобы смотреть - всё ли с ними ок.
2 118
4
Когда я преподавал в Практикуме (был такой период, да. еще и видео снимал), студенты часто возмущались, что менторы не дают прямого ответа на их вопросы. Они подсказывают направление для размышления, но не дают готовое решение. Я верю в то, что когда человек в процессе обучение натыкается на проблему и решает её сам - именно так он получает тот самый ✨ОПЫТ✨. Это не потерянное время. Наткнулся на видео от JetBrains Academy (7 минуток), про то, что происходит, когда новички учатся кодить с помощью Codex/Claude. Там разбирают интересное исследование, где группе студентов давали задачу и следили за тем как именно они приходят к решению. В большинстве случаев они давали рабочее решение, но студенты разделились на две группы с разным подходом: 1. понял, что нужно сделать - AI предлагает решение - оцениваю - принимаю/отбрасываю 2. не понимаю, что делать - AI предлагает решение - выглядит норм - принимаю Первые сами говорят агенту, что им нужно сделать. У них уже было в голове примерное направление действий, агент помогал ускорить весь процесс. Вторых, изначально недостаточно разобравшись в задаче, двигались туда, куда их двигал агент. Решение есть - понимания "почему так" нет. То есть программа в конце работает у обеих групп, только часть людей не обязательно стала лучше понимать технологию. Суть в чем, возвращаясь к моей изначальной идее: если ты закидываешь задачу в ЛЛМ и бездумно применяешь предложенное решение - опыта ты не получаешь. Грош - цена таким знаниям. Лучший вариант, это просить агента оценить твое решение, объяснить концепцию, дать подсказку, показать ошибку. Когда учишься - не перекладывай на AI сам процесс мышления. Используй AI для ускорения своей работы.
2 837
5
Некоторое время назад столкнулся с тем, что какой-то сервис отправил мне огромное количество логов в Локи. В цитируемом посте я рассказывал процесс поиска виновника. Сейчас проблема повторилась, и я разобрался как принудительно удалять старые логи. Во-первых, в конфигурации должен быть активирован compactor. У меня такой конфиг: compactor: working_directory: {{ loki_data_dir }}/retention compaction_interval: 10m retention_enabled: true retention_delete_delay: 30m retention_delete_worker_count: 150 delete_request_store: filesystem delete_request_cancel_period: 5m Затем делаем такой запрос в Loki с указанием query для фильтрации и таймштампами: curl -G -X POST 'http://localhost:3100/loki/api/v1/delete' \ --data-urlencode 'query={service="your-damned-service"}' \ --data-urlencode 'start=2026-06-05T00:00:00Z' \ --data-urlencode 'end=2026-06-25T00:00:00Z' Смотрим статус запроса: curl 'http://localhost:3100/loki/api/v1/delete' [{"request_id":"d7f76f7f","start_time":1780617600,"end_time":1782345600,"query":"{service="your-damned-service"}","status":"processed","created_at":1782393974.431}] Ждем некоторое время (compaction_interval: 10m, delete_request_cancel_period: 5m, retention_delete_delay: 30m) и наслаждаемся освобожденным местом.
1 733
6
Kubernetes — мощно, но сложно. 8 июля в Москве собираются инженеры, чтобы разобраться вместе: поговорить о реальных кейсах, п
Kubernetes — мощно, но сложно. 8 июля в Москве собираются инженеры, чтобы разобраться вместе: поговорить о реальных кейсах, пообщаться за круглыми столами и познакомиться с теми, кто решал похожие задачи. Будут доклад о домашнем кластере на несколько квартир и дата-центров, разбор ИИ-агента, который управляет инфрой как админ, и дискуссии по сетям, виртуализации и безопасности. Это юбилейный митап Deckhouse User Community — и на этот раз особенно много внимания тем, кто активно участвует в жизни сообщества. Коллеги поделятся запуском программы поддержки контрибьюторов. Только офлайн, количество мест ограниченно, регистрируйтесь!
1 818
7
Уже довольно давно я сталкиваюсь с плавающими проблемами связи между Яндекс Облаком и Hetzner: у рандомного https- соединения
Уже довольно давно я сталкиваюсь с плавающими проблемами связи между Яндекс Облаком и Hetzner: у рандомного https- соединения скорость может упасть до 100кб/с. Способы стабилизировать это нашлись, а вот полностью решить так и не получилось. Захотелось замониторить эту скорость скачивания, чтобы найти какие-то закономерности (спойлер, на нашел). За несколько гугл-запросов подходящий экспортер не нашелся - в основом все проверяют скорость до speedtest-а, а мне нужны кастомные URL - например свой Nexus. Время сейчас такое, что если чего то нет, или функционал не устраивает, ты просто пишешь своё. Вот и я сделал download speed exporter: 👉 https://github.com/etoosamoe/download_speed_exporter Собираем образ контейнера, запускаем, а в Prometheus или vmagent дергаем /probe-ручку с URL-ом целевого файла. В ответ получаем метрики по скорости скачивания. Дашборд есть в комплекте. На самом деле я сделал его уже давно, просто почему-то не рассказывал тут. На скриншоте пример того, как скорость скачивания вообще упала до минимума на пол дня.
2 632
8
Работа в небольшой продуктовой компании часто расширяет границы твоей позиции. Я уже давно не просто “девопс”, который перезагружает серверы и дебажит пайплайны. Вопрос плохо это или хорошо - оставлю каждому на размышление. Например, не так давно занимался сначала объединением двух "сайтов" Jira и Confluence в один. Такую попытку уже делал мой лид за полгода до этого, но всё разбилось об ограничения: проблемы при мерже, скачивание/загрузка бекапа и тд. А спустя время, оказалось, что Atlassian только-только выпустили новый инструмент как раз для переноса данных между “сайтами”. Он отработал просто превосходно, перенес проекты со всеми данными, аттачами и почти ничего не поломал. Затем приступил к миграции из Notion в Confluence. Это получилось более хаотично. Многие страницы в Ноушене были приватными. А к приватным страницам даже у админа нет доступа. Нужно просить сотрудников, у кого есть доступ, экспортировать архив, скидывать его мне, а я уже импортирую его в Confluence. Одновременно с этим руководство захотелось разделить все статьи по пространствам - например Sales, Finance, Product и так далее. Минус Confluence по сравнению с Notion или Google Docs в том, что ты не можешь дать доступ одному человеку к одному документу. Нужно добавить человека в раздел (Space), и тогда он получит доступ ко всем открытым статьям. Несмотря на то, что Confluence - хороший инструмент, переезд из Notion был воспринят коллегами не очень мягко. Было много непоняток с доступами, проблем с поисками “потерянных” статей, исправлений ссылок в легаси-местах, проблем с форматированием. Суммарно наэкономили около $24к в год. Жаль не в свой карман, конечно.
3 038
9
В последние недели погрузился в абсолютно новую для себя штуку - мобильную разработку. Точнее, DevOps-часть для нее. Всё новое, изначально ничего непонятно, даже то, с какой стороны подступаться к этому. Хорошо, что мы живем в 2026 году, где существуют ChatGPT и Claude. Но ответственность не позволяет навайбкодить все конфиги и решения. Вместо этого я сижу и делаю кросс-проверки, разбираюсь что и зачем нужно, какие инструменты есть, и почему именно они. Больше, конечно, ковырялся именно с iOS частью, потому что проверять сборку проще локально - андроид телефона у меня нет. Разобрался в целом с воркфлоу разработки: Xcode, Apple Developer аккаунт, bundle-id, загрузка в App Store Connect, публикация в TestFlight для внутренних тестировщиков. Понял, что если мы хотим тестировать приложение на dev-окружении, то по сути надо делать два приложения dev и prod. Потому что внутрь приложения зашивается конфигурация, в которой API-эндпоинты указаны. Получается, что можно собрать dev-приложение, загрузить его в App Store Connect и отправить в TestFlight для проверок. В ревью оно никогда не пойдет. А вот prod-приложение уже пойдет по пути TestFlight → Review → Store. Отсюда вытекает то, что нужно иметь схемы и конфигурации для сборки двух приложений в одном репозитории. И Firebase конфигурации тоже - этим прямо сейчас занимаюсь. Firebase - это платформа от гугла с кучей вспомогательных сервисов для мобилок, например: аналитика и push-уведомления. Ага, не забыть еще скриптик для инкремента версий приложений - сторы требуют обновления версии при каждой новой загрузке приложений. А еще требуют иконку для приложения. Чтобы на iOS картинку не зашакалило (частично из-за Liquid Glass), её надо сделать в Icon Composer-е с помощью SVG-элементов. Разобрался как работают OTA-апдейты (over the air). Это когда можешь пересобрать только внутреннюю часть, запаковать ее в js-бандл и залить в s3. Приложение при запуске проверит наличие апдейта, скачает и предложит перезапуститься. Самая большая выгода - для мелких багфиксов тебе не нужно публиковать новую версию в сторы и ждать ревью. Тут надо быть осторожным, потому что добавлять функционал через OTA-апдейты без ревью запрещается правилами сторов. Описал для разработчиков релизный процесс - как будем работать с фича-ветками, релизными ветками, версиями. Постарался сделать так, чтобы это минимально отличалось от уже привычного всем в команде процесса. В общем, погружение в новую область это всегда так - сначала чувствуешь себя идиотом, не знаешь основных терминов. Мозг к вечеру выжат настолько, что хочется просто сесть на диван и смотреть в стену. Главное помнить - глаза боятся, а руки делают. Потихоньку, мелкими итерациями, но в итоге картина становится яснее, начинаешь лучше во всем ориентироваться.
2 875
10
Немного бомбления про Docker Swarm. Есть опция max_replicas_per_node - позволяет ограничить количество реплик сервиса на одну ноду. Кейс простой: две ноды, две реплики сервиса → хочу строго 1+1, чтобы не было перекоса по памяти. Ставлю: max_replicas_per_node: 1 Логично? Логично. Дальше деплой новой версии с start-first (это как rolling update в кубе, чтобы без даунтайма): - Поднимается новый контейнер - Ждём healthcheck - Гасим старый - Повторяем Но тут начинается веселье. У нас уже есть по одной реплике на каждой ноде. Swarm пытается поднять новую реплику → и не может: - на node1 уже 1 → нельзя - на node2 уже 1 → нельзя - других нод нет И всё. Деплой просто блокируется с no suitable node. Почему? Потому что в Swarm это правило жёсткое. Он не умеет временно нарушать placement constraints, даже ради rolling update. Что с этим делать? Да фиг его знает ¯\_(ツ)_/¯
1 953
11
Deckhouse Conf 2026 — один день концентрированного технического хардкора. 9 апреля в Москве соберём 1000 инженеров, которые с
Deckhouse Conf 2026 — один день концентрированного технического хардкора. 9 апреля в Москве соберём 1000 инженеров, которые строят платформенные продукты и управляют инфраструктурами. Зачем приезжать: — Проверенные кейсы внедрения. — Демо технологий Deckhouse и возможность протестировать их своими руками. — Хардкорные технические доклады про виртуализацию, SDN, мониторинг, миграцию на микросервисы и многое другое. — Прямой контакт с разработчиками и ведущими инженерами. Один день, два трека, 1000 гостей, максимум полезного общения. Регистрируйтесь
1 297
12
Хочу немного рассказать про стек, о котором я раньше никогда не слышал, а четыре года назад пришлось плотненько познакомиться сначала со стороны администрирования, а спустя время - и чуть-чуть потраблшутить часть разработки. Добро пожаловать в мир Hadoop. Всё это поделие максимально плотно связано с Java и Apache. Есть несколько коммерческих дистрибутивов/платформ, но в целом всё это с горем пополам и чертовой матерью поднимается и вручную (хотя может я просто не умею его готовить). Hadoop - это фреймворк распределенной системы обработки данных, который состоит из нескольких продуктов. Ключевая идея - большие данные распределены и обрабатываются на кластере серверов. Расскажу про те, которые используются у нас. HDFS - Hadoop Distributed File System - это буквально сетевая файловая хранилка, с репликацией данных, большой пропускной способностью. Состоит из NameNode (мастер-ноды) со всеми метаданными, и DataNode (агенты) - фактическое хранение данных. HDFS фундаментально работает по системе Write Once Read Many - мы записали файл, и уже не можем его изменить (кроме как append и truncate). Это сильно упрощает модель согласованности данных: читатель гарантированно получает то, что было записано, без конфликтов. HDFS любит большие последовательные чтения файлов. Читать мелкие файлы - не очень. YARN (Yet Another Resource Negotiator) - это оркестратор задач. Грубо говоря как кубер, только для data-задач - он выделяет на CPU/RAM для них. Есть кластер из нескольких серверов, мы можем запускать на нем yarn-контейнеры(не путать с докером) и выполнять в них map-reduce задачки. Не только MR, конечно, есть еще Spark, Hive и тд. ResourceManager - мастер, NodeManager - агент на каждой ноде. При создании задачи появляется ApplicationMaster - он общается с ресурс-менеджером и управляет выполнением конкретного задания. HBase - распределённая NoSQL-база поверх HDFS: по сути, это огромная таблица-словарь, где данные лежат как `row_key → набор полей → значения` (а не как "строка строго фиксированных в схеме колонок", как в реляционках). В отличие от PostgreSQL, где схема задаёт одинаковые колонки для всех строк, в HBase "схема" обычно фиксирует только column family — условные "папки" полей. А внутри каждой такой "папки" у конкретной строки может быть любой набор колонок, и он может отличаться от строки к строке - удобно для разреженных данных и атрибутов, которые меняются со временем без бесконечных миграций. Это может быть сложно представить, поэтому вот очень грубый пример: 0001: profile:name, profile:city, profile:role, contacts:telegram 0002: profile:name, profile:role, contacts:vk, contacts:instagram 0003: profile:name (и всё) 0004: profile:name, profile:city, skills:java, skills:kafka, skills:spark Смысл: families (profile, contacts, skills) заранее "разрешены", а какие именно поля внутри них есть у конкретного пользователя - может отличаться. По кластеру таблица разъезжается через regions: это диапазоны строк по row_key, и каждый RegionServer обслуживает свой набор регионов. - HMaster - распределяет данные по нодам, DDL-операции - RegionServer - воркеры на нодах. Сами данные физически лежат в HDFS, а RegionServer отвечает за чтение, запись и кеширование. Клиент через ZooKeeper/мета‑информацию узнаёт, на каком RegionServer лежит нужный кусок, и дальше идёт напрямую туда. Во всех этих сервисах ZooKeeper используется как централизованное хранилище информации о состоянии кластера (кто жив, где мастер, где мета-данные). Как etcd в Кубере. MapReduce - подход к обработке больших данных в два шага. На этапе Map данные разбиваются на куски, YARN поднимает контейнеры-мапперы и каждый преобразует свой кусок в пары "ключ → значение". На этапе Reduce результаты группируются по ключу и агрегируются. Простой грубый пример: считаем сколько пользователей в каждом городе. Map проходит по записям и выдает "Belgrade → 1", "Moscow → 1", "Belgrade → 1". Reduce суммирует по ключу: "Belgrade → 2", "Moscow → 1".
2 398
13
Redis - это такая key-value база данных, часто используется для кэша, часто - как очередь для Celery, либо для других целей.
Redis - это такая key-value база данных, часто используется для кэша, часто - как очередь для Celery, либо для других целей. Ее особенность в том, что все данные она хранит в оперативной памяти, из-за чего очень быстро работает. Если данные нужны, то в случае перезагрузок/перезапусков или просто по времени Redis может сохранять слепок на диск. В одном внутреннем сервисе Redis стал кушать ну слишком много памяти. Даже виртуалку повесил по OOM-у. Полез разбираться. Оказалось, что в одной базе лежит неприлично больше количество ключей - около 2 млн. Посмотрел, что в ней хранится - токены сессий для API. Хм, а у них в коде MAX_AGE стоит 180 дней всего. Спускаюсь ниже по коду, там где для записи указывается TTL: expire=int(datetime.utcnow().timestamp()) + SESSION_MAX_AGE Ну вроде хорошо, берем текущий таймштамп и плюсуем 180 дней. Так и записываем. Пошел в документацию библиотечки: expire(key, timeout) Set a timeout on key Таймаут… не таймштамп.. получается что о_о Каждая запись создавалась с TTL 1780743477 секунд, что чуть больше 56 лет… Фикс выкатил. А Claude тем временем помог сделать скриптик, который пробежит по всем записям в Redis с TTL > 1 года и удалит старые. Скрипт запустил в фоне, крутится неспеша, память освобождается, все довольны.
2 629
14
Про миграции в базе данных, блокировки и lock queue. Значит, ситуация: CD-пайплайн запускает миграцию в БД, которая делает совершенно рядовой ALTER TABLE - например добавляет колонку или меняет поле. Миграция должна пройти за считанную секунду, но она зависает, и приходит алерт, что прод перестал отвечать на запросы. Представили? А мне и представлять не нужно. Рассказываю до чего докопался и что узнал. Каждый SELECT запрос в PostgreSQL создает блокировку - AccessShareLock на таблицы, которые он задействует. Это самый нестрогий лок, он не конфликтует с другими запросами и локами. Кроме одного. AccessExclusiveLock - создается при различных изменениях таблиц: DROP, ALTER, TRUNCATE и тд. Этот единственный лок, который блокирует другие SELECT запросы. (подробнее про локи тут). В базу отправляется жирнющий SELECT-запрос, который выполняется, скажем, 60 секунд. Все это время на таблице висит AccessShareLock, но он никак не мешает другим запросам - всё работает. И тут прилетает ALTER TABLE со своим AccessExclusiveLock. Он встает в очередь за предыдущей транзакцией - ждет пока она не завершится и не освободит таблицу от Share-лока. А заодно ExclusiveLock блокирует все последующие запросы в БД к этим таблицам, которые просто выстраиваются в очередь. А если табличка популярная, то и запросы с прода будут зависать. Вот схемка, спасибо Claude за визуализацию, все остальное писал сам: 10:00:00 — Большой SELECT с кучей JOIN-ов на 60 секунд 🟢 AccessShareLock взят 10:00:10 — ALTER TABLE запускается (нужен AEL) 🔴 Встал в очередь, ждёт окончания SELECT 10:00:11 — Обычный SELECT от приложения ⏸️ Встал в очередь ЗА миграцией 10:00:13 — SELECT, INSERT, SELECT, UPDATE... ⏸️ Все в очереди! 10:01:00 — Первый SELECT закончился 🔴 ALTER TABLE начал выполняться (5 секунд) 10:01:05 — Миграция завершилась ✅ Очередь разблокировалась В моем случае было еще немного классного легаси-кода: есть периодическая джоба, которая открывает транзакцию (сессию) к БД, забирает из часть данных (2-5 сек), начинает эти данные обрабатывать, работать со внешними системами, потом забирает еще часть данных, обрабатывает их, и так далее в цикле. И только в сааааамом конце - закрывает транзакцию в БД (делает `COMMIT`). Проблема в том, что все это время на задействованных таблицах висит AccessShareLock потому что транзакция не закрыта, COMMIT-а не было. Обычной работе это не мешает. А вот если во время работы этой джобы мы запускаем миграцию, то получается жопа. Что делаем? - Закрываем транзакции после того как сделали запрос. Лучше сделать 100 отдельных селектов только когда они нужны, и не держать транзакцию в idle in transaction, удерживая AccessShareLock. - Оптимизируем большие запросы. - Миграции запускаем с параметром PGOPTIONS="-c lock_timeout=5s" - тогда если миграция и уткнулась в ShareLock, то через 5 секунд она упадет и прод не будет лежать - Установить idle_in_transaction_session_timeout в настройках PostgreSQL - оно будет дропать подобные транзакции в Idle in transaction статусе. Но сначала надо починить старые запросы, которые создают такое. - Переносим SELECT-запросы на read-реплику (пока только теория) - Мониторинг и логирование. Пока еще думаю какое именно. - (еще почитать)
2 774
15
26 февраля — Deckhouse User Community meetup #4. Это митап для тех, кто хочет понимать Kubernetes глубже. Зарегистрироваться
26 февраля — Deckhouse User Community meetup #4. Это митап для тех, кто хочет понимать Kubernetes глубже. Зарегистрироваться Эксперты Deckhouse и приглашённые спикеры расскажут, как запускать K8s поверх любых дистрибутивов, эксплуатировать платформу в одиночку, развёртывать домашнюю виртуализацию на бюджетном железе и грамотно подходить к безопасности. И покажут: на митапе будет работать зона «Попробуй сам», где можно протестировать работу Deckhouse Kubernetes Platform Community Edition своими руками.
2 414
16
Классный пример поддержки от SaaS. Именно такой уровень вовлеченности ожидаешь от поддержки крупного (очень) сервиса. Я сейчас занимаюсь переездами в небезызвестный Confluence (поделие Atlassian) из всяких Notion-ов, из соседних пространств, меняю структуру разделов и т.д. И наткнулся на неочевидную проблему - не видно страниц внутри некоторых разделов. А поиском - видно. Написал тикет, поставил галочку "можно получить доступ в наш инстанс" и стал ждать. Через полчаса увидел в списке пользователей сотрудника Atlassian, а еще через полчаса в почту пришел ответ. В нем самое интересное. Во-первых, текст был не похож на LLM-ответ. Вероятно был отформатирован, но само написание выглядело человеческим. Такое - редкость сейчас. Во-вторых, он содержал несколько скринов, подробное описание того, в чем проблема. А главное, чел написал, что в одном разделе всё пофиксил и записал ВИДЕО ✨ о том, как починить во всех остальных разделах. Вот так вот. Проблема только в том, что мир SaaS ждут проблемы. С приходом LLM каждая компания по сути может сделать свой SaaS. Клонов Notion-а уже over9000. Зачем платить 10 баксов за юзера, если можно навайбкодить свои Jira и Confluence для внутреннего использования. Но условная Figma под меньшим риском - её сложнее повторить, это уже некий стандарт индустрии фронтенда/дизайна. Получается что под угрозой те SaaS-ы, которые раньше покупали из-за сложности разработки. Главная проблема даже не в том, что клиенты реально могут сделать качественный продукт (далеко не все понимают, что идет в архитектуру крупных систем). Проблема в том, что клиенты чувствуют, что могут. И этого может быть достаточно для отмены подписки на SaaS. Что же остается делать SaaS-ам? - Глубже подсаживать пользователей на свой продукт. Это когда один раз начав активно пользоваться сервисом, уже сложно будет переехать на другой (DataDog?). - Адаптироваться под клиента. Улучшать поддержку. - Менять продукт и становиться "ближе к деньгам клиента" В интересные времена живем...
2 885
17
В последнее время я не то что бы очень часто делаю что-то прям техническое-девопсерское руками, и когда выпадает возможность что-то потраблшутить, можно сказать даже радуюсь этому. Вот вечером приходит алерт, что на виртуалке с VictoriaMetrics и Loki осталось <5% места. А я последний раз когда смотрел, там 160+ гигов было (из 300). Что-то явно пошло не так. Вряд ли это метрики, я давно не добавлял новые скрейп-таргеты, значит какие-то логи начали лететь. Начинаю смотреть графики виртуалки: - 22 декабря с 7 утра начал активно забиваться диск Как понимаю, что это именно Loki жрет место. Аномально много места занимают два конкретных чанка данных. Смотрим в ncdu или du -sh на директории с данными VM и Loki: --- /var/lib/loki/chunks/fake --- /.. 71.1 GiB [##########] /71a536873561d772 71.0 GiB [######### ] /4ed05483e3664242 9.9 GiB [# ] /47a1d97545945804 4.0 GiB [ ] /ab30104fb5a49027 3.4 GiB [ ] /1fb8841baeb3ef1 2.3 GiB [ ] /eaeec6ed138da0a2 1.4 GiB [ ] /f973cac99619b763 1.4 GiB [ ] /39aad22606baff2b Причем видно, что файлы в них начали создаваться только после 22 декабря, значит ОНО: find /var/lib/loki/chunks/fake/71a536873561d772/ -type f -printf '%TY-%Tm-%Td\\n' | sort | uniq -c 872 2025-12-22 1390 2025-12-23 1390 2025-12-24 1394 2025-12-25 .... Посмотрим количество логов с разбивкой по лейблу job (тайм-рейндж выставляю на 1 час вокруг начала инцидента). Иду в Grafana Explore, выбираю Loki как data source: sum by (job) (count_over_time({job=~".+"}[5m])) Там вижу, что всплеск и дальнейшее плато выпадает на job-у containerlogs. В этот лейбл я собираю логи с контейнеров со всех серверов. Значит надо посмотреть с какого хоста это летит и с какого compose-сервиса (или swarm-stack-а): topk(5, sum by (host) (count_over_time({job="containerlogs"}[5m]))) Вижу на графике конкретный хост, добавляю его в фильтр. Так же меняю лейбл для суммирования, на stack_name (так как на этом сервере подняты сервисы Docker Swarm): sum by (stack_name) (count_over_time({job="containerlogs", host="host.com"}[5m])) Теперь на графике видно сбойный сервис. Беру в охапку автора и идем смотреть конкретные логи этого сервиса в момент инцидента. Вижу, что сервис в определенный момент начал сыпать ошибками. Обычно, если ошибок нет, то нет и логов от сервиса. А тут он начал писать по ~300к строк в минуту. Анализирую ошибки, понимаю, что они в целом одинаковые. Выясняем, что в сервис пришел кривой JSON, сервис не смог его обработать корректно, выдавал ошибку, возвращал JSON в очередь. А затем брал его же из очереди, выдавал ошибку... и так далее по кругу. Теперь самое сложное: что сделать, чтобы избежать подобного в будущем? - Более тщательный мониторинг занятого места на диске, лучше с predict-ом “с таким темпом место кончится через Х дней/часов”. Но расход был медленный, алерт пришел бы сильно не скоро. - Rate Limit на количество логов в минуту на стороне Loki, повесить алерт на превышение. - Ронять контейнер на каждый сбой? Норм, если есть алертинг на рестарты контейнеров. А что, если в очереди куча сбойных json-ов? - Складывать сбойные json-ы не обратно в основную очередь, а в другую. И мониторить ее наполняемость? А если json был нормальным, просто сбой был временным - тогда валидный json улетит в мусорку… - Настроить отправку exceptions в Sentry и сделать алерты? - 👉 Стоит ли вообще что-то делать, если сервис - легаси и ломается примерно никогда и не сильно влияет на прод? А можно ли удалить лишние логи? Я в свое время не нашел явного несложного способа дропнуть логи в Loki, например, за определенные даты. Только если настраивать новый ретеншен, либо грохать всё. В моем случае я просто оставлю, пока есть свободное место - в течение ретеншен-периода оно уйдет.
2 412
18
Пару постов назад я делился своим .zshrc файликом с алиасами и настройками ZSH. И в чате мне напомнили, что более сложные мож
Пару постов назад я делился своим .zshrc файликом с алиасами и настройками ZSH. И в чате мне напомнили, что более сложные можно оборачивать в функции и даже скинули пример для git add/commit/push одной командой (спасибо, Саша!). Я его немного допилил: - добавил возможность писать без кавычек несколько слов, чтобы они улетали как commit message - проверку, что ты коммитишь НЕ в master/main ветку Получилось очень удобно, теперь я просто в терминале пишу gacp Update Nginx configs, вместо череды из git add, git commit -m, git push (хотя эти команды уже где-то в подкорке сидят). Вот код функций, там я утянул еще из oh-my-zsh шаблонов одну: function git_current_branch() { local ref ref=$(git symbolic-ref --quiet HEAD 2> /dev/null) local ret=$? if [[ $ret != 0 ]]; then [[ $ret == 128 ]] && return # no git repo. ref=$(git rev-parse --short HEAD 2> /dev/null) || return fi echo ${ref#refs/heads/} } gacp() { if [ -z "$1" ]; then echo "Commit message required" return 1 fi local branch=$(git_current_branch) if [[ "$branch" == "master" || "$branch" == "main" ]]; then echo "You are on branch '$branch'" read "REPLY?Commit to $branch? (Y/n) " if [[ $REPLY =~ ^[Nn]$ ]]; then echo "Commit cancelled" return 1 fi fi git add -A git commit -m "$*" git push origin "$(git_current_branch)" } Как обычно код можно посмотреть у меня в репозитории, вот ссылка.
2 623
19
Небольшой отчет о том, чем занимался на работе в последнее время. У нас есть довольно большой проект на Python и вот мы решили мигрировать его со стандартного pip для управления зависимостями на современный и популярный uv. Как было раньше: два файла - requirements.txt с основным набором зависимостей и requirements-dev.txt - в нем добавлены некоторые дополнительные зависимости для локальной разработки и в тестах. Чтобы добавить их в pyproject.toml можно использовать uv add -r requirements.txt dev-файлик перезжает в uv в отдельную "группу" зависимостей. Зависимости такой группы можно поставить вместе с основными через флаг --group name. Сам uv генерит uv.lock файл, в котором зафиксирован слепок этих зависимостей, подзависимостей, транзитивных и так далее. Похоже на package-lock.json в Node. Основной воркфлоу теперь немного меняется: - uv sync создает виртуальное окружение (как раньше в .venv) и ставит в него все зависимости - uv run <command> запускает команду в виртуальном окружении, например uv run python main.py - учитывая наличие .venv после uv sync, можно сделать source .venv/bin/activate и работать как раньше Dockerfile тоже нужно переделать. Пример беру в официальном example-репозитории. Сам бинарник uv беру из образа через COPY, чтобы не конфликтовать по версии Python между pyproject.toml (там например зафиксирована 3.X.13) и официальным образом uv-python (они фиксируют только 3.X без патч-версии). Из интересных оптимизаций билда - использование mount-ов для RUN директив. Они позволяют использовать кэш на диске сборочного инстанса в случае инвалидации слоя - например поменялся слой с зависимостями, но мы все равно используем кэш, чтобы все не перекачивать. В gitlab-ci настраиваю использование кэша: cache: key: files: - uv.lock paths: - .cache/uv Если используете self-hosted докер-раннеры, то в настройках надо добавить: [[runners]] [runners.docker] disable_cache = false volumes = ["/var/path/to/cache:/cache"] А вот что не получилось пока что сделать: указать кастомный индекс (nexus-proxy) для Python-зависимостей только на сборках/тестах. Чтобы при локальной установке качать зависимости с PyPi, а на сборке - с Nexus. При изменении index-url uv меняет uv.lock файл, чего я делать не хочу. Но, наверное, это логично.
2 441
20
Наткнулся на статью в которой автор говорит, что у него Oh My Zsh тормозит запуск терминала: типа 380мс (на голый OMZ) это оч
Наткнулся на статью в которой автор говорит, что у него Oh My Zsh тормозит запуск терминала: типа 380мс (на голый OMZ) это очень медленно, а у него еще какой-то воркфлоу, который постоянно открывает новые окна терминала. Я полистал, и вспомнил, что у меня-то терминал тоже долго открывается. Оказалось до 1.47 сек. Неприятненько. Решил разобраться что к чему. Для начала небольшая экскурсия в часто-рекомендуемый стек работы с терминалом: - эмулятор терминала: например iTerm2 или ghostty. Отвечает за отрисовку терминала, GUI по сути. - zsh - командный интерпретатор. По дефолту в Макоси стоит именно zsh. Аналоги: bash, fish, sh - oh-my-zsh - фреймворк для zsh для более удобной установки плагинов, тем и т.д. - prompt - тема для строки приглашения, например: powerlevel10k или starship У zsh есть свой профайлер, чтобы его включить добавляем в ~/.zshrc: # в начало файла zmodload zsh/zprof ..... # в конец файла zprof Посмотрим, что там у меня больше всего тормозит: nvm_auto 398.78ms 50.55% __yc_bash_source 163.05ms 20.67% nvm 195.29ms 24.75% compinit 226.47ms 28.71% nvm, тулза для яндекс облака, и какой-то compinit. В принципе, вещи которыми я пользуюсь довольно редко. Скрипты nvm и yc в .zshrc поставились автоматически при установке соответствующих инструментов. Там всякие автокомплиты, обновления, сканы файлов… Для nvm и yc сделал “ленивую” загрузку, чтобы эти скрипты загружались в сессию только при запуске команды. (см. в примере ниже) compinit - это оказался автокомплит в zsh. Для него выключаю аудит файлов, проверку кэша: autoload -Uz compinit compinit -C -u Из той статьи я решил подчерпнуть минималистичность конфигурации и призыв ставить только то, что нужно. Поэтому удаляю все следы oh-my-zsh из .zshrc. Понадобится - поставлю. Плагин zsh-autosuggestions ставлю через brew. А любимую тему для prompt (powerlevel10k) ставлю без oh-my-zsh. Вот такое мне уже нравится: /usr/bin/time zsh -i -c exit 0.17 real Теперь хочется добавить в промпт информацию об активной версии Python и venv. В репозитории обновил свой актуальный .zshrc. ✨ А чем вы пользуетесь для работы с терминалом? Делитесь вашим стеком, темами и скриншотами в комментариях!
2 807