Как стать автором
Обновить

Организация распределённого хранения файлов с помощью git-annex

Время на прочтение14 мин
Количество просмотров9.1K

О чем статья

Поделюсь как пришел к выбору git-annex в качестве инструмента управления коллекцией файлов, кратко опишу процесс установки и настройки, расскажу о подводных камнях на разных файловых системах и устройствах: ext4 под linux, sdcardfs на телефоне с андроид, флешка с ntfs на wsl с windows 10.

Если вы выбираете способ управления файлами или просто интересуетесь annex, статья для вас.

Моя работа с ИТ не связана, в теме я знаю прикладной минимум или ещё меньше. Статья — скорее пользовательский бег по граблям, чем профессиональный отчёт. При этом найденные грабли помечены флажками, а тропинка утоптана.

Статью будет комфортнее читать при наличии базовых знаний git и ssh.

Дано

Некий набор данных:

  • Фотоальбом с видео/фото за 14 лет.

  • Коллекция контента: книги, музыка.

  • Скан-копии документов: договоры, счёта, гарантийный талоны.

  • Рабочие документы: офисные файлы, видеозаписи, markdown-документы.

Все эти 35 000 файлов и 85 Гб данных неравномерно нанесены на 3 ПК, 2 телефона, флешку и внешний HDD (бэкап).

В наличии Базовые знания git, ssh, десктопного linux на уровне пользователя.

Задачи

  1. Синхронизация между устройствами.

  2. Возможность частичной загрузки. На работе нужна только папка с рабочими файлами, на телефоне — музыка, а на сервере — всё.

  3. Управление коллекцией с любого устройства: менять структуру папок, переименовывать, добавлять, изменять и удалять файлы без загрузки содержимого.

  4. Шифрованный бэкап в гугл драйв.

  5. Управление количеством копий.

  6. Управляемое версионирование с возможностью полностью удалять ненужные версии.

Поиск решения

Бэкапим и копируем вручную

Это не очень удобно: что-то забыл, что-то задвоил, но оно работало. Поиск нового решения я начал, когда два диска на разных компах одновременно начали алертить в SMART.

Rsync

Вещь хорошая. Умеет синхронизировать, сжимать, шифровать.

Но:

  1. Версиями не управляет.

  2. Настройка на каждом из 4 устройств — слишком лениво.

Я довольно долго использовал rsync лишь для бэкапа на флешку. Спустя полгода флешка перешла в режим RO. Выяснилось, что при копировании на fat / exFat с ext4 многие файлы перезаписываются без необходимости. Что-то там не сложилось с метками времени.

Уверен, что вопрос в моём знании инструмента, но я двинулся дальше.

NAS

Была такая мысль. Приобретаем NAS от приличной компании, ставим клиенты на нужные устройства и горя не знаем. Останавливало то, что всё же это не полный контроль. Железо устаревает, обновления прекращаются. А некоторые события с известной компанией на букву «W» и потерянными данными, только подтвердили мою паранойю. Опять же, версионирование и контроль количества копий здесь недоступны (поправьте, если ошибаюсь).

git

Если у тебя в руках только git, все становится похожим на репозиторий... Попробовал организовать коллекцию, опираясь на эту статью.

На ПК — основные репы, на сервере — bare. Вместе с индексом git, вес получается более 130 Гб. Главный плюс:

  • + возможность откатиться к любой версии файла.

Но минусов больше:

  • - регулярно меняются двоичные файлы: офисные документы, изображения, а значит быстро растёт объём. Я стараюсь вести документы в md, но это слабо помогает на фоне двоичного контента.

  • - я не могу получить на телефон отдельный файл, поработать с ним и запушить обратно, нужно тянуть все 130 Гб. А память она не резиновая, да и «Гиги» не бесконечные в сотовой сети. Вариант с submodule, не полностью решает вопрос и усложняет схему.

  • - Git операции в моем кейсе медленные, нагрузка на диск — дикая.

В общем не оставляет ощущение, что мы забиваем гвоздь отвёрткой.

