Кейс 3: Разработка Internal Developer Platform (IDP) для хакатона
Роль: DevOps / Platform Engineer
Длительность: ~83 часа (полный цикл: от проектирования до поддержки в проде и демонтажа)
Стек: GitLab Self-Managed, Podman (rootless), Traefik, Terraform, Ansible, Bash, systemd.
Проблема и Контекст (Baseline)
Необходимо было предоставить 5 изолированным командам надежную среду для 2-дневного хакатона (стек: .NET, React, Telegram-боты, SQLite/PostgreSQL). Ключевые боли:
- Зависимость от внешних SaaS-решений с ограничениями на регистрацию и доступ.
- Риск того, что участники или наставники “сломают” пайплайн, пытаясь изменить его код “на лету”.
- Высокая вероятность сетевых коллизий при одновременном деплое нескольких команд.
Инженерные решения (Action)
1. Platform as a Product (Централизованные шаблоны)
- Создан единый платформенный репозиторий (
platform), содержащий переиспользуемые CI/CD workflow (черезinclude) и стандартизированныеdocker-composeманифесты. - Команды получали “скелет” репозитория с готовыми
Dockerfile(multi-stage) и.gitlab-ci.yml, что исключало ошибки базовой настройки.
2. Параметризация и безопасность (No-Code Deployment для наставников)
- Внедрена система управления деплоем через CI-переменные (
DEPLOY_MODE,DB_TYPE,NEEDS_DB). - Результат: Наставники могли переключать сценарий (например, с веб-приложения на Telegram-бота или с SQLite на PostgreSQL) прямо в UI GitLab, не внося изменений в код пайплайна.
3. Глубокий траблшутинг и изоляция
- Критический инцидент: Во время мероприятия несколько команд случайно подключались к чужим базам данных, так как использовали общее имя сервиса
postgresв общей сети Podman. - Решение: Логика генерации переменных перенесена в скрипт
deploy-remote.sh. Теперь при каждом деплое скрипт автоматически формирует и пробрасывает в контейнер уникальные переменные окружения (например,DB_HOST=team-2-hackathon-backend-postgres), полностью исключая ручной ввод и коллизии.
3.1. Rootless Podman и ACL-траблшутинг для GitLab Runner
Вся платформа развернута на rootless Podman: исполнение контейнерного движка без root-прав снижает attack surface на общем сервере мероприятия. Но это создало нетривиальную проблему доступа GitLab Runner к сокету Podman.
Baseline: GitLab Runner (docker executor) должен был подключаться к rootless сокету Podman пользователя deployer (/run/user/<UID>/podman/podman.sock). Решение через setfacl -m u:gitlab-runner:rw работало до первой перезагрузки сервера — после неё сокет пересоздавался, и ACL терялся.
Аудит и инсайт: Нужно автоматическое восстановление ACL при каждой активации сокета. Логичное решение — systemd drop-in. Но placement имеет значение:
podman.socket.d/— не работает:podman.socketэто.socketunit, секция[Service]сExecStartPostв нём недопустима и молча игнорируется systemd с варнингом «Unknown section ‘Service’».podman.service.d/— работает: socket-activation при первом подключении запускаетpodman.service, иExecStartPostвыполняется после старта — в этот момент сокет-файл уже создан и ACL можно установить.
Решение — drop-in конфиг ~/.config/systemd/user/podman.service.d/acl-fix.conf:
# Drop-in: автоматическое восстановление ACL при каждой активации podman.socket
# %t раскрывается в /run/user/<UID> (XDG_RUNTIME_DIR пользователя)
[Service]
ExecStartPost=/usr/bin/setfacl -m u:gitlab-runner:rw %t/podman/podman.sock
Активация user-level сокета через Ansible: стандартный модуль ansible.builtin.systemd с become_user + scope:user обращается к D-Bus через DBUS_SESSION_BUS_ADDRESS, но при sudo-become эта переменная недоступна — systemd user bus слушает на /run/user/<UID>/bus, который монтируется только при PAM-логине. Единственный надёжный способ — shell с явной передачей переменных окружения:
- name: Enable and start rootless podman.socket for deployer via shell
ansible.builtin.shell: |
systemctl --user enable podman.socket
systemctl --user start podman.socket
become: true
become_user: deployer
environment:
XDG_RUNTIME_DIR: "/run/user/"
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user//bus"
Этот же обходной манёвр (shell + явная передача сессионных переменных) применён позже в Ansible-роли для Restic (см. Кейс 4).
4. Оптимизация и надежность самой платформы
- Аудит бэкапов: выявлены два root-cause роста архивов до 9.1 ГБ — архивация Container Registry и баг
GITLAB_OMNIBUS_CONFIG(параметры не сохранялись при перезапуске). - Решение: Внедрена прямая запись в
gitlab.rbчерез Ansible + флагSKIP=registryпри бэкапе.
Результаты и Метрики
| Метрика | Результат |
|---|---|
| Изоляция и стабильность | 5 команд работали параллельно без сетевых коллизий и взаимного влияния. |
| Управляемость | 0 изменений кода пайплайна со стороны наставников; все настройки через CI-переменные. |
| Оптимизация платформы | Аудит бэкапов: устранены оба root-cause (архивация Registry, баг omnibus-конфига): размер ~9.1 ГБ → 44 МБ (~200 раз), время 15 мин → 48 сек. |
| Rootless-доступ GitLab Runner | Решена проблема потери ACL на сокете Podman после перезагрузки через systemd drop-in в podman.service.d/ (не в podman.socket.d/ — там [Service] игнорируется). |
| Безопасность | Rootless-исполнение, автоматическая очистка ресурсов после мероприятия (полный демонтаж через Terraform). |
Архитектура платформы
graph TD
subgraph Teams [Development Teams]
Dev[Participants/Mentors] -->|git push / change vars| GitLab[GitLab Self-Managed]
end
subgraph Platform [CI/CD Platform Repo]
GitLab -->|include| Templates[Reusable CI Templates]
Templates -->|Kaniko Build| Registry[Container Registry]
end
subgraph Hackathon [Hackathon Environment - Rootless Podman]
GitLab -->|Trigger| Runner[GitLab Runner]
Runner -->|SSH + Compose| DeployScript[deploy-remote.sh]
DeployScript -->|Inject Unique DB_HOST| Stack[Isolated Team Stack]
Stack --> Registry
end