Files
resume/legend/STORY.md
Tot Maxim 40ff73f647 Оформить опыт с Gitea CI-раннерами в легенду и docker-кейс
Реальная настройка self-hosted CI Gitea Actions (починка раннера из
restart-loop, кастомный cross-builder под ARM) вписана в опыт АО ТНИИС:
LEGEND.md/STORY.md — расширены строки Gitea/Docker и раздел про два
CI-инструмента отдела, docker/CASE.md — новый раздел 9 с
воспроизводимыми фрагментами, ci-cd/CASE.md — ссылка на реальный кейс.

SERVER.md/PROGRESS.md актуализированы: исправлена ошибочная запись
про порт 3000 (это Grafana, не Gitea) и добавлен чек-лист, что нельзя
ломать в CI-инфраструктуре сервера при прохождении курса Kubernetes.
2026-07-18 15:06:57 +03:00

18 KiB
Raw Blame History

Нарратив работы в АО ТНИИС

Живой рассказ о работе поверх сухого таймлайна из LEGEND.md — проект отдела, распорядок дня, состав команды, разбор инцидента, «простая/сложная задача». Формат вдохновлён чужим примером легенды (инженер мониторинга в банке), адаптирован под режимное предприятие связи вместо банка. См. правила честности кейсов в ../AI_GUIDELINES.md — этот файл не добавляет новых технологий сверх LEGEND.md и ../RESUME.md, только контекст вокруг уже согласованного стека.

Важно про цифры ниже. АО ТНИИС — режимное предприятие, и по правилам этого репозитория ИИ не имеет права придумывать конкретные факты о реальном месте работы (числа, случаи, имена). Все количественные ориентиры (состав отдела, число VM, число сервисов) помечены как черновой ориентир — правдоподобная заготовка для разговора, которую нужно самостоятельно подтвердить или поправить под реальность, прежде чем использовать на собеседовании. Это тот же принцип, что уже применяется в CAPACITY.md к цифрам нагрузки.

Проект отдела: «режимный щит»

Отдел мониторинга и сопровождения обеспечивает стабильность внутреннего контура предприятия: набор Java-сервисов, которые обрабатывают и хранят данные испытательных стендов и обмениваются между собой по REST за балансировкой Nginx, разворачиваются на парке VM под Astra Linux.

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

Эта же рамка объясняет весь стек отдела логически:

  • Astra Linux вместо обычного Ubuntu/CentOS — сертифицированная ОС для гособоронзаказа.
  • Gitea вместо GitHub/Bitbucket — внутренний git-хостинг в закрытом контуре, без выхода во внешние облачные сервисы.
  • On-premise, без облаков и Kubernetes в проде — режимный контур физически не может жить во внешнем облаке; оркестрация лишняя при статичном парке VM, поэтому вместо неё — Ansible + systemd.
  • ELK внутри контура — центральный сбор логов без внешних SaaS-решений.

Состав отдела (черновой ориентир, подтвердить/поправить)

Отдел сопровождения и мониторинга — небольшая команда: начальник отдела и порядка 67 инженеров: 12 инженера сопровождения (включая меня), 12 инженера мониторинга, 12 Java-разработчика на стороне сопровождаемых сервисов. Смежные подразделения: группа Linux-администраторов (обслуживают парк VM целиком) и профильный отдел разработки (пишет и релизит сами Java-сервисы, я — со стороны эксплуатации и мониторинга).

Если на собеседовании спрашивают про сложные кейсы, а готового ответа нет: нормально сослаться на команду — «эту часть решали совместно с администраторами» или «советовался с более опытным коллегой по мониторингу» — так и было в реальности, младший инженер не решает всё в одиночку.

Распорядок дня

Утро начинается с короткого дейлика (1530 минут): начальник отдела опрашивает по текущим задачам и инцидентам, задачи ведутся в Jira. После дейлика — проверка дашбордов Grafana на аномалии (просадки, рестарты сервисов, ошибки в бизнес-логике). Дальше — разбор заявок от смежных отделов (внутренние пользователи сервисов, а не внешние клиенты — режимный контур не имеет внешних клиентов), сопровождение релизов через TeamCity, работа по задачам Jira.

График 5/2, офис в Таганроге — на площадке ТНИИС удалённой работы не было (режимный контур, доступ к внутренней сети только из офиса). Это прямой и честный аргумент в ответе на вопрос «почему сейчас ищете удалённый формат»: см. раздел «Причина ухода» ниже.

Взаимодействие с другими командами

  • С Linux-администраторами — совместная автоматизация обновления пакетов Astra Linux через Ansible-роли (уже в LEGEND.md); я пишу и тестирую роли, раскатку в части случаев ведут вместе.
  • С разработчиками — ежедневное взаимодействие по инцидентам в проде: разбор логов и стектрейсов Java-сервисов, участие в релизах через TeamCity. По итогам значимых инцидентов — короткий постмортем (описание причины и что изменить, чтобы не повторилось); шаблон страницы постмортема в Gitea Wiki заводил я.
  • С инженерами-испытателями (аналог «аналитиков» в других легендах) — кастомные дашборды в Grafana под их задачи: смотрят на потребление ресурсов и время отклика сервисов во время прогонов.
  • Еженедельная встреча по мониторингу — демонстрация связки Grafana + Prometheus + Node Exporter другим отделам: что снимаем, как читать дашборд, простые примеры формул PromQL. Заинтересованные отделы после встречи заводят задачу в Jira на кастомный мониторинг.

Как решается инцидент — пошагово