svn

  • + Возможность частичной загрузки репозитория.

  • - Нет сервера (дома вырубили инет, свет) — нет возможности сбросить / получить файлы, синхронизировать состояние с другим клиентом.

  • - Я не знаю svn.

git lfs

Для моих целей — сплошной минус: надо ставить и настраивать отдельный сервер, где будет храниться содержимое, бэкапы в google disk недоступны. К слову, annex умеет хранить данные на сервере с поддержкой git-lfs.

UPD: Syncthing

@lizardus обратил моё внимание в комментариях, что наиболее релевантный инструмент я не рассматривал.

Syncthing — поистине царское решение! Он поддерживает систему relay-серверов. Можно пользоваться публичными (декларируется безопасность) или запустить свой, как публичный, так и приватный для объединения клиентов, которые не могут связаться напрямую. Большая часть задач, указанная в начале статьи и не только, решается им успешно, включая:

  • + настраиваемое версионирование.

  • + частичная синхронизация между разными ПК и мобильными устройствами.

  • + хождение за NAT (с оговорками).

  • + инструмент прост в установке и настройке.

При этом для моего кейса есть несколько узких мест:

  • - нет поддержки синхронизации локально, со съёмными устройствами. Есть обходные пути, вроде помещения на флешку портативной версии Syncthing.

  • - нет поддержки синхронизации с FTP, GDrive и прочей экзотикой. Есть обходной путь: синхронизировать папку, которую синхронизирует с гуглом клиент от гугла. Но этот вариант мне не кажется надёжным.

  • - корпоративный NAT с закрытыми портами, похоже, непреодолим. Если нужный порт открыт, можно опереться на relay-сервер, но, например, синхронизироваться через корпоративный ftp уже не получится.

  • - и главное лично для меня: недоступно оффлайн управление структурой всей коллекции файлов без скачивания их содержимого. А как же в дороге нашарить в телефоне папку Collections/Soft/Distr/Linux/Manjaro, обнаружить там Debian_10.iso и перенести его куда следует? При этом перемещение на клиентах, содержащих файл не будет стоить ничего: переместится только символьная ссылка, без нагрузки на диск.

В общем, если у вас нет паталогической любви к git, как у меня и для вас некритичны указанные моменты, то ваш выбор — Syncthing. Иначе — стоит читать дальше.

Всемогущий git annex

Краткая справка

git-annex — система управления файлами без индексации содержимого. С помощью git индексируются только имена, пути файлов и хэш содержимого. Контент хранится в недрах .git/annex. Или не хранится, если на конкретном устройстве он не нужен.

Git annex можно использовать для раздельного управления файлами: текстовые храним в git и управляем версиями содержимого, annex управляет двоичными. Всё это можно настроить, в том числе опираясь на тип контента и / или его размер, но в этой статье я сфокусируюсь на полном управлении файлами посредством annex.

На мой взгляд, инструмент решает все задачи и предоставляет ряд приятных бонусов, в том числе, например, возможность хранить дерево файлов без содержимого в закрытой репе github, что предельно упрощает синхронизацию состояния репозитория между устройствами. Здесь есть и веб-приложение в качестве GUI и ассистент для автоматического добавления файлов и много других интересных мелочей.

Установка

Для начала достаточно установить git, и git-annex характерным для вашей системы способом. Например, в Manjaro:

sudo pamac install git git-annex

Для windows git-annex отсутствует, Есть бета-версия git-annex под windows. Стабильную версию можно установить в подходящем дистрибутиве в WSL. В ubuntu 20.2 он есть в репозитории. Установка на телефоне в termux ненамного сложнее:

pkg install wget
wget https://git-annex.branchable.com/install/Android/git-annex-install
source git-annex-install

Для обновления достаточно запустить загруженный скрипт повторно.

Начало использования

Пожалуйста, потренируйтесь на некритичных или тестовых файлах. Хотя git annex дружелюбен и бережно относится к данным, способов отстрелить себе байты здесь достаточно!

Стартовать я буду с нового репозитория. Запускать управление файлами с git-annex можно и в действующем хранилище git, но это не то что мне нужно.

