Участник:Rain/Заметки/Onyx: различия между версиями
Rain (обсуждение | вклад) м (Описание скрипта) |
Rain (обсуждение | вклад) м (→Разное) |
||
(не показаны 164 промежуточные версии этого же участника) | |||
Строка 1: | Строка 1: | ||
Заметки по серверам для carid.com: | Заметки по серверам для carid.com: | ||
= Что хотелось бы сделать = | |||
== Кодировки MySQL == | |||
Навести порядок с кодировками базы. Сейчас в настройках на большинстве серверов указан (а местами не указан, а просто используется старое дефолтное значение) latin1. При этом в коде явно указывается использовать юникод через <source lang=mysql>SET NAMES 'utf8'</source> (иначе бы не получалось сохранять всякие там "®" и прочие юникодные символы) - хотя кое-где стоит <source lang=mysql>SET NAMES 'latin1'</source> (но такие места не должны вызвать проблем, в дальнейшем их можно поправить). Т.е., реально настройки указываются одни, база в свойствах таблиц указывает latin1, а реально данные хранятся другие; как следствие - имеем проблемы при работе с дампами.<br>По-идее, в настройках сервера можно сходу указать правильные параметры подключения - это не должно сломать текущую логику работы (нужна проверка на тестовом сервере); кодировка все равно задается через set names (но не уверен, что именно происходит при репликации и в чем (и какими настройками это регулируется) там передаются данные). Для перехода на правильные настройки можно использовать следующий алгоритм: | Навести порядок с кодировками базы. Сейчас в настройках на большинстве серверов указан (а местами не указан, а просто используется старое дефолтное значение) latin1. При этом в коде явно указывается использовать юникод через <source lang=mysql>SET NAMES 'utf8'</source> (иначе бы не получалось сохранять всякие там "®" и прочие юникодные символы) - хотя кое-где стоит <source lang=mysql>SET NAMES 'latin1'</source> (но такие места не должны вызвать проблем, в дальнейшем их можно поправить). Т.е., реально настройки указываются одни, база в свойствах таблиц указывает latin1, а реально данные хранятся другие; как следствие - имеем проблемы при работе с дампами.<br>По-идее, в настройках сервера можно сходу указать правильные параметры подключения - это не должно сломать текущую логику работы (нужна проверка на тестовом сервере); кодировка все равно задается через set names (но не уверен, что именно происходит при репликации и в чем (и какими настройками это регулируется) там передаются данные). Для перехода на правильные настройки можно использовать следующий алгоритм: | ||
Строка 18: | Строка 18: | ||
По-идее, основная часть работы (и downtime сайта) займет время однопоточной заливки дампа (т.е., минут 30). Как вариант - написать скрипт, который будет дампить и заливать базы впараллель, тогда downtime уменьшится минут до 10. | По-идее, основная часть работы (и downtime сайта) займет время однопоточной заливки дампа (т.е., минут 30). Как вариант - написать скрипт, который будет дампить и заливать базы впараллель, тогда downtime уменьшится минут до 10. | ||
=== InnoDB | {{Hider hiding | ||
|title=Заметка по работе с дампами на текущий момент | |||
|content= | |||
* <s>Дамп на бэкапе должен сниматься с default-charset=latin1 и skip-character-set</s> | |||
** Опции взаимоисключающие, но в целом работает. База в utf8, skip-character-set просто сливает ее в том виде, в каком она есть с учетом настроек сервера (т.е., latin1, но кодируя юникод-символы) | |||
: На деле лучше опцию убрать и использовать default-charset=utf8 - это сольет базу также в utf8, но дополнительно добавит всякие там SET NAMES utf8 в теги. | |||
* Дамп на import.carid.com должен сниматься без ключей | |||
** <s>Сейчас сливается с skip-charset, позже сделать по аналогии с backup-сервером, должно работать.</s> Сделано | |||
* <s>Заливаться дамп везде должен с default-charset=latin1</s> | |||
** Вроде уже неактуально. Дамп, сделанный в чистом utf8 (хоть и с параметром CHARSET=latin1 для таблицы) нормально заливается на боевом сервере; надо проверить на import'e. | |||
* На бэкап-сервере кодировки сервера и клиента различаются (latin1 и utf8) без каких-либо настроек в конфиге мускуля | |||
}} | |||
===Пробная замена кодировки на db4 2012.05.9 === | |||
Команда для заливки существующего на тот момент дампа на свежеразвернутый MySQL: | |||
pbunzip2 -c ../carid_2012_05_09.sql.bz2 | sed 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=utf8/g' | sqlpar-restore /dev/stdin localhost root pass carid | |||
После заливки дампа и попытки запуска slave'a обнаружил, что дополнительно надо сдампить функции и процедуры (хранятся в mysql.proc) - на будущее добавил ключик -R к mysqldump в скрипте на backup- и import-серверах, который дампит все это дело и размещает в конце дампа. Триггеры дампятся по дефолту. | |||
'''Проблемы, заметки и т.п:''' | |||
* Не захотела заливаться таблица '''sizeitup_mmy_correspondence''' с ошибкой "ERROR 1071 (42000) at line 31: Specified key was too long; max key length is 1000 bytes". Гуглеж показал, что это давняя (8 лет :) ) бага MySQL, на которую я наткнулся. [http://bugs.mysql.com/bug.php?id=4541 Описание тут]. Залить смог в том виде, в котором оно было в первоначальном дампе - с latin1, после чего сделал '''alter table sizeitup_mmy_correspondence charset=utf8;'''. Отработало нормально. | |||
* В триггерах указывается кодировка клиента при выполнении операций. В новой базе она осталась latin1. Можно подкорректировать замену sed'ом, чтобы убирал и такие моменты. С учетом того, что триггера только 2 - я просто их пересоздал уже с новой кодировкой: | |||
show triggers ; | |||
show create trigger orders_phones_ins ; | |||
drop trigger trigger orders_phones_ins ; | |||
: И дальше копируем то, что в выведенной строке при show create. Аналогично и для 2-го триггера | |||
* В двух функциях стоит CHARSET latin1 - в catname_from_id и detectMake. Аналогично, можно либо поменять sed'ом в дампе, в котором они есть - либо просто пересоздать с новыми параметрами. | |||
=== Ссылки === | |||
* [http://yoonkit.blogspot.com/2006/03/mysql-charset-from-latin1-to-utf8.html Вариант с использованием ALTER smth] - не надо переливать данные, но придется перебрать все колонки всех таблиц. | |||
* [http://blog.sjinks.pro/mysql/3-convert-mysql-database-from-one-charset-to-another/ Русская статья по конвертации] + см. комментарии. Вариант с ALTER может не работать. | |||
* [http://www.codenet.ru/db/mysql/mysql-charset/ Полезная табличка с описанием переменных для кодировок] | |||
* [http://www.rldp.ru/mysql/mysqlpro/sto_proc.htm По функциям и процедурам в MySQL] | |||
== InnoDB == | |||
Перевод базы на InnoDB. Считаю, что это решит нашу текущую проблему с локами базы при смешной нагрузке (как в случае с мастером, когда маленький апдейт лочит все остальные select'ы и они отжирают все доступные подключения - пример такой ситуации у меня есть; так и в случае со слейвом, когда пришедший репликационный запрос лочит вычитку - а в этот момент, например, какой-то скрипт активно использует сервер - в итоге все надолго умирает и идет отставание репликации - по-идее, благодаря версионности InnoDB репликация должна проходить проще. Можно достаточно легко проверить, переведя какой-то слейв на InnoDB и запустив на нем тяжелый скрипт, делающий много вычитки). На данный момент сменить тип движка нельзя для следующих таблиц: | Перевод базы на InnoDB. Считаю, что это решит нашу текущую проблему с локами базы при смешной нагрузке (как в случае с мастером, когда маленький апдейт лочит все остальные select'ы и они отжирают все доступные подключения - пример такой ситуации у меня есть; так и в случае со слейвом, когда пришедший репликационный запрос лочит вычитку - а в этот момент, например, какой-то скрипт активно использует сервер - в итоге все надолго умирает и идет отставание репликации - по-идее, благодаря версионности InnoDB репликация должна проходить проще. Можно достаточно легко проверить, переведя какой-то слейв на InnoDB и запустив на нем тяжелый скрипт, делающий много вычитки). На данный момент сменить тип движка нельзя для следующих таблиц: | ||
Строка 26: | Строка 66: | ||
xcart_categories_index | xcart_categories_index | ||
xcart_extra_field_values | xcart_extra_field_values - не требуется | ||
xcart_make_model | xcart_make_model | ||
xcart_order_details | xcart_order_details | ||
xcart_order_messages | xcart_order_messages | ||
xcart_products | xcart_products - done | ||
xcart_users_map | xcart_users_map | ||
</pre> | |||
<s>Ошибка при конвертации: | |||
"ERROR 1075 (42000) at line 1: Incorrect table definition; there can be only one auto column and it must be defined as a key" в xcart_counters | |||
с ошибкой разобраться,</s> | |||
: Пофиксили 2012-05-10 после того, как попадали slave-сервера из-за ошибки autoincrement'a | |||
: для таблиц потенциально можно вынести поиск в отдельную маленькую таблицу, оставив ее в MyISAM (требует правки кода, так что в крайнем случае можно оставить их как есть - все же лучше иметь 8 лочащихся таблиц, чем 300; однако среди них есть достаточно крупные с частым обращением). | |||
'''Поиск использования таблиц в коде (на выборку данных):''' | |||
<pre> | |||
Поиск файлов, использующих данные таблицы для выборки текста при поиске. Поиск идет по php-файлам по нужной таблице, после чего в найденных файлах ищется ключевое слово AGAINST. | |||
# grep -ilE '\bAGAINST *\(' `grep -HrEnlIo --include="*.php*" "xcart_products" /home/carid/httpdocs/ | grep -v 'var/log'` | |||
xcart_products: products | |||
/home/carid/httpdocs/include/classes/SearchMain.php | |||
/home/carid/httpdocs/include/search.php | |||
/home/carid/httpdocs/include/orders.php - только в products | |||
xcart_categories_index: | |||
/home/carid/httpdocs/include/search_redirect.php | |||
xcart_make_model: product_mm | |||
/home/carid/httpdocs/include/search_redirect.php | |||
/home/carid/httpdocs/include/classes/SearchMain.php | |||
xcart_users_map: users_map | |||
xcart_order_details: order_details | |||
/home/carid/httpdocs/include/orders.php - в order_details | |||
xcart_order_messages: order_messages | |||
/home/carid/httpdocs/include/orders.php - в order_messages | |||
xcart_extra_field_values: : не найдена в коде вообще и ничего не содержит | |||
</pre> | |||
Все файлы с AGAINST: | |||
<pre> | |||
/home/carid/httpdocs/include/search_redirect.php | |||
/home/carid/httpdocs/include/search.php | |||
/home/carid/httpdocs/include/orders.php | |||
/home/carid/httpdocs/include/classes/SearchMain.php | |||
/home/carid/httpdocs/kmp/admin/application/models/searchindex.php | |||
</pre> | </pre> | ||
---- | |||
Само по себе изменение остальных таблиц должно пройти гладко; на время конвертации можно переключить Slave-сервера на мастер (т.к. мастер мощный, а слейв могут долго работать с таблицей; как следствие - отставание репликации). Помимо ликвидации табличных локов, это позволит держать всю базу в оперативной памяти, т.е., будет не особо важно, насколько быстрый диск (остальные параметры на тему сброса данных на диск можно потюнить). | Само по себе изменение остальных таблиц должно пройти гладко; на время конвертации можно переключить Slave-сервера на мастер (т.к. мастер мощный, а слейв могут долго работать с таблицей; как следствие - отставание репликации). Помимо ликвидации табличных локов, это позволит держать всю базу в оперативной памяти, т.е., будет не особо важно, насколько быстрый диск (остальные параметры на тему сброса данных на диск можно потюнить). | ||
Строка 49: | Строка 132: | ||
После решения проблем подобных миграций при недостатке производительности (но не ранее) можно рассматривать переход на другие базы (на случай, если векторы развития MySQL и остальных баз не изменится) - на данный момент это MariaDB+XtraDB или Percona Server. | После решения проблем подобных миграций при недостатке производительности (но не ранее) можно рассматривать переход на другие базы (на случай, если векторы развития MySQL и остальных баз не изменится) - на данный момент это MariaDB+XtraDB или Percona Server. | ||
<s> | |||
<pre> | <pre> | ||
Про Foreign Key к Богдану - момент не понял, ведь если его явно не задавать, то возможностью можно просто не пользоваться и проблем это не вызовет. | Про Foreign Key к Богдану - момент не понял, ведь если его явно не задавать, то возможностью можно просто не пользоваться и проблем это не вызовет. | ||
</pre> | </pre> | ||
</s> | |||
: Разобрались, просто не поняли друг друга. | |||
=== Оптимизация использования MySQL-серверов, отказоустойчивость | === Ссылки === | ||
* [http://highload.com.ua/index.php/2011/03/18/percona-mysql-server/ Percona Server в двух словах] | |||
== Оптимизация использования MySQL-серверов, отказоустойчивость == | |||
Потенциально можно раскидывать запросы чтения с основного сайта с Master на Slave-сервер для разгрузки мастер-сервера. Подобные оптимизации можно делать только после ликвидации локов (что даст ускорение репликации и большую синхронность между серверами; как следствие - более корректную вычитку данных приложением). | Потенциально можно раскидывать запросы чтения с основного сайта с Master на Slave-сервер для разгрузки мастер-сервера. Подобные оптимизации можно делать только после ликвидации локов (что даст ускорение репликации и большую синхронность между серверами; как следствие - более корректную вычитку данных приложением). | ||
Строка 62: | Строка 152: | ||
** Как подпункт: в дальнейшем в таком варианте можно сделать, например, балансировку на уровне DNS'a - объявить для алиаса несколько IP-адресов и он в round robin-режиме будет отдавать разные сервера. | ** Как подпункт: в дальнейшем в таком варианте можно сделать, например, балансировку на уровне DNS'a - объявить для алиаса несколько IP-адресов и он в round robin-режиме будет отдавать разные сервера. | ||
* На уровне подключений: | * На уровне подключений: | ||
** mysql-proxy. Без наведения порядка с кодировками не взлетит, так как раскидывает подключения на уровне запросов (т.е., set names может уйти на один сервер, а запрос - на другой и получим кашу. Как вариант - жестко задать настройки | ** mysql-proxy. Без наведения порядка с кодировками не взлетит, так как раскидывает подключения на уровне запросов (т.е., set names может уйти на один сервер, а запрос - на другой и получим кашу. Как вариант - жестко задать настройки, чтобы на любой коннект принудительно устанавливалась нужная кодировка (т.е., set names в конфиге сервера) - должно помочь, но требует проверки. Прост в настройке, может раскидывать запросы на запись и чтения по разным серверам (в т.ч. несколько серверов чтения) | ||
** haproxy. Рулит подключениями на уровне TCP-соединений. Более навороченный вариант, требует больше настройки. | ** haproxy. Рулит подключениями на уровне TCP-соединений. Более навороченный вариант, требует больше настройки. | ||
Еще оптимизация - сделать Master-Master-репликацию. Тут есть 2 | Еще оптимизация - можно сделать Master-Master-репликацию. Тут есть 2 подпункта: | ||
* Можно вынести админку, с которой работают импортеры/апдейтеры и прочие отделы на другой мастер-сервер при условии малого пересечения таблиц, в которые пишет админка и сайт (подразумевается, например, что посетитель не пишет в таблицу с перечнем товаров, в которую пишет сотрудник и т.п. Как подвариант - пересекающиеся таблицы писать только на один сервер для исключения ситуации split brain'a) | * Можно вынести админку, с которой работают импортеры/апдейтеры и прочие отделы на другой мастер-сервер при условии малого пересечения таблиц, в которые пишет админка и сайт (подразумевается, например, что посетитель не пишет в таблицу с перечнем товаров, в которую пишет сотрудник и т.п. Как подвариант - пересекающиеся таблицы писать только на один сервер для исключения ситуации split brain'a) | ||
* HA-кластер. Умирает один мастер - переключаемся каким-то средством балансировки на другой (через haproxy/mysql-proxy/heartbeat и т.п.). Пока второй мастер живой - используется для админки (1-й пункт) или как слейв. Плюс как слейв можно завести 3-й сервер (освободившийся от memcached, например). | * HA-кластер. Умирает один мастер - переключаемся каким-то средством балансировки на другой (через haproxy/mysql-proxy/heartbeat и т.п.). Пока второй мастер живой - используется для админки (1-й пункт) или как слейв. Плюс как слейв можно завести 3-й сервер (освободившийся от memcached, например). На пару с избавлением от центрального memcached получим полностью отказоустойчивую систему. | ||
=== Memcached | === Быстродействие серверов === | ||
Небольшие заметки по быстродействию различных конфигураций серверов. | |||
* db4, тест 2012.05.09, база переведена на innodb, нормализована кодировка. 24 ядра E7540, HT отключен (т.е., все ядра честные), 128 Гб памяти, 160 Гб SSD под базу. Сливка данных в /dev/null: 15.8GB 0:05:45 [46.9MB/s]. mysqldump / mysql упирались в процессор, скорость иногда просаживалась до очень малых значений (единицы МБ). | |||
* Там же, вставка дампа (из которого потом и делался InnoDB-вариант). MyISAM: 3 минуты распаковка/нарезка/замена latin1 на utf8 + 31:37 чистая многопоточная вставка дампа (из них xcart_order_messages включали ключи 10:16). Практически все время был загружен проц. | |||
* Вставка аналогичного дампа на server1.carid.com. 2*E5620 (4*2,4 + HT, т.е., 8 настоящих, 16 виртуальных ядер), 16 Гб памяти, одиночный диск (500-ка, RAID Edition). Все таблицы залились за 15 минут, еще ~6 минут включались ключи для xcart_order_messages (т.е., разница в скорости работы бэкапа и db4 превышает разницу в частоте (х1,25)) | |||
* DB4, 2012.05.24, новый SSD, нормальная разбивка, свежеснятый дамп, HT включен. 3:30 распаковка, 26:30 - заливка всех таблиц до начала включения ключей в xcart_order_messages, ключи включались еще +9:30 (т.е., общее время выполнения с распаковкой - около 40 минут) | |||
== Memcached == | |||
Убрать единый сервер Memcached и создать собственный Memcached на каждом веб-сервере. Плюсы: | Убрать единый сервер Memcached и создать собственный Memcached на каждом веб-сервере. Плюсы: | ||
Строка 78: | Строка 177: | ||
* Не держим лишний мощный, но практически не используемый сервер | * Не держим лишний мощный, но практически не используемый сервер | ||
Недостаток такого варианта - выделение памяти на каждом сервере. На деле из 36 Гб на веб-серверах используется 2-3 Гб. Memcached за долгое время набрал данных лишь на 8 Гб (на | Недостаток такого варианта - выделение памяти на каждом сервере. На деле из 36 Гб на веб-серверах используется 2-3 Гб. Memcached за долгое время набрал данных лишь на 8 Гб (на момент написания текста), из них многое - из-за постоянных тестов. Эффективность кэша на момент 8 Гб - порядка 80 процентов (update: 80 процентов из всех выборок чтения. На деле доля удачных чтений из всех запросов на уровне 60 процентов), что мало. Итого, если выделить даже по 16 Гб под memcached на каждом сервере - этого с головой хватит и для нужд веб-сервера, и для memcached. По процессору - даже единый memcached практически не нагружает процессор (нагрузка на уровне пары процентов для одного ядра сервера). При разделении нагрузка на каждый отдельный memcached уменьшится до 3 раз на текущий момент. Когда начнем упираться в процессор на веб-сервере - наличие на нем memcached будет далеко не самой большой проблемой (иначе говоря, мы скорее заведем новый сервер впараллель к трем существующим, чем упремся в процессор). | ||
Варианты реализации: | Варианты реализации: | ||
Строка 86: | Строка 185: | ||
* Использовать средства приложения: Богдан говорит, что приложение при легкой правке кода может принимать массив серверов, при этом можно указывать их вес (т.е., для 127.0.0.1 указать максимальный приоритет, для других узлов - меньший). | * Использовать средства приложения: Богдан говорит, что приложение при легкой правке кода может принимать массив серверов, при этом можно указывать их вес (т.е., для 127.0.0.1 указать максимальный приоритет, для других узлов - меньший). | ||
==== Тест латентности сети | ---- | ||
{{Hider hiding | |||
|title=Скриптик для мониторинга эффективности кэша | |||
|content= | |||
<source lang=bash> | |||
while sleep 1 | |||
do | |||
clear | |||
echo -e "stats\nquit" | nc localhost 11211 | | |||
awk ' | |||
/cmd_get/{cg=$3} | |||
/cmd_set/{cs=$3} | |||
/get_hits/{gh=$3} | |||
/delete_misses/{dm=$3} | |||
/delete_hits/{dh=$3} | |||
END { | |||
t=cg+cs+dm+dh | |||
dt=dm+dh | |||
print "Report: \t[R: "cg/t*100"% (only "gh/t*100"% successful) + W: "cs/t*100"% + D: "dt/t*100"% (only "dh/t*100"% successful)] from "t" total requests" | |||
}' | |||
done | |||
</source> | |||
}} | |||
=== Перезапуск Memcached 2012.06.26 === | |||
* Объем данных за час: примерно 500 Мб | |||
* Объем данных за день: 3,4 Gb | |||
* 2 дня: 4,2 Гб | |||
* 3 дня: 5 Gb | |||
: => прирост ~800 Mb / day. Итого на хранение данных за неделю (даже без удаления устаревших) вполне хватит 8 Гб. | |||
=== Тест латентности сети === | |||
Провел небольшой тест на латентность сети при работе с мелкими данными в кэше. | Провел небольшой тест на латентность сети при работе с мелкими данными в кэше. | ||
Строка 100: | Строка 234: | ||
for ($i = '1'; $i <= "$argv[3]"; $i++) { | for ($i = '1'; $i <= "$argv[3]"; $i++) { | ||
$m->set("$i", " | $a=rand().rand().rand(); | ||
$m->set("$i", "$a"); | |||
} | } | ||
?> | ?> | ||
</source> | </source> | ||
---- | |||
Для Ъ: создание указанного 3-м параметром числа ключей и занесение в каждый из ключей рандомного числа. | Для Ъ: создание указанного 3-м параметром числа ключей и занесение в каждый из ключей рандомного числа. | ||
Было сделано 4 тестирования - скрипт на локалхосте + memcached на локалхосте, скрипт на локалхосте + memcached на машине в локальной сети и для | Было сделано 4 тестирования - скрипт на локалхосте + memcached на локалхосте, скрипт на локалхосте + memcached на машине в локальной сети и для обоих вариантов скрипт и memcached менялись местами. | ||
==== 100 Мбит ==== | |||
Линк 100 Мбит (Реально сервер подключен к гигабитному свичу, но десктоп - к 100 Мбит. При случае протестирую на полностью гигабитной сети). Результаты повторяемые: | |||
Тест на десктопе: | Тест на десктопе: | ||
Строка 150: | Строка 292: | ||
sys 0m15.245s | sys 0m15.245s | ||
</pre> | </pre> | ||
==== 1 Гбит ==== | |||
{{ToDo|Сделать тест для гигабитной незагруженной сети}} | |||
==== Сеть vs Сокет ==== | |||
Сделал еще тест для сравнения скорости работы через сеть и через unix-сокет. Правда, для этого пришлось перейти на php5-memcache (php5-memcached не хотел подключаться к сокету - или я что-то не так сделал). Соответственно, по правкам скрипта - new Memcache() и connect вместо addServer. Тест сделан на одной и той же незагруженной машине с двухядерным процессором. Результаты: | |||
Сокет: | |||
<pre> | |||
root@developer:~# time php file.php unix:///tmp/mem.sock 0 1000000 | |||
real 0m30.652s | |||
user 0m10.109s | |||
sys 0m9.465s | |||
</pre> | |||
Сеть: | |||
<pre> | |||
root@developer:~# time php file.php 127.0.0.1 22122 1000000 | |||
real 0m41.677s | |||
user 0m11.513s | |||
sys 0m15.353s | |||
</pre> | |||
Результаты стабильно повторяемы. | |||
=== Ссылки === | |||
* https://www.linux.org.ru/forum/admin/7465868 - пронаблюдать тред | |||
* [http://sourceforge.net/projects/repcached/forums/forum/749745/topic/5080246/ Аналогичная моей проблема c репликацией цепочкой] | |||
= Безопасность = | |||
== Web == | |||
=== Soft === | |||
* Apache/nginx на веб-серверах сейчас работают через права группы (контент под carid:users, Апач - под apache:users (почему-то :) кроме 2-го веба), nginx - через nginx:users), но контент часто имеет права 666 или 777. <s>Определиться, нужна ли запись данных Апачем в каталоги сайта; если да - перевести Апач под пользователя сайта</s> (Не нужна), если нет - просто переключить с users на выделенную группу (см. ниже). | |||
* Запретить выполнение скриптов из каталогов аплоада файлов (/images, /files и вообще любых каталогов, где могут появляться файлы) <s>(без разницы, аплоадятся они через веб или FTP)</s> (Не важно. Аплоадятся на другом сервере и только через FTP). На данный момент в images/ есть ряд скриптов сомнительной нужности (в этом каталоге): | |||
<pre> | |||
root@345735-web1:/home/carid/httpdocs/images# find . -iname "*.php" | |||
./mcafee3.php | |||
./ebay/left_menu.php | |||
./ebay/contact.php | |||
./ebay/header_menu_d.php | |||
./gps-systems/garmin/downloads/script.php | |||
</pre> | |||
: По возможности перенести их в другое место, а каталог закрыть. | |||
==== PHP ==== | |||
* Позапрещать ненужные функции в настройках PHP. Сейчас свободно работают всякие там system, exec и прочее, что позволяет свободно пользоваться веб-шеллами (проверено). При использовании багов в коде сайта по серверу можно свободно разгуливать и запускать программы с правами apache (48) (а с учетом старости софта - вполне возможно дальнейшее повышение привилегий). | |||
---- | |||
: Набросок однострочников для поиска ненужных функций: | |||
<source lang=bash> | |||
# Поиск функций в коде | |||
LANG=C grep -hEIro --include="*.php*" "[ ^]\b[a-z0-9_]+\(" /home/carid/httpdocs/ | sed -r 's/\(|^ //g' | sort -u > carid_func_list | |||
# вывод доступных функций в PHP | |||
php -r '$functions = get_defined_functions();sort($functions['internal']);print_r($functions['internal']);' 2>/dev/null | awk '/[0-9]\] =>/{print $3}' > php_func_int | |||
# Показ используемых встроенных функций | |||
fgrep -x -f carid_func_list php_func_int | |||
# Показ неиспользуемых встроенных функций | |||
fgrep -x -v -f carid_func_list php_func_int | |||
# Поиск функций в коде | |||
echo list_of_functions | tr ' ' '\n' | while read i | |||
do | |||
grep -HrEnIo --include="*.php*" "[ ^]\b$i\(" /home/carid/httpdocs/ || echo "${i}: NOT_USED" | |||
done | awk '/NOT/; /\($/{gsub(/\(/, "", $2); gsub(/:$/, "", $1); print $2": "$1}' > flist | |||
</source> | |||
---- | |||
На данный момент в коде найдена 631 встроенная функция. | |||
<pre> | |||
При тестовом запуске заметил, что в коде бывает встречаются реально не используемые функции - например, в модулях Zend'a | |||
(TODO: запросить у программеров, какие модули Zend'a и прочих фреймворков реально нужны) | |||
или в каких-нибудь OS-специфичных проверках - например, в ./modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php попалось такое: | |||
if (OS_WINDOWS && isset($section_text['RETURNS'])) { | |||
ob_start(); | |||
system($cmd, $return_value); | |||
и это единственное место использования system() в коде. Соответственно, можно безопасно это отключить. | |||
</pre> | |||
Наброски списка функций для отключения: | |||
---- | |||
<source lang=ini> | |||
# Остатки нагугленных функций, которые отключали другие люди. Не удалил на всякий случай, может пригодится. | |||
# disable_functions = eval, PHP_SELF, highlight_file, ini_restore, ini_set, posix_setpgid, posix_setsid | |||
# Не найденные в документации функции. | |||
# proc, dll, max_execution_time, proc_show, cpuinfo, meminfo, cmd, uname, ftp, debugger_on, debugger_off, listen, ioctl, leak, ssthru, view_size | |||
# Разное | |||
disable_functions = dl, posix_mkfifo, chown, link, symlink, phpinfo, ftp_exec, chgrp, ssh2_exec, posix_setuid, posix_setgid, posix_kill, escapeshellarg, escapeshellcmd | |||
disable_functions = syslog, closelog, define_syslog_variables, openlog | |||
# Требуется проверка - функции могут для чего-то использоваться | |||
disable_functions = chmod, set_time_limit, php_uname, posix_uname, disk_free_space, diskfreespace, disk_total_space, get_env, ini_get_all, ini_get, error_log | |||
# Зависит от используемого варианта PHP. Работает только в том случае, когда PHP подключен через mod_php | |||
disable_functions = apache_get_modules, apache_get_version, apache_note, apache_setenv | |||
# Выполнение команд | |||
disable_functions = shell_exec, exec, system, passthru, popen, proc_open, proc_nice, proc_get_status, proc_close, proc_terminate | |||
# Работа с процессами | |||
disable_functions = pcntl_alarm, pcntl_exec, pcntl_fork, pcntl_getpriority, pcntl_setpriority, pcntl_signal_dispatch, pcntl_signal, pcntl_wait | |||
disable_functions = pcntl_waitpid, pcntl_wexitstatus, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig | |||
</source> | |||
---- | |||
: Проверка показала использование в коде 73 функций из списка выше (без тех, что требуют проверки, но с php_uname и posix_uname), из них 2 - в комментариях. | |||
{{Hider hiding | |||
|title=Список протестированных функций | |||
|content= | |||
<pre> | |||
pcntl_waitpid: NOT_USED | |||
pcntl_wexitstatus: NOT_USED | |||
pcntl_wifexited: NOT_USED | |||
pcntl_wifsignaled: NOT_USED | |||
pcntl_wifstopped: NOT_USED | |||
pcntl_wstopsig: NOT_USED | |||
pcntl_wtermsig: NOT_USED | |||
pcntl_alarm: NOT_USED | |||
pcntl_exec: NOT_USED | |||
pcntl_fork: NOT_USED | |||
pcntl_getpriority: NOT_USED | |||
pcntl_setpriority: NOT_USED | |||
pcntl_signal_dispatch: NOT_USED | |||
pcntl_signal: NOT_USED | |||
pcntl_wait: NOT_USED | |||
shell_exec: NOT_USED /home/carid/httpdocs/include/classes/class.upload.php:424 | |||
exec: /home/carid/httpdocs/include/classes/Zend/Db/Adapter/Pdo/Abstract.php:256 | |||
exec: NOT_USED /home/carid/httpdocs/include/classes/class.upload.php:424 | |||
exec: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Builder.php:378 | |||
exec: /home/carid/httpdocs/modules/HTML_PDF/dompdf/load_font.php:231 | |||
system: NOT_USED /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:449 | |||
passthru: NOT_USED | |||
popen: /home/carid/httpdocs/payment/cc_saferpay.php:65 | |||
popen: /home/carid/httpdocs/payment/cc_saferpay.php:137 | |||
popen: /home/carid/httpdocs/include/func/func.https_ssleay.php:103 | |||
popen: /home/carid/httpdocs/include/func/func.https_httpscli.php:100 | |||
popen: /home/carid/httpdocs/include/func/func.https_curl.php:169 | |||
popen: /home/carid/httpdocs/include/func/func.https_openssl.php:108 | |||
popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Builder.php:442 | |||
popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:454 | |||
popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:535 | |||
popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:724 | |||
popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:247 | |||
proc_open: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:125 | |||
proc_open: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:131 | |||
proc_nice: NOT_USED | |||
proc_get_status: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:168 | |||
proc_close: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:173 | |||
proc_terminate: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:156 | |||
apache_get_modules: /home/carid/httpdocs/m1info.php:926 | |||
apache_get_version: NOT_USED | |||
apache_note: NOT_USED | |||
apache_setenv: NOT_USED | |||
syslog: /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php:84 | |||
syslog: /home/carid/httpdocs/include/classes/Zend/Log/Writer/Syslog.php:265 | |||
syslog: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR.php:84 | |||
closelog: /home/carid/httpdocs/include/classes/Zend/Log/Writer/Syslog.php:237 | |||
define_syslog_variables: NOT_USED | |||
openlog: /home/carid/httpdocs/include/classes/Zend/Log/Writer/Syslog.php:177 | |||
dl: /home/carid/httpdocs/kmp/admin/ci_system/libraries/Image_lib.php:1458 | |||
dl: /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php:743 | |||
dl: /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php:748 | |||
dl: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR.php:743 | |||
dl: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR.php:748 | |||
posix_mkfifo: NOT_USED | |||
chown: NOT_USED | |||
link: /home/carid/httpdocs/include/classes/Zend/Feed/Entry/Atom.php:243 | |||
link: /home/carid/httpdocs/include/classes/Zend/Feed/Entry/Atom.php:252 | |||
link: /home/carid/httpdocs/include/classes/Zend/Feed/Atom.php:121 | |||
link: /home/carid/httpdocs/include/classes/Zend/Feed/Atom.php:130 | |||
symlink: NOT_USED | |||
phpinfo: /home/carid/httpdocs/wde-master/admin/general.php:63 | |||
phpinfo: /home/carid/httpdocs/kmp/admin/application/models/bugreportmodel.php:54 | |||
phpinfo: /home/carid/httpdocs/include/classes/Zend/Tool/Framework/System/Provider/Phpinfo.php:35 | |||
phpinfo: /home/carid/httpdocs/include/classes/class.upload.php:491 | |||
phpinfo: /home/carid/httpdocs/include/classes/class.upload.php:2986 | |||
phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:180 | |||
phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:574 | |||
phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:662 | |||
phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:902 | |||
phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:357 | |||
phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:410 | |||
phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:454 | |||
phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:722 | |||
phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:968 | |||
ftp_exec: NOT_USED | |||
chgrp: NOT_USED | |||
ssh2_exec: NOT_USED | |||
posix_setuid: NOT_USED | |||
posix_setgid: NOT_USED | |||
posix_kill: NOT_USED | |||
escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_doc_lib.php:22 | |||
escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_rtf_lib.php:25 | |||
escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_pdf_lib.php:24 | |||
escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_ppt_lib.php:31 | |||
escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:440 | |||
escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:447 | |||
escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:525 | |||
escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:529 | |||
escapeshellarg: /home/carid/httpdocs/modules/HTML_PDF/dompdf/load_font.php:231 | |||
escapeshellcmd: NOT_USED | |||
php_uname: /home/carid/httpdocs/wde-master/admin/general.php:104 | |||
php_uname: /home/carid/httpdocs/m1info.php:595 | |||
php_uname: /home/carid/httpdocs/include/classes/CyberSorce/CYBERSOURCE.php:199 | |||
php_uname: /home/carid/httpdocs/include/classes/Zend/Mail/Storage/Writable/Maildir.php:417 | |||
php_uname: /home/carid/httpdocs/include/classes/Zend/Mail.php:1117 | |||
php_uname: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:311 | |||
php_uname: /home/carid/httpdocs/check_requirements.php:346 | |||
php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/DependencyDB.php:452 | |||
php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Dependency2.php:275 | |||
php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Registry.php:780 | |||
php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:26 | |||
php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:88 | |||
php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:129 | |||
php_uname: /home/carid/httpdocs/modules/HTML_PDF/dompdf/include/functions.inc.php:205 | |||
posix_uname: NOT_USED | |||
</pre> | |||
}} | |||
* Использовать open_basedir - сейчас доступ к файловой системе через PHP для основного сайта никак не ограничен; для доменов на mail.carid.com кое-где указан, но "/tmp" используется общий для всех сайтов | |||
* По возможности актуализировать (и поддерживать в дальнейшем) версии используемых фреймворков и прочих веб-компонентов: | |||
** /home/carid/httpdocs/include/classes/Zend/Version.php: текущая версия '''1.11.7''', актуальная: '''1.11.11''', [http://framework.zend.com/changelog/1.11.8 changelog (и далее)] | |||
** /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2: текущая версия '''1.7.2''' (Rel: 2008-05-17), актуальная: '''1.9.4''' (Rel: 2011-07-07), [http://pear.php.net/package/PEAR/download/1.9.4 changelog (+более ранние)], + проверить используемые модули. | |||
** /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php: текущая версия '''1.7.1''' (Rel: 2008-02-03), остальное аналогично предыдущему. | |||
* Протестировать связку suhosin + mod_security + fail2ban | |||
== FTP == | |||
* /etc/proftpd.conf@mail.carid.com: Umask 027 | |||
===== Ссылки ===== | |||
* [http://forum.antichat.ru/showthread.php?p=352564 Варианты веб-шеллов в обход system() и т.п.] | |||
* [http://habrahabr.ru/blogs/php/51485/ Заметки по open_basedir] | |||
== Общее == | |||
=== Сервисы === | |||
==== Общее ==== | |||
* Полностью выпилить Mail-сервера (smtp, pop/imap) на вебах и DB-серверах, перейти на nullmailer-like пересылку с форвардом на mail.carid.com - избавление от лишних потенциально уязвимых сервисов, требующих обслуживания. | |||
** Все bounced-письма или недоставленные сообщения должны собираться и обрабатываться на mail.carid.com. | |||
* Убрать automount. Идет в дефолтной поставке и на серверах не выполняет ничего полезного. После этого можно будет убрать каталоги '''/misc''', '''/net''' | |||
* Определиться с софтом, необходимым хостеру. На данный момент непонятный статус у следующих процессов: | |||
<pre> | |||
Web: | |||
dsm_* | |||
rhnsd | |||
snmpd | |||
nimbus | |||
Mail: | |||
+ brcm_iscsiuio | |||
+ iscsid | |||
+ *simpana* (cvd, EvMgrC) | |||
---- | ---- | ||
==== | + miniserv WebMin'a | ||
</pre> | |||
{{Hider hiding | |||
|title=Заметки по сервисам | |||
|content= | |||
; rs-sysmon: Rackspace'овский пакет, содержащий баш-скрипт, через каждые 10 минут (задается в настройках) по крону делающий снапшот состояния системы - процессов, соединений и т.п. и складирующий логи в /var/log/rs-sysmon/ | |||
; holland: Изначально Rackspace'овская разработка, система бэкапа. Бэкапы лежат в /var/spool/holland/, последние - годичной давности. Не заметил запускающих скриптов или записей в cron'e - возможно, запускается руками. | |||
; rs-rhntools: Некая хреновина для слежения за обновлениями. Из README: "General usage is to have a cronjob run nightly that calls 'rs-rhntools checkupdate'. Should updates be available, checkupdate will call up2date/yum to install updates." | |||
; psacct: [http://unixhome.org.ua/blog/programs/362.html Описание]. У нас установлена, но отключена (не работает). | |||
}} | |||
* Сделать аудит crontab'ов и периодически запускаемых процессов в них, расписать, зачем нужен каждый скрипт и кем (по чьему запросу) он был добавлен. | |||
* После удаления лишних сервисов удалить ненужные, временные и т.п. аккаунты. | |||
* Пообновлять пароли на серверах, перейти на sha512-хэши. | |||
* Ввести политику использования ssh-ключей, запретить использование парольного входа, запретить рут-логин, (запретить туннели?). | |||
* Избавиться от реальных пользователей для proftpd на mail.carid.com, перейти на виртуальных | |||
** Определиться со списком нужных пользователей и тем, куда они должны ходить; далее либо перевести proftpd под nobody, а для каталогов сделать UserOwner/GroupOwner <юзер/группа сайта> (это сейчас не работает, хоть и встречается в конфиге), что попутно позволит избежать проблем с правами, либо (если заработает) использовать одного пользователя/группу в файле описания виртуальных пользователей. | |||
<pre> | |||
Заработает. Делаем что-то вроде: | |||
AuthUserFile /etc/proftpd/auf | |||
# Или вообще выпилить обычных системных юзеров и разрешить только тех, которые описаны в файле выше - т.е., убрать mod_auth_unix.c | |||
AuthOrder mod_auth_file.c mod_auth_unix.c | |||
RequireValidShell off | |||
в конфиге сервера, после чего | |||
echo PASSWORD | ftpasswd --passwd --gid=2521 --file=/etc/proftpd/auf --md5 --not-system-password --name USER --shell /usr/sbin/nologin --uid 10001 --home=/bla/bla/images --stdin | |||
Получаем кучку виртуальных юзеров, работающих с контентом под одним пользователем, но с различающимися логинами. В GECOS можно загнать полные имена, чтобы потом проще было находить виновных :). | |||
Файлик читается юзером proftpd (или тем, кто определен в конфиге как User), т.е., он должен иметь права на чтение файла (думаю, есть смысл сделать proftpd:nobody 640) | |||
</pre> | |||
* Перевод SUID-бинарников на группы | |||
{{Hider hiding | |||
|title=Заметка | |||
|content= | |||
<source lang=bash> | |||
find / -xdev -perm -4000 -ls | |||
groupadd -r suid | |||
bins='/bin/su /bin/ping /bin/mount /bin/ping6 /bin/umount /usr/bin/chsh /usr/bin/chfn /usr/bin/newgrp /usr/bin/passwd /usr/bin/gpasswd /sbin/mount.nfs' | |||
chown :suid $bins | |||
chmod 4750 $bins | |||
find / -xdev -perm -4000 -ls | |||
</source> | |||
}} | |||
==== Mail ==== | |||
* После изменений архитектуры определить, нужен ли отдельный mysql-сервер на mail.carid.com - как минимум определиться с тем набором баз, которые в данный момент на нем есть. | |||
Вроде используется для CPanel, но кроме него в базе есть еще куча мусора, вроде старой копии базы для carid | |||
* Убрать xinetd: через него запускаются только почтовые сервисы (qmail и т.п.), ftp и rsync. Почта и FTP используются постоянно, у нас не так мало ресурсов, чтобы использовать суперсервер, поэтому можно перевести их в standalone-режим. rsyncd как демон теперь не используется. | |||
* Убрать rsyncd. Заменен lsync'ом, который работает с rsync через ssh. | |||
==== SQL-DB-сервера ==== | |||
Определиться с тем, какие у нас сервера будут работать (в т.ч. определиться по архитектуре и требуемой мощности серверов), потом смотреть сервисы | |||
==== NOSQL-сервер (memcached) ==== | |||
Потенциально можно убрать вообще.<s> На данный момент, помимо перечисленного в общем разделе, из лишнего там работают: | |||
* vsftpd | |||
* mysqld | |||
* xinetd | |||
</s> | |||
Убрано. | |||
== | === Файловая система === | ||
* Сделать лучшую разбивку дискового пространства на разделы, использовать запрещающие опции монтирования для разделов, доступных на запись, использовать квоты. | |||
<pre> | |||
Кандидаты на вынесение на другой раздел: | |||
/home/ (особенно для Web'ов, где в /home хранится веб-контент) | |||
/var | |||
/var/log | |||
/usr (статический контент, можно даже сделать с RO, но надо проверить работоспособность бэкапа - или доработать скрипты) | |||
/tmp (кое-где уже есть) | |||
/var/qmail/ для Mail-сервера | |||
/var/lib/mysql/ вынести в отдельный каталог (по аналогии с master-серверами) и на отдельный раздел | |||
/home/carid/httpdocs/var/ на вебах вынести в /var/log/<что-то> или просто на отдельный раздел. | |||
/var/run и /var/lock - подумать насчет вынесения в tmpfs | |||
</pre> | |||
* На вебах и master-сервере сменить права на контент на 640(750), раз в сутки делать поиск по cron'у файлов с кривыми правами и фиксить. | |||
* Убрать дефолтную системную группу (users, 100) для веб-контента, использовать выделенную группу (завести группу carid, например, сделать ее основной для пользователя carid). | |||
** nginx перевести в эту группу (ему надо только читать контент - хватит и группы с RO-правами) | |||
= Разное = | |||
Заметки на различные тематики: | Заметки на различные тематики / для сортировки: | ||
* Варианты балансировки обращения к базе: | * Варианты балансировки обращения к базе: | ||
Строка 170: | Строка 664: | ||
* Оптимизация/изменение настроек MySQL: | * Оптимизация/изменение настроек MySQL: | ||
** Не запускать slave-сервер автоматом при перезапуске. | ** Не запускать slave-сервер автоматом при перезапуске. | ||
** Сделать ограничение по max_user_connections, а не по max_connections | |||
* Вынести каталог с сервер-специфичными файлами (в частности, var) из каталога httpdocs | * Вынести каталог с сервер-специфичными файлами (в частности, var) из каталога httpdocs | ||
* | * <s>LOW_PRIORITY_UPDATES актуально только для движков с табличными локами, т.е., на InnoDB апдейты будут идти с нормальным приоритетом.</s> | ||
Неактуально, на деле это 2 разные проблемы и проблема с низкоприоритетными апдейтами была из-за забытого в коде ключа. Проблема мной решена. | |||
* По работающей в данный момент почте на серверах: | |||
** memcached: почта не работает совсем. Postfix шлет почту на полное имя, не может отрезолвить домен, бонсит мыло опять-таки на полный домен, которое тоже никуда не приходит. Про то, что он отвечает за *db2* - не знает, так как в базе mydestination - левый домен. | |||
** old db: В конфиге есть mynetworks = 1.1.1.1/32. По использованию: шлются отчеты по обновлениям на rgerashenko | |||
** <s>slave: почтой явно давно не пользовались, в логах за последнее время пусто</s> Slave сменен | |||
** <s>master: По использованию - аналогично с old db</s> Неактуально | |||
** web1: в mydestination кривой домен получателя - стоит web2 (да еще и hostname!=mailname, что создает свои проблемы), но локально почта пытается работать благодаря тому, что @web1.carid.com резолвится. На деле - см. memcached. В конфиге разрешен релей для 1.1.1.1/32. "mail for web1.carid.com loops back to myself". | |||
** web2: Полностью аналогичен web1, только в качестве левого домена указан web3 | |||
** web3: Домен в mydestination соответствует хостнейму (но не mailname, как и на остальных серверах), поэтому почта так же само зацикливается. Разрешенный релей - 1.1.1.1/32. | |||
* monit на все основные сервисы на всех серверах | |||
* Максимально запретить crontab'ы, особенно на веб-серверах. | |||
* Аудит модулей Апача на вебах, удаление лишних (показ загруженных: apachectl -t -D DUMP_MODULES 2>&1) | |||
* Сделать ежедневный (минимум, а можно и несколько раз в день) снапшот контента веб-сайта на мастер-сервере (минимум (для быстрой развертки контента в случае аварии), как вариант - еще где-то на стороне, в изолированном от боевых серверов месте), хранить снапшоты хотя бы за неделю. В случае с мастером надо определиться с хостерской backup-системой, чтобы она не начала бэкапить 100500 снапшотов одного и того же контента по 100 Гб каждый. | |||
{{Hider hiding | |||
|title=Снапшотилка для бэкап-сервера | |||
|content= | |||
<source lang=bash> | |||
td="/home/carid/snapshot/$(date +%Y/%m/%d)" ; mkdir -p "${td}" ; cp -al /home/carid/httpdocs/ ${td} | |||
</source> | |||
}} | |||
* Вспомнить, зачем на вебах apache в группе nagios'a (если это делал я) | |||
* Сделать delayed-репликацию где-нибудь на бэкапе или любом сервере, где будет хватать ресурсов. Использовать сдвиги, например, на 3 и 6 часов, а каждые 12 часов делать бэкап (например, один у нас, один на бэкапе). Delayed-репликация есть в dev-версиях (5.6) MySQL'a; можно развернуть и запустить пару собственных сборок в отдельном каталоге с собственными каталогами данных. Другой вариант - использовать обычные сборки (т.к. поддерживается еще с 4-й версии) с отдельным каталогом данных и использовать [https://forums.mysql.com/read.php?26,43705,44051#msg-44051 такой способ] (подробнее про синтаксис можно глянуть в [https://dev.mysql.com/doc/refman/5.0/en/start-slave.html описании START SLAVE]). Второй способ более костыльный, но позволит пользоваться любой версией мускуля. | |||
* Повыявлять и сделать аудит существующих костылей "напарничка" на серверах; убрать лишние и то, что с ними связано. На данный момент из найденного: | |||
** '''@web{1..3}''': '''/etc/logrotate.d/maillog''' - cat'ает maillog в несуществующий файл, после чего запускает '''/home/carid/scripts/mailanalyse.sh'''. Тот в свою очередь пытается подключаться на старую офисную базу, обламывается, в результате чего на серверах днями висят ветки процессов cron->logrotate->mysql. Ненужность: 1) Оно в любом случае не работает; 2) maillog'ов на вебах не будет вообще. | |||
*** '''/home/carid/scripts''' содержит только упомянутый скрипт (и его вариации), поэтому после зачистки можно каталог полностью удалить. | |||
== Что сделано | == Лишние пакеты == | ||
Собирательный раздел для лишних пакетов (например, установленных, но неиспользуемых/нерабочих сервисов), а также сервисов, подлежащих удалению, согласно описанному выше. | |||
{| border=1 width=100% style="text-align:center" | |||
|- | |||
! Service || {{done}} Web (все) || Mail || {{done}} Master DB || {{done}} Old || {{done}} Slave || {{done}} Memcached | |||
|- | |||
| httpd || || || X <ref name="master_web">работает, никуда не смотрит. Присутствуют конфиги для webalizer, squirrelmail и т.п. </ref> || X <ref>пытается запуститься, но фейлится на своей конфигурации </ref> || X <ref name="dis_in_init">отключен в init-скриптах </ref> || X <ref name="master_web"></ref> | |||
|- | |||
| nginx || <!-- Web --> || <!-- Mail --> || <!-- Master --> || X <ref>работает, есть конфиг для carid</ref> || <!-- Slave --> || <!-- Memcached --> | |||
|- | |||
| autofs || X || X || X || X || X || X | |||
|- | |||
| xinetd || X || X <ref>Mail и FTP перевести на standalone </ref> || X || X || X || X | |||
|- | |||
| subversion || X || <!-- Mail --> || X <ref name="dis_in_init"></ref> || X <ref name="dis_in_init"></ref> || X <ref name="dis_in_init"></ref> || X <ref name="dis_in_init"></ref> | |||
|- | |||
| postfix || X <ref>После перехода на nullmailer</ref> || <!-- Mail --> || X || X || X || X | |||
|- | |||
| dovecot || X || <!-- Mail --> || X || X || X || X | |||
|- | |||
| squirrelmail || X || <!-- Mail --> || X || X || X || X | |||
|- | |||
| webalizer || <!-- Web --> || <!-- Mail --> || X || X || X || X | |||
|- | |||
| bind || X <ref name="inst_but_no_run">установлен, но не запущен и не используется</ref> || <!-- Mail --> || X || X || X || X | |||
|- | |||
| spamassassin || X || <!-- Mail --> || X || X || X || X | |||
|-<!-- | |||
| cyrus-sasl* || X || <!-- Mail --> || X || X || X || X | |||
|- --> | |||
| vsftpd || X || <!-- Mail --> || X || X || X || X | |||
|- | |||
| sendmail || X || <!-- Mail --> || X || X || X || X | |||
|- | |||
| ppp || X <ref name="inst_but_no_run"></ref> || X<ref name="inst_but_no_run"></ref> || X<ref name="inst_but_no_run"></ref> || X<ref name="inst_but_no_run"></ref> || X<ref name="inst_but_no_run"></ref> || X<ref name="inst_but_no_run"></ref> | |||
|- | |||
| rsyncd || <!-- Web --> || X <ref>Не является отдельным пакетом, необходимо просто удалить запуск в xinetd и конфиги</ref> || <!-- Master --> || <!-- Old --> || <!-- Slave --> || <!-- Memcached --> | |||
|- | |||
| mysqld || X || <!-- Mail --> || <!-- Master --> || <!-- Old --> || <!-- Slave --> || X | |||
|- | |||
| php-* || <!-- Web --> || <!-- Mail --> || X || X || X || X | |||
|- | |||
|} | |||
<references /> | |||
= Что сделано = | |||
На данный момент из полезного сделано: | На данный момент из полезного сделано: | ||
* inotify-based синхронизация контента между 6 (master -> web{1,3}/import/backup) серверами. Практически не вызывает нагрузки, контент копируется в реальном времени. | == Разное == | ||
* {{done}} Поправлен лимит на предел памяти для memcached. | |||
* {{done}} Было бы интересно собрать полную (в т.ч. User Agent'ы) статистику посещений сайта. - в процессе. | |||
* {{done}} Восстановить Nagios, скорректировать работу его и/или серверов для снижения текущего числа уведомлений. | |||
* {{done}} <s>Уже не знаю, куда запихнуть, пусть тут будет.</s> Сделано: Для DB: | |||
yum remove php-* httpd-* dovecot* sendmail* autofs* kernel-devel* ppp* rp-pppoe* wireshark* xorg* kernel-headers* bind bind-chroot vsftpd* portmap* spamassassin* nfs-utils* subversion xinetd net-snmp | |||
: Для web{1,3} | |||
yum remove dovecot* autofs* kernel-devel* ppp* rp-pppoe* wireshark* xorg* kernel-headers* bind bind-chroot vsftpd* subversion xinetd net-snmp mysql-server webmin squirrelmail webalizer | |||
* {{done}} Оптимизация/изменение настроек MySQL: Увеличить время хранения бинлогов/relay-логов - должно с запасом покрывать выходные, т.е., 3-4 дня. | |||
* {{done}} inotify-based синхронизация контента между 6 (master -> web{1,3}/import/backup) серверами. Практически не вызывает нагрузки, контент копируется в реальном времени. | |||
{{Hider hiding | |||
|title=Мусор, выкинутый после введения lsyncd | |||
|content= | |||
<source lang=bash> | |||
www@import.carid.com | |||
*/15 * * * * RSYNC_PASSWORD="djh8PKq" /usr/local/bin/rsync -rzt carid@50.56.1.164::images/ /www/import.carid.com/images > /dev/null 2>&1 | |||
0 */1 * * 1,2,3,4,5 RSYNC_PASSWORD="djh8PKq" /usr/local/bin/rsync --exclude 'phpmyadmin/' --exclude 'phpmyadmin3/' --exclude 'import2/' --exclude 'images/' --exclude 'import/' --exclude 'config.php' --exclude 'config/config.xml' --exclude 'index.php' --exclude 'files/' --exclude 'var/tmp/' --exclude 'var/log/' --exclude 'var/templates_c/' --exclude 'var/cache/' --exclude 'wde-master/admin/.htaccess' --exclude 'include/refine_search_detection.php' --exclude 'csv_parser/' --exclude '.htaccess' --exclude='.svn' -rzt carid@50.56.1.164::httpdocs/ /www/import.carid.com > /dev/null 2>&1 && /bin/rm -f /www/import.carid.com/var/templates_c/* > /dev/null 2>&1 | |||
root@mail | |||
* * * * * /root/bin/ftpwatch2sql.sh | |||
shell@mail> | |||
fix /var/www/vhosts/carid.com/private/svn/svn_sync.sh | |||
carid@mail | |||
* * * * * /usr/local/bin/psqlftpfile.sh 2>>/tmp/sync.log | |||
0 2 * * * rsync -trvn --del --exclude='wde-master' --exclude='.svn' --exclude='/config.php' --exclude='config/*' --exclude='/robots.txt' --exclude='.htaccess' --exclude="var/*" --exclude="private/*" --exclude="sync-stat/" /var/www/vhosts/carid.com/httpdocs/ web3:/home/carid/httpdocs/ | mail -s "Master->web3 rsync report" team.admins@onyxenterprises.com | |||
0 2 * * * rsync -trvn --del --exclude='wde-master' --exclude='.svn' --exclude='/config.php' --exclude='config/*' --exclude='/robots.txt' --exclude='.htaccess' --exclude="var/*" --exclude="private/*" --exclude="sync-stat/" /var/www/vhosts/carid.com/httpdocs/ web2:/home/carid/httpdocs/ | mail -s "Master->web2 rsync report" team.admins@onyxenterprises.com | |||
0 2 * * * rsync -trvn --del --exclude='wde-master' --exclude='.svn' --exclude='/config.php' --exclude='config/*' --exclude='/robots.txt' --exclude='.htaccess' --exclude="var/*" --exclude="private/*" --exclude="sync-stat/" /var/www/vhosts/carid.com/httpdocs/ web1:/home/carid/httpdocs/ | mail -s "Master->web1 rsync report" team.admins@onyxenterprises.com | |||
carid@web{1,2,3} | |||
0 * * * * sleep 15; RSYNC_PASSWORD="djh8PKq" nice -n 19 ionice -c 2 -n 7 rsync -rqvt carid@192.168.100.164::files/ /home/carid/httpdocs/files >> /tmp/sync.log | |||
0 * * * * sleep 15; RSYNC_PASSWORD="djh8PKq" nice -n 19 ionice -c 2 -n 7 rsync -rqvt carid@192.168.100.164::pages/ /home/carid/httpdocs/skin1/pages >> /tmp/sync.log | |||
0 * * * * sleep 15; RSYNC_PASSWORD="djh8PKq" nice -n 19 ionice -c 2 -n 7 rsync -rqvt --delete carid@192.168.100.164::images/ /home/carid/httpdocs/images >> /tmp/sync.log | |||
*/30 * * * * scp -rp mail1:~carid/httpdocs/authorized-installers ~carid/httpdocs/ > /dev/null 2>&1 | |||
0 8 * * 1,3,5 scp -rp mail1:~carid/httpdocs/sitemaps ~carid/httpdocs > /dev/null 2>&1 | |||
</source> | |||
}} | |||
* {{done}} Перейти на RAID10 на Slave-сервере. 5-й сильно уж тормозит - дамп заливался несколько часов против 40 минут (при тесте на RAID10 на Mail-сервере). RAID5 неприменим для базы. | |||
{{Hider hiding | |||
|title=Тест на Mail-сервере | |||
|content= | |||
<source lang=bash> | |||
root@345731-mail1:~# time { pv carid_2012_02_20.sql.bz2 | pbunzip2 -c | mysql -hlocalhost -uroot -p'pass' raidtestdb ; } | |||
1.85GB 0:40:09 [ 803kB/s] [===================================================>] 100% | |||
real 40m20.638s | |||
user 16m31.050s | |||
sys 0m20.355s | |||
</source> | |||
}} | |||
== Web == | |||
* {{done}} '''/var/www/vhosts/carid.com/httpdocs/images/mcafee3.php''' вынести с вебов (он везде одинаковый и генерит одну картинку, которую можно просто создавать на мастере) | |||
=== Сервисы === | |||
==== Web ==== | |||
* {{done}} Убрать vsftpd (FTP на веб-серверах не нужен, все изменения делаются через мастер-сервер, для того, чтобы забрать какой-то файлик с веба (логи, например) есть NFS) | |||
* {{done}} Убрать xinetd (не несет никакой полезной нагрузки, просто лишний сервис) | |||
* {{done}} mysqld (не используется, нет ни одной базы) |
Текущая версия на 12:55, 31 июля 2012
Заметки по серверам для carid.com:
Что хотелось бы сделать
Кодировки MySQL
Навести порядок с кодировками базы. Сейчас в настройках на большинстве серверов указан (а местами не указан, а просто используется старое дефолтное значение) latin1. При этом в коде явно указывается использовать юникод через
SET NAMES 'utf8'
(иначе бы не получалось сохранять всякие там "®" и прочие юникодные символы) - хотя кое-где стоит
SET NAMES 'latin1'
(но такие места не должны вызвать проблем, в дальнейшем их можно поправить). Т.е., реально настройки указываются одни, база в свойствах таблиц указывает latin1, а реально данные хранятся другие; как следствие - имеем проблемы при работе с дампами.
По-идее, в настройках сервера можно сходу указать правильные параметры подключения - это не должно сломать текущую логику работы (нужна проверка на тестовом сервере); кодировка все равно задается через set names (но не уверен, что именно происходит при репликации и в чем (и какими настройками это регулируется) там передаются данные). Для перехода на правильные настройки можно использовать следующий алгоритм:
- Прописать правильные настройки на Master-сервере
- Перевести slave-алиасы на мастер-сервер
- STOP SLAVE; на слейв-сервере
- Одновременно перезапускаем Master-сервер
- Заливаем дамп напрямую с Slave-сервера на Master-сервер, заменяя в потоке charset в дампе на юникодный и проверив, что льется юникодный поток.
- Снимаем уже правильный дамп с Master-сервера
- Настраиваем Slave-сервера на работу с юникодом
- Восстанавливаем работу Slave'ов
- Переключаем slave сайта на восстановленные слейвы.
По-идее, основная часть работы (и downtime сайта) займет время однопоточной заливки дампа (т.е., минут 30). Как вариант - написать скрипт, который будет дампить и заливать базы впараллель, тогда downtime уменьшится минут до 10.
Дамп на бэкапе должен сниматься с default-charset=latin1 и skip-character-set- Опции взаимоисключающие, но в целом работает. База в utf8, skip-character-set просто сливает ее в том виде, в каком она есть с учетом настроек сервера (т.е., latin1, но кодируя юникод-символы)
- На деле лучше опцию убрать и использовать default-charset=utf8 - это сольет базу также в utf8, но дополнительно добавит всякие там SET NAMES utf8 в теги.
- Дамп на import.carid.com должен сниматься без ключей
Сейчас сливается с skip-charset, позже сделать по аналогии с backup-сервером, должно работать.Сделано
Заливаться дамп везде должен с default-charset=latin1- Вроде уже неактуально. Дамп, сделанный в чистом utf8 (хоть и с параметром CHARSET=latin1 для таблицы) нормально заливается на боевом сервере; надо проверить на import'e.
- На бэкап-сервере кодировки сервера и клиента различаются (latin1 и utf8) без каких-либо настроек в конфиге мускуля
Пробная замена кодировки на db4 2012.05.9
Команда для заливки существующего на тот момент дампа на свежеразвернутый MySQL:
pbunzip2 -c ../carid_2012_05_09.sql.bz2 | sed 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=utf8/g' | sqlpar-restore /dev/stdin localhost root pass carid
После заливки дампа и попытки запуска slave'a обнаружил, что дополнительно надо сдампить функции и процедуры (хранятся в mysql.proc) - на будущее добавил ключик -R к mysqldump в скрипте на backup- и import-серверах, который дампит все это дело и размещает в конце дампа. Триггеры дампятся по дефолту.
Проблемы, заметки и т.п:
- Не захотела заливаться таблица sizeitup_mmy_correspondence с ошибкой "ERROR 1071 (42000) at line 31: Specified key was too long; max key length is 1000 bytes". Гуглеж показал, что это давняя (8 лет :) ) бага MySQL, на которую я наткнулся. Описание тут. Залить смог в том виде, в котором оно было в первоначальном дампе - с latin1, после чего сделал alter table sizeitup_mmy_correspondence charset=utf8;. Отработало нормально.
- В триггерах указывается кодировка клиента при выполнении операций. В новой базе она осталась latin1. Можно подкорректировать замену sed'ом, чтобы убирал и такие моменты. С учетом того, что триггера только 2 - я просто их пересоздал уже с новой кодировкой:
show triggers ; show create trigger orders_phones_ins ; drop trigger trigger orders_phones_ins ;
- И дальше копируем то, что в выведенной строке при show create. Аналогично и для 2-го триггера
- В двух функциях стоит CHARSET latin1 - в catname_from_id и detectMake. Аналогично, можно либо поменять sed'ом в дампе, в котором они есть - либо просто пересоздать с новыми параметрами.
Ссылки
- Вариант с использованием ALTER smth - не надо переливать данные, но придется перебрать все колонки всех таблиц.
- Русская статья по конвертации + см. комментарии. Вариант с ALTER может не работать.
- Полезная табличка с описанием переменных для кодировок
- По функциям и процедурам в MySQL
InnoDB
Перевод базы на InnoDB. Считаю, что это решит нашу текущую проблему с локами базы при смешной нагрузке (как в случае с мастером, когда маленький апдейт лочит все остальные select'ы и они отжирают все доступные подключения - пример такой ситуации у меня есть; так и в случае со слейвом, когда пришедший репликационный запрос лочит вычитку - а в этот момент, например, какой-то скрипт активно использует сервер - в итоге все надолго умирает и идет отставание репликации - по-идее, благодаря версионности InnoDB репликация должна проходить проще. Можно достаточно легко проверить, переведя какой-то слейв на InnoDB и запустив на нем тяжелый скрипт, делающий много вычитки). На данный момент сменить тип движка нельзя для следующих таблиц:
Используется FULLTEXT: xcart_categories_index xcart_extra_field_values - не требуется xcart_make_model xcart_order_details xcart_order_messages xcart_products - done xcart_users_map
Ошибка при конвертации: "ERROR 1075 (42000) at line 1: Incorrect table definition; there can be only one auto column and it must be defined as a key" в xcart_counters
с ошибкой разобраться,
- Пофиксили 2012-05-10 после того, как попадали slave-сервера из-за ошибки autoincrement'a
- для таблиц потенциально можно вынести поиск в отдельную маленькую таблицу, оставив ее в MyISAM (требует правки кода, так что в крайнем случае можно оставить их как есть - все же лучше иметь 8 лочащихся таблиц, чем 300; однако среди них есть достаточно крупные с частым обращением).
Поиск использования таблиц в коде (на выборку данных):
Поиск файлов, использующих данные таблицы для выборки текста при поиске. Поиск идет по php-файлам по нужной таблице, после чего в найденных файлах ищется ключевое слово AGAINST. # grep -ilE '\bAGAINST *\(' `grep -HrEnlIo --include="*.php*" "xcart_products" /home/carid/httpdocs/ | grep -v 'var/log'` xcart_products: products /home/carid/httpdocs/include/classes/SearchMain.php /home/carid/httpdocs/include/search.php /home/carid/httpdocs/include/orders.php - только в products xcart_categories_index: /home/carid/httpdocs/include/search_redirect.php xcart_make_model: product_mm /home/carid/httpdocs/include/search_redirect.php /home/carid/httpdocs/include/classes/SearchMain.php xcart_users_map: users_map xcart_order_details: order_details /home/carid/httpdocs/include/orders.php - в order_details xcart_order_messages: order_messages /home/carid/httpdocs/include/orders.php - в order_messages xcart_extra_field_values: : не найдена в коде вообще и ничего не содержит
Все файлы с AGAINST:
/home/carid/httpdocs/include/search_redirect.php /home/carid/httpdocs/include/search.php /home/carid/httpdocs/include/orders.php /home/carid/httpdocs/include/classes/SearchMain.php /home/carid/httpdocs/kmp/admin/application/models/searchindex.php
Само по себе изменение остальных таблиц должно пройти гладко; на время конвертации можно переключить Slave-сервера на мастер (т.к. мастер мощный, а слейв могут долго работать с таблицей; как следствие - отставание репликации). Помимо ликвидации табличных локов, это позволит держать всю базу в оперативной памяти, т.е., будет не особо важно, насколько быстрый диск (остальные параметры на тему сброса данных на диск можно потюнить).
Момент, необходимый для рассмотрения - держать ли все таблицы в одном ibdata-файле или нет. С одной стороны работа с таблицами, разделенными по файлам проще (нагляднее, проще для восстановления, наглядно видно занимаемое и свободное место, можно мигрировать с диска на диск на ходу (а надо ли, если замена диска все равно подразумевает отключение сервера?) и т.п.), с другой - в случае использования одного файла можно указать в качестве файла полностью FIO Drive - без таблицы разделов (привет выравниванию) и без файловой системы (привет фрагментации).
До этого момента рассматривать варианты, вроде перехода на MySQL-кластер или Percona-сервер - бред, так как первое, фактически - мультимастер-сервер с синхронной репликацией (и не самым хорошим движком), второе хорошо прежде всего XtraDB (который модифицированный InnoDB - т.е., опять-таки упираемся в переход на InnoDB).
После решения проблем подобных миграций при недостатке производительности (но не ранее) можно рассматривать переход на другие базы (на случай, если векторы развития MySQL и остальных баз не изменится) - на данный момент это MariaDB+XtraDB или Percona Server.
Про Foreign Key к Богдану - момент не понял, ведь если его явно не задавать, то возможностью можно просто не пользоваться и проблем это не вызовет.
- Разобрались, просто не поняли друг друга.
Ссылки
Оптимизация использования MySQL-серверов, отказоустойчивость
Потенциально можно раскидывать запросы чтения с основного сайта с Master на Slave-сервер для разгрузки мастер-сервера. Подобные оптимизации можно делать только после ликвидации локов (что даст ускорение репликации и большую синхронность между серверами; как следствие - более корректную вычитку данных приложением).
Варианты реализации:
- На уровне приложения. Объявить новую переменную с подключением к слейву, подключения на вычитку данных цеплять на эту переменную. Требует правки кода и знания того, как работает сайт, поэтому пока труднореализуемо.
- Как подпункт: в дальнейшем в таком варианте можно сделать, например, балансировку на уровне DNS'a - объявить для алиаса несколько IP-адресов и он в round robin-режиме будет отдавать разные сервера.
- На уровне подключений:
- mysql-proxy. Без наведения порядка с кодировками не взлетит, так как раскидывает подключения на уровне запросов (т.е., set names может уйти на один сервер, а запрос - на другой и получим кашу. Как вариант - жестко задать настройки, чтобы на любой коннект принудительно устанавливалась нужная кодировка (т.е., set names в конфиге сервера) - должно помочь, но требует проверки. Прост в настройке, может раскидывать запросы на запись и чтения по разным серверам (в т.ч. несколько серверов чтения)
- haproxy. Рулит подключениями на уровне TCP-соединений. Более навороченный вариант, требует больше настройки.
Еще оптимизация - можно сделать Master-Master-репликацию. Тут есть 2 подпункта:
- Можно вынести админку, с которой работают импортеры/апдейтеры и прочие отделы на другой мастер-сервер при условии малого пересечения таблиц, в которые пишет админка и сайт (подразумевается, например, что посетитель не пишет в таблицу с перечнем товаров, в которую пишет сотрудник и т.п. Как подвариант - пересекающиеся таблицы писать только на один сервер для исключения ситуации split brain'a)
- HA-кластер. Умирает один мастер - переключаемся каким-то средством балансировки на другой (через haproxy/mysql-proxy/heartbeat и т.п.). Пока второй мастер живой - используется для админки (1-й пункт) или как слейв. Плюс как слейв можно завести 3-й сервер (освободившийся от memcached, например). На пару с избавлением от центрального memcached получим полностью отказоустойчивую систему.
Быстродействие серверов
Небольшие заметки по быстродействию различных конфигураций серверов.
- db4, тест 2012.05.09, база переведена на innodb, нормализована кодировка. 24 ядра E7540, HT отключен (т.е., все ядра честные), 128 Гб памяти, 160 Гб SSD под базу. Сливка данных в /dev/null: 15.8GB 0:05:45 [46.9MB/s]. mysqldump / mysql упирались в процессор, скорость иногда просаживалась до очень малых значений (единицы МБ).
- Там же, вставка дампа (из которого потом и делался InnoDB-вариант). MyISAM: 3 минуты распаковка/нарезка/замена latin1 на utf8 + 31:37 чистая многопоточная вставка дампа (из них xcart_order_messages включали ключи 10:16). Практически все время был загружен проц.
- Вставка аналогичного дампа на server1.carid.com. 2*E5620 (4*2,4 + HT, т.е., 8 настоящих, 16 виртуальных ядер), 16 Гб памяти, одиночный диск (500-ка, RAID Edition). Все таблицы залились за 15 минут, еще ~6 минут включались ключи для xcart_order_messages (т.е., разница в скорости работы бэкапа и db4 превышает разницу в частоте (х1,25))
- DB4, 2012.05.24, новый SSD, нормальная разбивка, свежеснятый дамп, HT включен. 3:30 распаковка, 26:30 - заливка всех таблиц до начала включения ключей в xcart_order_messages, ключи включались еще +9:30 (т.е., общее время выполнения с распаковкой - около 40 минут)
Memcached
Убрать единый сервер Memcached и создать собственный Memcached на каждом веб-сервере. Плюсы:
- Убираем единую точку отказа
- Убираем оверхед при перегонке мелких данных по сети.
- Не держим лишний мощный, но практически не используемый сервер
Недостаток такого варианта - выделение памяти на каждом сервере. На деле из 36 Гб на веб-серверах используется 2-3 Гб. Memcached за долгое время набрал данных лишь на 8 Гб (на момент написания текста), из них многое - из-за постоянных тестов. Эффективность кэша на момент 8 Гб - порядка 80 процентов (update: 80 процентов из всех выборок чтения. На деле доля удачных чтений из всех запросов на уровне 60 процентов), что мало. Итого, если выделить даже по 16 Гб под memcached на каждом сервере - этого с головой хватит и для нужд веб-сервера, и для memcached. По процессору - даже единый memcached практически не нагружает процессор (нагрузка на уровне пары процентов для одного ядра сервера). При разделении нагрузка на каждый отдельный memcached уменьшится до 3 раз на текущий момент. Когда начнем упираться в процессор на веб-сервере - наличие на нем memcached будет далеко не самой большой проблемой (иначе говоря, мы скорее заведем новый сервер впараллель к трем существующим, чем упремся в процессор).
Варианты реализации:
- Репликация данных: не затрагивает приложение, но repcached на данный момент умеет работать только в режиме master<->master или мастер с несколькими слейвами (кстати, пробовал репликацию цепочкой - не взлетело, да и не является отказоустойчивой). Для наших трех серверов не подходит. Из того, что протестировал между двумя серверами - плюс в том, что при восстановлении сервера реплицируются все данные, т.е., снова разогревать кэш не придется.
- Redis. Проблемы с репликацией те же, что и в первом пункте, в остальном вроде как является более быстрой альтернативой memcached (что пока не требуется).
- Использовать средства приложения: Богдан говорит, что приложение при легкой правке кода может принимать массив серверов, при этом можно указывать их вес (т.е., для 127.0.0.1 указать максимальный приоритет, для других узлов - меньший).
while sleep 1
do
clear
echo -e "stats\nquit" | nc localhost 11211 |
awk '
/cmd_get/{cg=$3}
/cmd_set/{cs=$3}
/get_hits/{gh=$3}
/delete_misses/{dm=$3}
/delete_hits/{dh=$3}
END {
t=cg+cs+dm+dh
dt=dm+dh
print "Report: \t[R: "cg/t*100"% (only "gh/t*100"% successful) + W: "cs/t*100"% + D: "dt/t*100"% (only "dh/t*100"% successful)] from "t" total requests"
}'
done
Перезапуск Memcached 2012.06.26
- Объем данных за час: примерно 500 Мб
- Объем данных за день: 3,4 Gb
- 2 дня: 4,2 Гб
- 3 дня: 5 Gb
- => прирост ~800 Mb / day. Итого на хранение данных за неделю (даже без удаления устаревших) вполне хватит 8 Гб.
Тест латентности сети
Провел небольшой тест на латентность сети при работе с мелкими данными в кэше.
Скрипт:
<?php
$m = new Memcached();
$m->addServer("$argv[1]", "$argv[2]");
for ($i = '1'; $i <= "$argv[3]"; $i++) {
$a=rand().rand().rand();
$m->set("$i", "$a");
}
?>
Для Ъ: создание указанного 3-м параметром числа ключей и занесение в каждый из ключей рандомного числа.
Было сделано 4 тестирования - скрипт на локалхосте + memcached на локалхосте, скрипт на локалхосте + memcached на машине в локальной сети и для обоих вариантов скрипт и memcached менялись местами.
100 Мбит
Линк 100 Мбит (Реально сервер подключен к гигабитному свичу, но десктоп - к 100 Мбит. При случае протестирую на полностью гигабитной сети). Результаты повторяемые:
Тест на десктопе:
Все локально:
rain@debian:~$ time php /tmp/phpset.php localhost 22122 1000000 real 0m28.818s user 0m7.656s sys 0m6.068s
Memcached на другой машине в сети:
rain@debian:~$ time php /tmp/phpset.php 10.0.2.2 22122 1000000 real 1m58.699s user 0m7.284s sys 0m5.788s
Тест на удаленной машине:
Все локально:
rain@developer:~$ time php /tmp/file.php 10.0.2.2 22122 1000000 real 0m34.526s user 0m6.844s sys 0m12.541s
Memcached на другой машине в сети:
rain@developer:~$ time php /tmp/file.php 10.0.2.19 22122 1000000 real 2m0.241s user 0m12.253s sys 0m15.245s
1 Гбит
Сеть vs Сокет
Сделал еще тест для сравнения скорости работы через сеть и через unix-сокет. Правда, для этого пришлось перейти на php5-memcache (php5-memcached не хотел подключаться к сокету - или я что-то не так сделал). Соответственно, по правкам скрипта - new Memcache() и connect вместо addServer. Тест сделан на одной и той же незагруженной машине с двухядерным процессором. Результаты:
Сокет:
root@developer:~# time php file.php unix:///tmp/mem.sock 0 1000000 real 0m30.652s user 0m10.109s sys 0m9.465s
Сеть:
root@developer:~# time php file.php 127.0.0.1 22122 1000000 real 0m41.677s user 0m11.513s sys 0m15.353s
Результаты стабильно повторяемы.
Ссылки
- https://www.linux.org.ru/forum/admin/7465868 - пронаблюдать тред
- Аналогичная моей проблема c репликацией цепочкой
Безопасность
Web
Soft
- Apache/nginx на веб-серверах сейчас работают через права группы (контент под carid:users, Апач - под apache:users (почему-то :) кроме 2-го веба), nginx - через nginx:users), но контент часто имеет права 666 или 777.
Определиться, нужна ли запись данных Апачем в каталоги сайта; если да - перевести Апач под пользователя сайта(Не нужна), если нет - просто переключить с users на выделенную группу (см. ниже). - Запретить выполнение скриптов из каталогов аплоада файлов (/images, /files и вообще любых каталогов, где могут появляться файлы)
(без разницы, аплоадятся они через веб или FTP)(Не важно. Аплоадятся на другом сервере и только через FTP). На данный момент в images/ есть ряд скриптов сомнительной нужности (в этом каталоге):
root@345735-web1:/home/carid/httpdocs/images# find . -iname "*.php" ./mcafee3.php ./ebay/left_menu.php ./ebay/contact.php ./ebay/header_menu_d.php ./gps-systems/garmin/downloads/script.php
- По возможности перенести их в другое место, а каталог закрыть.
PHP
- Позапрещать ненужные функции в настройках PHP. Сейчас свободно работают всякие там system, exec и прочее, что позволяет свободно пользоваться веб-шеллами (проверено). При использовании багов в коде сайта по серверу можно свободно разгуливать и запускать программы с правами apache (48) (а с учетом старости софта - вполне возможно дальнейшее повышение привилегий).
- Набросок однострочников для поиска ненужных функций:
# Поиск функций в коде
LANG=C grep -hEIro --include="*.php*" "[ ^]\b[a-z0-9_]+\(" /home/carid/httpdocs/ | sed -r 's/\(|^ //g' | sort -u > carid_func_list
# вывод доступных функций в PHP
php -r '$functions = get_defined_functions();sort($functions['internal']);print_r($functions['internal']);' 2>/dev/null | awk '/[0-9]\] =>/{print $3}' > php_func_int
# Показ используемых встроенных функций
fgrep -x -f carid_func_list php_func_int
# Показ неиспользуемых встроенных функций
fgrep -x -v -f carid_func_list php_func_int
# Поиск функций в коде
echo list_of_functions | tr ' ' '\n' | while read i
do
grep -HrEnIo --include="*.php*" "[ ^]\b$i\(" /home/carid/httpdocs/ || echo "${i}: NOT_USED"
done | awk '/NOT/; /\($/{gsub(/\(/, "", $2); gsub(/:$/, "", $1); print $2": "$1}' > flist
На данный момент в коде найдена 631 встроенная функция.
При тестовом запуске заметил, что в коде бывает встречаются реально не используемые функции - например, в модулях Zend'a (TODO: запросить у программеров, какие модули Zend'a и прочих фреймворков реально нужны) или в каких-нибудь OS-специфичных проверках - например, в ./modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php попалось такое: if (OS_WINDOWS && isset($section_text['RETURNS'])) { ob_start(); system($cmd, $return_value); и это единственное место использования system() в коде. Соответственно, можно безопасно это отключить.
Наброски списка функций для отключения:
# Остатки нагугленных функций, которые отключали другие люди. Не удалил на всякий случай, может пригодится.
# disable_functions = eval, PHP_SELF, highlight_file, ini_restore, ini_set, posix_setpgid, posix_setsid
# Не найденные в документации функции.
# proc, dll, max_execution_time, proc_show, cpuinfo, meminfo, cmd, uname, ftp, debugger_on, debugger_off, listen, ioctl, leak, ssthru, view_size
# Разное
disable_functions = dl, posix_mkfifo, chown, link, symlink, phpinfo, ftp_exec, chgrp, ssh2_exec, posix_setuid, posix_setgid, posix_kill, escapeshellarg, escapeshellcmd
disable_functions = syslog, closelog, define_syslog_variables, openlog
# Требуется проверка - функции могут для чего-то использоваться
disable_functions = chmod, set_time_limit, php_uname, posix_uname, disk_free_space, diskfreespace, disk_total_space, get_env, ini_get_all, ini_get, error_log
# Зависит от используемого варианта PHP. Работает только в том случае, когда PHP подключен через mod_php
disable_functions = apache_get_modules, apache_get_version, apache_note, apache_setenv
# Выполнение команд
disable_functions = shell_exec, exec, system, passthru, popen, proc_open, proc_nice, proc_get_status, proc_close, proc_terminate
# Работа с процессами
disable_functions = pcntl_alarm, pcntl_exec, pcntl_fork, pcntl_getpriority, pcntl_setpriority, pcntl_signal_dispatch, pcntl_signal, pcntl_wait
disable_functions = pcntl_waitpid, pcntl_wexitstatus, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig
- Проверка показала использование в коде 73 функций из списка выше (без тех, что требуют проверки, но с php_uname и posix_uname), из них 2 - в комментариях.
pcntl_waitpid: NOT_USED pcntl_wexitstatus: NOT_USED pcntl_wifexited: NOT_USED pcntl_wifsignaled: NOT_USED pcntl_wifstopped: NOT_USED pcntl_wstopsig: NOT_USED pcntl_wtermsig: NOT_USED pcntl_alarm: NOT_USED pcntl_exec: NOT_USED pcntl_fork: NOT_USED pcntl_getpriority: NOT_USED pcntl_setpriority: NOT_USED pcntl_signal_dispatch: NOT_USED pcntl_signal: NOT_USED pcntl_wait: NOT_USED shell_exec: NOT_USED /home/carid/httpdocs/include/classes/class.upload.php:424 exec: /home/carid/httpdocs/include/classes/Zend/Db/Adapter/Pdo/Abstract.php:256 exec: NOT_USED /home/carid/httpdocs/include/classes/class.upload.php:424 exec: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Builder.php:378 exec: /home/carid/httpdocs/modules/HTML_PDF/dompdf/load_font.php:231 system: NOT_USED /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:449 passthru: NOT_USED popen: /home/carid/httpdocs/payment/cc_saferpay.php:65 popen: /home/carid/httpdocs/payment/cc_saferpay.php:137 popen: /home/carid/httpdocs/include/func/func.https_ssleay.php:103 popen: /home/carid/httpdocs/include/func/func.https_httpscli.php:100 popen: /home/carid/httpdocs/include/func/func.https_curl.php:169 popen: /home/carid/httpdocs/include/func/func.https_openssl.php:108 popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Builder.php:442 popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:454 popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:535 popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:724 popen: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:247 proc_open: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:125 proc_open: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:131 proc_nice: NOT_USED proc_get_status: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:168 proc_close: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:173 proc_terminate: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/RunTest.php:156 apache_get_modules: /home/carid/httpdocs/m1info.php:926 apache_get_version: NOT_USED apache_note: NOT_USED apache_setenv: NOT_USED syslog: /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php:84 syslog: /home/carid/httpdocs/include/classes/Zend/Log/Writer/Syslog.php:265 syslog: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR.php:84 closelog: /home/carid/httpdocs/include/classes/Zend/Log/Writer/Syslog.php:237 define_syslog_variables: NOT_USED openlog: /home/carid/httpdocs/include/classes/Zend/Log/Writer/Syslog.php:177 dl: /home/carid/httpdocs/kmp/admin/ci_system/libraries/Image_lib.php:1458 dl: /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php:743 dl: /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php:748 dl: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR.php:743 dl: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR.php:748 posix_mkfifo: NOT_USED chown: NOT_USED link: /home/carid/httpdocs/include/classes/Zend/Feed/Entry/Atom.php:243 link: /home/carid/httpdocs/include/classes/Zend/Feed/Entry/Atom.php:252 link: /home/carid/httpdocs/include/classes/Zend/Feed/Atom.php:121 link: /home/carid/httpdocs/include/classes/Zend/Feed/Atom.php:130 symlink: NOT_USED phpinfo: /home/carid/httpdocs/wde-master/admin/general.php:63 phpinfo: /home/carid/httpdocs/kmp/admin/application/models/bugreportmodel.php:54 phpinfo: /home/carid/httpdocs/include/classes/Zend/Tool/Framework/System/Provider/Phpinfo.php:35 phpinfo: /home/carid/httpdocs/include/classes/class.upload.php:491 phpinfo: /home/carid/httpdocs/include/classes/class.upload.php:2986 phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:180 phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:574 phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:662 phpinfo: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:902 phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:357 phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:410 phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:454 phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:722 phpinfo: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Install.php:968 ftp_exec: NOT_USED chgrp: NOT_USED ssh2_exec: NOT_USED posix_setuid: NOT_USED posix_setgid: NOT_USED posix_kill: NOT_USED escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_doc_lib.php:22 escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_rtf_lib.php:25 escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_pdf_lib.php:24 escapeshellarg: /home/carid/httpdocs/kmp/admin/application/libraries/index_ppt_lib.php:31 escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:440 escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:447 escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:525 escapeshellarg: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Command/Package.php:529 escapeshellarg: /home/carid/httpdocs/modules/HTML_PDF/dompdf/load_font.php:231 escapeshellcmd: NOT_USED php_uname: /home/carid/httpdocs/wde-master/admin/general.php:104 php_uname: /home/carid/httpdocs/m1info.php:595 php_uname: /home/carid/httpdocs/include/classes/CyberSorce/CYBERSOURCE.php:199 php_uname: /home/carid/httpdocs/include/classes/Zend/Mail/Storage/Writable/Maildir.php:417 php_uname: /home/carid/httpdocs/include/classes/Zend/Mail.php:1117 php_uname: /home/carid/httpdocs/ioncube/ioncube-loader-helper.php:311 php_uname: /home/carid/httpdocs/check_requirements.php:346 php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/DependencyDB.php:452 php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Dependency2.php:275 php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/PEAR/Registry.php:780 php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:26 php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:88 php_uname: /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2/PEAR/OS/Guess.php:129 php_uname: /home/carid/httpdocs/modules/HTML_PDF/dompdf/include/functions.inc.php:205 posix_uname: NOT_USED
- Использовать open_basedir - сейчас доступ к файловой системе через PHP для основного сайта никак не ограничен; для доменов на mail.carid.com кое-где указан, но "/tmp" используется общий для всех сайтов
- По возможности актуализировать (и поддерживать в дальнейшем) версии используемых фреймворков и прочих веб-компонентов:
- /home/carid/httpdocs/include/classes/Zend/Version.php: текущая версия 1.11.7, актуальная: 1.11.11, changelog (и далее)
- /home/carid/httpdocs/modules/Amazon_Payment_Checkout/lib/PEAR-1.7.2: текущая версия 1.7.2 (Rel: 2008-05-17), актуальная: 1.9.4 (Rel: 2011-07-07), changelog (+более ранние), + проверить используемые модули.
- /home/carid/httpdocs/kmp/admin/application/libraries/PEAR/PEAR.php: текущая версия 1.7.1 (Rel: 2008-02-03), остальное аналогично предыдущему.
- Протестировать связку suhosin + mod_security + fail2ban
FTP
- /etc/proftpd.conf@mail.carid.com: Umask 027
Ссылки
Общее
Сервисы
Общее
- Полностью выпилить Mail-сервера (smtp, pop/imap) на вебах и DB-серверах, перейти на nullmailer-like пересылку с форвардом на mail.carid.com - избавление от лишних потенциально уязвимых сервисов, требующих обслуживания.
- Все bounced-письма или недоставленные сообщения должны собираться и обрабатываться на mail.carid.com.
- Убрать automount. Идет в дефолтной поставке и на серверах не выполняет ничего полезного. После этого можно будет убрать каталоги /misc, /net
- Определиться с софтом, необходимым хостеру. На данный момент непонятный статус у следующих процессов:
Web: dsm_* rhnsd snmpd nimbus Mail: + brcm_iscsiuio + iscsid + *simpana* (cvd, EvMgrC) ---- + miniserv WebMin'a
- rs-sysmon
- Rackspace'овский пакет, содержащий баш-скрипт, через каждые 10 минут (задается в настройках) по крону делающий снапшот состояния системы - процессов, соединений и т.п. и складирующий логи в /var/log/rs-sysmon/
- holland
- Изначально Rackspace'овская разработка, система бэкапа. Бэкапы лежат в /var/spool/holland/, последние - годичной давности. Не заметил запускающих скриптов или записей в cron'e - возможно, запускается руками.
- rs-rhntools
- Некая хреновина для слежения за обновлениями. Из README: "General usage is to have a cronjob run nightly that calls 'rs-rhntools checkupdate'. Should updates be available, checkupdate will call up2date/yum to install updates."
- psacct
- Описание. У нас установлена, но отключена (не работает).
- Сделать аудит crontab'ов и периодически запускаемых процессов в них, расписать, зачем нужен каждый скрипт и кем (по чьему запросу) он был добавлен.
- После удаления лишних сервисов удалить ненужные, временные и т.п. аккаунты.
- Пообновлять пароли на серверах, перейти на sha512-хэши.
- Ввести политику использования ssh-ключей, запретить использование парольного входа, запретить рут-логин, (запретить туннели?).
- Избавиться от реальных пользователей для proftpd на mail.carid.com, перейти на виртуальных
- Определиться со списком нужных пользователей и тем, куда они должны ходить; далее либо перевести proftpd под nobody, а для каталогов сделать UserOwner/GroupOwner <юзер/группа сайта> (это сейчас не работает, хоть и встречается в конфиге), что попутно позволит избежать проблем с правами, либо (если заработает) использовать одного пользователя/группу в файле описания виртуальных пользователей.
Заработает. Делаем что-то вроде: AuthUserFile /etc/proftpd/auf # Или вообще выпилить обычных системных юзеров и разрешить только тех, которые описаны в файле выше - т.е., убрать mod_auth_unix.c AuthOrder mod_auth_file.c mod_auth_unix.c RequireValidShell off в конфиге сервера, после чего echo PASSWORD | ftpasswd --passwd --gid=2521 --file=/etc/proftpd/auf --md5 --not-system-password --name USER --shell /usr/sbin/nologin --uid 10001 --home=/bla/bla/images --stdin Получаем кучку виртуальных юзеров, работающих с контентом под одним пользователем, но с различающимися логинами. В GECOS можно загнать полные имена, чтобы потом проще было находить виновных :). Файлик читается юзером proftpd (или тем, кто определен в конфиге как User), т.е., он должен иметь права на чтение файла (думаю, есть смысл сделать proftpd:nobody 640)
- Перевод SUID-бинарников на группы
find / -xdev -perm -4000 -ls
groupadd -r suid
bins='/bin/su /bin/ping /bin/mount /bin/ping6 /bin/umount /usr/bin/chsh /usr/bin/chfn /usr/bin/newgrp /usr/bin/passwd /usr/bin/gpasswd /sbin/mount.nfs'
chown :suid $bins
chmod 4750 $bins
find / -xdev -perm -4000 -ls
- После изменений архитектуры определить, нужен ли отдельный mysql-сервер на mail.carid.com - как минимум определиться с тем набором баз, которые в данный момент на нем есть.
Вроде используется для CPanel, но кроме него в базе есть еще куча мусора, вроде старой копии базы для carid
- Убрать xinetd: через него запускаются только почтовые сервисы (qmail и т.п.), ftp и rsync. Почта и FTP используются постоянно, у нас не так мало ресурсов, чтобы использовать суперсервер, поэтому можно перевести их в standalone-режим. rsyncd как демон теперь не используется.
- Убрать rsyncd. Заменен lsync'ом, который работает с rsync через ssh.
SQL-DB-сервера
Определиться с тем, какие у нас сервера будут работать (в т.ч. определиться по архитектуре и требуемой мощности серверов), потом смотреть сервисы
NOSQL-сервер (memcached)
Потенциально можно убрать вообще. На данный момент, помимо перечисленного в общем разделе, из лишнего там работают:
- vsftpd
- mysqld
- xinetd
Убрано.
Файловая система
- Сделать лучшую разбивку дискового пространства на разделы, использовать запрещающие опции монтирования для разделов, доступных на запись, использовать квоты.
Кандидаты на вынесение на другой раздел: /home/ (особенно для Web'ов, где в /home хранится веб-контент) /var /var/log /usr (статический контент, можно даже сделать с RO, но надо проверить работоспособность бэкапа - или доработать скрипты) /tmp (кое-где уже есть) /var/qmail/ для Mail-сервера /var/lib/mysql/ вынести в отдельный каталог (по аналогии с master-серверами) и на отдельный раздел /home/carid/httpdocs/var/ на вебах вынести в /var/log/<что-то> или просто на отдельный раздел. /var/run и /var/lock - подумать насчет вынесения в tmpfs
- На вебах и master-сервере сменить права на контент на 640(750), раз в сутки делать поиск по cron'у файлов с кривыми правами и фиксить.
- Убрать дефолтную системную группу (users, 100) для веб-контента, использовать выделенную группу (завести группу carid, например, сделать ее основной для пользователя carid).
- nginx перевести в эту группу (ему надо только читать контент - хватит и группы с RO-правами)
Разное
Заметки на различные тематики / для сортировки:
- Варианты балансировки обращения к базе:
- mysql-proxy (ro, rw)
- haproxy (ro, rw)
- DNS round robin (ro)
- iptables DNAT range (ro)
- Оптимизация/изменение настроек MySQL:
- Не запускать slave-сервер автоматом при перезапуске.
- Сделать ограничение по max_user_connections, а не по max_connections
- Вынести каталог с сервер-специфичными файлами (в частности, var) из каталога httpdocs
LOW_PRIORITY_UPDATES актуально только для движков с табличными локами, т.е., на InnoDB апдейты будут идти с нормальным приоритетом.
Неактуально, на деле это 2 разные проблемы и проблема с низкоприоритетными апдейтами была из-за забытого в коде ключа. Проблема мной решена.
- По работающей в данный момент почте на серверах:
- memcached: почта не работает совсем. Postfix шлет почту на полное имя, не может отрезолвить домен, бонсит мыло опять-таки на полный домен, которое тоже никуда не приходит. Про то, что он отвечает за *db2* - не знает, так как в базе mydestination - левый домен.
- old db: В конфиге есть mynetworks = 1.1.1.1/32. По использованию: шлются отчеты по обновлениям на rgerashenko
slave: почтой явно давно не пользовались, в логах за последнее время пустоSlave смененmaster: По использованию - аналогично с old dbНеактуально- web1: в mydestination кривой домен получателя - стоит web2 (да еще и hostname!=mailname, что создает свои проблемы), но локально почта пытается работать благодаря тому, что @web1.carid.com резолвится. На деле - см. memcached. В конфиге разрешен релей для 1.1.1.1/32. "mail for web1.carid.com loops back to myself".
- web2: Полностью аналогичен web1, только в качестве левого домена указан web3
- web3: Домен в mydestination соответствует хостнейму (но не mailname, как и на остальных серверах), поэтому почта так же само зацикливается. Разрешенный релей - 1.1.1.1/32.
- monit на все основные сервисы на всех серверах
- Максимально запретить crontab'ы, особенно на веб-серверах.
- Аудит модулей Апача на вебах, удаление лишних (показ загруженных: apachectl -t -D DUMP_MODULES 2>&1)
- Сделать ежедневный (минимум, а можно и несколько раз в день) снапшот контента веб-сайта на мастер-сервере (минимум (для быстрой развертки контента в случае аварии), как вариант - еще где-то на стороне, в изолированном от боевых серверов месте), хранить снапшоты хотя бы за неделю. В случае с мастером надо определиться с хостерской backup-системой, чтобы она не начала бэкапить 100500 снапшотов одного и того же контента по 100 Гб каждый.
td="/home/carid/snapshot/$(date +%Y/%m/%d)" ; mkdir -p "${td}" ; cp -al /home/carid/httpdocs/ ${td}
- Вспомнить, зачем на вебах apache в группе nagios'a (если это делал я)
- Сделать delayed-репликацию где-нибудь на бэкапе или любом сервере, где будет хватать ресурсов. Использовать сдвиги, например, на 3 и 6 часов, а каждые 12 часов делать бэкап (например, один у нас, один на бэкапе). Delayed-репликация есть в dev-версиях (5.6) MySQL'a; можно развернуть и запустить пару собственных сборок в отдельном каталоге с собственными каталогами данных. Другой вариант - использовать обычные сборки (т.к. поддерживается еще с 4-й версии) с отдельным каталогом данных и использовать такой способ (подробнее про синтаксис можно глянуть в описании START SLAVE). Второй способ более костыльный, но позволит пользоваться любой версией мускуля.
- Повыявлять и сделать аудит существующих костылей "напарничка" на серверах; убрать лишние и то, что с ними связано. На данный момент из найденного:
- @web{1..3}: /etc/logrotate.d/maillog - cat'ает maillog в несуществующий файл, после чего запускает /home/carid/scripts/mailanalyse.sh. Тот в свою очередь пытается подключаться на старую офисную базу, обламывается, в результате чего на серверах днями висят ветки процессов cron->logrotate->mysql. Ненужность: 1) Оно в любом случае не работает; 2) maillog'ов на вебах не будет вообще.
- /home/carid/scripts содержит только упомянутый скрипт (и его вариации), поэтому после зачистки можно каталог полностью удалить.
- @web{1..3}: /etc/logrotate.d/maillog - cat'ает maillog в несуществующий файл, после чего запускает /home/carid/scripts/mailanalyse.sh. Тот в свою очередь пытается подключаться на старую офисную базу, обламывается, в результате чего на серверах днями висят ветки процессов cron->logrotate->mysql. Ненужность: 1) Оно в любом случае не работает; 2) maillog'ов на вебах не будет вообще.
Лишние пакеты
Собирательный раздел для лишних пакетов (например, установленных, но неиспользуемых/нерабочих сервисов), а также сервисов, подлежащих удалению, согласно описанному выше.
Service | Web (все) | Master DB | Old | Slave | Memcached | |
---|---|---|---|---|---|---|
httpd | X [1] | X [2] | X [3] | X [1] | ||
nginx | X [4] | |||||
autofs | X | X | X | X | X | X |
xinetd | X | X [5] | X | X | X | X |
subversion | X | X [3] | X [3] | X [3] | X [3] | |
postfix | X [6] | X | X | X | X | |
dovecot | X | X | X | X | X | |
squirrelmail | X | X | X | X | X | |
webalizer | X | X | X | X | ||
bind | X [7] | X | X | X | X | |
spamassassin | X | X | X | X | X | |
vsftpd | X | X | X | X | X | |
sendmail | X | X | X | X | X | |
ppp | X [7] | X[7] | X[7] | X[7] | X[7] | X[7] |
rsyncd | X [8] | |||||
mysqld | X | X | ||||
php-* | X | X | X | X |
- ↑ 1,0 1,1 работает, никуда не смотрит. Присутствуют конфиги для webalizer, squirrelmail и т.п.
- ↑ пытается запуститься, но фейлится на своей конфигурации
- ↑ 3,0 3,1 3,2 3,3 3,4 отключен в init-скриптах
- ↑ работает, есть конфиг для carid
- ↑ Mail и FTP перевести на standalone
- ↑ После перехода на nullmailer
- ↑ 7,0 7,1 7,2 7,3 7,4 7,5 7,6 установлен, но не запущен и не используется
- ↑ Не является отдельным пакетом, необходимо просто удалить запуск в xinetd и конфиги
Что сделано
На данный момент из полезного сделано:
Разное
- Поправлен лимит на предел памяти для memcached.
- Было бы интересно собрать полную (в т.ч. User Agent'ы) статистику посещений сайта. - в процессе.
- Восстановить Nagios, скорректировать работу его и/или серверов для снижения текущего числа уведомлений.
-
Уже не знаю, куда запихнуть, пусть тут будет.Сделано: Для DB:
yum remove php-* httpd-* dovecot* sendmail* autofs* kernel-devel* ppp* rp-pppoe* wireshark* xorg* kernel-headers* bind bind-chroot vsftpd* portmap* spamassassin* nfs-utils* subversion xinetd net-snmp
- Для web{1,3}
yum remove dovecot* autofs* kernel-devel* ppp* rp-pppoe* wireshark* xorg* kernel-headers* bind bind-chroot vsftpd* subversion xinetd net-snmp mysql-server webmin squirrelmail webalizer
- Оптимизация/изменение настроек MySQL: Увеличить время хранения бинлогов/relay-логов - должно с запасом покрывать выходные, т.е., 3-4 дня.
- inotify-based синхронизация контента между 6 (master -> web{1,3}/import/backup) серверами. Практически не вызывает нагрузки, контент копируется в реальном времени.
www@import.carid.com
*/15 * * * * RSYNC_PASSWORD="djh8PKq" /usr/local/bin/rsync -rzt carid@50.56.1.164::images/ /www/import.carid.com/images > /dev/null 2>&1
0 */1 * * 1,2,3,4,5 RSYNC_PASSWORD="djh8PKq" /usr/local/bin/rsync --exclude 'phpmyadmin/' --exclude 'phpmyadmin3/' --exclude 'import2/' --exclude 'images/' --exclude 'import/' --exclude 'config.php' --exclude 'config/config.xml' --exclude 'index.php' --exclude 'files/' --exclude 'var/tmp/' --exclude 'var/log/' --exclude 'var/templates_c/' --exclude 'var/cache/' --exclude 'wde-master/admin/.htaccess' --exclude 'include/refine_search_detection.php' --exclude 'csv_parser/' --exclude '.htaccess' --exclude='.svn' -rzt carid@50.56.1.164::httpdocs/ /www/import.carid.com > /dev/null 2>&1 && /bin/rm -f /www/import.carid.com/var/templates_c/* > /dev/null 2>&1
root@mail
* * * * * /root/bin/ftpwatch2sql.sh
shell@mail>
fix /var/www/vhosts/carid.com/private/svn/svn_sync.sh
carid@mail
* * * * * /usr/local/bin/psqlftpfile.sh 2>>/tmp/sync.log
0 2 * * * rsync -trvn --del --exclude='wde-master' --exclude='.svn' --exclude='/config.php' --exclude='config/*' --exclude='/robots.txt' --exclude='.htaccess' --exclude="var/*" --exclude="private/*" --exclude="sync-stat/" /var/www/vhosts/carid.com/httpdocs/ web3:/home/carid/httpdocs/ | mail -s "Master->web3 rsync report" team.admins@onyxenterprises.com
0 2 * * * rsync -trvn --del --exclude='wde-master' --exclude='.svn' --exclude='/config.php' --exclude='config/*' --exclude='/robots.txt' --exclude='.htaccess' --exclude="var/*" --exclude="private/*" --exclude="sync-stat/" /var/www/vhosts/carid.com/httpdocs/ web2:/home/carid/httpdocs/ | mail -s "Master->web2 rsync report" team.admins@onyxenterprises.com
0 2 * * * rsync -trvn --del --exclude='wde-master' --exclude='.svn' --exclude='/config.php' --exclude='config/*' --exclude='/robots.txt' --exclude='.htaccess' --exclude="var/*" --exclude="private/*" --exclude="sync-stat/" /var/www/vhosts/carid.com/httpdocs/ web1:/home/carid/httpdocs/ | mail -s "Master->web1 rsync report" team.admins@onyxenterprises.com
carid@web{1,2,3}
0 * * * * sleep 15; RSYNC_PASSWORD="djh8PKq" nice -n 19 ionice -c 2 -n 7 rsync -rqvt carid@192.168.100.164::files/ /home/carid/httpdocs/files >> /tmp/sync.log
0 * * * * sleep 15; RSYNC_PASSWORD="djh8PKq" nice -n 19 ionice -c 2 -n 7 rsync -rqvt carid@192.168.100.164::pages/ /home/carid/httpdocs/skin1/pages >> /tmp/sync.log
0 * * * * sleep 15; RSYNC_PASSWORD="djh8PKq" nice -n 19 ionice -c 2 -n 7 rsync -rqvt --delete carid@192.168.100.164::images/ /home/carid/httpdocs/images >> /tmp/sync.log
*/30 * * * * scp -rp mail1:~carid/httpdocs/authorized-installers ~carid/httpdocs/ > /dev/null 2>&1
0 8 * * 1,3,5 scp -rp mail1:~carid/httpdocs/sitemaps ~carid/httpdocs > /dev/null 2>&1
- Перейти на RAID10 на Slave-сервере. 5-й сильно уж тормозит - дамп заливался несколько часов против 40 минут (при тесте на RAID10 на Mail-сервере). RAID5 неприменим для базы.
root@345731-mail1:~# time { pv carid_2012_02_20.sql.bz2 | pbunzip2 -c | mysql -hlocalhost -uroot -p'pass' raidtestdb ; }
1.85GB 0:40:09 [ 803kB/s] [===================================================>] 100%
real 40m20.638s
user 16m31.050s
sys 0m20.355s
Web
- /var/www/vhosts/carid.com/httpdocs/images/mcafee3.php вынести с вебов (он везде одинаковый и генерит одну картинку, которую можно просто создавать на мастере)
Сервисы
Web
- Убрать vsftpd (FTP на веб-серверах не нужен, все изменения делаются через мастер-сервер, для того, чтобы забрать какой-то файлик с веба (логи, например) есть NFS)
- Убрать xinetd (не несет никакой полезной нагрузки, просто лишний сервис)
- mysqld (не используется, нет ни одной базы)