Веб сервер на много коннектов и высокую нагрузку (nginx+php-fpm+mysql)

Опубликовано admin в Втр, 19/04/2011 - 17:00


Предисловие.

Вот хочу поделится с коллегами своими наработками, думаю они многим пригодятся.

1. Начнем с подготовки фри к постановке ее на веб сервер.

Сетапим на сервер фрю семерку архтиктуры amd64 (можно и i386 но там надо делать PAE ядро и расширять адресацию), обнавляем исходники

nice -20 csup /usr/src/csup

ee /usr/src/csup
*default host=cvsup6.ru.FreeBSD.org
*default base=/var/db
*default prefix=/usr
*default release=cvs tag=RELENG_7
*default delete use-rel-suffix
*default compress
 
src-all
ports-all tag=.
doc-all prefix=/usr/src tag=.

Правим конфиги

ee /etc/make.conf
# оптимизация под мой камень, применяется везде где только используется команда make,  
# позваляет получить немного лишних процентов производительности 
CPUTYPE?=core2
NO_PROFILE=yes 
NO_GAMES=yes 
DOC_LANG=en_US.ISO8859-1 ru_RU.KOI8-R
WITHOUT_X11=YES 
NO_X11=YES 
WITH_IDEA=yes 
MAKE_IDEA=yes 
WITHOUT_GAMES=yes 
WITHOUT_INET6=yes 
WITHOUT_INET6_SUPPORT=yes 
WITHOUT_PROFILE=yes 
WITHOUT_IPV6=yes 
 
PORTSDIR?=      /usr/ports
 
DEFAULT_MYSQL_VER=51 
.if ${.CURDIR} == ${PORTSDIR}/databases/mysql51-server
WITH_PROC_SCOPE_PTH=yes  # треды 
BUILD_OPTIMIZED=yes      # флаги оптимизации компилятору 
# дает небольшой прирост производительности и независимость от внешних библиотек 
BUILD_STATIC=yes 
.endif
.if ${.CURDIR} == ${PORTSDIR}/databases/mysql51-client
WITH_PROC_SCOPE_PTH=yes 
BUILD_OPTIMIZED=yes 
BUILD_STATIC=yes 
.endif

ee /etc/src.conf
WITHOUT_INET6=yes            # рубим ipv6, рассадник дыр в безопастности и глюков  
WITHOUT_INET6_SUPPORT=yes    # практическая его полездность будет еще не скоро... 
WITHOUT_PROFILE=yes          

и пересобираем систему

cd /usr/src
nice -20 make -j6 buildworld

Пока собирается система можно занятся конфигом ядра в следующщей консоле (или другом ssh терминале).

cd /usr/src/sys/amd64/conf
cp GENERIC SERVER64
ee SERVER64

И правим ядро для наших целей, я например викидываю от туда дебаг, ipv6, и все не нужные мне драйвера
(nfs ntfs fat все рейды и сетевухи которых у меня нету fairwair usb wlan), и добавляю следующщее

options         SC_HISTORY_SIZE=8192    # чтобы в консоли можно было далеко листать историю 
options         IPFIREWALL
options         IPFIREWALL_VERBOSE
options         IPFIREWALL_VERBOSE_LIMIT=4000 
options         IPFIREWALL_DEFAULT_TO_ACCEPT
options         IPFIREWALL_FORWARD
options         DUMMYNET
options         ACCEPT_FILTER_DATA        # фильтры для nginx 
options         ACCEPT_FILTER_HTTP        # ... 
options         HZ=1000 
options         DEVICE_POLLING

После того как сборка мира в первой консоле успешно закончится, можно приступать к сборке и установке нового ядра

nice -20 make -j2 kernel KERNCONF=SERVER64  # с -j6 иногда не собирается почемуто... 

Так как хороший одмин должен быть ленивым, я создаю в корне скриптик

ee /upgrade && chmod 755 /upgrade
#!/bin/sh 
fsck -p
mount -u /
mount -a
cd /usr/src
adjkerntz -i 
mergemaster -p
make installworld &&\
make delete-old &&\
mergemaster -i

