Участник:Rain/Заметки/Onyx: различия между версиями

Материал из Linux Wiki
Перейти к навигацииПерейти к поиску
м (Про RAID5 и RAID10)
 
(не показана 161 промежуточная версия этого же участника)
Строка 1: Строка 1:
Заметки по серверам для carid.com:
Заметки по серверам для carid.com:


== Что хотелось бы сделать ==
= Что хотелось бы сделать =


=== Кодировки MySQL ===
== Кодировки 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; однако среди них есть достаточно крупные с частым обращением).


Ошибка при конвертации:
'''Поиск использования таблиц в коде (на выборку данных):'''


"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
<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>


: с ошибкой разобраться, для остальных таблиц потенциально можно вынести поиск в отдельную маленькую таблицу, оставив ее в MyISAM (требует правки кода, так что в крайнем случае можно оставить их как есть - все же лучше иметь 8 лочащихся таблиц, чем 300).
----


Само по себе изменение остальных таблиц должно пройти гладко; на время конвертации можно переключить 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 может уйти на один сервер, а запрос - на другой и получим кашу. Как вариант - жестко задать настройки (пусть и неправильные), чтобы на любой коннект принудительно устанавливалась нужная кодировка (т.е., 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 Гб (на данный момент - 13), из них многое - из-за постоянных тестов. Эффективность кэша на момент 8 Гб - порядка 80 процентов, что мало. Итого, если выделить даже по 16 Гб под memcached на каждом сервере - этого с головой хватит и для нужд веб-сервера, и для memcached. По процессору - даже единый memcached практически не нагружает процессор (нагрузка на уровне пары процентов для одного ядра сервера). При разделении нагрузка на каждый отдельный memcached уменьшится до 3 раз на текущий момент. Когда начнем упираться в процессор на веб-сервере - наличие на нем memcached будет далеко не самой большой проблемой (иначе говоря, мы скорее заведем новый сервер впараллель к трем существующим, чем упремся в процессор).
Недостаток такого варианта - выделение памяти на каждом сервере. На деле из 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", "rand()");
$a=rand().rand().rand();
 
$m->set("$i", "$a");
}
}
?>
?>
Строка 109: Строка 245:
Для Ъ: создание указанного 3-м параметром числа ключей и занесение в каждый из ключей рандомного числа.
Для Ъ: создание указанного 3-м параметром числа ключей и занесение в каждый из ключей рандомного числа.


Было сделано 4 тестирования - скрипт на локалхосте + memcached на локалхосте, скрипт на локалхосте + memcached на машине в локальной сети и для обоих вариантов скрипт и memcached менялись местами. Линк 100 Мбит (Реально сервер подключен к гигабитному свичу, но десктоп - к 100 Мбит. При случае протестирую на полностью гигабитной сети). Результаты повторяемые:
Было сделано 4 тестирования - скрипт на локалхосте + memcached на локалхосте, скрипт на локалхосте + memcached на машине в локальной сети и для обоих вариантов скрипт и memcached менялись местами.
 
==== 100 Мбит ====
 
Линк 100 Мбит (Реально сервер подключен к гигабитному свичу, но десктоп - к 100 Мбит. При случае протестирую на полностью гигабитной сети). Результаты повторяемые:


Тест на десктопе:
Тест на десктопе:
Строка 152: Строка 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
* https://www.linux.org.ru/forum/admin/7465868 - пронаблюдать тред
</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>


== Безопасность ==
Убрано.


{{ToDo|Заполнить}}
=== Файловая система ===


== Разное ==
* Сделать лучшую разбивку дискового пространства на разделы, использовать запрещающие опции монтирования для разделов, доступных на запись, использовать квоты.
<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-правами)
 
= Разное =


Заметки на различные тематики:
Заметки на различные тематики / для сортировки:


* Варианты балансировки обращения к базе:
* Варианты балансировки обращения к базе:
Строка 172: Строка 664:


