Назад | Перейти на главную страницу

Атомарное копирование нескольких каталогов

Этот вопрос касается развертывания веб-приложений.

Вступление (можно пропустить)

Я использую django, и то, как мой хостинг-провайдер настроил поддержку django, приводит к тому, что веб-приложение разбросано по крайней мере в трех местах:

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

Актуальный вопрос:

Есть ли способ сделать атомарное копирование файлов? Я ни в коем случае не «эксперт» в системах Linux, так что, пожалуйста, простите мое невежество.

Операция копирования включала несколько деревьев каталогов, два или три, в основном:

copy _tree1 to tree1
copy _tree2 to tree2

Под атомным я имею в виду:

Моя идея - иметь что-то вроде двойной буферизации: я готовлю все в промежуточной области, например, _tree_x, а потом копировать переместить это в tree_x with должна быть атомарной операцией, которая просто изменяет указатели на диске.

Я думаю, что один такой копировать Операция перемещения в Linux является атомарной (не так ли?), но мне нужно, чтобы несколько таких операций также были атомарными; Я хочу, чтобы с ними обращались, как если бы они были одной операцией перемещения.

Я думаю, вы на правильном пути с плацдармом. Я не знаю никаких атомарных команд, но если вы создадите свои файлы, а затем используете сценарий для удаления первого каталога и перемещения (не копирования) второго и сделаете это для всех трех каталогов, это должно быть очень быстро.

В качестве альтернативы вы можете использовать символические ссылки. Таким образом, вы могли примерно:

/version/22/<my-apps>
/version/22/<my-site>
/version/22/<my-media>

и развернуть /version/23 каталог с такими же подкаталогами. Затем, где будет находиться фактический файл (и опять же, для скорости вам понадобится скрипт), вы можете использовать символическую ссылку, чтобы, когда кто-либо переходит на последнюю страницу, он получает любую текущую версию (и все это происходит прозрачно и они понятия не имеют). Преимущество этого в том, что ваша старая работа все еще существует, пока вы не решите ее удалить. [Хотя, конечно, система контроля версий - лучший вариант для сохранения старых работ.]

Вам нужно будет проверить, что 1) вы можете запускать сценарии (и таким образом, чтобы веб-пользователи не могли!), И 2) что вы можете использовать символические ссылки (поскольку некоторые веб-серверы настроены так, чтобы не следовать им.)

Возможно, я не обдумываю это полностью, но почему бы не выполнить операцию копирования в новый каталог? Когда это будет сделано, «mv» старый каталог на другое имя и «mv» новый каталог на желаемое имя.

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

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

Для одного размещенного сайта, возможно, имеет смысл закрыть веб-сайт, поместив закрытую страницу сайта в index.html в корневую папку, а затем внося изменения.

Если вам действительно нужно поддерживать работу веб-сайта как можно дольше, могу ли я предложить следующее:

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

  2. Это не решает ваших проблем - просто снижает экспозицию. Для версий до и после могут потребоваться разные поля данных базы данных, поэтому SQL-запрос также необходимо будет выполнить. Человек может загружать страницу одновременно с вашим обновлением. В начале загрузки веб-страницы могут загружаться страницы до изменения, а в последних частях загрузки страницы могут использоваться файлы, полученные после копирования.

Во-первых, лучший способ сделать это - изменить конфигурацию httpd так, чтобы она указывала на новые каталоги, а затем перезапустить httpd. Я полагаю, это невозможно.

У меня есть идея, которая предполагает, что данные в ваших трех каталогах не меняются все время, поскольку для этого требуются неатомарные перемещения этих трех исходных каталогов в копии этих каталогов. Я не уверен на 100%, что это сработает, но вы можете это проверить. Мне проще написать это в виде сценария, чем объяснять по-английски. Дайте мне знать, если мне нужно что-нибудь объяснить.

Предположим, что есть три номинальных пути: / pathA / dir1, / pathA / dir2, / pathB / dir3