Цепляем ip-kvm, ну или топаем туда где стоит сервер (нужна консоль), и перезагружаем его в однопользовательский режим.

nextboot -o "-s" -k kernel && reboot

Загружается система, на приглашение к выбору шела нажимаем Enter и после того как появиться консоль вводим

/upgrade && exit 

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

2. Устанавливаем софт

Собственно ставим сам nginx, на запросы отвечаем так:

cd /usr/ports/www/nginx/ ; nice -20 make install clean 
[ ] DEBUG                 Enable nginx debugging
[ ] GOOGLE_PERFTOOLS      Enable google perftools module
[X] HTTP_MODULE           Enable HTTP module
[X] HTTP_ADDITION_MODULE  Enable http_addition module
[X] HTTP_DAV_MODULE       Enable http_webdav module
[X] HTTP_FLV_MODULE       Enable http_flv module
[X] HTTP_PERL_MODULE      Enable http_perl module
[X] HTTP_REALIP_MODULE    Enable http_realip module
[X] HTTP_REWRITE_MODULE   Enable http_rewrite module
[X] HTTP_SSL_MODULE       Enable http_ssl module
[X] HTTP_STATUS_MODULE    Enable http_stub_status module
[X] HTTP_SUB_MODULE       Enable http_sub module
[ ] MAIL_MODULE           Enable IMAP4/POP3/SMTP proxy module
[ ] MAIL_IMAP_MODULE      Enable IMAP4 proxy module
[ ] MAIL_POP3_MODULE      Enable POP3 proxy module
[ ] MAIL_SMTP_MODULE      Enable SMTP proxy module
[ ] MAIL_SSL_MODULE       Enable mail_ssl module
[X] WWW                   Enable html sample files

Дальше ставим мускул и пхп:

cd /usr/ports/databases/mysql51-server/ ; nice -20 make install clean &&\
cd /usr/ports/lang/php5/ ; nice -20 make install clean 
 
[X] CLI        Build CLI version
[X] CGI        Build CGI version
[ ] APACHE     Build Apache module
[ ] DEBUG      Enable debug
[X] SUHOSIN    Enable Suhosin protection system (not for jails)
[X] MULTIBYTE  Enable zend multibyte support
[ ] IPV6       Enable ipv6 support
[X] MAILHEAD   Enable mail header patch
[X] REDIRECT   Enable force-cgi-redirect support (CGI only)
[X] DISCARD    Enable discard-path support (CGI only)
[X] FASTCGI    Enable fastcgi support (CGI only)
[X] PATHINFO   Enable path-info-check support (CGI only)

Cтавим сопутствующщий софт:

cd /usr/ports/lang/php5-extensions/ ; nice -20 make install clean 

Выбираем себе нужные модули для пыха.

И не забываем про ZendOptimizer (довольно часто попадаются зазенденные скрипты, поэтому всегда втыкаю его докучи, авось пригодится)

/usr/ports/devel/ZendOptimizer/ ; nice -20 make install clean 

Ставим phpMyAdmin (вечное требованние программеров на пыхе, в консоли для них не кошерно...)

/usr/ports/databases/phpmyadmin/ ; nice -20 make install clean 

Ставим wget из /usr/ports/ftp/wget  (это типа консольная качалка такая)
скачиваем порт php-fpm под фрю (я же говорил уже что хороший одмин он ленивый, и с патчами в исходниках ковырятся ему должно быть лень если все и так хорошо работает), распаковываем его в /usr/ports/lang/php5-fpm идем туда и сетапим его:

cd /usr/ports/ftp/wget ; nice -20 make install clean &&\
cd /usr/ports/lang &&\
wget http://php-fpm.anight.org/downloads/freebsd-port/php5-fpm-0.5.9.tar.gz &&\
tar xfz php5-fpm-0.5.9.tar.gz &&\
cd php5-fpm &&\
nice --20 make install clean 
 
