Виртуальная машина в AWS может иметь два принципиально разных типа дисков: сетевой EBS-том и локальный instance store, физически припаянный к хосту, где запущена виртуалка. Разница между ними не только в цене и скорости: это разница в модели отказа. Данные на instance store исчезают при остановке инстанса, выходе из строя хоста или изменении типа инстанса. Данные на EBS живут независимо от виртуалки, переживают stop/start, детачатся и пристёгиваются к другому инстансу. От того, насколько осознанно сделан выбор, зависит не только быстродействие, но и то, будешь ли ты восстанавливать базу из бэкапа в воскресенье ночью.
Что за диски и почему они так отличаются
EBS это блочное сетевое хранилище. Том живёт в одной зоне доступности, данные реплицируются внутри неё, а к инстансу том подключён по сети. Современные типы: gp3 (базовый универсальный SSD, до 16 000 IOPS и 1000 МБ/с пропускной способности, оба параметра настраиваются отдельно от размера), io2 Block Express (до 256 000 IOPS, устойчивая задержка субмиллисекундного уровня, дорого), st1 (throughput-оптимизированный HDD, до 500 МБ/с) и sc1 (холодный HDD, дешевле всех). Раньше у gp2 производительность росла с размером тома (3 IOPS на гигабайт, с пиком до 16 000 на больших томах), и это была классическая ловушка: маленький том давал низкие IOPS, и люди покупали 1 ТБ вместо 100 ГБ только ради производительности. С gp3 эта ловушка исчезла - IOPS и throughput покупаются отдельно.
Instance store это NVMe-диски в самом хосте. Они быстрее EBS: на современных поколениях вроде i4i или i7i один инстанс с восемью NVMe по 3.75 ТБ выдаёт миллионы IOPS на чтение и задержки порядка десятков микросекунд, что на сетевом EBS просто недостижимо без io2 Block Express с огромным бюджетом. Расплата - эфемерность. Данные теряются при stop инстанса (не reboot - перезагрузку instance store переживает, потому что виртуалка остаётся на том же хосте), при hibernate, при выходе хоста из строя и при изменении типа инстанса, если новый тип не поддерживает нужное число томов.
Отдельный технический момент: объём instance store фиксируется типом инстанса. Нельзя докупить ещё один локальный диск к c5d.large, у него один NVMe на 50 ГБ, и всё. Диски не шифруются KMS из коробки - для чувствительных данных надёжнее держать их на EBS с включённым encryption by default.
Когда instance store оправдан и где он кусается
Классический сценарий - кэш. Redis или Memcached на i4i.large с локальным NVMe обслуживают тяжёлый read-heavy трафик, а при потере ноды кэш пересобирается с master. Instance store удобен и для временных файлов обработки: ffmpeg, перекодирующий видео, временные файлы сортировки в PostgreSQL (work_mem не влезает, и запрос валится в temp files на диск), scratch-пространство для CI-раннеров. Здесь потеря данных безразлична, а скорость первична.
Второй сценарий - распределённые системы с собственной репликацией, где потеря одной ноды не страшна. Kafka с replication factor 3, Cassandra или ScyllaDB с RF=3, Elasticsearch с репликами шардов - все они рассчитаны на то, что нода умрёт, и кластер ребалансирует данные. Именно такие кластеры исторически и ставят на i-семейства: совпадают и скоростные требования, и модель отказоустойчивости.
Где instance store кусается - любые истории "ребутнул и потерял данные" это на самом деле "сделал stop и потерял". На reboot данные живут. Но есть куда более подлые сценарии. AWS периодически планирует обслуживание хоста (scheduled event): виртуалку мигрируют на новый хост, и instance store при этом теряется. Про это есть уведомление в консоли за недели, но кто читает уведомления. Вторая засада - отказ самого хоста: виртуалка перезапустится на здоровом железе, instance store останется на вышедшем из строя. Получается, что любые данные на instance store надо держать реплицированными на уровне приложения и сразу, а не "потом синхронизируем".
Производительность EBS и сюрпризы после ребута
С gp3 производительность довольно предсказуема: вы покупаете фиксированные IOPS и throughput, и получаете их. Но есть нюансы. Первый - baseline на маленьких томах: любой gp3 стабильно держит 3000 IOPS и 125 МБ/с уже с гигабайта размера, выше - за доплату. Второй нюанс - инициализация томов из snapshot. Только что восстановленный из снапшота EBS-том грузит блоки лениво: первое обращение к каждому блоку идёт из S3, и latency первого чтения ощутимо выше. Для базы данных это значит, что первые минуты после restore том работает медленнее обещанного. Решение - разогрев: sudo fio --filename=/dev/xvdf --rw=read --bs=128k --iodepth=32 --ioengine=libaio --direct=1 --name=warm. Есть и примитивный dd if=/dev/xvdf of=/dev/null bs=1M - медленнее, но проще запомнить.
Третий нюанс после ребута - проверка монтирования. EBS-тома лёгкие в управлении, но nofail в /etc/fstab спасает: если том не пристёгнут, boot завершится, и система не повиснет на emergency mode. Типичная строка: UUID=xxxx /data ext4 defaults,nofail 0 2. Четвёртый нюанс - серийные имена устройств. На Nitro-инстансах EBS-диски видны как /dev/nvme1n1, /dev/nvme2n1 и т.д. вне зависимости от того, что написали в Block Device Mapping. Выход - всегда монтировать по UUID или по label через e2label, иначе после добавления нового тома порядок nvme-устройств может съехать, и база запустится с пустым каталогом.
Ещё одна тонкость: производительность EBS привязана к типу самого инстанса. Инстанс имеет EBS-оптимизированную полосу: у t3.medium это 2085 Мбит/с baseline с burst, а у m5.8xlarge полоса для дисков EBS держит 6800 Мбит/с при общей сетевой полосе 10 Гбит/с. Теоретический io2 на 64 000 IOPS не выдаст обещанного, если инстанс не тянет такую полосу. В документации у каждого типа инстанса указан предел EBS bandwidth и IOPS, и его проверяют до того, как платить за дорогой том.
Замер производительности до того, как придёт беда
Выбрать тип диска вслепую это плохая привычка: цифры из документации редко совпадают с тем, что видит конкретный инстанс под вашей нагрузкой. Практичный цикл проверки перед вводом в продакшен выглядит так: на кандидатном типе инстанса с кандидатным томом запускается короткий бенчмарк. Минимальный набор тестов fio описывает реальную картину. Случайное чтение блоками 4k с iodepth 32 показывает IOPS, которые увидит база данных:
fio --name=randread --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --direct=1 --size=10G --numjobs=1 --runtime=60 --group_reporting
Последовательная запись блоками 1M покажет пропускную способность для логов и backup-подобных задач. Тест со случайной записью 4k выявит, есть ли write amplification конкретно для вашей СУБД: поведение записи журнала и flush меняют картину. Замер длится минуту, а сохраняет недели разбирательств.
Отдельно смотрят latency, а не только пропускную способность. Для OLTP-запросов важнее стабильная задержка: средняя и 99-й перцентиль. У io2 Block Express этот параметр субмиллисекундный и стабильный, у gp3 чуть выше и с большим разбросом под конкурентной нагрузкой. fio пишет clat percentiles прямо в отчёт, и именно по столбцу 99.00th решают, тянет ли том требования приложения.
Миграция данных между типами дисков без простоя
Самая частая операция - переезд данных с EBS на instance store и обратно. Делается это через pv и rsync, без выключения приложения, если объём позволяет держать две копии одновременно. Сначала новый диск форматируется и монтируется рядом: mkfs.ext4 /dev/nvme1n1, mount /dev/nvme1n1 /mnt/newdata. Затем идёт первая, грубая, синхронизация при работающем приложении: rsync -aHAX --info=progress2 /var/lib/myapp/ /mnt/newdata/. Эта копия длинная, но приложение продолжает работать. Затем приложение ставится на краткую паузу (maintenance режим или replica lag promotion), делается финальный rsync - и он короткий, потому что тащит только изменения. После этого переключают точку монтирования и поднимают приложение уже с нового диска.
Обратный переезд с instance store на EBS безопаснее по своей природе, потому что источник не пропадёт из-за stop. Но важно помнить про заморозку файловой системы: если приложение активно пишет, rsync без продуманной стратегии может создать несогласованную копию. Для БД используют файловую заморозку fsfreeze или snapshot на уровне приложения, иначе копия будет несогласованной.
Ещё один сценарий - изменение типа EBS-тома на живую. У AWS есть elastic volumes, позволяющие без остановки инстанса поменять gp2 на gp3, поднять IOPS у io2 или увеличить размер. Команда aws ec2 modify-volume делает это онлайн, а уже после завершения модификации (смотрят aws ec2 describe-volumes-modifications) поднимают файловую систему resize2fs или xfs_growfs, в зависимости от ФС. Ограничение: объём уменьшить нельзя, только увеличить. И после увеличения диска следующее изменение у того же тома возможно только через несколько часов - AWS ограничивает частоту модификаций.
Что выбрать для конкретных задач
Складка простая, и удобно сверяться с ней при проектировании:
- Системный том операционной системы - EBS gp3, 20-50 ГБ, никаких сомнений: нужна независимость от жизни инстанса, snapshot-ы и лёгкая замена.
- PostgreSQL или MySQL production - EBS io2 (или gp3 с докупленными IOPS, если нагрузка умеренная), ни в коем случае instance store на одиночной ноде.
- Redis/Memcached в режиме кэша - instance store подходит и экономит, но кластерный режим обязателен.
- Kafka, Cassandra, Elasticsearch с репликацией - instance store ради latency и цены, при RF>=3.
- CI-воркеры и временные сборки - instance store идеален.
- Логи приложений на инстансе - EBS st1, если их вообще хранят локально; по-хорошему логи уезжают в CloudWatch или Loki, а локальный диск маленький.
- Сырые данные больших бэкапов перед сбросом в S3 - st1 или sc1, потому что это sequential throughput, а не IOPS.
Цена тоже не последний аргумент: gp3 обходится примерно в 0.08 USD за ГБ в месяц плюс плата за докупленные IOPS и throughput; io2 - примерно 0.125 USD за ГБ плюс ощутимая плата за IOPS выше базовых. Instance store входит в цену инстанса, как бы бесплатно, но инстансы семейства i* сами по себе дороже из-за железа.
Практический итог для повседневной эксплуатации
Решение обычно принимается не "EBS или instance store", а "instance store там, где приложение само умеет переживать потерю диска, EBS везде, где не умеет". Это разграничивает архитектуру сильнее, чем любые тесты скорости. После выбора остаются технические дисциплины: монтирование по UUID, nofail в fstab, прогрев томов после restore из snapshot, контроль полосы EBS у типа инстанса и включённое шифрование EBS по умолчанию на уровне региона (aws ec2 enable-ebs-encryption-by-default).
И последнее: издержки мониторинга. Стоит следить за метриками BurstBalance (у старых gp2), VolumeQueueLength и VolumeIdleTime в CloudWatch. Растущая очередь при нормальной нагрузке говорит, что том упёрся, и пора либо докупать IOPS, либо переходить на instance store там, где логика приложения это допускает.
Выбор диска перестаёт пугать, когда он задокументирован в runbook: для каждой роли инстанса в инфраструктурном репозитории есть строка "root: gp3 30G; data: io2 500G, 10000 IOPS; cache: instance store". И дальше выбор превращается не в ежемесячный спор, а в одну строку кода, которую меняют, только когда появляется новое поколение инстансов или новый сервисный запрос по latency. С таким набором тип диска перестаёт быть источником сюрпризов и превращается в обычный параметр в Terraform-коде, о котором потом никто не вспоминает - и это лучший признак того, что выбор был сделан правильно.
Полезно помнить и про limits самого сервиса: по умолчанию у свежего AWS-аккаунта есть квоты на суммарный объём EBS-томов и на число IOPS у provisioned томов в регионе. Перед большим развёртыванием их проверяют через aws service-quotas и поднимают заранее, потому что заявка обрабатывается не всегда мгновенно, а обнаружить лимит в момент инцидента - худший из сценариев.