Міграція 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