Политики ветвления, код-ревью процессы, бэкапы и аварийное восстановление, оптимизация производительности, масштабирование.
Политики ветвления, процессы код-ревью, бэкапы и аварийное восстановление, оптимизация производительности, масштабирование
Вы внедрили Gitea, настроили аутентификацию, CI/CD и мониторинг. Но как обеспечить долгосрочную стабильность и эффективность платформы?
Проблемы без best practices:
Эта тема — о проверенных практиках эксплуатации Gitea в production.
main (production)
↑
│ merge + release tag
develop (integration)
↑
│ merge PR
feature/* (features)
↑
│ commit
feature/login-page
Правила:
| Ветка | Назначение | Защита | Merge требования |
|---|---|---|---|
main | Production код | ✅ Protected | 2 approve, CI pass, no force push |
develop | Интеграция фич | ✅ Protected | 1 approve, CI pass |
feature/* | Разработка фич | ❌ Не защищена | — |
hotfix/* | Срочные исправления | ⚠️ Частично | 1 approve, CI pass |
release/* | Подготовка релиза | ✅ Protected | 1 approve |
Через веб-интерфейс:
mainBranch name pattern: main
✅ Require pull request review before merging
Minimum approvals: 2
Dismiss stale reviews
✅ Require status checks to pass
Contexts: ci/test, ci/build, ci/security-scan
✅ Include administrator
✅ Lock branch (запрет прямого push)
✅ Block merge if changes not reviewed
✅ Require signed commits (опционально)
Через API (массовая настройка):
#!/bin/bash
GITEA_URL="http://git.company.ru"
TOKEN="your_admin_token"
# Список репозиториев
REPOS=("backend/api" "frontend/web" "shared/lib")
for repo in "${REPOS[@]}"; do
curl -X POST "$GITEA_URL/api/v1/repos/$repo/branch_protections" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"branch_name": "main",
"enable_push_whitelist": true,
"push_whitelist_usernames": ["admin", "tech-lead"],
"enable_merge_whitelist": true,
"merge_whitelist_usernames": ["admin", "tech-lead", "senior-dev"],
"required_approvals": 2,
"enable_status_check": true,
"status_check_contexts": ["ci/test", "ci/build"],
"block_on_official_review_requests": true,
"block_on_rejected_reviews": true,
"block_on_outdated_branch": true
}'
echo "✓ Настроена защита main для $repo"
doneGitHub Flow (для continuous deployment):
main (always deployable)
↑
│ PR + deploy preview
feature/*
Trunk-Based Development (для зрелых команд):
main (trunk)
↑
│ short-lived feature branches (< 1 day)
│ или feature flags
feature/*
Для автора PR:
Для ревьювера:
| Приоритет | Время первого ответа | Время merge |
|---|---|---|
| Critical (hotfix) | 30 минут | 2 часа |
| High (блокер спринта) | 4 часа | 1 день |
| Normal (стандартный) | 1 день | 3 дня |
| Low (улучшение) | 3 дня | 1 неделя |
Автоматизация напоминаний:
# .gitea/workflows/review-reminder.yml
name: Review Reminder
on:
pull_request:
types: [opened, synchronize]
jobs:
remind:
runs-on: ubuntu-latest
steps:
- name: Check PR age
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const now = new Date();
const created = new Date(pr.created_at);
const hoursOld = (now - created) / 1000 / 60 / 60;
if (hoursOld > 24) {
// Напоминание в Slack/Telegram
console.log(`PR #${pr.number} не ревьюирован ${Math.floor(hoursOld)} часов`);
}| Метрика | Целевое значение | Как измерить |
|---|---|---|
| Time to First Review | < 4 часов | Gitea API + скрипт |
| Time to Merge | < 2 дней | Gitea API |
| Review Depth | 5-20 комментариев/PR | Gitea API |
| PR Size | < 400 строк | Gitea API |
| Rework Rate | < 30% PR требуют значительных правок | Анализ итераций |
Скрипт сбора метрик:
#!/bin/bash
GITEA_URL="http://git.company.ru"
TOKEN="your_token"
ORG="myorg"
# Получение всех PR за месяц
curl -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$ORG/*/pulls?state=closed&updated_after=$(date -d '30 days ago' -I)" \
| jq -r '.[] | {
number: .number,
title: .title,
created_at: .created_at,
merged_at: .merged_at,
additions: .additions,
deletions: .deletions,
comments: .comments
}'3 копии данных
2 разных типа носителей
1 копия вне площадки
Для Gitea:
Копия 1: Production сервер (основная)
Копия 2: Локальный бэкап на NAS (ежедневный)
Копия 3: Офлайн бэкап в другом здании (еженедельный)
/opt/gitea/backup-production.sh:
#!/bin/bash
set -euo pipefail
# Конфигурация
BACKUP_DIR="/opt/gitea/backups"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
HOSTNAME=$(hostname)
LOG_FILE="/var/log/gitea-backup.log"
# Функция логирования
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# Проверка места на диске
check_disk_space() {
local available=$(df -B1 "$BACKUP_DIR" | awk 'NR==2 {print $4}')
local required=$((5 * 1024 * 1024 * 1024)) # 5 GB минимум
if [ "$available" -lt "$required" ]; then
log "ERROR: Недостаточно места для бэкапа. Доступно: $((available / 1024 / 1024)) MB"
exit 1
fi
}
# Создание бэкапа Gitea
backup_gitea() {
log "Начало бэкапа Gitea..."
docker exec gitea gitea dump -F "$BACKUP_DIR/gitea-$DATE.zip"
if [ -f "$BACKUP_DIR/gitea-$DATE.zip" ]; then
local size=$(du -h "$BACKUP_DIR/gitea-$DATE.zip" | cut -f1)
log "✓ Бэкап Gitea создан: $size"
else
log "ERROR: Не удалось создать бэкап Gitea"
exit 1
fi
}
# Бэкап PostgreSQL
backup_postgres() {
log "Начало бэкапа PostgreSQL..."
docker exec gitea-db pg_dump -U gitea gitea | \
gzip > "$BACKUP_DIR/postgres-$DATE.sql.gz"
if [ -f "$BACKUP_DIR/postgres-$DATE.sql.gz" ]; then
local size=$(du -h "$BACKUP_DIR/postgres-$DATE.sql.gz" | cut -f1)
log "✓ Бэкап PostgreSQL создан: $size"
else
log "ERROR: Не удалось создать бэкап PostgreSQL"
exit 1
fi
}
# Проверка целостности бэкапа
verify_backup() {
log "Проверка целостности бэкапов..."
# Проверка ZIP архива
if ! unzip -t "$BACKUP_DIR/gitea-$DATE.zip" > /dev/null 2>&1; then
log "ERROR: Бэкап Gitea повреждён"
exit 1
fi
# Проверка SQL дампа
if ! gzip -t "$BACKUP_DIR/postgres-$DATE.sql.gz" > /dev/null 2>&1; then
log "ERROR: Бэкап PostgreSQL повреждён"
exit 1
fi
log "✓ Целостность бэкапов подтверждена"
}
# Очистка старых бэкапов
cleanup_old() {
log "Очистка бэкапов старше $RETENTION_DAYS дней..."
find "$BACKUP_DIR" -name "gitea-*.zip" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "postgres-*.sql.gz" -mtime +$RETENTION_DAYS -delete
local count=$(find "$BACKUP_DIR" -name "*.zip" -o -name "*.sql.gz" | wc -l)
log "✓ Осталось бэкапов: $count"
}
# Копирование на удалённый сервер (опционально)
copy_offsite() {
if [ -n "${OFFSITE_HOST:-}" ]; then
log "Копирование на удалённый сервер..."
rsync -avz \
"$BACKUP_DIR/gitea-$DATE.zip" \
"$BACKUP_DIR/postgres-$DATE.sql.gz" \
"$OFFSITE_HOST:/backups/gitea/"
log "✓ Копирование завершено"
fi
}
# Основная логика
main() {
log "========================================="
log "Запуск бэкапа Gitea ($DATE)"
log "========================================="
check_disk_space
backup_gitea
backup_postgres
verify_backup
cleanup_old
copy_offsite
log "========================================="
log "Бэкап завершён успешно"
log "========================================="
}
main "$@"Сценарий 1: Восстановление после сбоя
#!/bin/bash
set -euo pipefail
BACKUP_FILE="/opt/gitea/backups/gitea-20260319_020000.zip"
log "Начало восстановления из $BACKUP_FILE"
# Остановка текущей Gitea
docker compose down
# Очистка данных
rm -rf /opt/gitea/data/*
# Восстановление
docker exec gitea gitea restore --from "$BACKUP_FILE"
# Запуск
docker compose up -d
log "Восстановление завершено"Сценарий 2: Миграция на новый сервер
# На старом сервере
scp /opt/gitea/backups/gitea-latest.zip user@new-server:/opt/gitea/
# На новом сервере
docker compose up -d
docker exec gitea gitea restore --from /opt/gitea/gitea-latest.zipЕжеквартальный drill:
[server]
; Отключение Gravatar (ускоряет загрузку)
DISABLE_GRAVATAR = true
; Сжатие gzip
ENABLE_GZIP = true
; Кэширование статики
STATIC_CACHE_TIME = 86400
[cache]
; Redis для кэша
ADAPTER = redis
HOST = redis:6379
ITEM_TTL = 16h
[session]
; Redis для сессий
PROVIDER = redis
PROVIDER_CONFIG = redis:6379
[database]
; Пул соединений
MAX_IDLE_CONNS = 10
MAX_OPEN_CONNS = 100
; Логирование медленных запросов
LOG_SQL = true
SLOW_QUERY_THRESHOLD = 1s
[repository]
; Ограничение размера push
MAX_PUSH_FILES = 100
; Кэширование git
GC_ARGS = --aggressive --auto
[indexer]
; Elasticsearch для поиска
ISSUE_INDEXER_TYPE = elasticsearch
ISSUE_INDEXER_CONN_STR = http://elasticsearch:9200
REPO_INDEXER_ENABLED = trueКлючевые метрики:
| Метрика | Норма | Критично |
|---|---|---|
| Response Time (p95) | < 500ms | > 2s |
| Database Query Time | < 100ms | > 1s |
| Git Clone Time (средний репо) | < 5s | > 30s |
| Memory Usage | < 70% | > 90% |
| Disk I/O Wait | < 10% | > 30% |
Профилирование:
# Включение профайлера
[server]
PPROF_ENABLED = true
PPROF_DATA_PATH = /data/pprof
# Доступ к профайлеру
curl http://git.company.ru:3000/debug/pprof/heap > heap.prof
go tool pprof heap.prof-- Анализ медленных запросов
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- Индексы для частых запросов
CREATE INDEX CONCURRENTLY idx_issue_repo ON issue(repo_id);
CREATE INDEX CONCURRENTLY idx_comment_issue ON comment(issue_id);
CREATE INDEX CONCURRENTLY idx_action_user ON action(user_id);
-- Vacuum и analyze
VACUUM ANALYZE;| Компонент | Минимум | Medium | Large |
|---|---|---|---|
| CPU | 2 cores | 4 cores | 8 cores |
| RAM | 4 GB | 8 GB | 16 GB |
| Disk | 50 GB SSD | 200 GB SSD | 500 GB NVMe |
| Пользователей | до 50 | до 200 | до 500 |
Архитектура HA:
┌─────────────────┐
│ Load Balancer │
│ (HAProxy/ │
│ NGINX) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌──────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ Gitea 1 │ │ Gitea 2 │ │ Gitea 3 │
│ :3000 │ │ :3000 │ │ :3000 │
└──────┬──────┘ └─────┬─────┘ └──────┬──────┘
│ │ │
└──────────────┼──────────────┘
│
┌────────▼────────┐
│ PostgreSQL │
│ (репликация) │
└────────┬────────┘
│
┌────────▼────────┐
│ NFS / S3 │
│ (репозитории) │
└─────────────────┘
docker-compose для HA:
version: '3.8'
services:
gitea-1:
image: gitea/gitea:latest
environment:
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=postgres-primary:5432
volumes:
- /shared/gitea-data:/data
depends_on:
- postgres-primary
gitea-2:
# same config
gitea-3:
# same config
postgres-primary:
image: bitnami/postgresql:15
environment:
- POSTGRESQL_REPLICATION_MODE=master
- POSTGRESQL_REPLICATION_USER=repl_user
postgres-replica:
image: bitnami/postgresql:15
environment:
- POSTGRESQL_REPLICATION_MODE=slave
- POSTGRESQL_MASTER_HOST=postgres-primaryКурс завершён! Вы освоили полный цикл внедрения Gitea: от развёртывания до production best practices.
Следующие шаги:
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.