mkdir /pathC
ln -s pathC /linkI
cd /pathA
tar pcf - dir1 | (cd /pathC; tar pxf -)
tar pcf - dir2 | (cd /pathC; tar pxf -)
cd /pathB
tar pcf - dir3 | (cd /pathC; tar pxf -)
cd /pathA
mv dir1 dir1.orig && ln -s /linkI/dir1 dir1
mv dir2 dir2.orig && ln -s /linkI/dir2 dir2
cd /pathB
mv dir3 dir3.orig && ln -s /linkI/dir3 dir3
mkdir /pathD
cd /pathD
mkdir dir1
mkdir dir2
mkdir dir3
(cd dir1 && )
(cd dir2 && )
(cd dir3 && )
cd /
ln -sf pathD /linkI

(Edit: Хм, почему-то я пропустил ответ Клинтона Блэкмора выше, который в основном идентичен тому, что я предлагаю. Так что неважно.)

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

Мой ответ основан на ответе Клинтона Блэкмора (принятый в настоящее время ответ).

Несколько каталогов (или несколько файлов, если на то пошло) не могут быть изменены атомарно. Поэтому мы не можем использовать каталоги напрямую. Отдельные файлы можно обновлять атомарно, используя системный вызов rename () (заменяя старый файл новым). Символические ссылки можно обновить таким же образом, используя mv -T позволить mv не разыменовать пункт назначения.

Итак, если у нас есть эти каталоги:

/srv/dirA/
/srv/dirB/
/srv/dirC/

Мы можем сделать их все символическими ссылками на три других каталога:

/srv/dirA -> /version/current/dirA
/srv/dirB -> /version/current/dirB
/srv/dirC -> /version/current/dirC

/version/currentв свою очередь, это просто символическая ссылка на каталог с текущей версией:

/version/current -> /version/22/

Затем все веб-приложение может быть обновлено двумя простыми командами, последняя из которых «заменяет» все три каталога сразу (фактически не заменяет каталоги, а только заменяет то место, на которое указывают эти каталоги):

$ ln -s /version/23/ /version/next
$ mv -T /version/next /version/current

Я на самом деле не тестировал это, но он должен работать. В -T flag может быть нестандартным флагом. Как альтернатива, python -c "import os; os.rename('/version/next', '/version/current')" может использоваться вместо него (также не проверено).

Я не знаю, как это повлияет на производительность, но сомневаюсь, что это будет значительным, если вы уже используете Django. Я думаю, это может иметь значение, если вы обслуживаете огромное количество статических файлов с минимально возможной задержкой (например, CDN), и даже в этом случае это, вероятно, будет лишь незначительным влиянием на производительность. Короче говоря, вам не нужно беспокоиться о производительности.

Обратите внимание, что есть несколько ошибок: Django является сервером и не будет перезапущен в одно и то же время. Чтобы это было по-настоящему атомарным, Django должен быть настроен таким образом, чтобы полностью исключить понятие «текущая версия». Вместо этого вы должны запустить Django из текущей версии для производственного использования. Для обновления будет запущена следующая версия, затем перезапущен сервер (я предполагаю, что у большинства веб-серверов есть способ перезапуска без перехода в автономный режим), и весь процесс должен быть атомарным. Но я не специалист в этой области.

Еще одна проблема (как упомянул Птолемей) заключается в том, что на загруженных серверах будет некоторые люди, которые видят страницы частично из одной версии и частично из другой из-за кеширования и того факта, что несколько ресурсов запрашиваются в разное время во время загрузки страницы (между загрузками ресурсов может быть несколько секунд). Я думаю, что из этих двух кеширование будет наиболее важным, но его также легче всего обойти. Однако я сомневаюсь, что это будет серьезной проблемой на практике.

Вы пробовали использовать атомный-rsync? Он использует комбинацию команд rsync и mv, как это предлагается в других ответах.