* Оптимизация/изменение настроек MySQL:
* Оптимизация/изменение настроек MySQL:
** Увеличить время хранения бинлогов - должно с запасом покрывать выходные, т.е., 3-4 дня.
** Сделать более синхронную обработку логов (опция sync_binlog=1 + еще какая-то, не помню)
** Не запускать slave-сервер автоматом при перезапуске.
** Не запускать slave-сервер автоматом при перезапуске.
** Сделать ограничение по max_user_connections, а не по max_connections


* Вынести каталог с сервер-специфичными файлами (в частности, var) из каталога httpdocs
* Вынести каталог с сервер-специфичными файлами (в частности, var) из каталога httpdocs
* Было бы интересно собрать полную (в т.ч. User Agent'ы) статистику посещений сайта.
* <s>LOW_PRIORITY_UPDATES актуально только для движков с табличными локами, т.е., на InnoDB апдейты будут идти с нормальным приоритетом.</s>
* LOW_PRIORITY_UPDATES актуально только для движков с табличными локами, т.е., на InnoDB апдейты будут идти с нормальным приоритетом.
Неактуально, на деле это 2 разные проблемы и проблема с низкоприоритетными апдейтами была из-за забытого в коде ключа. Проблема мной решена.
* Перейти на RAID10 на Slave-сервере. 5-й сильно уж тормозит - дамп заливался несколько часов против 40 минут (при тесте на Mail-сервере). RAID5 неприменим для базы.
 
* По работающей в данный момент почте на серверах:
** 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'ом в дампе, в котором они есть - либо просто пересоздать с новыми параметрами.

Ссылки

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 Гбит

ToDo: Сделать тест для гигабитной незагруженной сети

Сеть 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

Результаты стабильно повторяемы.

Ссылки

Безопасность

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

Mail

  • После изменений архитектуры определить, нужен ли отдельный 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 содержит только упомянутый скрипт (и его вариации), поэтому после зачистки можно каталог полностью удалить.

Лишние пакеты

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

Service ­done! Web (все) Mail ­done! Master DB ­done! Old ­done! Slave ­done! 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. 1,0 1,1 работает, никуда не смотрит. Присутствуют конфиги для webalizer, squirrelmail и т.п.
  2. пытается запуститься, но фейлится на своей конфигурации
  3. 3,0 3,1 3,2 3,3 3,4 отключен в init-скриптах
  4. работает, есть конфиг для carid
  5. Mail и FTP перевести на standalone
  6. После перехода на nullmailer
  7. 7,0 7,1 7,2 7,3 7,4 7,5 7,6 установлен, но не запущен и не используется
  8. Не является отдельным пакетом, необходимо просто удалить запуск в xinetd и конфиги

Что сделано

На данный момент из полезного сделано:

Разное

  • ­done! Поправлен лимит на предел памяти для memcached.
  • ­done! Было бы интересно собрать полную (в т.ч. User Agent'ы) статистику посещений сайта. - в процессе.
  • ­done! Восстановить Nagios, скорректировать работу его и/или серверов для снижения текущего числа уведомлений.
  • ­done! Уже не знаю, куда запихнуть, пусть тут будет. Сделано: Для 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) серверами. Практически не вызывает нагрузки, контент копируется в реальном времени.
Мусор, выкинутый после введения lsyncd
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
  • ­done! Перейти на RAID10 на Slave-сервере. 5-й сильно уж тормозит - дамп заливался несколько часов против 40 минут (при тесте на RAID10 на Mail-сервере). RAID5 неприменим для базы.
Тест на Mail-сервере
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

  • ­done! /var/www/vhosts/carid.com/httpdocs/images/mcafee3.php вынести с вебов (он везде одинаковый и генерит одну картинку, которую можно просто создавать на мастере)

Сервисы

Web

  • ­done! Убрать vsftpd (FTP на веб-серверах не нужен, все изменения делаются через мастер-сервер, для того, чтобы забрать какой-то файлик с веба (логи, например) есть NFS)
  • ­done! Убрать xinetd (не несет никакой полезной нагрузки, просто лишний сервис)
  • ­done! mysqld (не используется, нет ни одной базы)