Миграция VM → Docker/K8s: контейнеризация с гарантией

Перенос приложения с виртуальных машин в контейнеры часто оборачивается неожиданными проблемами: legacy-код, скрытые зависимости и окружение, которое «работало только на сервере». Мы помогаем пройти этот путь без боли — проводим аудит, контейнеризируем сервисы и разворачиваем их в Docker или Kubernetes. Наша команда реализует проект под ключ: от инвентаризации VM до запуска в оркестрации, обеспечивая стабильную работу и последующую поддержку.
Часто задаваемые вопросы

Миграция с виртуальных машин на контейнеры (VM → Docker/K8s)

Перенести приложение с VM на контейнеры — задача, с которой рано или поздно сталкивается каждая команда. Мы в своей практике видели, как legacy-приложения, работавшие годами на выделенных серверах, ломались в контейнерах из-за неочевидных зависимостей. Наши инженеры помогли десяткам клиентов сократить время деплоя в 3 раза и исключить проблемы с окружением. Ниже — пошаговая методология, проверенная на проектах с 50+ микросервисами. Сравнение VM и контейнеров: контейнеры стартуют за 1-2 секунды против 30-60 секунд у VM, а TCO снижается на 30-50%.

Почему контейнеризация выгоднее VM?

VM требует установки ОС, настройки репозиториев, обновлений ядра — вручную или через Ansible. Контейнер собирается один раз и запускается где угодно. По нашим данным, разработка на контейнерах ускоряет цикл CI/CD на 40%. В Kubernetes вы получаете autoscaling (HPA/VPA), self-healing и rolling updates «из коробки». Плюс экономия на лицензиях ОС — до 50% затрат на инфраструктуру.

С чего начинается инвентаризация VM?

Перед написанием первого Dockerfile нужно понять что именно запущено на VM. Часто оказывается, что на одной «прод-машине» живут: nginx как reverse proxy, само приложение на Node.js, PostgreSQL, Redis, cron-задача для бэкапов и локальный почтовый агент Postfix. Это семь отдельных контейнеров, если делать правильно.

# Что запущено на VM
systemctl list-units --type=service --state=running
ps auxf | grep -v grep

# Какие порты слушают
ss -tlnp

# Какие крон-задачи
crontab -l
ls /etc/cron.d/ /etc/cron.daily/

# Зависимости приложения (пример для Node.js)
cat package.json | jq '.dependencies | keys[]'
node --version; npm --version

Результат инвентаризации — список всего что нужно перенести, с пониманием зависимостей между компонентами.

Dockerfile: типичные грабли при контейнеризации

Логирование. Приложение пишет в /var/log/app/access.log. В контейнере логи должны идти в stdout/stderr — иначе их не подхватит Docker daemon, Fluentd или Loki. Вариант: добавить симлинк ln -sf /dev/stdout /var/log/app/access.log в Dockerfile. Вариант лучше — исправить конфигурацию логирования в самом приложении.

Конфигурация через переменные окружения. Хардкоженный путь /etc/app/config.yaml нужно заменить на ENV-переменные или volume mount. Twelve-Factor App — не просто модная концепция, а требование для нормальной работы в K8s.

Пользователи и права. Приложение запускается от root на VM. В контейнере это security risk. Нужно добавить непривилегированного пользователя:

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]

Init-система. Приложение использует supervisord или запускает дочерние процессы напрямую. В контейнере PID 1 должен корректно обрабатывать SIGTERM и перенаправлять сигналы дочерним процессам. Если приложение не умеет этого — нужен tini как init:

RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]

Stateful данные. Приложение пишет загруженные файлы в /var/uploads/. В контейнере это нужно вынести в PersistentVolume (K8s) или монтировать через -v (Docker). Данные не должны жить внутри контейнера.

Как выглядит типовой манифест для Kubernetes?

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-backend
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        fsGroup: 1001
      containers:
        - name: backend
          image: registry.internal/app-backend:1.2.3
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database-url
            - name: REDIS_URL
              value: "redis://redis-service:6379"
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              cpu: 1000m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
          volumeMounts:
            - name: uploads
              mountPath: /app/uploads
      volumes:
        - name: uploads
          persistentVolumeClaim:
            claimName: app-uploads-pvc

Отсутствие resources — одна из самых частых ошибок при первом деплое в K8s. Без limits pod может сожрать всю память на ноде, без requests scheduler не может корректно распределить поды.

Cron-задачи → CronJob

Системный cron переносится в K8s CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  schedule: "0 2 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: registry.internal/db-backup:latest
              env:
                - name: DATABASE_URL
                  valueFrom:
                    secretKeyRef:
                      name: app-secrets
                      key: database-url

concurrencyPolicy: Forbid предотвращает запуск нового job если предыдущий ещё выполняется.

Как обеспечить надёжность при миграции?

Мы гарантируем, что после перехода все ключевые метрики SLO будут под контролем. Настройка мониторинга Prometheus+Grafana входит в стандартный пакет. Все конфиги версионируются в Git — это устраняет риск потери изменений. Наша команда — сертифицированные Kubernetes администраторы и облачные инженеры с 10+ лет опыта. Мы уже провели более 30 успешных миграций для fintech, e-commerce и SaaS-продуктов.

Критерий VM Контейнеры (Docker/K8s)
Время запуска 30-60 сек 1-2 сек
Использование ресурсов Тяжелая ОС, изоляция через гипервизор Легковесный процесс, разделяемое ядро
Стоимость (TCO) Выше из-за лицензий и избыточного резервирования Ниже на 30-50% за счёт compact placement

Что входит в нашу работу по миграции

  • Аудит VM-инфраструктуры и схема зависимостей
  • Dockerfile для каждого микросервиса с multi-stage build
  • Helm-чарты для деплоя в K8s
  • Настройка CI/CD (GitLab CI / GitHub Actions)
  • Мониторинг и алертинг (Prometheus, Loki, Grafana)
  • Документация по эксплуатации и runbook
  • Передача знаний команде заказчика (1-2 дня обучения)

Какую стратегию миграции выбрать?

Правильная последовательность для production-приложения:

  1. Контейнеризация — написать Dockerfile, проверить локально
  2. CI для образа — сборка и push в registry при каждом merge в main
  3. Деплой в staging на K8s, прогон тестов
  4. Параллельный запуск — старая VM и новые поды работают одновременно, трафик на VM
  5. Переключение трафика — Ingress или DNS переключается на K8s Service
  6. Наблюдение — неделя в production, сравнение метрик с baseline
  7. Вывод VM — после подтверждения стабильности

Никогда не переключайте трафик и одновременно не выключайте VM. Это кажется очевидным, но в стрессе перед дедлайном об этом забывают.

Готовы начать миграцию? Получите консультацию нашего инженера.

Сроки

Сложность приложения Срок контейнеризации Срок миграции в K8s
Простое (1 процесс, без state) 1–2 дня 2–3 дня
Среднее (2–3 компонента, есть state) 3–5 дней 1 неделя
Сложное (5+ компонентов, legacy зависимости) 1–2 недели 2–4 недели

Первоначальный аудит VM-инфраструктуры и оценка — 1–2 дня. Итоговые сроки определяются после него. Закажите аудит VM-инфраструктуры — мы подготовим план миграции за 1–2 дня.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    816
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1141