Инициализируем новый гит реп, затем annex, потом добавим все файлы в annex, только после этого добавим файлы в git и закоммитим что получилось:

git init
git annex init homepc
git annex add .
 add file.x
 ok
 add file.y
 ok
 ...
git commit -am "git annex first commit"

При инициализации annex репозитория можно не указывать имя (у нас это homepc), это можно сделать позже командой describe.

Теперь все ваши файлы превратились в символьные ссылки, а содержимое отправилось в .git/annex/objects. Annex создал ветку для хранения своей технической информации "git-annex" и ветку промежуточной синхронизации "synced/master". Эти вещи использует сам annex, нам туда ходить не нужно.

Если ваша файловая система не поддерживает ссылки, например exfat или fat32, файлы будут продублированы в .git/annex, а ветка сменится на особую adjusted/unlocked. Пользоваться annex на "crippled filesystem" довольно накладно по занимаемому месту, но не накладнее, чем git, ведь с annex вы можете дропнуть ненужные в этом месте файлы с конкретного устройства.

Git annex, настраиваем и пробуем

Отмечу, что если команда git annex требует путь, а вы его не указали, считается что вы поставили точку вместо пути, указав текущий каталог.

Для начала давайте отредактируем какой-нибудь файл и сохраним его. И сразу, как говорит гугл хром: «Опаньки». По умолчанию, annex блокирует изменение файла, защищая его содержимое: снимает права на запись. Для изменения файла нужно: разблокировать его, изменить и закоммитить:

git annex unlock myfile.docx
....edit
git commit -am "Улучшен myfile"

Заметьте, при git commit содержимое добавится в annex, благодаря установленным git annex хукам. В git зафиксируется только изменение ссылки. В данный момент, файл всё еще разблокирован, можно использовать git annex lock myfile.docx для блокировки.

Можно не коммитить изменения, если осмысленное описание изменений вам не нужно, при синхронизации annex это сам, подставив имя ветки и репозитория в описание. При разблокировании ссылка заменяется обычным файлом. Поскольку содержимое у нас сохранено, если откатиться на коммит назад, git вернет ссылку на старый файл, содержимое которого в annex есть. Мы ничего не потеряли.

Если старые версии содержимого не нужны, запускаем поиск неиспользуемых файлов и удаляем все или часть из них.

git annex unused
1 ...
2 ...
3 ...

git annex dropunused all

Если файл используется в какой-то ветке, он не попадёт в список. При этом annex не позволит удалить единственную копию неиспользуемого файла без ключа --force, защищая ваши данные. Это поведение настраивается изменением параметра mincopies:

# смотрим текущую настройку
git annex mincopies
1

# устанавливаем своё значение
git annex mincopies 2
(recording state in git)

Существует второй параметр: numcopies. Это — желательное количество копий. Можно настроить репозиторий так, чтобы он получал файлы, общего количества копий которых не хватает до желательного.

Если редактировать файлы вам требуется часто, стоит перейти на «сдвинутую» (adjusted) ветку.

git annex adjust --unlock

Annex вернет все ваши файлы на место, заменив ими ссылки и откроет их для редактирования. Теперь вы на ветке "adjusted/master(unlocked)". Такую сдвинутую ветку можно сделать для любой исходной. При синхронизации все автоматом сольется в оригинальную ветку.

Внимательный читатель заметил, что файлы теперь задублированы: они и в рабочем каталоге и в хранилище annex. Если место для вас критично, вы можете заменить их на жёсткие ссылки. Достаточно установить параметр annex.thin в true:

git config annex.thin true

При этом вы берете на себя риски потери оригинального содержимого файла. Такой фокус не пройдёт на fat, но здесь всегда придётся пользоваться сдвинутой веткой, т.к. fat не поддерживает ссылки в принципе.

Git annex умеет создавать другие сдвинутые ветки, например, скрывая файлы, содержимого которых нет в репозитории. Здесь можно посмотреть подробнее.

Пару слов об удалении и перемещении. При удалении файлов, достаточно git add ., так как содержимое файлов не менялось. Однако, при перемещении / переименовании, уже одним git не обойтись, т.к. ссылки ломаются, важно сделать

