> Как понять, что сервис в продакшн работает корректно (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: VK

Стек: Go

> Пример ответа

Короткий ответ

Корректность сервиса в продакшн определяется не одним чеком, а системой наблюдаемости: health checks, метрики, логи и трейсинг. Для Go-сервиса это: readiness/liveness эндпоинты, метрики в Prometheus формате (RPS, latency, error rate, saturation), структурированные логи с корреляционными ID, и алерты на SLO. Плюс - проверка бизнес-логики через синтетические запросы или канареечный анализ. Ключевой сигнал - отсутствие деградации latency и error rate при росте трафика.

Подробное объяснение

Корректность в продакшн - это не "работает/не работает", а соответствие ожидаемому поведению под нагрузкой и в нештатных ситуациях. Для Go-сервиса нужно разделять:

  • Liveness - процесс жив, не в deadlock, не в бесконечной GC-паузе. Проверяется дешёвым эндпоинтом /healthz, который не ходит во внешние зависимости.
  • Readiness - сервис готов принимать трафик: подключился к БД, кэшу, Kafka, прогрел пулы. Эндпоинт /readyz проверяет зависимости с таймаутами.
  • Метрики - RED-методология для HTTP/gRPC: Rate (RPS), Errors (5xx, 4xx), Duration (p50, p95, p99). Для Go дополнительно: goroutine count, heap, GC pause, open FDs.
  • Логи - структурированные (JSON), с request ID, без PII. Ошибки - с stack trace, но не на каждый 4xx.
  • Трейсинг - распределённые трейсы (OpenTelemetry) для понимания, где теряется время: БД, внешние вызовы, своя логика.
  • SLO и алерты - например, 99.9% запросов < 300ms за 5 минут. Алерт - не на "ошибку", а на приближение к бюджету ошибок.
  • Синтетика - периодические запросы к критичным эндпоинтам извне (blackbox exporter), проверка не только ответа, но и содержимого.
  • Канареечный деплой - сравнение метрик новой версии с базовой линией старой.

Для Go-специфики: проверять runtime.NumGoroutine() - утечка горутин видна по росту без снижения; runtime.ReadMemStats - heap не должен расти бесконечно; net/http/pprof - для профилирования по требованию, но не в проде без ограничений.

На практике

  1. Настроить /healthz и /readyz через http.Server с отдельными таймаутами. Readiness должен включать проверку зависимостей с контекстом 2-3 секунды.
  2. Экспортировать метрики через prometheus/client_golang - middleware для HTTP-хендлеров: count, histogram, status code.
  3. Логировать через log/slog (Go 1.21+) в JSON, с request_id из middleware.
  4. Настроить алерты в Prometheus + Alertmanager: error_rate > 5% за 5 минут, p99 > 500ms за 10 минут, goroutines > N - потенциальная утечка.
  5. Для проверки бизнес-корректности - отдельный тестовый эндпоинт (например, /internal/check), который дёргает ключевые сценарии с фейковыми данными и возвращает результат. Доступ - только изнутри кластера.
  6. При деплое - сравнить метрики за 15 минут до и после. Если p99 вырос на 20% - откат.

Пример кода

GO
// healthz + readyz + metrics в одном сервере
mux := http.NewServeMux()
// liveness - только проверка процесса
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
// readiness - проверка зависимостей
mux.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
http.Error(w, "db unavailable", http.StatusServiceUnavailable)
return
}
// аналогично для redis, kafka и т.д.
w.WriteHeader(http.StatusOK)
})
// метрики Prometheus
mux.Handle("/metrics", promhttp.Handler())
// middleware для RED-метрик
func metricsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rec := &statusRecorder{ResponseWriter: w, status: 200}
next.ServeHTTP(rec, r)
httpRequestsTotal.WithLabelValues(r.URL.Path, strconv.Itoa(rec.status)).Inc()
httpRequestDuration.WithLabelValues(r.URL.Path).Observe(time.Since(start).Seconds())
})
}
srv := &http.Server{
Addr: ":8080",
Handler: metricsMiddleware(mux),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}

Как отвечать на собеседовании

Начни с разделения liveness и readiness - это базовый сигнал, что ты понимаешь разницу между "процесс жив" и "готов принимать трафик". Затем переходи к метрикам: RED для HTTP, четыре золотых сигнала Google (latency, traffic, errors, saturation). Упомяни SLO и алерты на бюджет ошибок - это отличает senior от middle.

Для Go добавь специфику: runtime.NumGoroutine, pprof, GOMAXPROCS, GC pause. Скажи, что метрики Go runtime экспортируются автоматически через prometheus/client_golang.

Если спросят про бизнес-корректность - говори про синтетические проверки и канареечный деплой. Не своди всё к "посмотреть логи".

Что проверяет интервьюер

  • Понимание разницы между liveness и readiness.
  • Знание метрик уровня сервиса, а не только инфраструктуры.
  • Умение проектировать наблюдаемость, а не просто "настроить Grafana".
  • Понимание trade-off: метрики vs логи vs трейсинг - когда что использовать.
  • Go-специфику: горутины, GC, pprof.
  • Умение формулировать SLO и алерты, а не просто "порог по CPU".

Типичные ошибки

  • Отвечать "смотреть логи" - это не наблюдаемость, а реактивное действие.
  • Путать liveness и readiness: readiness не должен падать при недоступности БД, если сервис может работать офлайн.
  • Не упоминать SLO: "работает корректно" без числовых критериев - пустое утверждение.
  • Ставить алерты на каждый 5xx - шум, алерт должен быть на тренд или бюджет ошибок.
  • Игнорировать Go-специфику: утечка горутин, рост heap, GC pause - это частые причины деградации в проде.
  • Предлагать pprof в проде без ограничений - это сам по себе риск (блокировки, утечка данных).

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью