Docker и Kubernetes: развертывание микросервиса маршрутизации пациентов от Dev до Prod | AdminWiki

Docker и Kubernetes: развертывание микросервиса маршрутизации пациентов от Dev до Prod

20 июля 2026 8 мин. чтения

Архитектура микросервиса и план развертывания

Микросервис маршрутизации пациентов принимает входящие запросы, определяет по набору правил целевой сервис и перенаправляет вызов. Это критичный компонент в медицинских информационных системах, где задержка или ошибка маршрутизации напрямую влияет на скорость оказания помощи. Сервис состоит из трёх слоёв: REST API на входе, движок правил маршрутизации и хранилище конфигураций.

Полный цикл развертывания от Dev до Prod укладывается в шесть этапов. Сначала приложение упаковывается в Docker-образ с многоэтапной сборкой. Затем создаётся Helm-чарт с разделением конфигураций по окружениям. Чарт деплоится в Kubernetes-кластер, где Ingress-контроллер и балансировщик открывают доступ к сервису. На четвёртом этапе настраивается горизонтальное автомасштабирование и распределение подов по узлам. Финальный шаг - экспорт метрик в Prometheus и настройка алертов для контроля корректности маршрутизации. Все примеры проверены на Kubernetes 1.31 и совместимы с облачными и bare-metal кластерами.

Если вы только начинаете переход от монолитной архитектуры к микросервисам, изучите наш план миграции монолита на микросервисы. Для тех, кто уже работает с контейнерами, но ещё не переносил приложения из Docker Compose, подготовлено руководство по миграции в Kubernetes.

Упаковка микросервиса в Docker-образ

Для примера возьмём микросервис на Go - он даёт минимальный размер образа и быстрый старт. Сервис слушает порт 8080, имеет эндпоинты /route для маршрутизации и /health для проверки готовности. Контейнеризация начинается с Dockerfile, который определяет среду выполнения и способ сборки.

Оптимизация Dockerfile для production

Многоэтапная сборка сокращает итоговый образ в 8–12 раз. На первом этапе компилируем бинарник в golang:alpine, на втором - копируем его в минимальный scratch-образ. Такой подход исключает компилятор, исходный код и системные утилиты из production-контейнера.

# Этап сборки
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /patient-router .

# Production-образ
FROM scratch
COPY --from=builder /patient-router /patient-router
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 1001
EXPOSE 8080
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
  CMD ["/patient-router", "-healthcheck"]
ENTRYPOINT ["/patient-router"]

Сравнение размеров: образ на golang:alpine занимает 380 МБ, на ubuntu:22.04 - 820 МБ, итоговый scratch-образ - 14 МБ. Разница критична для скорости загрузки в кластер и занимаемого дискового пространства на узлах.

Файл .dockerignore исключает из контекста сборки тесты, документацию и Git-историю:

*.md
.git
.gitignore
*_test.go
docs/
helm/

Запуск от непривилегированного пользователя (UID 1001) блокирует повышение привилегий внутри контейнера. Директива HEALTHCHECK даёт Kubernetes информацию о готовности процесса - без неё kubelet не сможет определить, завис ли сервис после старта.

Сборка и тегирование образа:

docker build -t patient-router:1.0.0 .
docker tag patient-router:1.0.0 registry.example.com/patient-router:1.0.0
docker push registry.example.com/patient-router:1.0.0

Для production-окружения обязательно используйте собственный registry. Если вы разворачиваете инфраструктуру в облаке, Timeweb Cloud предоставляет управляемый Kubernetes с интегрированным container registry и балансировщиком нагрузки.

Создание Helm-чарта для микросервиса

Helm-чарт упаковывает все Kubernetes-манифесты в шаблоны с параметризацией. Структура чарта для patient-router:

patient-router/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-prod.yaml
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    ├── hpa.yaml
    └── servicemonitor.yaml

Файл Chart.yaml содержит метаданные и версию чарта. В values.yaml вынесены параметры по умолчанию: количество реплик, образ, лимиты ресурсов, хост для Ingress. Шаблон deployment.yaml использует эти значения через синтаксис {{ .Values.replicaCount }}.

