Задача клиента: даунтайм при каждом обслуживании
Летом 2025 года к нам обратился небольшой хостинг-провайдер HostLine из Екатеринбурга. Компания предоставляла VPS-хостинг для малого бизнеса: 50 клиентских виртуальных серверов на базе Proxmox VE, размещённых на 5 физических нодах в двух стойках дата-центра.
Критическая проблема: диски виртуальных машин хранились локально на каждой ноде. Это означало:
- Нет live migration — перенос VM между нодами требовал остановки, копирования диска и запуска
- Каждое обновление = даунтайм — обновление ядра ноды требовало остановки всех VM на ней (до 10 штук)
- Нет отказоустойчивости хранилища — при выходе диска из строя клиентские данные терялись
- Неравномерная утилизация — на одних нодах 90% диска занято, на других 30%
«Каждый месяц — окно обслуживания. Мы предупреждаем клиентов за неделю, но они всё равно жалуются. Двое ушли к конкурентам, которые обещают 99.99% uptime» — директор HostLine.
За последний год было 3 инцидента с дисками. В одном случае RAID-массив на ноде деградировал, и восстановление заняло 8 часов. Клиенты подняли волну негатива на хостинг-форумах. Инженеры АйТи Фреш предложили развернуть Ceph — распределённое хранилище, которое решает все описанные проблемы разом.
Почему Ceph, а не NFS или GlusterFS
Мы рассмотрели три варианта распределённого хранилища:
| Критерий | NFS | GlusterFS | Ceph |
|---|
| Block storage (RBD для VM) | Нет | Через gluster-block | Нативный |
| Интеграция с Proxmox | Есть, но медленно | Ограниченная | Нативная, в ядре |
| Live migration | Работает, но с лагами | Ненадёжно | Бесшовно |
| Производительность IOPS | Низкая (NFS overhead) | Средняя | Высокая (kernel RBD) |
| Самовосстановление | Нет | Частичное | Полное автоматическое |
| Масштабирование | Вертикальное | Горизонтальное | Горизонтальное |
Ceph победил по всем критериям для нашего кейса. Нативная интеграция с Proxmox VE означала, что после развёртывания Ceph live migration будет работать «из коробки» — без дополнительных плагинов и костылей.
Архитектура Ceph для хостинга
Мы спроектировали кластер с учётом существующего оборудования клиента (5 нод) и бюджета:
# Архитектура Ceph-кластера
# Ноды (5 серверов, dual Xeon E5-2680v4, 128 GB RAM)
Node1 (10.0.10.1): MON + MGR + OSD × 4 (4 × 2TB NVMe)
Node2 (10.0.10.2): MON + MGR + OSD × 4 (4 × 2TB NVMe)
Node3 (10.0.10.3): MON + OSD × 4 (4 × 2TB NVMe)
Node4 (10.0.10.4): OSD × 4 (4 × 2TB NVMe)
Node5 (10.0.10.5): OSD × 4 (4 × 2TB NVMe)
# Сеть
Public network: 10.0.10.0/24 (клиентский трафик, 10 Gbit)
Cluster network: 10.0.20.0/24 (репликация Ceph, 10 Gbit, отдельный VLAN)
# Итого
OSD: 20 дисков × 2 TB = 40 TB raw
Usable (replication × 3): ~13 TB
Usable (replication × 2): ~20 TB
Мы выбрали фактор репликации 3 для пула VM-дисков (любые 2 ноды могут выйти из строя без потери данных) и фактор 2 для пула бэкапов (экономия места при допустимом риске).
Развёртывание Ceph через cephadm
Cephadm — современный инструмент развёртывания Ceph, пришедший на смену ceph-deploy и ceph-ansible. Он управляет демонами Ceph через контейнеры (Podman/Docker) и SSH.
Подготовка нод и сетевой инфраструктуры
Перед развёртыванием мы подготовили все 5 серверов:
# На всех нодах (Node1-Node5):
# 1. Обновляем систему
sudo apt-get update && sudo apt-get upgrade -y
# 2. Настраиваем NTP (критично для Ceph!)
sudo apt-get install -y chrony
sudo systemctl enable chrony
chronyc tracking
# Reference ID : A29FC801 (ntp1.vniiftri.ru)
# System time : 0.000000231 seconds fast of NTP time
# 3. Настраиваем отдельную сеть для кластерного трафика
# /etc/netplan/01-cluster.yaml
sudo tee /etc/netplan/01-cluster.yaml > /dev/null <<'EOF'
network:
version: 2
ethernets:
ens4:
addresses:
- 10.0.20.X/24 # X = номер ноды (1-5)
mtu: 9000 # Jumbo frames для производительности
EOF
sudo netplan apply
# 4. Проверяем MTU 9000 между нодами
ping -M do -s 8972 10.0.20.2
# PING 10.0.20.2 (10.0.20.2) 8972(9000) bytes of data.
# 8980 bytes from 10.0.20.2: icmp_seq=1 ttl=64 time=0.154 ms
# 5. Настраиваем SSH без пароля с Node1 на все ноды
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
for i in 1 2 3 4 5; do
ssh-copy-id root@10.0.10.$i
done
Инициализация кластера Ceph
Cephadm bootstrap создаёт первый монитор и менеджер на Node1:
# На Node1: установка cephadm
curl --silent --remote-name --location \
https://download.ceph.com/rpm-squid/el9/noarch/cephadm
chmod +x cephadm
sudo mv cephadm /usr/local/bin/
# Bootstrap кластера
sudo cephadm bootstrap \
--mon-ip 10.0.10.1 \
--cluster-network 10.0.20.0/24 \
--initial-dashboard-user admin \
--initial-dashboard-password 'S3cur3D4sh!' \
--dashboard-password-noupdate \
--allow-fqdn-hostname
# Вывод:
# Ceph Dashboard is now available at:
# URL: https://node1:8443/
# User: admin
# Password: S3cur3D4sh!
# You can access the Ceph CLI with:
# sudo /usr/local/bin/cephadm shell -- ceph -s
# Установка CLI-утилит на хост
sudo cephadm install ceph-common
# Проверяем статус кластера
sudo ceph -s
# cluster:
# id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
# health: HEALTH_WARN
# OSD count 0 < osd_pool_default_size 3
#
# services:
# mon: 1 daemons, quorum node1
# mgr: node1.abcdef(active)
Добавление нод и OSD
Далее мы добавили остальные ноды и диски в кластер:
# Добавляем ноды в кластер
sudo ceph orch host add node2 10.0.10.2
sudo ceph orch host add node3 10.0.10.3
sudo ceph orch host add node4 10.0.10.4
sudo ceph orch host add node5 10.0.10.5
# Назначаем роли мониторов (нечётное количество — 3)
sudo ceph orch apply mon --placement="node1,node2,node3"
# Назначаем роли менеджеров (2 для отказоустойчивости)
sudo ceph orch apply mgr --placement="node1,node2"
# Проверяем, что ноды добавлены
sudo ceph orch host ls
# HOST ADDR LABELS STATUS
# node1 10.0.10.1
# node2 10.0.10.2
# node3 10.0.10.3
# node4 10.0.10.4
# node5 10.0.10.5
# Смотрим доступные диски
sudo ceph orch device ls --wide
# HOST PATH TYPE SIZE AVAIL
# node1 /dev/nvme0n1 ssd 2.0T Yes
# node1 /dev/nvme1n1 ssd 2.0T Yes
# node1 /dev/nvme2n1 ssd 2.0T Yes
# node1 /dev/nvme3n1 ssd 2.0T Yes
# ... (ещё 16 дисков на нодах 2-5)
# Добавляем все доступные диски как OSD
sudo ceph orch apply osd --all-available-devices
# Ждём 2-3 минуты и проверяем
sudo ceph osd tree
# ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT
# -1 36.37999 root default
# -3 7.27600 host node1
# 0 ssd 1.81900 osd.0 up 1.00000
# 1 ssd 1.81900 osd.1 up 1.00000
# 2 ssd 1.81900 osd.2 up 1.00000
# 3 ssd 1.81900 osd.3 up 1.00000
# -5 7.27600 host node2
# ... (ещё 16 OSD)
# Финальная проверка здоровья
sudo ceph -s
# cluster:
# health: HEALTH_OK
#
# services:
# mon: 3 daemons, quorum node1,node2,node3
# mgr: node1.abcdef(active, standbys: node2.ghijkl)
# osd: 20 osds: 20 up, 20 in
Настройка пулов и интеграция с Proxmox
После развёртывания кластера нужно создать пулы хранения и подключить их к Proxmox VE.
Создание RADOS-пулов с CRUSH-правилами
Мы создали три пула для разных задач:
# Пул для VM-дисков (replication factor 3)
sudo ceph osd pool create vm-disks 128
sudo ceph osd pool set vm-disks size 3
sudo ceph osd pool set vm-disks min_size 2
sudo ceph osd pool application enable vm-disks rbd
# Пул для ISO-образов и шаблонов (replication factor 2)
sudo ceph osd pool create templates 32
sudo ceph osd pool set templates size 2
sudo ceph osd pool set templates min_size 1
sudo ceph osd pool application enable templates rbd
# Пул для бэкапов (replication factor 2, erasure coding в будущем)
sudo ceph osd pool create backups 64
sudo ceph osd pool set backups size 2
sudo ceph osd pool set backups min_size 1
sudo ceph osd pool application enable backups rbd
# Инициализируем RBD на пулах
sudo rbd pool init vm-disks
sudo rbd pool init templates
sudo rbd pool init backups
# Проверяем пулы
sudo ceph osd lspools
# 1 vm-disks
# 2 templates
# 3 backups
# Проверяем распределение placement groups
sudo ceph pg stat
# 224 pgs: 224 active+clean; 0 B data, 945 MiB used, 36.4 TiB / 36.4 TiB avail
Интеграция Ceph с Proxmox VE
Proxmox VE имеет нативную поддержку Ceph RBD. Мы создали пользователя и настроили подключение на всех Proxmox-нодах:
# Создаём пользователя для Proxmox
sudo ceph auth get-or-create client.proxmox \
mon 'profile rbd' \
osd 'profile rbd pool=vm-disks, profile rbd pool=templates, profile rbd pool=backups' \
mgr 'profile rbd pool=vm-disks, profile rbd pool=templates, profile rbd pool=backups'
# Получаем ключ
sudo ceph auth get client.proxmox
# [client.proxmox]
# key = AQBz1abc2DEFghi3JKLmno4PQRstu5VWXyz6==
# На каждой Proxmox-ноде: сохраняем ключ
sudo mkdir -p /etc/pve/priv/ceph
echo 'AQBz1abc2DEFghi3JKLmno4PQRstu5VWXyz6==' > /etc/pve/priv/ceph/vm-disks.keyring
# Копируем ceph.conf на все Proxmox-ноды
sudo scp /etc/ceph/ceph.conf root@proxmox-node1:/etc/ceph/
sudo scp /etc/ceph/ceph.conf root@proxmox-node2:/etc/ceph/
# ... (на все 5 нод)
Добавляем хранилище через Proxmox CLI:
# Добавляем Ceph RBD storage в Proxmox
pvesm add rbd ceph-vm \
--pool vm-disks \
--monhost 10.0.10.1,10.0.10.2,10.0.10.3 \
--username proxmox \
--content images,rootdir \
--krbd 1
pvesm add rbd ceph-templates \
--pool templates \
--monhost 10.0.10.1,10.0.10.2,10.0.10.3 \
--username proxmox \
--content iso,vztmpl
pvesm add rbd ceph-backups \
--pool backups \
--monhost 10.0.10.1,10.0.10.2,10.0.10.3 \
--username proxmox \
--content backup
# Проверяем доступность хранилищ
pvesm status
# Name Type Status Total Used Avail
# ceph-vm rbd active 13421772 0 13421772
# ceph-templates rbd active 20132659 0 20132659
# ceph-backups rbd active 20132659 0 20132659
# local dir active 102400 5120 97280
Миграция существующих VM на Ceph
Ключевой этап — перенос 50 клиентских VM с локальных дисков на Ceph RBD. Мы делали это поочерёдно, в ночные окна:
# Миграция диска VM с локального хранилища на Ceph
# Для каждой VM (пример: VM ID 101)
# 1. Проверяем текущее хранилище
qm config 101 | grep -E '^(scsi|virtio|ide|sata)'
# scsi0: local-lvm:vm-101-disk-0,size=50G
# 2. Переносим диск на Ceph (online, но с замедлением I/O)
qm move-disk 101 scsi0 ceph-vm --delete 1
# Прогресс:
# Moving disk 'local-lvm:vm-101-disk-0' to 'ceph-vm'
# creating full clone of drive scsi0 (local-lvm:vm-101-disk-0)
# Logical volume "vm-101-disk-0" successfully removed.
# drive mirror is complete
# 3. Проверяем, что диск теперь на Ceph
qm config 101 | grep scsi0
# scsi0: ceph-vm:vm-101-disk-0,size=50G
# 4. Проверяем RBD-образ в Ceph
rbd ls vm-disks
# vm-101-disk-0
rbd info vm-disks/vm-101-disk-0
# rbd image 'vm-101-disk-0':
# size 50 GiB in 12800 objects
# order 22 (4 MiB objects)
# block_name_prefix: rbd_data.abc123
# format: 2
# features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
Для автоматизации мы написали скрипт массовой миграции:
#!/bin/bash
# migrate-all-vms.sh — перенос всех VM на Ceph
for VMID in $(qm list | awk 'NR>1 {print $1}'); do
echo "=== Миграция VM $VMID ==="
DISKS=$(qm config $VMID | grep -oP '(scsi|virtio)\d+' | sort -u)
for DISK in $DISKS; do
STORAGE=$(qm config $VMID | grep "^${DISK}:" | grep -oP '^[^:]+:\K[^,]+')
if [[ "$STORAGE" == local* ]]; then
echo " Перенос $DISK с $STORAGE на ceph-vm"
qm move-disk $VMID $DISK ceph-vm --delete 1
echo " $DISK перенесён успешно"
else
echo " $DISK уже на Ceph, пропускаем"
fi
done
echo ""
done
Live migration: нулевой даунтайм при обслуживании
После переноса всех VM на Ceph RBD стала доступна live migration — перенос работающей VM между нодами без остановки.
Тестирование live migration
Мы провели тестирование в присутствии клиента:
# Проверяем, на какой ноде работает VM 101
qm status 101 --verbose | grep node
# node: node1
# Запускаем непрерывный ping к VM изнутри
# (с отдельной машины)
ping -i 0.1 10.0.1.101
# Выполняем live migration на node3
qm migrate 101 node3 --online
# Вывод:
# Starting migration of VM 101 to node 'node3'
# Volume 'scsi0': skipped (on shared storage 'ceph-vm')
# migration active, transferred 256 MiB of 8192 MiB VM RAM...
# migration active, transferred 1024 MiB of 8192 MiB VM RAM...
# migration active, dirty: 12 MiB, transferred 7680 MiB...
# migration completed, downtime: 45 ms
# TASK OK
# Результат ping:
# 64 bytes from 10.0.1.101: icmp_seq=842 time=0.3 ms
# 64 bytes from 10.0.1.101: icmp_seq=843 time=0.3 ms
# 64 bytes from 10.0.1.101: icmp_seq=844 time=12.1 ms ← момент переключения
# 64 bytes from 10.0.1.101: icmp_seq=845 time=0.4 ms
# 64 bytes from 10.0.1.101: icmp_seq=846 time=0.3 ms
Даунтайм при live migration: 45 миллисекунд. Даже SSH-сессия не прерывалась.
Автоматическая эвакуация при обслуживании ноды
Мы написали скрипт для безопасного обслуживания ноды — он мигрирует все VM на соседние ноды:
#!/bin/bash
# evacuate-node.sh — эвакуация всех VM с ноды
NODE=$1
if [ -z "$NODE" ]; then
echo "Использование: $0 "
exit 1
fi
# Получаем список VM на ноде
VMs=$(pvesh get /nodes/${NODE}/qemu --output-format json | jq -r '.[].vmid')
VM_COUNT=$(echo "$VMs" | wc -w)
echo "Найдено $VM_COUNT VM на ноде $NODE"
echo "Начинаем эвакуацию..."
# Получаем список целевых нод (все ноды кроме текущей)
TARGET_NODES=($(pvesh get /nodes --output-format json | jq -r '.[].node' | grep -v "$NODE"))
TARGET_COUNT=${#TARGET_NODES[@]}
IDX=0
for VMID in $VMs; do
TARGET=${TARGET_NODES[$((IDX % TARGET_COUNT))]}
echo " VM $VMID → $TARGET (live migration)"
qm migrate $VMID $TARGET --online 2>&1 | tail -1
IDX=$((IDX + 1))
done
echo "Эвакуация завершена. Нода $NODE свободна для обслуживания."
echo "После обслуживания запустите: $0 --return $NODE"
# Пример использования
./evacuate-node.sh node2
# Найдено 10 VM на ноде node2
# Начинаем эвакуацию...
# VM 101 → node1 (live migration)
# migration completed, downtime: 38 ms
# VM 102 → node3 (live migration)
# migration completed, downtime: 52 ms
# ...
# Эвакуация завершена. Нода node2 свободна для обслуживания.
# Теперь можно безопасно обновить ядро на node2
ssh root@node2 'apt-get upgrade -y && reboot'
Мониторинг и обслуживание Ceph-кластера
Ceph-кластер требует постоянного мониторинга. Мы настроили несколько инструментов.
Ceph Dashboard и Prometheus
Ceph имеет встроенный Dashboard на порту 8443. Дополнительно мы включили Prometheus-экспорт:
# Включаем модуль Prometheus в Ceph MGR
sudo ceph mgr module enable prometheus
# Проверяем метрики
curl -s http://10.0.10.1:9283/metrics | head -20
# # HELP ceph_cluster_total_bytes Cluster total bytes
# ceph_cluster_total_bytes 4.0007767e+13
# # HELP ceph_cluster_total_used_bytes Cluster total used bytes
# ceph_cluster_total_used_bytes 1.2345678e+12
# Ключевые алерты для Ceph в Prometheus
# /etc/prometheus/rules/ceph_alerts.yml
# /etc/prometheus/rules/ceph_alerts.yml
groups:
- name: ceph
rules:
- alert: CephHealthWarning
expr: ceph_health_status == 1
for: 5m
labels:
severity: warning
annotations:
summary: "Ceph кластер в состоянии HEALTH_WARN"
- alert: CephHealthError
expr: ceph_health_status == 2
for: 1m
labels:
severity: critical
annotations:
summary: "Ceph кластер в состоянии HEALTH_ERR"
- alert: CephOSDDown
expr: ceph_osd_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "OSD {{ $labels.ceph_daemon }} не работает"
- alert: CephClusterNearFull
expr: ceph_cluster_total_used_bytes / ceph_cluster_total_bytes > 0.75
for: 10m
labels:
severity: warning
annotations:
summary: "Ceph кластер заполнен на {{ $value | humanizePercentage }}"
Performance tuning для NVMe
Для максимальной производительности на NVMe-дисках мы применили ряд оптимизаций:
# Оптимизация Ceph для NVMe
# Устанавливаем в ceph.conf через cephadm
sudo ceph config set osd bluestore_cache_size_ssd 4294967296 # 4 GB cache per OSD
sudo ceph config set osd bluestore_min_alloc_size_ssd 4096
sudo ceph config set osd osd_memory_target 4294967296 # 4 GB RAM per OSD
sudo ceph config set osd osd_op_num_threads_per_shard_ssd 2
# Увеличиваем количество PG для лучшего распределения
sudo ceph osd pool set vm-disks pg_num 256
sudo ceph osd pool set vm-disks pgp_num 256
# Включаем compression для пула бэкапов
sudo ceph osd pool set backups compression_mode aggressive
sudo ceph osd pool set backups compression_algorithm zstd
# Бенчмарк после оптимизации
sudo rados bench -p vm-disks 30 write --no-cleanup
# Total writes made: 8942
# Write size: 4194304
# Object size: 4194304
# Bandwidth (MB/sec): 1192.34
# Average IOPS: 298
# Stddev IOPS: 42
# Max IOPS: 367
# Min IOPS: 198
sudo rados bench -p vm-disks 30 rand
# Total reads made: 15234
# Read size: 4194304
# Average IOPS: 507
# Bandwidth (MB/sec): 2030.12
Результаты бенчмарка: 1.2 GB/s на запись и 2 GB/s на чтение — более чем достаточно для 50 VPS с типичными нагрузками хостинга.
Результаты внедрения
Проект занял 3 недели: 1 неделя на развёртывание кластера, 1 неделя на миграцию VM, 1 неделя на тестирование и оптимизацию. Вот что получил хостинг-провайдер HostLine после внедрения Ceph специалистами АйТи Фреш:
| Метрика | До Ceph | После Ceph |
|---|
| Даунтайм при обслуживании ноды | 30–60 минут (остановка VM) | 0 (live migration за 45 мс) |
| Время восстановления при отказе диска | 4–8 часов (ручное) | Автоматическое (rebalance) |
| Окна обслуживания в месяц | 1–2 (плановые) | 0 (всё прозрачно для клиентов) |
| Потеря данных при отказе диска | Возможна (RAID rebuild) | Исключена (3× репликация) |
| Утилизация дискового пространства | 30–90% (неравномерно) | Равномерная по всем нодам |
| Отток клиентов за квартал | 3–5 клиентов | 0 клиентов |
Через месяц после внедрения на Node4 вышел из строя один NVMe-диск. Ceph автоматически запустил recovery: данные с повреждённого OSD были восстановлены на оставшихся дисках за 40 минут. Ни один клиент не заметил проблемы. Инженеры HostLine заменили диск в рабочее время без какого-либо воздействия на клиентские VM.
«До Ceph каждый выход диска — это ночной аврал. Сейчас мы получаем уведомление в Telegram, заказываем замену и меняем диск в удобное время. Клиенты даже не знают, что что-то произошло» — системный администратор HostLine.