[X] CLI        Build CLI version
[ ] DEBUG      Enable debug
[X] SUHOSIN    Enable Suhosin protection system (not for jails)
[X] MULTIBYTE  Enable zend multibyte support
[ ] IPV6       Enable ipv6 support
[X] MAILHEAD   Enable mail header patch
[X] PATHINFO   Enable path-info-check support (CGI only)

Ну вот как бы и усе как бы для голого веб сервера все уже готово, переходим к настройке.

3. Конфигурируем

Мой конфиг nginx:

less /usr/local/etc/nginx/nginx.conf
# рекомендуют делать по количеству ядер 
# но время отклика реально уменьшяется если запутить побольше воркеров, 
# т.к. у меня памяти много сделал сразу 100 
worker_processes 100;
# приоретет воркеров, сделал повыше  
# чтобы из за медленных скриптов и корявых 
# мускульных запросов статика отдавалась без лагов 
worker_priority -5;
worker_rlimit_nofile 51200;
 
events {
    worker_connections  51200;
    use                 kqueue;
}
 
http {
    include             mime.types;
    default_type        application/octet-stream;
 
    log_format  main    '$remote_addr - $remote_user [$time_local] $request '
                        '"$status" $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
 
    server_names_hash_bucket_size       512;
    sendfile            on;
    tcp_nopush          on;
    keepalive_timeout   70 30;
    send_timeout        30s;
    reset_timedout_connection   on;
    resolver            127.0.0.1;
    resolver_timeout    10s;
 
    open_file_cache             max=100000 inactive=40s;
    open_file_cache_valid       60s;
    open_file_cache_min_uses    2;
    open_file_cache_errors      on;
 
    server {
        listen          80;
        server_name     www.ru;
        charset         koi8-r;
        location / {
            root        /usr/local/www/nginx;
            index       index.html index.htm;
        }
    }
 
    server {
        # фильтр позволяет отфильтровывать  
        # все не хттп соеденения до их установки на уровне ядра  
        # (собираются все пакеты анализируется GET загаловок,  
        # если его нет соеденение отбрасывается, если есть то соедененине  
        # появляется в системе и нгинксе, оч полезно от примитивного доса). 
        listen          80  default accept_filter=httpready;
 
        server_name     www.mysite.ru mysite.ru
 
        charset windows-1251;
 
        access_log      /home/igrosoft/access_log       main;
        error_log       /home/igrosoft/error_log        error;
 
        location / {
            root        /home/site;
            index       index.html index.htm index.php;
        }
 
        location /mrtg/ {                         # это mrtg 
            alias       /usr/local/www/mrtg/;
            index       index.html index.htm;
            auth_basic  Admin;
            auth_basic_user_file /usr/local/www/mrtg/.htpasswd;
        }
        # а это для phpmyadmin, чтобы норально обрабатывались пхп скрипты 
        # надо создать линк из основной директории коммандой 
        # ln -s /usr/local/www/phpMyAdmin /home/site/.,ak,234sfyf34.:,.s 
        location /.,ak,234sfyf34.:,.s/ {                               
            alias       /usr/local/www/phpMyAdmin/;
            index       index.html index.htm index.php;
            auth_basic  Admin;
            auth_basic_user_file /usr/local/www/phpMyAdmin/.htpasswd;
        }
        # передаем обработку пхп скриптов на php-fpm 
        location ~* \.php$ {
            fastcgi_pass        unix:/tmp/php-fpm.sock;
            fastcgi_index       index.php;
            fastcgi_param       SCRIPT_FILENAME  /home/site$fastcgi_script_name;
            include             fastcgi_params;
        }
 
        location ~ /\.ht {     # закрываем доступ к файлам .htpasswd 
            deny        all;
        }
 
        error_page      500 502 503 504         /50x.html;
        location = /50x.html {
            root        /usr/local/www/nginx-dist;
        }
        # с этой странички мы будем брать статистику по нгинксу для мртг 
        location /nginx_status {
            stub_status on;
            access_log   off;
            allow 127.0.0.1;
            deny all;
        }
 
 
 
 
    # HTTPS 
    server {
        listen       443;
        server_name  localhost;
 
        ssl                  on;                   # задаем настройки для ssl  
        ssl_certificate      cert.pem;
        ssl_certificate_key  cert.key;
        ssl_session_timeout  5m;
        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
        ssl_prefer_server_ciphers   on;
 
        server_name     www.myserver.ru myserver.ru
 
             # а дальше все тоже самое описание location'ов 
             # аналогичное тому что было указанно выше  
 
    }
}

Теперь поправим конфиг у fpm'a

ee /usr/local/etc/php-fpm.conf
я там изменил только следующщие строчки:
   <!-- меняем сокет, вместо tcp'шного делаем -->
   <!-- локальный чтобы лишний раз стек не напрягать -->
<value name="listen_address">/tmp/php-fpm.sock</value>
 
   <!-- устанавливаем количество процессов php-cgi -->
   <!-- отнестить к этому нужно внимательно т.к. -->
   <!-- именно столько максимум php скриптов сможет выполняться одновременно-->
<value name="max_children">120</value>
 
   <!-- Очень полезная для оптимизации скриптов и мускульных запросов фича -->
   <!-- записывает в лог все данные о скрипте который -->
   <!-- выполняется дольше указанного времени -->
<value name="request_slowlog_timeout">5s</value>
<value name="slowlog">/var/log/php-slow.log</value>
 
   <!-- сколько запросов выполнит один cgi процесс прежде чем его перезапустят -->
<value name="max_requests">1500</value>

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

ee /usr/local/etc/php.ini
[PHP]
engine = On
zend.ze1_compatibility_mode = Off
short_open_tag = On
asp_tags = Off
precision    =  14 
y2k_compliance = On
output_buffering = 4096 
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func=
serialize_precision = 100 
allow_call_time_pass_reference = Off
safe_mode = Off
safe_mode_gid = Off
safe_mode_include_dir =
safe_mode_exec_dir =
safe_mode_allowed_env_vars = PHP_
safe_mode_protected_env_vars = LD_LIBRARY_PATH
disable_functions =
disable_classes =
expose_php = On
max_execution_time = 30 
max_input_time = 60 
memory_limit = 128M               ; увеличиваем лимит по памяти для "тяжелых" скриптов
error_reporting  =  E_ALL & ~E_NOTICE
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 10240 
ignore_repeated_errors = On
ignore_repeated_source = On
report_memleaks = On
track_errors = On
variables_order = "EGPCS"
register_globals = On
register_long_arrays = Off
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 32M
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
doc_root =
user_dir =
enable_dl = On
file_uploads = On
upload_max_filesize = 800M
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 60 
 
[Syslog]
define_syslog_variables  = Off
 
[mail function]
SMTP = localhost 
smtp_port = 25 
 
[SQL]
sql.safe_mode = Off
 
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1 
odbc.max_links = -1 
odbc.defaultlrl = 4096 
odbc.defaultbinmode = 1 
 
[MySQL]
mysql.allow_persistent = On
mysql.max_persistent = -1 
mysql.max_links = -1 
mysql.default_port =
mysql.default_socket =
mysql.default_host =
mysql.default_user =
mysql.default_password =
mysql.connect_timeout = 60 
mysql.trace_mode = Off
 
[MySQLi]
mysqli.max_links = -1 
mysqli.default_port = 3306 
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
 
[mSQL]
msql.allow_persistent = On
msql.max_persistent = -1 
msql.max_links = -1 
 
[PostgresSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1 
pgsql.max_links = -1 
pgsql.ignore_notice = 0 
pgsql.log_notice = 0 
 
[Sybase]
sybase.allow_persistent = On
sybase.max_persistent = -1 
sybase.max_links = -1 
sybase.min_error_severity = 10 
sybase.min_message_severity = 10 
sybase.compatability_mode = Off
 
[Sybase-CT]
sybct.allow_persistent = On
sybct.max_persistent = -1 
sybct.max_links = -1 
sybct.min_server_severity = 10 
sybct.min_client_severity = 10 
 
[bcmath]
bcmath.scale = 0 
 
[Informix]
ifx.default_host =
ifx.default_user =
ifx.default_password =
ifx.allow_persistent = On
ifx.max_persistent = -1 
ifx.max_links = -1 
ifx.textasvarchar = 0 
ifx.byteasvarchar = 0 
ifx.charasvarchar = 0 
ifx.blobinfile = 0 
ifx.nullformat = 0 
 
[Session]
session.save_handler = files
session.use_cookies = 1 
session.name = PHPSESSID
session.auto_start = 0 
session.cookie_lifetime = 0 
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php 
session.gc_probability = 1 
session.gc_divisor     = 1000 
session.gc_maxlifetime = 256000 
session.bug_compat_42 = 0 
session.bug_compat_warn = 1 
session.referer_check =
session.entropy_length = 0 
session.entropy_file =
session.cache_limiter = nocache
session.cache_expire = 18000 
session.use_trans_sid = 0 
session.hash_function = 0 
session.hash_bits_per_character = 5 
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
 
[Tidy]
tidy.clean_output = Off
 
[soap]
soap.wsdl_cache_enabled=1 
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400 
 
[Zend]
zend_optimizer.optimization_level=2   ; 2 потому что на архетитуре amd64 
                                      ; при дефолтном значении 15 кладет пых в корку
zend_extension_manager.optimizer="/usr/local/lib/php/20060613/Optimizer"
zend_extension_manager.optimizer_ts="/usr/local/lib/php/20060613/Optimizer_TS"
zend_extension="/usr/local/lib/php/20060613/ZendExtensionManager.so"
zend_extension_ts="/usr/local/lib/php/20060613/ZendExtensionManager_TS.so"

Тепер переходим к тюнингу tcp стека:

ee /etc/sysctl.conf
net.inet.tcp.blackhole=2      # включаем черные дыры для tcp 
net.inet.udp.blackhole=1      # и udp 
kern.ipc.nmbclusters=65536    # увеличиваем лимиты 
kern.ipc.somaxconn=32768      
kern.ipc.maxsockets=204800 
kern.maxfiles=256000 
kern.maxfilesperproc=230400 
net.inet.ip.portrange.first=1024 
net.inet.ip.portrange.last=65535 
net.inet.ip.portrange.randomized=0 
net.inet.tcp.maxtcptw=40960 
# самый на мой взгляд важный параметр, субьективно: 
# при уменьшении уменьшается количество вейтов, сервер лудьше держит нагрузку 
# при увеличении реже рветься связь на плохих каналах, нагрузка возрастает 
net.inet.tcp.msl=40000 
net.inet.tcp.finwait2_timeout=40000 
net.inet.tcp.syncookies=1 
net.inet.tcp.sack.enable=1 
net.inet.tcp.nolocaltimewait=1 
net.inet.tcp.fast_finwait2_recycle=1 
net.inet.icmp.drop_redirect=1 
net.inet.icmp.log_redirect=1 
net.inet.ip.redirect=0 
kern.polling.enable=1 
kern.polling.burst_max=1000 
kern.polling.each_burst=50 
net.inet.icmp.icmplim=5000 

ee /boot/loader.conf
net.inet.tcp.syncache.hashsize=1024 
net.inet.tcp.syncache.bucketlimit=100 
net.inet.tcp.tcbhashsize=4096 

По оптимизации стека и настройку sysctl можно почитать следующщее:
http://www.opennet.ru/base/net/tune_freebsd.txt.html
http://www.lissyara.su/?id=1147

По настроке nginx:
http://sysoev.ru/nginx/docs/
http://www.lexa.ru/nginx-ru/

Ну и напоследок гугл еще никто не отменял ;-)


Уф, великовата уже статейка получилась, пора бы и закруглятся, ждите продолжений.





Источник: http://www.lissyara.su/articles/freebsd/www/nginx+php-fpm+mysql/