Пример параметризованного Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app: patient-router
  template:
    metadata:
      labels:
        app: patient-router
    spec:
      containers:
        - name: patient-router
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: {{ .Values.resources.requests.cpu }}
              memory: {{ .Values.resources.requests.memory }}
            limits:
              cpu: {{ .Values.resources.limits.cpu }}
              memory: {{ .Values.resources.limits.memory }}

Разделение конфигураций для Dev и Prod окружений

Вместо дублирования чарта используйте несколько values-файлов. Базовый values.yaml содержит общие настройки, values-dev.yaml и values-prod.yaml переопределяют специфичные для окружения параметры.

Dev-окружение - одна реплика, минимальные ресурсы, внутренний Ingress-хост:

# values-dev.yaml
replicaCount: 1
image:
  tag: latest
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi
ingress:
  host: patient-router-dev.internal

Prod-окружение - три реплики, гарантированные ресурсы, публичный хост с TLS:

# values-prod.yaml
replicaCount: 3
image:
  tag: 1.0.0
resources:
  requests:
    cpu: 500m
    memory: 256Mi
  limits:
    cpu: 1000m
    memory: 512Mi
ingress:
  host: patient-router.example.com
  tls:
    enabled: true
    secretName: patient-router-tls

Установка в разные окружения одной командой:

# Dev
helm install patient-router ./patient-router -f values-dev.yaml -n dev

# Prod
helm install patient-router ./patient-router -f values-prod.yaml -n production

Такой подход исключает рассинхронизацию шаблонов между окружениями - структура Deployment, Service и Ingress всегда идентична, меняются только значения параметров.

Развертывание в Kubernetes и настройка балансировщика

После установки чарта проверяем состояние подов и сервисов:

kubectl get pods -n production -l app=patient-router
kubectl get svc -n production
kubectl get ingress -n production

Ingress-контроллер принимает внешний трафик и направляет его к Service patient-router. Манифест Ingress с правилами маршрутизации:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Release.Name }}
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - {{ .Values.ingress.host }}
      secretName: {{ .Values.ingress.tls.secretName }}
  rules:
    - host: {{ .Values.ingress.host }}
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ .Release.Name }}
                port:
                  number: 8080

Для bare-metal кластеров без облачного балансировщика используйте MetalLB. Он назначает внешний IP из пула адресов для Service типа LoadBalancer, к которому привязан Ingress-контроллер. Настройка пула адресов в MetalLB:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.1.200-192.168.1.210

Подробный разбор настройки Ingress-контроллеров и Service Mesh для production-сред читайте в нашем руководстве по оркестрации динамических приложений.

Проверка работоспособности и отладка

Три команды решают 90% проблем после деплоя. Первая - логи конкретного пода:

kubectl logs -n production deployment/patient-router --tail=50

Вторая - детальное описание пода с событиями:

kubectl describe pod -n production -l app=patient-router

Третья - проверка эндпоинтов Service (должны совпадать с IP подов):

kubectl get endpoints -n production patient-router

Типичные ошибки при деплое:

  • ImagePullBackOff - неверный тег образа или отсутствие доступа к registry. Проверьте imagePullSecrets в Deployment и наличие образа в registry.
  • CrashLoopBackOff - приложение падает после старта. Изучите логи пода, чаще всего причина в неверных переменных окружения или недоступности внешних зависимостей.
  • ErrImagePull - неверный URL registry. Проверьте поле image.repository в values-файле.

Горизонтальное масштабирование и отказоустойчивость

Horizontal Pod Autoscaler автоматически изменяет количество реплик на основе метрик. Базовая настройка по CPU и памяти:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: patient-router
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: patient-router
  minReplicas: 3
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300

Параметр stabilizationWindowSeconds: 300 предотвращает резкое уменьшение реплик при кратковременном спаде нагрузки. HPA опрашивает метрики каждые 15 секунд, но решение о масштабировании принимает на основе среднего значения за окно стабилизации.

PodDisruptionBudget защищает от одновременного отключения всех подов при обслуживании узлов:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: patient-router-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: patient-router

При minAvailable: 2 и трёх репликах кластер никогда не оставит сервис без ответа - даже во время планового drain'а узла минимум два пода продолжат обработку запросов.

