Миграция монолита в Kubernetes: опыт команды Noxet
В этом кейсе инженеры Noxet делятся практическим опытом перевода крупного монолитного сервиса с физических серверов в облачную оркестрацию Kubernetes. Проект решал классические проблемы зрелых продуктов: медленный ручной релизный цикл, деградация производительности в часы пиковых нагрузок и риск простоев при выкатке обновлений.
Исходное состояние и архитектурные вызовы
До начала модернизации приложение представляло собой тяжелый монолитный бэкенд, развернутый на двух выделенных серверах под управлением systemd и Nginx:
- Stateful-зависимости: Сессии пользователей хранились в оперативной памяти конкретного процесса, из-за чего на балансировщике требовалось использовать Sticky Sessions (привязку клиентов по IP/cookie).
- Файловые блокировки: Загружаемые пользователями файлы сохранялись на локальный диск сервера, требуя ненадежной синхронизации через NFS.
- Ручной деплой: Выкатка релизов занимала до 3 часов, требовала ночных окон обслуживания и периодически сопровождалась недоступностью сервиса из-за перезапуска служб.
- Отсутствие автоскейлинга: В моменты маркетинговых акций серверы упирались в полку по CPU и RAM, приводя к отказам 502/504 Gateway Timeout.
Этап 1. Подготовка приложения (12-Factor App)
Перенос монолита в контейнеры «как есть» без адаптации архитектуры приводит лишь к переносу старых проблем на новый уровень абстракции. Первым шагом команда выполнила рефакторинг:
- Экстернализация состояния (Stateless): Хранение пользовательских сессий перенесено в распределенный кластер Redis Sentinel. Это позволило запускать любое количество параллельных инстансов без потери авторизации пользователей.
- Объектное хранилище: Работа с локальными файлами заменена на взаимодействие по S3 API (кластер MinIO в On-Premise контуре).
- Graceful Shutdown: Реализована корректная обработка сигналов
SIGTERM. При получении сигнала приложение перестает принимать новые HTTP-соединения, завершает обработку текущих запросов в течение 15 секунд и только затем освобождает ресурсы. - Структурированное логирование: Вывод логов переведен в
stdout/stderrв формате JSON, что обеспечило прозрачный сбор логов демоном Vector в централизованное хранилище VictoriaLogs / OpenSearch.
Этап 2. Оптимизация сборки контейнеров
Безопасность и размер образов критически влияют на скорость старта подов в Kubernetes. Мы разработали multi-stage Dockerfile:
# --- Этап сборки (Builder) ---
FROM golang:1.22-alpine AS builder
WORKDIR /app
RUN apk add --no-cache git ca-certificates
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /bin/server ./cmd/server
# --- Финальный минимальный образ (Runtime) ---
FROM gcr.io/distroless/static-debian12:nonroot
WORKDIR /app
COPY --from=builder /bin/server /app/server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/app/server"]
Результат оптимизации образа:
- Размер итогового контейнера снизился с 1.4 ГБ до 28 МБ.
- Полное отсутствие пакетных менеджеров и утилит (
sh,bash,curl) в финальном образе исключает вектор выполнения произвольного кода при уязвимостях. - Запуск от непривилегированного пользователя (
nonroot:nonroot).
Этап 3. Архитектура развертывания в Kubernetes
Для обеспечения бесшовного обновления (Zero-Downtime Deployment) и автоматического масштабирования манифест приложения параметризован через Helm:
apiVersion: apps/v1
kind: Deployment
metadata:
name: core-api
labels:
app: core-api
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
selector:
matchLabels:
app: core-api
template:
metadata:
labels:
app: core-api
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
image: registry.noxet.ru/project/core-api:v2.4.1
resources:
requests:
cpu: "300m"
memory: "512Mi"
limits:
cpu: "1500m"
memory: "1536Mi"
readinessProbe:
httpGet:
path: /healthz/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 2
livenessProbe:
httpGet:
path: /healthz/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
Ключевые механизмы устойчивости:
maxUnavailable: 0: Старые поды не выводятся из эксплуатации до тех пор, пока новый под не пройдет проверкуreadinessProbeи не начнет принимать пользовательский трафик.- HorizontalPodAutoscaler (HPA): Автоматическое масштабирование количества реплик от 4 до 24 подов при утилизации CPU выше 70% или превышении порога в 250 rps на инстанс.
- PodDisruptionBudget (PDB): Гарантирует наличие минимум 75% доступных подов во время планового обновления и дренажа нод кластера.
Этап 4. Автоматизированный CI/CD пайплайн в GitLab
Сборка и доставка автоматизированы через GitLab CI с использованием Kaniko (безопасная сборка без использования privileged docker-in-docker сокета):
stages:
- test
- build
- deploy
run_tests:
stage: test
image: golang:1.22-alpine
script:
- go test -v -race ./...
build_image:
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.20.0-debug
entrypoint: [""]
script:
- /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"
--destination "${CI_REGISTRY_IMAGE}:latest"
--cache=true
only:
- main
deploy_production:
stage: deploy
image: alpine/helm:3.14.0
script:
- helm upgrade --install core-api ./helm \
--namespace production \
--set image.tag=${CI_COMMIT_SHORT_SHA} \
--wait --timeout 5m
environment:
name: production
only:
- main
Измеримые результаты внедрения
- Время деплоя: Сократилось с 2–3 часов ручной работы до 6 минут полного цикла (тестирование, сборка Kaniko, раскатка Helm).
- Доступность 100% при релизах: Благодаря
RollingUpdate,readinessProbeиpreStop hookни один входящий пользовательский запрос не сбрасывается во время обновления. - Автоматическая реакция на всплески нагрузки: При проведении промо-акций кластер за 90 секунд автоматически масштабирует пул подов, удерживая задержку (p99 latency) в пределах 45 мс.
- Безопасность среды: Изоляция сети через Kubernetes NetworkPolicies и запуск контейнеров в режиме Read-Only корневой файловой системы.
Если вашей инфраструктуре требуется миграция в Kubernetes, построение надежного CI/CD или аудит отказоустойчивости — обратитесь к инженерам Noxet.