git annex add your_new_path

Это чинит ссылки, после чего нужно добавить и зафиксировать всё в git.

Как я писал выше, коммитить можно вручную или просто делать annex sync, коммит пройдёт автоматически.

Настраиваем второй репозиторий

Давайте добавим второй репозиторий. Я хотел бы забекапить всё на домашний мини-сервер, который будет транслировать медиаконтент по dlna. Удобно: снял видео / фото на телефон, синхронизировал, и можно смотреть все на большом экране.

Соединяться будем по ssh. Предполагается, что настроен доступ по ключу.

Файлы на другом устройстве можно сохранить разными способами:

  1. Список файлов в хэшированной структуре папок (имена и пути создаются по хэшу), "special remote" без истории git.

  2. Простое дерево папок и файлов, обычный вид, "special remote" без истории git.

  3. Bare репозиторий git с историей.

  4. Обычный реп git с историей.

Сейчас нам подходит вариант 4, так как это и бэкап и источник медиа-данных для dlna-сервера.

На втором пк должен быть установлен git и annex.

Делаем на втором пк

git clone ssh://homepc/home/user/myrepo
cd myrepo
git annex init miniserver

Git annex init нужен, если хотим хранить в репозитории содержимое файлов. Если этого не сделать, annex пометит репозиторий как only git хранилище и файлы туда отправлять откажется, только историю git.

Так как у нас не bare репозиторий, а пушить мы сюда хотим, настраиваем git:

git config receive.denyCurrentBranch updateInstead

Теперь при push в репозиторий, файлы будут обновляться.

На первом пк

Cчитаем, что в .ssh/config второй пк настроен как miniserver

git remote add miniserver ssh://miniserver/home/user/myrepo
git annex sync

Annex прочитает хэш-номер второго репа, его описание, после чего синхронизирует все git-ветки. Но данные всё ещё на одном ПК, на втором — битые ссылки. Также отмечу, что в текущей конфигурации минисервер не может сам пушить данные обратно или забирать с homepc. Если мы этого хотим, нужно настроить git receive.denyCurrentBranch на homepc, а затем на miniserver подключить homepc по аналогии с действиями выше.

Теперь отправим на miniserver файлы. На самом деле, annex поддерживает крутую систему групп и настроек wanted content. Можно очень гибко указать какие файлы по весу, количеству копий, типу папок, имени, типу содержимого нужны в каждом репозитории. Есть преднастроенные группы: backup, archive, client, transfer. Но тема обширная, в рамках этой статьи будем все делать вручную, хотя на бою стоит, конечно, ознакомиться с этой информацией здесь. При правильной настройке вы можете одним

git annex sync --content

Скопировать файлы в соответствующие подключенные репозитории и удалить ненужные файлы оттуда.

Чтобы сказать annex, что в репозитории нужны все файлы, присваиваем репозиторию группу backup и тип желательного содержимого standard — использовать дефолтные настройки группы:

git annex group here backup
git annex wanted here standard

# посмотреть / поменять файл настроек для содержимого и репозиториев
git annex vicfg

Ну а мы вернёмся к ручному труду. Чтобы отправить файл на miniserver делаем:

git annex copy myfile -t miniserver
  ok
(recording state in git)

Если не указывать имя файла и запустить команду в корне репозитория, скопируются все файлы. Можно не только копировать, но и перемещать (move). Теперь содержимое файла можно дропнуть с первого ПК:

git annex drop myfile
  locking myfile on miniserver
  ok
(recording state in git)

Annex проверил, что файл действительно есть на miniserver, mincopies у нас 1, а значит можно файл безопасно удалить. Обратите внимание, что annex именно сходил на miniserver и проверил фактическое наличие файла, а не просто глянул в лог. Если сервер недоступен, annex не будет удалять файл без --force. Такой подход мне очень импонирует, так как снижает риски потерь содержимого.

После любой операции с файлами, annex сохраняет лог в своей ветке гит, но только локально. Чтобы все подключенные репозитории знали актуальное состояние содержимого: где лежит, сколько копий, важно сделать