Распределение подов по узлам кластера

PodAntiAffinity гарантирует, что реплики не окажутся на одном физическом узле. При отказе узла остальные реплики продолжают работу:

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchLabels:
            app: patient-router
        topologyKey: kubernetes.io/hostname

Правило requiredDuringSchedulingIgnoredDuringExecution жёсткое - планировщик не разместит под на узле, где уже есть реплика с такой же меткой. Для кластеров из трёх узлов это даёт равномерное распределение: по одному поду на узел.

Мониторинг и алертинг для микросервиса

Микросервис экспортирует метрики через эндпоинт /metrics в формате Prometheus. Ключевые метрики для маршрутизации пациентов:

# HELP patient_routing_latency_seconds Время выполнения маршрутизации
# TYPE patient_routing_latency_seconds histogram
patient_routing_latency_seconds_bucket{le="0.01"} 1523
patient_routing_latency_seconds_bucket{le="0.05"} 3842
patient_routing_latency_seconds_bucket{le="0.1"} 4102
patient_routing_latency_seconds_bucket{le="+Inf"} 4120
patient_routing_latency_seconds_sum 184.5
patient_routing_latency_seconds_count 4120

# HELP patient_routing_errors_total Общее количество ошибок маршрутизации
# TYPE patient_routing_errors_total counter
patient_routing_errors_total{reason="rule_not_found"} 12
patient_routing_errors_total{reason="timeout"} 3

# HELP patient_routing_requests_total Общее количество запросов
# TYPE patient_routing_requests_total counter
patient_routing_requests_total{status="200"} 4089
patient_routing_requests_total{status="404"} 12
patient_routing_requests_total{status="500"} 19

ServiceMonitor сообщает Prometheus, с каких подов собирать метрики:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: patient-router
spec:
  selector:
    matchLabels:
      app: patient-router
  endpoints:
    - port: http
      path: /metrics
      interval: 15s

Для визуализации используйте Grafana с дашбордом, который показывает: latency по перцентилям (p50, p95, p99), rate ошибок в минуту, количество реплик и загрузку CPU. Готовый JSON дашборда доступен в репозитории чарта.

Примеры правил алертинга для маршрутизации

PrometheusRule определяет условия, при которых отправляется уведомление. Два критичных алерта для сервиса маршрутизации пациентов:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: patient-router-alerts
spec:
  groups:
    - name: patient-router
      rules:
        - alert: PatientRoutingLatencyHigh
          expr: |
            histogram_quantile(0.95,
              rate(patient_routing_latency_seconds_bucket[5m])
            ) > 0.2
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "Высокая задержка маршрутизации пациентов"
            description: "95-й перцентиль задержки превышает 200 мс в течение 5 минут. Текущее значение: {{ $value }}с"

        - alert: PatientRoutingErrorRateHigh
          expr: |
            rate(patient_routing_errors_total[5m]) > 0.05
          for: 3m
          labels:
            severity: critical
          annotations:
            summary: "Высокий уровень ошибок маршрутизации"
            description: "Количество ошибок превышает 0.05 в секунду. Проверьте доступность downstream-сервисов и правила маршрутизации."

Пороговые значения подобраны для медицинской системы, где задержка маршрутизации выше 200 мс неприемлема. Алерт PatientRoutingErrorRateHigh срабатывает за три минуты - этого достаточно, чтобы отфильтровать кратковременные всплески, но не пропустить реальную деградацию. Настройте интеграцию Alertmanager с Telegram, Slack или PagerDuty для мгновенного оповещения дежурной смены.

Полный цикл observability - от сбора логов до настройки дашбордов - разобран в нашем руководстве по мониторингу и логированию Docker в production. Для комплексного взгляда на DevOps-практики 2026 года обратитесь к обзору актуальных инструментов и конфигураций.

Развёрнутый кластер с мониторингом и алертингом готов к приёму production-трафика. Метрики подтверждают корректность маршрутизации, HPA держит нагрузку, PodDisruptionBudget страхует от отказов инфраструктуры. При росте объёмов достаточно увеличить maxReplicas в HPA и добавить узлы в кластер - архитектура масштабируется линейно.

Поделиться:
Сохранить гайд? В закладки браузера