11.08.2010
Очередной тестинг на днях заморозили и я решил, что пришло время по крайней мере личные сервера апнуть. Расскажу о тех проблемах с которыми столкнулся при апгрейде своего основного web-хоста и как их победил.
Это вообще для меня одна из самых ожидаемых фич Squeeze. Было бы вообще здорово перейти с 2.5 сразу на 2.7, но так уж сложилось, что основная работа по миграции на 2.6 была завершена до релиза 2.7. Печально, но это цена за стабильность.
Так или иначе, в связи с изменением версии python, пришлось изменять конфигурацию WSGI-приложений. Тут выяснилась одна тонкость. Приложения у меня публикуются через mod_wsgi + и давно-давно я имел привычку добавлять нужные пути в PYTHONPATH через обработчик WSGI. Потом, с развитием mod_wsgi, в нём появились опции расширения PYTHONPATH и я стал обработчик писать совсем простым, а пути добавлять в конфиге Apache. Сейчас это вышло боком. Дело в том, что в обработчике пути вычисляются программно с учётом текущей версии Python, а в конфиге Апача — прописываются жёстко. В результате для старых приложений пришлось все только пересоздать виртуальное окружение, а для более новых — ещё править конфиги. Поставил в голове галку на будущее.
Я был бы несказанно рад и счастлив, если бы всё у меня было идеологически верно, но есть одно приложение, от которого я никак не могу избавиться. Это форум на базе SMF. Через это безобразие я вынужден заставлять Апач парсить PHP и держать на хосте MySQL.
В логах Апача после апгрейда стали появляться вот такие записи:
[Tue Aug 10 23:23:28 2010] [error] [client 95.108.158.134] sh: host: command not found
[Tue Aug 10 23:23:43 2010] [error] [client 95.108.158.134] sh: host: command not found
[Tue Aug 10 23:23:54 2010] [error] [client 95.108.158.134] sh: host: command not found
Оказалось, что SMF для обратного резольвинга имён сначала пытается использовать команду /usr/bin/host
, и только потом, если не получается, откатывается к родному пехапешному gethostbyaddr
. Разработчики в комментариях мотивируют это тем, что для запуска команды в оболочке можно задать таймаут, а для gethostbyaddr
нельязя. Чудны дела твои, Господи! Благослови Королеву и Open Source. В общем выкинул из SMF этот чудесный кусок кода. А этот форум давно собирался переписать, он во многом не устраивает.
Как я уже сказал, MySQL приходится на хосте держать исключительно ради прихоти SMF, который научился работать с Postgres лишь в версии 2.0, которая пока ещё до состояния релиза не дошла. Ещё разработчики затрудняются ответить на вопрос когда же будет реализована возможность миграции с MySQL на Postgres и будет ли она вообще.
Так или иначе, есть MySQL — нужно его бэкапить. Для резервного копирования и на серверах и на настольных машинах я уже давно применяю Duplicity, который умеет наборы данных выливать на Amazon S3. Невероятно удобно, невероятно надёжно, невероятно дёшево, с учётом того, что Amazon уже что-то около года держит цены на входящий трафик равными нулю. Но сам Duplicity резервировать умеет только файловую систему, что для БД не подходит. Поэтому я настроил ежедневный дамп всех баз данных MySQL. Всех, потому, что когда-то она была не одна и ещё здорово резервировать информацию о пользователях тоже, а она хранится в отдельной БД. И выглядело это вот так:
#!/bin/sh
for DB in `echo 'show databases;' | mysql --silent --batch --disable-pager`; do
mysqldump --create-options --quote-names "${DB}" | gzip > "${DUMP}"
done
После апгрейда стало валить вот такое сообщение:
mysqldump: Got error: 1044: Access denied for user 'root'@'localhost' to database 'information_schema' when using LOCK TABLES
Полез в man, там нашёл разгадку:
mysqldump does not dump the INFORMATION_SCHEMA database by default. As of MySQL 5.1.38, mysqldump dumps INFORMATION_SCHEMA if you name it explicitly on the command line, although currently you must also use the –skip-lock-tables option. Before 5.1.38, mysqldump silently ignores INFORMATION_SCHEMA even if you name it explicitly on the command line.
Т.е. раньше он INFORMATION_SCHEMA не дампил и было ему счастье. Теперь хочет и счастья нет. Пришлось скрипт усложнить:
#!/bin/bash
for DB in `echo 'show databases;' | mysql --silent --batch --disable-pager`; do
DUMP="/var/backups/mysql/${DB}-$(date +%y%m%d%H%M).sql.gz"
OPTS="--create-options --quote-names"
if [[ $DB == information_schema ]]; then
OPTS="${OPTS} --skip-lock-tables"
fi
mysqldump $OPTS "${DB}" | gzip > "${DUMP}"
done
Забавно, что все проблемы связаны не с самим Debian, а с тем что под него написано своими (или чужими в случае с SMF) кривыми ручонками. Последний сюрприз приподнёс pg_dump
, который используется таким же точно способом, как mysqldump
. Было вот так:
for DB in $(psql -lt|grep -v template|cut -d'|' -f1); do
pg_dump -Fc $DB | gzip -9 > /var/backups/postgres/${DB}_$(date +%Y%m%d).pgdump.gz
done
Ошибка после апгрейда какая-то уж совсем неприличная:
connection to database ":" failed: FATAL: database ":" does not exist
/var/backups/postgres/postgres=CTc/postgres_20100811.pgdump.gz: No such file or directory
pg_dump: [archiver (db)] connection to database "(null)" failed: invalid connection option "postgres"
pg_dump: [archiver (db)] connection to database ":" failed: FATAL: database ":" does not exist
/var/backups/postgres/=c/postgres_20100811.pgdump.gz: No such file or directory
pg_dump: [archiver (db)] connection to database "(null)" failed: invalid connection option ""
Оказалось всё предельно просто, pgsql -lt
стал выводить некоторые записи в две строки. Пришлось пошуршать мозгом и подключить суперсилу AWK
:
#!/bin/sh
for DB in $(psql -lt|awk 'NF>2 {print $1}'); do
if [[ ! $DB =~ template ]]; then
pg_dump -Fc $DB | gzip -9 > /var/backups/postgres/${DB}_$(date +%Y%m%d).pgdump.gz
fi
done
На всё ушло около четырёх часов. Дольше всего разбирался с этим чудо-форумом SMF. Я им пользуюсь уже наверное лет 7 или 8, а он всё ещё полон сюрпризов. Сказать что это любовь — не могу, но определённо у меня с ним какая-то связь.
15.08.10 22:56 kostix.myopenid.com комментирует:
В скрипте бэкапа баз MySQL два типичных башизма: нужно либо в шебанг написать /bin/bash, либо переправить
if [[ $DB == information_schema ]]; then
на
if [ $DB = information_schema ]; then
Подробнее, например, тут: https://wiki.ubuntu.com/DashAsBinSh#[[
А то поставите себе dash в качестве шелла по умолчанию (что вполне логично для скорости работы) и этот скрипт работать перестанет.
17.08.10 10:48 uptimebox комментирует:
Верно. Спасибо.