git annex sync

Иначе miniserver сам не будет знать, что у него есть какой-то файл.

Если после sync добавить имя репозитория, синхронизация пройдёт только с ним.

Посмотреть местонахождение файлов:

# конкретный файл
git annex whereis file

# список файлов, которые здесь
git annex list -i here

# список файлов, которые не на miniserver
git annex list --not -i mserver

Настраиваем репозиторий на телефоне

На телефоне всё практически так же, как и в описании выше, кроме нескольких особенностей.

Во-первых, нужно решить где вы будете хранить файлы:

A) В папке termux.

  • + ext4. Файлы хранятся правильно, не дублируются.

  • - открытие файла в другой программе превращается в квест. При использовании termux-open, файл копируется во временную папку или в Download и открывается. Посмотреть фотки, листая в сторонней программе, или открыть папку с музыкой во встроенном плеере не получится. В таком кейсе рекомендую использовать файловый менеджер с возможностью добавления папки termux как хранилища: Material files, Total commander, Mix и т.д. Встроенные в файлменеджер средства просмотра и прослушивания, отчасти компенсируют упомянутый минус.

B) В общедоступном хранилище в памяти телефона или на sd карте.

  • + все программы имеют нормальный доступ к файлам.

  • - на хранилище Android с файловой системой sdcardfs не работают хуки annex для git, что критично.

  • - sdcardfs не поддерживает ссылки ни в каком виде, загруженные файлы дублируются.

С хуками вопрос можно решить двумя способами:

  1. Перенести папку .git/hooks в домашнюю папку termux и настроить в git:

    git config core.hooksPath /data/data/com.termux/files/home/annexhooks
  2. Перенести всю папку .git в домашнюю папку termux, оставив вместо неё в рабочем репозитории текстовый файл .git с адресом:

    echo "gitdir: /data/data/com.termux/files/home/mygitfolder" > .git

Во-вторых иногда annex просто повисает при работе в termux. Если операция работает более 20 минут, а в htop ни один запущенный процесс не тратит ни секунды времени, возможно пора жёстко закрыть termux и перезапустить операцию.

Для повышения скорости операций, регулярно стоит делать git gc && git prune --expire=now

Добавляем бэкап на внешнем носителе

Вариант с добавление репа git очевиден, рассмотрим экспорт дерева файлов без истории git на "crippled fs": exFat.

К примеру, я хочу иметь HDD диск, который супруга может подключить к ТВ или ПК с windows у друзей и посмотреть фоточки.

git annex initremote BigBlueHDD\
	type=directory\
  directory=/run/media/user/myhdd/OurFiles\
  encryption=none\ 
  exporttree=yes

Параметр exporttree=yes говорит annex, что экспортируем мы файлы натуральным деревом с их именами, в противном случае экспорт пройдёт в хешированное дерево.

Параметр encryption позволяет зашифровать файлы, но только при использовании special remotes: если файлы храним не в git репозитории. Чуть подробнее расскажу о данном параметре ниже, при подключении google drive. Здесь указано значение "none": без шифрования.

Есть и параметр importtree, но я крайне не рекомендую его использовать, если диск с "crippled fs". Ниже расскажу почему.

Если теперь вы сделаете

git annex sync --content BigBlueHDD

Файлики полетят на внешний диск. Какие возможны проблемы: annex не может скопировать файлы, имена которых недопустимы в windows: содержащие определённые символы, вроде двоеточия. Также есть баг с копированием на exfat файлов с несколькими точками в конце имени. Баг известен, но пока не исправлен.

Если вы сделаете sync --content в хранилище с настроенными на "yes" importtree и exporttree, получится такая загогулина:

  1. Некоторые файлы могут не скопироваться по приведенным выше причинам.

  2. При импорте, не найдя некоторых файлов на внешнем диске, annex посчитает, что они были удалены и удалит их с основного диска.

Впрочем, если вы проверили, что annex синхронизирует вашу коллекцию без ошибок и имена пишутся нормально, параметр можно включить. Но изменить параметр для уже созданного репозитория не выйдет, нужно добавлять новый.

