Что не так с Copy-on-Write под Linux при копировании
Предупреждение: суть этой статьи относится ко всем CoW файловым системам, поддерживаемыми Linux и поддерживающими reflink при копировании. В данный момент это: BTRFS, XFS и OCFS2.
Прошу воздержаться от холиваров о том какая ФС лучше: Btrfs, XFS, Reiser4, NILFS2, ZFS или какая-то неупомянутая.
Предыстория
- 21 июля 2001 года — Namesys публикует анонс Reiser4.
- 20 ноября 2003 — Namesys публикует некоторые бенчмарки Reiser4.
- 24 августа 2004 — Namesys делает публичный релиз Reiser4.
- 14 сентября 2004 — анонс ZFS.
- 16 ноября 2005 — ZFS включена в 27 сборку OpenSolaris.
- сентябрь 2006 — Ханс Райзер арестован за убийство жены, начало конца Namesys
- 12 июня 2007 — анонс Btrfs Крисом Мейсоном (бывший сотрудник Namesys).
- 22 сентября 2011 — В ZFSonLinux появился тикет с запросом на реализацию reflink.
- 2012 — Btrfs признана стабильной Oracle Linux и SUSE Linux Enterprise
- 21 января 2013 — метка экспериментальности была снята c Btrfs в исходных кодах ядра Linux.
- May 2019 — Btrfs удалена из RHEL 8 (подозревается скрытые политические причины, так как Btrfs продвигается Oracle)
Ожидания от копирования в CoW-файловых системах
Когда в 2001 году анонсировалась Reiser4, я был вдохновлён и увлечён возможностями Copy-on-Write. Подумать только, мы можем легко и просто иметь сколько угодно копий разных проектов, а физически диск будет хранить только отличия между ними!
К тому же скорость копирования должна была неприлично вырасти. Это тогда выглядело панацеей для создания аккаунтов для shared-хостинга, а сейчас оптимальным решением для лёгких виртуальных машин и контейнеров. Ведь мы могли бы не тратить место на одинаковые файлы, вместе с тем позволяя пользователям их беспроблемно менять.
Однако у Ханса Райзена поехала крыша и он убил жену, а его детище (скорее всего по политическим причинам) не включили в ядро. Может всё-таки история зависит от конкретной личности?
ZFS была опубликована под несовместимой с Linux лицензией, и потому не вошла в ядро. Поэтому внедрение ZFS в Linux оказалось надолго заторможено.
После этого я стал ждать Btrfs. И только через 6 лет после анонса она была признана разработчиками ядра Linux стабильной.
После этого я не торопился использовать Cow-системы, так как сама парадигма Copy-on-Write предполагает повышенную фрагментацию, потому что изменения данных каждый раз записываются в новое место.
Для HDD фрагментация убивает производительность, так как процесс перепозиционирования блока считывающих головок — очень длительная операция.
Поэтому лично я откладывал внедрение btrfs на своих машинах, пока они не перешли на SSD.
Замечу, что SSD тоже не любят фрагментацию, всем известно что для SSD линейная запись/чтение могут быть в десятки раз быстрее, чем случайный доступ.
Но производительность фрагментированного SSD падает не так драматически, как у HDD.
Итак, что со скоростью копирования при CoW?
И вот, наконец, настал тот час. Когда SSD стали достаточно надёжны, я стал вовсю использовать CoW-файловые системы. А конкретнее — Btrfs и Nilfs2.
Осваивая захватывающие возможности снимков (снепшотов), на время я забыл о моих ожиданиях из 2000-х годов о сверхбыстром копировании файлов.
Через некоторое время я решил провести тесты. И к моему великому разочарованию не увидел никакого прироста скорости от CoW при копировании. Вот результат на SATA SSD:
time cp -a /usr /usr1
real 0m15,572s
user 0m0,240s
sys 0m4,739s
Оказалось, что для использования CoW нужно указывать специальный ключ.
time cp -a --reflink=auto /usr /usr2
real 0m3,166s
user 0m0,178s
sys 0m2,891s
Только в этом случае мы видим 5-ти кратное преимущество. К слову говоря, размер преимущества неограниченно растёт при увеличении длины файлов. В папке /usr
, которую я копировал, в основном мелкие файлы.
Я был несказанно удивлён, почему ключевое преимущество CoW-файловой системы не используется по умолчанию. Ведь для этого она и создавалась!
В данном случае я тестировал копирование на Btrfs-разделе. Но подобный результат вы получите и с любой другой CoW-файловлй системой, поддерживающей reflink.
В чём проблема, Билли?
Проблема в cp. По-умолчанию она не использует CoW при копировании. Хотя может.
Почему разработчики coreutils было принято столь неоднозначное решение, которое перечеркнуло половину преимуществ CoW файловых систем?
Оказывается, так решил Pádraig Brady, ответственный за развитие GNU coreutils.
Вот его логика:
- По-умолчанию cp не использует CoW, так как кто-то может использовать копирование для того, чтобы повысить вероятность сохранения файла на диске после разрушения файловой системы.
- С точки зрения производительности, если есть некий чувствительный к задержкам процесс, вы можете захотеть, чтобы основная запись была сделана именно во время копирования, так если это произойдёт потом, то может возникнуть лаг при перепозиционировании головок жёсткого диска. Обратите внимание, начиная с версии 8.24 coreutils, mv по-умолчанию использует опцию reflink.
Текст ответа Pádraig Brady на английскомIt’s not the default since for robustness reasons one may want a copy to take place to protect against data corruption. Also for performance reasons you may want the writes to happen at copy time rather than some latency sensitive process working on a CoW file and being delayed by the writes possibly to a different part of a mechanical disk. Note that from coreutils v8.24 mv will reflink by default, since it doesn’t have the above constraints.
В плане скорости для mv практически нет никакой пользы от CoW при перемещении файлов. В пределах одной файловой системы mv практически всегда работает очень быстро.
Опять возникает вопрос о влиянии личности на историю. Поставив по другому несколько буковок в исходном коде программы, можно замедлить копирование у десятков/сотен миллионов пользователей (тут нужно учесть, что сейчас большинство людей пользуются облачными сервисами в том или ином виде, даже если у них нет компьютера), уменьшить эффективность использования накопителей и повысить их продажи по всему миру.
Разбор аргументов Pádraig
В первом аргументе есть кое-какой смысл. Неискушенные пользователи действительно могут делать бакапы ценных файлов на одной и той же файловой системе.
Второй аргумент. Если почитать дальнейшие комментарии насчёт лагов от Pádraig, то обнаружится что он имел ввиду ситуации с базами данных, когда при записи в существующий файл может возникнуть задержка из-за того, что файловая система будет искать свободное место. Но в случае CoW-файловой системы всегда будет искаться новые сектора для записи в силу природы CoW, как заметил Jan Kanis. Поэтому, по моему мнению, второй аргумент несостоятелен.
Однако на CoW системах действительно можно получить задержку или даже ошибку «Закончилось пространство» при записи в файл базы данных. Для избежания этого нужно для базы изначально создавать пустой каталог с отключенным CoW.
Отключается CoW на каталоге/файле так:
chattr +C /dir/file
chattr +C /dir/dir
Есть ещё опция монтирования nodatacow. Она будет применена ко всем новосоздаваемым файлам.
Как всё-таки быть, если мы хотим по-умолчанию использовать CoW при копировании?
- Радикальный путь — это патчить coreutils. Возможно, создать свой пакет в своём частном репозитории.
Патчим файл cp.c:
-x->reflink_mode = REFLINK_NEVER; +x->reflink_mode = REFLINK_AUTO;
- Менее радикальное решение — это прописать алиас cp для вашей оболочки. Для bash, например:
В папке
/etc/profile.d
создаём файликcp_reflink.sh
c содержимым:#!/bin/bash alias cp='cp --reflink=auto'
Это решение будет работать почти во всех случаях, когда к cp идёт обращение из оболочки по имени. Но если в скриптах будет использоваться /bin/cp, то алиас не сработает и копирование будет происходить по-старинке.
Выводы
С наступление эпохи повсеместной виртуализации и SSD стало очень актуально использовать CoW-файловые системы. Они удобны для создания снимков, быстрого копирования. Фактически, при использовании CoW при копировании мы автоматически делаем дедупликацию данных.
Сейчас только 3 файловых системы поддерживают этот тип копирования: BTRFS, XFS и OCFS2.
Я искренне надеюсь, что поддержку reflink допилят в ZFS и NILFS2, так как по внутренним механизмам они итак поддерживают CoW.
Однако во всех дистрибутивах Linux CoW при копировании файлов отключена, и нам нужно или явно указывать соответвующие ключи, или использовать различные трюки типа алиасов или патчей.
С момента анонса Reiser4 прошло уже 18 лет, однако до сих пор лёгкое CoW копирование не вошло в нашу жизнь повсеместно.
P.S. Docker и CoW
А вы знаете что Docker поддерживает btrfs для своего хранилища? Эту опцию нужно включать. Она не выбрана по-умолчанию.
CoW файловая система в теории является идеальным дополнением к лёгкой виртуализации, когда разные виртуальные машины используют одно и то же ядро.
На мой взгляд, гораздо более органично чем OverlayFS и Aufs, которые представляют из себя технологические костыли для имитации CoW.
Для использования Btrfs в Docker нужно:
- В свежей (без образов и виртуальных машин) инсталляции Docker примонтировать отдельный том Btrfs к
/var/lib/docker
- Добавить опции в конфигурационный файл запуска Docker
Для Gentoo и Calculate Linux это /etc/conf.d/docker
DOCKER_OPTS="--storage-driver btrfs --data-root /var/lib/docker"
А вот полная инструкция для всех дистрибутивов.
Благодарности:
- Компании RUVDS за поддержку и возможность публикации в своем блоге на Хабре.
- За изображение TripletConcept.
P.P. S. Замеченные ошибки направляйте в личку. Повышаю за это карму.
Вы можете поэкспериментировать с CoW-файловыми системами заказав виртуальную машину у RUVDS по купону ниже.