
Всем привет! Меня зовут Сергей, и я Backend Kotlin разработчик в компании занимающейся разработкой систем повышающую безопасность дорожного движения. И я расскажу, как мы с помощью Jetpack Compose и GitLab API упростили процесс деплоя на 100+ распределённых серверов, повысив при этом удобство и предсказуемость процесса.
В ежедневной работе нашей команды стоит задача регулярного деплоя обновлений на большое количество серверов - их на данный момент уже более сотни. Рутинные операции через веб-интерфейс GitLab отнимали много времени и были подвержены человеческим ошибкам. Вариант внедрения Argo CD нам не подходил, так как мы используем Docker Compose и переходить на Kubernetes не планировали. Из-за привязки серверов к определённым трассам деплой должен осуществляться не сразу на все сервера, а группами по согласованию с ответственными лицами с возможностью группировки по направлениям. Мы задумались об автоматизации, но хотели не просто скрипт, а удобный, наглядный и надёжный инструмент. И мы его создали, использовав, казалось бы, неожиданную технологию - Jetpack Compose.
Проблема: Деплой как головная боль
Представьте себе:
Масштаб: 100+ серверов, разделенных на несколько стейджинг‑окружений и продакшен‑кластеров.
Процесс: Зайти в GitLab CI/CD → найти нужный пайплайн → найти нужный джоб → нажать «Run» → выбрать ветку → повторить 100 раз.
Риски: Легко запустить деплой не на том сервере, перепутать ветку, пропустить ошибку в логах из‑за усталости.
Наш старый подход был неэффективен, медленен и деморализующий для инженеров. Нужно было решение, которое предоставит:
Единую точку входа для управления всеми деплоями.
Визуализацию статуса каждого сервера в реальном времени.
Минимизацию ручных действий до нескольких кликов.
Возможность отбора серверов для деплоя по группам
Надёжность и защиту от случайных ошибок.
Выбор технологий: Почему Jetpack Compose?
Когда зашла речь о UI, классические варианты были очевидны: Electron (JS/TS), Qt (C++/Python), JavaFX. Но у меня был опыт разработки Android приложений, и мы обратили внимание на Jetpack Compose for Desktop.
Наши аргументы в его пользу:
Скорость разработки: Декларативный подход Compose позволяет быстро итеративно строить сложные и отзывчивые интерфейсы. «Что видишь, то и получаешь».
Современный и родной Kotlin: Вся логика приложения и UI пишутся на одном языке. Это уменьшает контекстные переключения и упрощает поддержку. Kotlin — это основной язык для разработки backend и Android приложений в нашем проекте.
Производительность: В отличие от Electron, приложение на Compose for Desktop — это нативное исполняемое файлы с минимальными overhead. Оно быстро запускается и не жрет память.
Экосистема Kotlin: Мы могли использовать мощные корутины (kotlinx.coroutines) для асинхронных вызовов GitLab API и богатую экосистему библиотек (Ktor для сетевых запросов, jackson для парсинга JSON и Yaml).
Сердце системы: GitLab API
Вторая ключевая технология — GitLab API. Мы использовали библиотеку gitlab4j‑api. Оно предоставляет программный доступ ко всему, что умеет делать веб-интерфейс:
Авторизация через Private-Token
Запуск, отмена, перезапуск джобов
Получение логов пайплайнов в реальном времени
Получение списка проектов, веток
Наше приложение стало умной прослойкой между инженером и GitLab CI/CD.
Архитектура нашего решения
Архитектуру приложения мы решили не усложнять, чтобы не затаскивать в проект дополнительные библиотеки, поэтому оставили в нём два основных слоя:
UI Layer (Compose for Desktop):
Отвечает за отрисовку всего интерфейса
Отображает список серверов (проектов) с их текущим статусом
Реагирует на действия пользователя и отправляет команды в слой логики
Data Layer (GitLab API):
Репозиторий, который инкапсулирует все сетевые запросы
Использует GitLab REST API для выполнения операций
Авторизация через Private-Token
Преобразует JSON-ответы от GitLab в доменные модели Kotlin (data class Pipeline, data class Job)

fun GitLabApi.getLastBranch(project: Project) =
repositoryApi.getBranchesStream(project.id, "prod-")
.asSequence()
.sortedByDescending { it.name.toNumber() }
.firstOrNull()
fun GitLabApi.getLastPipeline(project: Project, tagName: String): Pipeline? =
pipelineApi.getPipelinesStream(
project.id,
PipelineFilter().withRef(tagName).withScope(Constants.PipelineScope.TAGS)
).asSequence().minByOrNull { it.committedAt }
fun GitLabApi.getJobsForPipeline(project: Project, pipelineId: Long) =
jobApi.getJobsStream(project.id, pipelineId)
Ключевые фичи готового приложения
ЕдиныйDashboard: Все 100+ серверов на одном экране. Цветовые индикаторы сразу показывают общую картину
Массовый деплой: Возможность выбрать группу серверов (например, все сервера «стейджинга») и запустить деплой на всех одной кнопкой
«Ленивый» мониторинг: Приложение в фоне периодически опрашивает GitLab API и автоматически обновляет статусы деплоев. Пользователь видит прогресс в реальном времени
Возможность перейти из приложения для деплоя непосредственно в пайплайн Gitlab
Выводы и результаты
Спустя несколько месяцев активного использования мы можем подвести итоги. Что мы получили:
Скорость: Время затрачиваемое на проведение деплоя уменьшилось в разы благодаря удобства работы с новым интерфесом
Надежность: Количество ошибок, связанных с «человеческим фактором», стремится к нулю
Удобство: Инструмент получился настолько удобным, что большую часть выкатки релиза передана непосредственно продакт‑менеджеру
Технический долг: Мы не только решили бизнес‑задачу, но и освоили перспективную технологию (Compose for Desktop), которая может пригодиться и для других внутренних инструментов