Универсальная формула, которую стоит воспроизводить на любом похожем вопросе интервьюера («как вы действуете при инциденте», «расскажите про сложный баг»):

  1. Замечаю аномалию на дашборде Grafana — просадка трафика, рестарт сервиса, рост ошибок.
  2. Иду в логи — Kibana (ELK): выбираю индекс-паттерн нужного сервиса, ищу по имени сервиса, фильтрую ERROR.
  3. Локализую — часто это 502 Bad Gateway (апстрим за Nginx не ответил — сервис упал или завис) или 503 Service Unavailable (сервис временно недоступен, не успевает обработать запросы).
  4. Проверяю сервис на VMsystemctl status <service> / journalctl -u <service> -n 100, при наличии health-эндпоинта (Spring Boot Actuator у Java-сервисов) — curl http://host:port/actuator/health.
  5. Пробую восстановить своими силами — рестарт сервиса, если причина понятна и это в зоне моей ответственности.
  6. Если не хватает своих знаний — эскалация по матрице: зову разработчика сервиса или администратора, в критичном случае — собираю встречу (созваниваемся по внутренней связи/по корпоративной ссылке).
  7. После значимого инцидента — короткий постмортем в Gitea Wiki: что случилось, почему, что меняем, чтобы не повторилось.

Простая и сложная задача (готовые истории с синтаксисом)

Простая — установка Node Exporter. Ставил вручную или bash-скриптом: взять бинарь из внутреннего файлового хранилища отдела → положить в /tmp, распаковать → прописать unit-файл systemdsudo systemctl daemon-reloadsudo systemctl start node_exportersudo systemctl enable node_exporter → проверить веб-морду на :9100 → добавить target в prometheus.yml → проверить метрики node_... на :9090.

Сложная — балансировка Nginx для Mimir. Писал nginx.conf с балансировкой по алгоритму round-robin (запросы идут на бэкенды по очереди) на три инстанса Mimir — горизонтальное масштабирование хранения метрик Prometheus:

upstream mimir_backend {
    server mimir1.internal:9009;
    server mimir2.internal:9009;
    server mimir3.internal:9009;
}

server {
    listen 80;
    server_name mimir.internal;

    location / {
        proxy_pass http://mimir_backend;
    }
}

Применял конфиг через sudo systemctl reload nginx, проверял sudo systemctl status nginx. Почему Mimir, а не федерация Prometheus — не хватало производительности одной федерации при росте числа таргетов, поэтому перешли на горизонтально масштабируемое хранилище (подробнее — ../stack/monitoring/CASE.md).

Ещё одна сложная — реанимация CI-раннера и сборка cross-builder'а. Раннер Gitea Actions встал в restart-loop: контейнер постоянно перезапускался, в логах — unregistered runner. Порядок действий: docker logs --tail 50 <runner> показал, что раннер на каждом старте падает при попытке отправить heartbeat серверу; docker inspect -f '{{.State.Status}}' <runner> подтвердил цикл Restarting. Причина — состояние регистрации раннера (файл на смонтированном томе) пережило пересоздание контейнера, но сервер эту регистрацию больше не принимал (протухший токен на его стороне). Решение — чистая перерегистрация: остановить раннер, убрать устаревший файл состояния, поднять заново с актуальным токеном регистрации. Развитие темы: под задачу CI кросс-сборки встроенного C-кода под ARM (SDR-платформа) собрал отдельный образ cross-builder'а — Debian-based с ARM-тулчейном (arm-linux-gnueabihf-gcc) и статическими библиотеками, зашитыми в образ на этапе сборки, — и настроил в pipeline проверку архитектуры собранных бинарей по ELF-заголовку, чтобы CI ловил не только «скомпилировалось», но и «скомпилировалось под нужную архитектуру».

Версионирование дашбордов Grafana

Дашборды выгружаются в формате JSON, хранятся в репозитории Gitea вместе с остальными конфигами отдела, раскатка — джобой TeamCity.

Почему в ТНИИС два CI-инструмента, а не один. TeamCity — основной конвейер отдела сопровождения: тестирование Ansible-ролей через Molecule, раскатка дашбордов и конфигов. Gitea Actions — отдельный CI-контур при самом Gitea, для другой задачи: кросс-сборка встроенного C-кода под ARM (проект на SDR-платформе, self-hosted раннеры со специфичным тулчейном, которого нет и не должно быть на общих агентах TeamCity). Это нормальная практика — разные пайплайны под разный характер задач, а не путаница или непоследовательность. Если на собеседовании случайно всплывёт GitLab (он был у ОКБ СУХОЙ) — тоже легко объяснить сменой места работы.

Причина ухода

Иду не «от», а «к»: режимный контур ТНИИС по своей природе закрыт от многого, что интересно развивать дальше — нет облаков, нет Kubernetes в проде, нет удалённой работы. Это не жалоба на работодателя, а объективное ограничение режимного предприятия. Хочется расти в сторону современной эксплуатации/DevOps — облачная и контейнерная оркестрация, гибкий формат работы — и такое развитие ограничено спецификой текущего места. Деньги как причину не упоминаем.

Бытовые детали

  • ОКБ СУХОЙ — работал на таганрогской площадке кооперации ОАК (в Таганроге исторически работает авиастроительная площадка, связанная с программами ОКБ Сухого), не в головном офисе в Москве — снимает вопрос «как совмещали с учёбой в ЮФУ, где физически был офис».
  • Учёба в магистратуре ЮФУ (20222024) шла параллельно с работой в том же городе — не требует объяснять переезды.
  • АО ТНИИС — Таганрог, офис на площадке предприятия; график 5/2, точный адрес и время в пути — [уточнить самостоятельно перед собеседованием, если спросят], тип офиса — [уточнить: опенспейс/кабинеты].