Перенос сайта(ов) без простоя и потери данных между выделенными серверами

в 7:22, , рубрики: linux, Linux для всех, веб-сервер, системное администрирование, метки: , ,

Казалось бы, что сложного перенести сайт на другой сервер? Наверно многие из нас копировали свои первые хоумпейджи с хостинга на хостинг в поисках лучшего, будучи еще учась в школе или на первых курсах университета. Или вешая на уши лапши руководству, объясняли, что задача эта не решается мгновенно и сайт сутки работать не будет, ибо DNS такая штука…

А если в целом, ерундовая задача, правда? Но как сделать все идеально? Так, чтобы без простоев, чтобы не было расхождений в БД, чтобы не потерялись какие-нибудь файлики-аватарки-картинки, когда обновляется DNS запись IN A и у половины пользователей старый сайт, а у половины новый. А если это higthload? Или сайт делало несколько поколений «лучших» программистов, считающих своим долгом создать новое подключение к БД в своем «модуле» для сайта. Наконец, если таких сайтов >10?

  • Скопировать файлы
  • Сделать дамб баз данных
  • Развернуть на новом сервере
  • На старом сервере заменить все коннекты к бд к новому
  • Настроить DNS
  • ...

Сколько это займет времени? Сколько драгоценных минут сайт не будет работатьне будет работать правильно?

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

Все это актуально скорее для сайтов, написанных на РНР, для администраторов столкнувшихся с проектами, где не использовались фреймворки, cms и голова при написании кода.

1. Перенос файлов

Лучший способ через ssh при помощи rsync:

rsync -avz -e ssh логин@удалённый.хост:/путь/откуда/ /путь/куда/

Так rsync синхронизирует папки инкрементально, то есть копирует только то — чего не хватает и не тронет что-то новое. Например, можно написать небольшой bash скрипт, который будет синхронизировать все папки с старого сервера на новый в момент обновления DNS, но все же лучшим вариантом будет спустя пару суток синхронизировать папки с ключом -n (--dry-run) чтобы просто увидеть расхождения. Думаю в документации по rsync найдутся решения любых задач по синхронизации файлов. Программа должна быть установлена на оба сервера.

По протоколу FTP rsync работать не умеет, тогда используем lftp:

lftp -e 'mirror каталог-на-сервере локальный-каталог; bye;' -u логин:пароль@удалённый.хост
2. Перенос БД на примере MySQL

На обоих машинах открываем порт сервера MySQL в мир:

[mysqld]
...
bind-address            = * #127.0.0.1
...

Я напишу пример как я делаю бекапы каждый день, вырезка из logrotate.d:

#!/bin/bash
CMD="mysqldump --defaults-file=/root/my.cnf --no-create-info=FALSE --order-by-primary=FALSE --force=FALSE --no-data=FALSE --tz-utc=TRUE --flush-privileges=FALSE --compress=TRUE --replace=FALSE --host=localhost --insert-ignore=FALSE --extended-insert=TRUE --quote-names=TRUE --hex-blob=FALSE --complete-insert=FALSE --add-locks=TRUE --port=<b>3306</b> --disable-keys=TRUE --delayed-insert=FALSE --create-options=TRUE --delete-master-logs=FALSE --comments=TRUE --default-character-set=<b>utf8</b> --max_allowed_packet=1G --flush-logs=FALSE --dump-date=TRUE --lock-tables=TRUE --allow-keyw
ords=FALSE --events=FALSE --databases --routines"

for i in $(mysql --defaults-file=/root/my.cnf --batch --skip-column-names -e 'SHOW DATABASES' | grep -v '^information_schema$'); do
	if [ ! -e /srv/dumps/$i ]; then mkdir -m 700 /srv/dumps/$i; fi
		$CMD $i | gzip -c > /srv/dumps/$i/$i.sql.gz
done

/root/my.cnf

[client]
user = root
password = passw

Почему не классически «mysqldump -u root -p database > dump.sql»? Тогда не будут сохранены view и хранимые процедуры. Правильно делает дампы MySQL Workbench, оттуда я и взял эту команду.

Далее, в переменную $CMD добавляем -h ip_старый_сервер и меняем строчку $CMD $i | gzip -c > /srv/dumps/$i/$i.sql.gz на:

$CMD $i | mysql -u root -ppasswod $i

Конечно нужно предварительно создать все базы данных:

mysql --defaults-file=/root/my.cnf --batch --skip-column-names -e 'SHOW DATABASES' | grep -v '^information_schema$'

3. Перенаправление всех соединений к localhost:3306 — старый сервер на новый сервер

Довольно просто, используем ssh tunnel (предварительно остановив mysql):

ssh -Nf -L 3306:REMOTE_IP:3306 user@localhost

Причем ssh требуется только на локальной машине. Проверяем:

netstat -lnpt | grep 3306
telnet 127.0.0.1 3306

Получаем удаленный MySQL сервер на локальной машине. Все бы хорошо, но MySQL client если видит, что соединение идет к localhost то соединяется с сервером через локальный socket и никакие настройки его делать так отучить не могут. Т.е.

telnet localhost 3306

Или как обычно мы пишем в скриптах:

$resource = new mysqli('localhost', 'user', 'password');

Работать не будут, т.к. такое соединение тоже будет по обработано через локальный сокет. Конечно можно пробежаться по всем соединениям сайта и заменить localhost на 127.0.0.1 или внешний IP сервера, уже заработает, но есть способ лучше.

Нужно отредактировать в hex редакторе файлы:

libmysqlclient.so.15.0.0
libmysqlclient_r.so.15.0.0
или (зависит от версии mysql)
libmysqlclient.so.16.0.0
libmysqlclient_r.so.16.0.0

Найти единственное совпадение по тексту localhost и заменить на что-нибудь другое, вроде lacalhost, в обоих файлах. После локальные соединения будут работать через TCP/IP! Единственное MySQL не пускает юзеров у которых удаленный хост может быть только localhost в привилегиях. Это надо исправить на % во время переноса.

Пропустим настройки веб сервера, по этой теме полно материалов.
В итоге мы имеем две рабочие машины с одним сервером БД. Спокойно перенастраиваем DNS и идем спать!

p.s. Так же пришла в голову идея проксировать запросы с старого сервера на новый через nginx. Кто-нибудь делал так?

Автор: htaccess

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js