С арендованного тестового сервера на домашний переехали 14 сайтов: WordPress, два проекта на Laravel, крупный сайт на Bitrix и статика, около 29 ГБ на старте. Технически это rsync файлов и дампы баз. На практике почти каждая ошибка за эти выходные не выдавала сообщения, а тихо давала неверный результат: сайт открывается, но не тот; база импортируется, но в двадцать раз медленнее; текст на месте, но кракозябрами. Ниже — все ловушки по порядку.

Это шестая часть цикла «Домашняя инфраструктура».

Главный урок: копия должна быть один к одному

Первым заходом я «сэкономил»: не стал копировать node_modules, wp-content/cache и прочие кэши — «их можно пересобрать». Пересобрать их можно, но это стоило часа пересборки, которого можно было не тратить. Правило после этого:

  • копируем всё; исключаем только файлы с реквизитами окружения: wp-config.php, .env, конфиги подключения к базе;
  • никакого --delete: rsync -a --update, повторный прогон дописывает недостающее и ничего не стирает;
  • проверка «забрано всё» — по числу файлов, а не по объёму: rsync -an --update --itemize-changes должен показать ноль к переносу.

Ловушка 1: переменные одного сайта утекли в другой

Сайты поднимались в цикле, и для каждого читался его .env через set -a; . .env. Переменная COMPOSE_PROJECT_NAME оставалась в окружении и перебивала .env следующего сайта. Итог: контейнер одного сайта оказался смонтирован на каталог другого. Ошибок — ноль, просто открывается не тот сайт.

# вместо source всего файла — точечно и явно
name=$(grep -E '^COMPOSE_PROJECT_NAME=' "$dir/docker/.env" | cut -d= -f2)
docker compose -p "$name" --project-directory "$dir/docker" up -d
# и проверка, что смонтировано именно своё
docker inspect -f '{{range .Mounts}}{{.Source}} {{end}}' "${name}-php-1"

Ловушка 2: chown сломал базу

Чтобы поправить права, я сделал chown -R на весь каталог сайта, включая каталог данных MariaDB. База перестала читать свои файлы (OS error 13), и дальше пошли обманчивые сообщения вроде Table … is read only, хотя дело было совсем не в режиме таблицы. Права меняем только в docker/ и src/, данные базы не трогаем.

Рядом — ещё одна мелочь: MariaDB читает MARIADB_USER и MARIADB_PASSWORD из окружения только при инициализации пустого каталога данных. Поменяли пароль в .env у уже работающей базы — ничего не изменится.

Ловушка 3: импорт базы шёл со скоростью 45 КБ/с

Дамп крупного сайта — таблица на 6,6 миллиона строк — заливался так медленно, что я подумал на диск. Виноват был буферный пул InnoDB: 192 МБ на такой объём. Увеличил на ходу, без перезапуска:

SET GLOBAL innodb_buffer_pool_size = 640 * 1024 * 1024;

Попадания в буфер стали 999 из 1000, скорость — около 5000 строк в секунду, остальные 859 таблиц легли за пару минут. Прогресс, кстати, смотрел по числу таблиц в information_schema и по номеру записи в SHOW PROCESSLIST: счётчик Innodb_rows_inserted всё это время показывал ноль, и верить ему было нельзя.

Ловушка 4: кракозябры из-за прав, а не кодировки

На сайте с Bitrix после переноса вместо русского текста пошла мешанина. Первое желание — перекодировать дамп. Но данные в базе были целы: шестнадцатеричное представление строк совпадало с дампом байт в байт. Причина оказалась в правах. Каталог с дополнительным конфигом MariaDB приехал с правами 700, база (uid 999) не смогла прочитать свой my.cnf и молча запустилась с кодировкой по умолчанию latin1. init_connect с нужной кодировкой не применился, и на выходе получилась двойная перекодировка.

-- проверка, с какими настройками на самом деле работает база
SELECT @@character_set_server, @@collation_server, @@init_connect;

Лечение — chmod a+rX на каталог конфига и перезапуск контейнера. Ни одного байта в базе менять не пришлось.

Ловушка 5: кэш миниатюр пересобирался на каждом запросе

PHP в новых контейнерах работает от пользователя с uid 1000, а файлы приехали от uid 33 старого сервера. 401 каталог и 51 257 файлов оказались недоступны для записи, и кэш миниатюр пытался создаваться заново на каждом запросе — 468 предупреждений в сутки. После chown 1000:1000 по деревьям сайтов (72 966 объектов) за полчаса создалось 1158 файлов кэша, предупреждений — ноль.

Мелочи, каждая на полчаса

  • docker restart не применяет правки compose: он перезапускает контейнер с той командой, с которой его создали. Поменяли command: — нужен up -d;
  • Laravel ждал корень проекта, а не public/: приложение строило пути от DOCUMENT_ROOT/public. Пришлось try_files $uri /public$uri /public/index.php и явный запрет на app, config, storage/logs, vendor, artisan, composer.*;
  • абсолютный симлинк public/storage указывал на путь старого сервера и вернулся при повторном rsync. Пересоздан относительным;
  • ssh внутри while read съедает стандартный ввод цикла, и цикл обрабатывает только первую строку. Нужен ssh -n;
  • «55 425 изменённых файлов» в git — это только бит исполнения (в репозитории 755, на диске 664). Лечится git config core.filemode false;
  • сайт отдавал 500, потому что в дампе приехала опция режима обслуживания. Не дефект переноса — состояние источника;
  • Let’s Encrypt упёрся в лимит — пять неудачных проверок в час на имя, — пока DNS ещё указывал на старый сервер. Сертификаты выпускать после переключения DNS, а пробовать — на тестовом сервере ACME.

Перед переносом — проверка на заражение

Сайты переезжали вскоре после массового взлома WordPress через уязвимость ядра, поэтому перед переносом каждого сайта прогоняли поиск по известным сигнатурам того взлома. Искать надо по содержимому файлов, а не по факту наличия mu-plugins или дропинов — у многих сайтов они легальны. И отдельная находка на старом сервере: приватный SSH-ключ лежал с правами 644, читаемый всеми. Такой ключ считаем скомпрометированным и заменяем.

Переносите сайты на новый сервер?

Напишите, сколько их и на чём они работают, — составлю порядок переноса и проверки, чтобы ничего не потерялось молча.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.