Git Hosting на Go: Serverless MVP на Yandex Cloud
Курс по разработке Git-хостинга на Go поверх Serverless-стека Yandex Cloud. Студент проходит путь от первого git clone через Cloud Function до полноценного MVP с PR, вебхуками, CI/CD-интеграцией и защитой от гонок. Всё это — за ~50₽/мес в free-tier.
Философия курса
Serverless — не серебряная пуля, а инструмент для MVP. Мы не продаём Serverless как идеальное решение. Мы показываем, что для 12 разработчиков с 50 push/день и 200 clone/день Serverless покрывает всё в free-tier, а при росте > 1k RPS — мигрируем на VPS. Паттерны те же — инфраструктура меняется.
Каждый модуль решает одну инженерную проблему. Не «изучаем YDB», а «как сохранить метаданные push, если нет FOR UPDATE». Не «изучаем Message Queue», а «как гарантировать доставку вебхука, если консьюмер упал». Паттерн — ответ на боль, не самоцель.
Cost-first: считаем деньги с первого модуля. FinOps-калькулятор в Модуле 1, cost-алерты в каждом модуле, ROI-отчёт в финале. Архитектура, которая стоит как плохая эксплуатация — плохая архитектура.
Сквозной User Story. Пользователь Вася делает git push origin main. В Модуле 2 мы смотрим, что происходит на этапе Git Gateway. В Модуле 3 — как метаданные сохраняются в YDB. В Модуле 4 — как событие RepositoryPushed доставляется в CI/CD. В Модуле 5 — как блокировка защищает от конкурентных push. В Модуле 6 — как merge PR проходит через стейт-машину.
Цель курса
Вырастить инженера, который:
- Разворачивает Git-хостинг на Serverless-стеке Yandex Cloud за ~50₽/мес
- Реализует Git Smart HTTP протокол в Cloud Function (стриминг, CGI,
exec.Command) - Хранит метаданные в YDB Serverless без
FOR UPDATE(атомарный UPDATE с условием + retry с exponential backoff) - Гарантирует доставку событий через Transactional Outbox на YDB + Message Queue
- Защищает систему от гонок через Bulkhead (
concurrency_limit) и идемпотентность - Реализует PR merge как стейт-машину Saga на Cloud Function Timer Trigger
- Мониторит систему через OpenTelemetry + Jaeger + Cloud Monitoring
- Деплоит всё через Terraform + GitHub Actions + Lockbox
- Доказывает надёжность через e2e-тесты и DR-тесты с
git fsck
Формат
Модульный, проектный, с Code Review. Каждый модуль: инцидент → теория → практика → антипаттерн → чек-лист → мостик. В каждом модуле — Mermaid-диаграмма и сквозной User Story.
Длительность
4–6 недель, 10–15 часов в неделю.
Результат
- Работающий Git-хостинг MVP на Yandex Cloud (~50₽/мес)
git clone/git push/git fetchчерез Smart HTTP в Cloud Function- PR merge через стейт-машину Saga с компенсациями
- Вебхуки CI/CD через Outbox + Message Queue
- Мониторинг через OpenTelemetry + Jaeger
- IaC через Terraform + CI/CD через GitHub Actions
- 8 ADR по единому шаблону с количественными trade-offs
- Cost-отчёт: Serverless vs VPS vs K8s
- Документ «Defense Q&A» для защиты перед комитетом
Стек
Только Go 1.22+ (без альтернативных треков).
Инфраструктура
Yandex Cloud (Cloud Functions, API Gateway, YDB Serverless, Message Queue, Object Storage, NFS), Terraform (IaC), OpenTelemetry, Jaeger, Cloud Monitoring, Docker, GitHub Actions.
Сквозные требования
- Стриминг через
io.Copy(неio.ReadAll— лимит 32 MB) - Парсинг CGI-заголовков:
parseCGIHeadersотделяет HTTP-заголовки от тела exec.Commandсcontext.Contextиcmd.Wait()(не fire-and-forget)- Защита от Directory Traversal (
assertSafeRepoс проверкой абсолютного пути) - Атомарный UPDATE с условием вместо
FOR UPDATE(YDB не поддерживает) - Retry с exponential backoff + jitter при конфликтах, метрика
conflict_rate concurrency_limitна Cloud Function (Bulkhead)- Идемпотентность через YDB UNIQUE constraint +
Idempotency-Keyдля REST - Идемпотентный ответ (409/200 no-op) возвращается ДО входа в concurrency-слот
- Cost-алерты на free-tier лимиты
- Terraform для всей инфраструктуры (ClickOps запрещён)
- Секреты в Lockbox (не в
.env), авто-ротация HMAC через Cloud Function + Timer Trigger - PII-маскирование через middleware во всех Cloud Functions
- Кардинальность метрик: никаких ID в labels, только категории
- Валидация
traceparentперед использованием
Место в экосистеме курсов
| Курс | Роль | Связь |
|---|---|---|
| Этот курс | Входная точка: практика, cost-first, ~50₽/мес | Serverless-стек, MVP |
| Архитектура распределённых систем | Карта мира: паттерны как ответы на боли роста | Полный стек (PostgreSQL, Kafka, K8s), теория |
| Git Hosting: от 10k до 100k RPS | Территория на масштабе | Storage Nodes, Citus, CDN, шардирование |
Рекомендуемый порядок (по желанию, курс самодостаточен): этот курс → Архитектура → Highload.
Модули: стадия роста → боль → паттерн
| # | Файл | Стадия роста | Боль | Паттерн-лекарство | Сигнал «пора изучать» |
|---|---|---|---|---|---|
| 1 | 01-setup.md | Идея / прототип | «На чём запускать? Бюджет < 200₽/мес» | Serverless-стек, FinOps-калькулятор, Terraform | Проект ещё не написан |
| 2 | 02-smart-http.md | MVP / первые юзеры | «Как Git общается с хостингом в Cloud Function?» | Git Smart HTTP, CGI, стриминг io.Copy, exec.Command | git clone не работает |
| 3 | 03-ydb.md | MVP / первые юзеры | «Как хранить метаданные без SQL и FOR UPDATE?» | YDB Serverless, YQL, атомарный UPDATE + retry | Метаданные push теряются |
| 4 | 04-outbox.md | Интеграции | «Вебхуки теряются при недоступности CI/CD» | Transactional Outbox на YDB + Message Queue | CI/CD не запускается после push |
| 5 | 05-idempotency-bulkhead.md | Интеграции | «Конкурентные push ломают refs; API перегружен» | Bulkhead (concurrency_limit), идемпотентность | Два push одновременно → конфликт |
| 6 | 06-saga.md | Сложные процессы | «Merge PR завис на полпути» | Saga на YDB + Cloud Function Timer Trigger | PR в статусе merging бесконечно |
| 7 | 07-observability.md | Продакшен 24/7 | «Не видно, что ломается; счёт за облако растёт» | OpenTelemetry + Jaeger + Cloud Monitoring + cost-алерты | Инцидент расследуется > 1 часа |
| 8 | 08-defense.md | Защита | «Как это всё задеплоить и защитить?» | Terraform + CI/CD + Security + DR + Cost-отчёт | Комитет задаёт «почему не Y» |
Сквозные требования к ADR
Каждый ADR в курсе приводится к единому шаблону:
# ADR-{N}: {Название решения}
## Статус
Принят | {дата}
## Проблема
{1–2 предложения: какую проблему решаем}
## Контекст
- {Текущее состояние системы}
- {Ограничения (free-tier, latency, стоимость)}
- {Бизнес-требования}
## Варианты решения
### Вариант 1: {Название}
### Вариант 2: {Название}
### Вариант 3 (если есть): {Название}
## Критерии выбора
| Критерий | Вес | Вариант 1 | Вариант 2 | Вариант 3 |
|----------|-----|-----------|-----------|-----------|
| Стоимость | 30% | ... | ... | ... |
| Latency (p99) | 25% | ... | ... | ... |
| RTO | 20% | ... | ... | ... |
| Сложность реализации | 15% | ... | ... | ... |
| Поддерживаемость | 10% | ... | ... | ... |
## Решение
Выбран **Вариант {N}**, потому что: ...
## Последствия (Trade-offs)
- Плюсы / Минусы / Риски
## План миграции / отката
- Триггер миграции
- Шаги миграции
- План отката
## Связь с другими модулями
Критерий сдачи курса: студент заполняет Cost-отчёт (Serverless vs VPS vs K8s), приводит все 8 ADR к единому шаблону и защищает каждое решение перед комитетом, опираясь на документ «Defense Q&A» — без единого «потому что Serverless модный».
Связь с Архитектурой v2
Студент, прошедший курс «Архитектура распределённых систем», уже знает:
- Outbox на PostgreSQL с
FOR UPDATE SKIP LOCKED(m3) → здесь мы изучаем Outbox на YDB с атомарным UPDATE - Bulkhead через
chan struct{}в коде (m5) → здесь мы изучаем Bulkhead черезconcurrency_limitв Terraform - Идемпотентность через DB unique index (m2) → здесь мы изучаем идемпотентность через YDB UNIQUE constraint
- Saga с goroutine-оркестратором (m4) → здесь мы изучаем Saga на Cloud Function Timer Trigger
Этот курс даёт практику на упрощённом стеке (YDB, Message Queue, Cloud Functions), тогда как Архитектура даёт теоретический фундамент и полный стек (PostgreSQL, Kafka, K8s). Паттерны те же — инфраструктура разная.
Сводная таблица Trade-offs курса
| Модуль | Решение (Serverless MVP) | Плюс | Минус |
|---|---|---|---|
| 01 | Serverless (не VPS, не K8s) | ~50₽/мес; автомасштаб | Cold Start; 32 MB push; 10 мин таймаут |
| 02 | io.Copy + parseCGIHeaders (не io.ReadAll + cmd.Stdout = w) | O(1) память; корректные HTTP-заголовки | Сложнее отлаживать |
| 03 | YDB Serverless (не PostgreSQL) | 0₽/мес при 10 RU/с | Нет FOR UPDATE; YQL ≠ SQL |
| 03 | Retry + conflict_rate (не FOR UPDATE) | Оптимистичные блокировки без внешних сервисов | Latency при конфликтах |
| 04 | Outbox + Timer Trigger с concurrency_limit=1 (не goroutine) | Нет гонок; нет долгоживущих процессов | Задержка до 5 сек |
| 05 | concurrency_limit + идемпотентность ДО слота (не chan struct{}) | Платформа ограничивает; 409/200 не тратят слот | Нет гибкости |
| 06 | Mark merge-failed + retry (не ROLLBACK) | Git не трогаем; точечный retry | Ручной шаг для администратора |
| 07 | Cloud Monitoring SDK (не Prometheus) | Управляемый; cost-алерты | Привязка к YC; нет PromQL |
| 07 | Кардинальность как архитектурный запрет | Нет взрыва рядов; стабильный мониторинг | Ограничение детализации |
| 08 | Lockbox + govulncheck + миграция (не .env + Gitea) | Least privilege; CVE-scan; путь роста | Сложнее Terraform |
| 08 | Авто-ротация HMAC + S3-эволюция + e2e | Zero-downtime ротация; путь масштабирования | Сложнее Terraform |
Финальный чек-лист курса (Критерии сдачи)
- Архитектура: 4 Cloud Functions (api, git-gateway, outbox-relay, saga-orchestrator) на Yandex Cloud.
- Git Smart HTTP: io.Copy стриминг, parseCGIHeaders, exec.Command с context, assertSafeRepo.
- YDB: метаданные, атомарный UPDATE с условием, EXPLAIN в CI, 10 RU/с free-tier.
- YDB: retry с exponential backoff при конфликтах, метрика
conflict_rate+ алерт. - Outbox: атомарная запись в YDB, Timer Trigger, concurrency_limit=1, DLQ с Redrive Policy в Terraform.
- Bulkhead: concurrency_limit=5 на git-gateway, идемпотентность ДО слота, Idempotency-Key для REST.
- Saga: стейт-машина PR merge, компенсация = mark failed (не ROLLBACK), различение конфликт vs таймаут, логирование переходов.
- e2e тест: полный цикл
git push→mergeв CI nightly. - Observability: OTel + Jaeger, Cloud Monitoring SDK, cost-алерты, yc logging read, PII masking.
- Observability: кардинальность проверена (нет ID в labels), PII-middleware во всех функциях.
- Security: секреты в Lockbox, govulncheck в CI, ротация HMAC, Trivy scan.
- Security: автоматическая ротация HMAC через Cloud Function + Timer Trigger.
- DR: снапшоты NFS раз в день, Object Storage бэкап, RTO/RPO план, тест DR.
- DR: тест с
git fsck+git ls-remoteпосле восстановления. - IaC: Terraform для всего стека, Drift Detection в CI, ClickOps запрещён.
- Документация: 8 ADR по единому шаблону с количественными trade-offs и планом миграции.
- Cost-отчёт: Serverless vs VPS vs K8s для 12/50/100 юзеров, точка миграции.
- Планы миграции: YDB → PostgreSQL, Cloud Functions → VPS.
- Эволюция: S3-хранилище как альтернатива NFS описана в ADR-SV8.
- Защита: единый шаблон ADR + документ Q&A для комитета.
Защита перед комитетом
Финальный артефакт курса — документ «Defense Q&A» (Модуль 8, раздел 8.9) с готовыми ответами на жёсткие вопросы:
- «Почему не Gitea?»
- «Что при росте 10× (120 юзеров)?»
- «Что если NFS упадёт на 2 часа?»
- «Почему Serverless, а не K8s?»
- «Почему YDB, а не PostgreSQL?»
- «Где точка отказа?»
- «Что если Outbox Relay упадёт?»
- «Как доказать, что Saga работает?»
- «Почему идемпотентность ДО concurrency-слота?»
- «Что если превысим free-tier?»
- «Почему parseCGIHeaders, а не cmd.Stdout = w?»
- «Как защищено от Directory Traversal?»
- «Где секреты?»
- «Есть ли CVE в зависимостях?»
- «Как мигрировать YDB → PostgreSQL?»
Правила защиты:
- Всегда с цифрами. Не «быстро», а «p99 = 180 мс». Не «дёшево», а «50₽/мес vs 500₽/мес».
- Всегда с планом миграции. Не «это лучшее решение», а «это лучшее для MVP, при > X мигрируем на Y».
- Всегда с trade-offs. Не «только плюсы», а «плюсы X, минусы Y, риски Z».
- Всегда с тестами. Не «мы думаем, что работает», а «e2e-тест в CI nightly, DR-тест раз в неделю».