Чтобы активировать ранее созданное хранилище BigBlueHDD в другом репозитории, просто инициализируем его с указанием пути. Все настройки special remote, кроме пути, сохранены в момент создания, менять их нельзя.

git annex enableremote BigBlueHDD /mnt/hdd

Подводные камни в WSL windows 10

Windows 10 я пользуясь на работе. У нас довольно строгие корпоративные ограничения и у юзеров нет административных прав, что, в целом, верно. К счастью есть возможность, установить и настроить wsl (после трёх заявок и пяти объяснений, что я выпью больше крови эникеев, при установке ffmpeg, optipng, jpegoptim, git и прочих утилит под виндой отдельными заявками, и регулярном их обновлении). Конечно, ни о какой инсайдерской версии Windows речь не идёт, так что монтирование внешних дисков, флешек с ext4 в моей версии wsl недоступно.

В ходе экспериментов с флешкой, выяснилось:

  • Флешку с fat32, exfat, использовать в annex не получается, при том что в git должно работать. Возникает permission denied при внутренних операциях с содержимым, в частности с перемещением файлов. Единственный вариант: использование ntfs-флешки.

  • Для использования флешки с ntfs, нужно монтировать её с параметром metadata, характерным для wsl способом через гланды drvfs:

    sudo mount -t drvfs D: /mnt/d -o metadata

Я заметил следующие особенности:

  • В репозитории стоит в git включить настройку annex.crippledfylesystem=true и перейти на сдвинутую разблокированную ветку, если всё это не произошло автоматически при git annex init.

  • НЕ следует переключать в true настройку annex.pidlock, это приведёт к знакомым проблемам с permission denied.

  • В annex в wsl иногда ломается кодировка русских описаний коммитов. Копать проблему не стал, перешёл на английский.

Официальные рекомендации по работе с NTFS в WSL.

Добавляем облачное хранилище на примере gdrive

Для бэкапа на облачные хранилища, annex использует rclone.

Установите rclone, запустите rclone config и настройте нужное хранилище, перед подключением его в git annex. В целом, с внешними хранилищами, включая gdrive, я.диск, ftp и другие логика одинакова: настраиваем их в rclone и подключаем в annex.

Процесс описан здесь. Предварительно стоит узнать ID нужной папки, для чего просто скопируйте ссылку на папку в самом Google Drive, частью ссылки будет ID.

Итак, добавляем хранилище на гугл диске. В примере "gbackup" — это имя облачного хранилища в rclone и, одновременно, имя репа в annex.

git annex initremote gbackup \
	type=external \
  externaltype=rclone \
  target=gbackup \
  prefix=repo \
  chunk=50MiB \
  encryption=shared \
  rclone_layout=lower

target — имя хранилища в rclone,

prefix — папка внутри целевого каталога, в которой сформируется структура папок,

chunk — на части какого размера пилить файлы. В примере мы делим файлы на части не более 50 Мбайт, это может упростить загрузку крупных файлов. Также это полезно, если какой-нибудь сервер ограничивает максимальный размер файла.

ecryption — тип шифрования. Я выбрал "shared": каждый репозиторий с историей git, имеет ключ. О других вариантах вы можете почитать здесь.

rclone_layout — настройка структуры папок. Я выбрал рекомендуемую: "lower" — до 2 уровней вложенности.

Выводы

Git-annex позволяет решить все обозначенные во вводной части статьи задачи. Для комфортного использования, рекомендую почитать walkthrough, попробовать возможности, связанные с ассистентом и webapp. Получать файлы можно по bittorrent, https, в общем, в статье раскрыта только малая часть возможностей git annex.

При первом знакомстве с технологией, я не сразу понял зачем мне использовать git, который не отслеживает содержимое, но спустя полгода, этот подход кажется мне самым гибким и удобным способом хранения данных.

Буду благодарен за обратную связь и дополнения в комментариях.

Теги:
Хабы:
Всего голосов 5: ↑5 и ↓0+5
Комментарии11

Публикации

Истории

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург