Настраиваем nginx и php-fpm
Сегодня мы научимся устанавливать веб службы на слабые сервера. Точнее, даже, не обязательно на слабые. Просто, для разнообразия, мы откажемся от использования стандартной LAMPы (Linux+Apache+MySQL+PHP). Ещё точнее, мы откажемся от использования Apache. Мы будем настраивать связку nginx + php-fpm + MySQL. В первой части статьи мы поднимем наш сервер.
Начнём с того, что у нас есть сервер с только что поставленной CentOS 6.6. Сервер функционирует в сети с доменным суффиксом local, включённым DHCP и работающим DNS, разрешающим имена в зоне local. Для сокращения текста, все команды мы будем выполнять из контекста root-а. Для начала — обновим систему:
[admin@sandbox ~]$ su - Password: [root@sandbox ~]# yum update [root@sandbox ~]#
Теперь можно устанавливать nginx. В официальных репозиториях необходимого нам пакета нет, по этому мы создадим файл для официального репозитория nginx.org:
[root@sandbox ~]# cat /etc/yum.repos.d/nginx.repo [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1 [root@sandbox ~]#
После чего устанавливаем nginx традиционным путём. Не забываем включить выполнение nginx при старте системы командой chkconfig:
[root@sandbox ~]# yum install nginx [root@sandbox ~]# chkconfig nginx on [root@sandbox ~]#
Забудем пока про nginx, он, надеюсь, без ошибок установился у нас. Теперь будем устанавливать MySQL. Тут всё тоже довольно тривиально, но на всякий случай напомню. Устанавливаем через yum обе части — mysql и mysql-server, включаем запуск при загрузке и запускаем mysqld. Теперь выполняем скрипт безопасной установки, который почистит свежепоставленный MySQL от ненужного хлама, заменит пароль рутовому пользователю и ограничит для него подключения. Попутно, памятуя о будущей безопасности, засунем mysql.pid в будущее chroot окружение:
[root@sandbox ~]# yum install mysql mysql-server [root@sandbox ~]# chkconfig mysqld on [root@sandbox ~]# service mysqld start Starting mysqld: [ OK ] [root@sandbox ~]# /usr/bin/mysql_secure_installation NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY! In order to log into MySQL to secure it, we'll need the current password for the root user. If you've just installed MySQL, and you haven't set the root password yet, the password will be blank, so you should just press enter here. Enter current password for root (enter for none): OK, successfully used password, moving on... Setting the root password ensures that nobody can log into the MySQL root user without the proper authorisation. Set root password? [Y/n] y New password: Re-enter new password: Password updated successfully! Reloading privilege tables.. ... Success! By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? [Y/n] y ... Success! Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? [Y/n] y ... Success! By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? [Y/n] y - Dropping test database... ... Success! - Removing privileges on test database... ... Success! Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? [Y/n] y ... Success! Cleaning up... All done! If you've completed all of the above steps, your MySQL installation should now be secure. Thanks for using MySQL! [root@sandbox ~]# mkdir -p /var/www/var/run/mysqld [root@sandbox ~]# mount --bind /var/run/mysqld/ /var/www/var/run/mysqld/ [root@sandbox ~]#
На данном этапе MySQL нам тоже больше не нужен. Теперь перейдём к установке php. Для начала подключим два репозитория — EPEL и REMI. Они, кстати, вполне могут пригодиться и не только в рамках данной статьи — в них много полезнейших пакетов, которые обновляются раньше, чем в официальных репах.
[root@sandbox ~]# yum install yum-priorities [root@sandbox ~]# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm [root@sandbox ~]# rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
REMI нам сейчас лучше включить, так как установка всей требухи php будет происходить именно с него:
[root@sandbox ~]# cat /etc/yum.repos.d/remi.repo [remi] name=Les RPM de remi pour Enterprise Linux 6 - $basearch #baseurl=http://rpms.famillecollet.com/enterprise/6/remi/$basearch/ mirrorlist=http://rpms.famillecollet.com/enterprise/6/remi/mirror enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi [root@sandbox ~]#
Всё, можно смело ставить php-fpm и прочие пехапешные необходимости всё тем же традиционным yum. Не забываем включить старт при загрузке:
[root@sandbox ~]# yum install php php-fpm [root@sandbox ~]# yum install php-gd php-mysql php-mbstring php-xml php-mcrypt [root@sandbox ~]# yum install php-mysql php-gd php-imap php-ldap php-mbstring php-odbc php-pear php-xml php-xmlrpc [root@sandbox ~]# chkconfig php-fpm on
Установка завершена!
Создадим директории для нашего будущего хоста и для логов php-fpm в chroot, назначим необходимые права на них и, для экспериментов, создадим в корневой директории нашего хоста файл index.php, который будет нам отдавать результат выполнения phpinfo:
[root@sandbox ~]# mkdir -p /var/www/sandbox [root@sandbox ~]# mkdir -p /var/www/var/log/php-fpm [root@sandbox ~]# chwon nginx:nginx /var/www/var/log/php-fpm [root@sandbox ~]# echo "<?php phpinfo (); ?>" > /var/www/sandbox/index.php [root@sandbox ~]# chown -R nginx:nginx /var/www/sandbox/ [root@sandbox ~]# chmod -R 750 /var/www/sandbox/ [root@sandbox ~]# ln /etc/hosts /var/www/etc/hosts [root@sandbox ~]# ln /etc/resolv.conf /var/www/etc/resolv.conf [root@sandbox ~]# ln /etc/localtime /var/www/etc/localtime [root@sandbox ~]# cp -R /usr/share/zoneinfo/ /var/www/usr/share/
В конфигурационном файле php-fpm зададим пользователя и группу, от которых он будет выполняться
[root@sandbox ~]# cat /etc/php-fpm.d/www.conf | grep -6 'user/group' ; Default Values: user and group are set as the running user ; mode is set to 0660 ;listen.owner = nobody ;listen.group = nobody ;listen.mode = 0660 ; Unix user/group of processes ; Note: The user is mandatory. If the group is not set, the default user's group ; will be used. ; RPM: apache Choosed to be able to access some dir as httpd user = nginx ; RPM: Keep a group allowed to write in log dir. group = nginx [root@sandbox ~]#
и зададим место, где будем его chroot-ить. chdir указывается здесь уже относительно chroot, будем внимательны
[root@sandbox ~]# cat /etc/php-fpm.d/www.conf | grep -10 'chdir' ; Chroot to this directory at the start. This value must be defined as an ; absolute path. When this value is not set, chroot is not used. ; Note: chrooting is a great security feature and should be used whenever ; possible. However, all PHP paths will be relative to the chroot ; (error_log, sessions.save_path, ...). ; Default Value: not set chroot = /var/www ; Chdir to this directory at the start. This value must be an absolute path. ; Default Value: current directory or / when chroot ;chdir = /var/www chdir = / ; Redirect worker stdout and stderr into main error log. If not set, stdout and ; stderr will be redirected to /dev/null according to FastCGI specs. ; Default Value: no ;catch_workers_output = yes ; Limits the extensions of the main script FPM will allow to parse. This can ; prevent configuration mistakes on the web server side. You should only limit ; FPM to .php extensions to prevent malicious users to use other extensions to ; exectute php code. [root@sandbox ~]#
Естественно, не забываем выставить правильный часовой пояс в настройках php
[root@sandbox ~]# cat /etc/php.ini | grep -5 'date.timezone' ; Whether the CLI web server uses ANSI color coding in its terminal output. cli_server.color = On [Date] ; Defines the default timezone used by the date functions ; http://php.net/date.timezone date.timezone = Europe/Moscow ; http://php.net/date.default-latitude ;date.default_latitude = 31.7667 ; http://php.net/date.default-longitude [root@sandbox ~]#
Приступим теперь к конфигурированию нашеего хоста. Начнём с основного файла конфигурации nginx.
[root@sandbox ~]# cat /etc/nginx/nginx.conf user nginx; # От этого пользователя будет работать nginx worker_processes 1; # Устанавливается значение по числу ядер процессора (процессоров) timer_resolution 100ms; # Уменьшается разрешение таймеров времени в рабочих процессах worker_rlimit_nofile 8192; # Изменяется ограничение на максимальное число открытых файлов для рабочих процессов. worker_priority -5; # Увиличивается приоритет error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; events { worker_connections 1024; # Максимальное количество подключений на процесс } http { include /etc/nginx/mime.types; access_log /var/log/nginx/access.log; sendfile on; # Включается соответствующий системный вызов keepalive_timeout 65; # Время открытого постоянного соединения tcp_nodelay on; # разрешает использование опции сокета TCP_NODELAY port_in_redirect off; # Запрет указания порта в редиректах nginx gzip on; # Включается сжатие данных сервером gzip_comp_level 3; # Устанавливается степень сжатия gzip_disable "msie6";# Запрещается сжатие для проблемного MS Internet Explorer 6 gzip_min_length 1024; # Устанавливается минимальный размер сжиаемого содержимого gzip_vary on; # Разрешается выдача в ответе поле заголовка “Vary: Accept-Encoding” gzip_proxied any; # Разрешается сжатие для всех проксированных запросов gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; # Разрешается сжатие для указанных MIME Types include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; }
И сконфигурируем наш хост. Обращаем внимание на то, что в контексте location, отвечающем за обработку php, пути, передаваемые php-fpm, указываются относительно нашего будущего chroot
[root@sandbox ~]# cat /etc/nginx/conf.d/default.conf server { listen 80; # Устанавливается порт server_name sandbox.local www.sandbox.local; # Устанавливаются имена на которые отвечает сервер root /var/www/sandbox; # Устанавливается путь к каталогу с содержимым index index.php; # Устанавливается индексный файл location / { try_files $uri $uri/ /index.php?q=$uri&$args; # Проверка существования файлов для обработки запроса } location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico)$ { access_log off; # Не заносить в access.log доступ к статичному содержимому expires max; # Установить максимальное время кэширования } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; # Указывает где слушает php-fpm fastcgi_index index.php; # Указывает индексный файл fastcgi_param DOCUMENT_ROOT /sandbox; fastcgi_param SCRIPT_FILENAME /sandbox$fastcgi_script_name; fastcgi_param PATH_TRANSLATED /sandbox$fastcgi_script_name; include fastcgi_params; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_intercept_errors on; fastcgi_ignore_client_abort off; fastcgi_connect_timeout 60; fastcgi_send_timeout 180; fastcgi_read_timeout 180; fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; fastcgi_temp_file_write_size 256k; # Устанавливаются параметры обращения к php_fpm } location = /favicon.ico { log_not_found off; # Не писать в логи попытки доступа к favicon.ico access_log off; } location = /robots.txt { allow all; # Не писать в лог попытки доступа к robots.txt log_not_found off; access_log off; } location ~ /\.ht { deny all; # Запрет доступа к файлам .htpassword и .htaccess } } [root@sandbox ~]#
Мы уже почти закончили. Теперь нам осталось проверить конфигурацию nginx, запускаем его и php-fpm:
[root@sandbox ~]# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful [root@sandbox ~]# service nginx start Starting nginx: [ OK ] [root@sandbox ~]# service php-fpm start Starting php-fpm: [ OK ] [root@sandbox ~]#
и не забываем внести строку в наш файервол, которая разрешит нам подключаться на 80-й порт примерно так:
[root@sandbox sysconfig]# iptables -L --line-numbers Chain INPUT (policy ACCEPT) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED 2 ACCEPT icmp -- anywhere anywhere 3 ACCEPT all -- anywhere anywhere 4 ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh 5 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited Chain FORWARD (policy ACCEPT) num target prot opt source destination 1 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited Chain OUTPUT (policy ACCEPT) num target prot opt source destination [root@sandbox sysconfig]# iptables -I INPUT 4 -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT [root@sandbox sysconfig]# iptables-save > /etc/sysconfig/iptables
теперь мы вполне можем обратиться к нашему хосту http://sandbox.local и насладиться выводом команды phpinfo();. Если, конечно, мы всё сделали правильно.
В следующей части я расскажу как на всё это накатить что-нибудь серьёзнее phpinfo и как всё это оптимизировать ещё дальше…
Спасибо огромное за статью. очень помогла с настройкой.
Сейчас совершенно нет времени написать вторую часть статьи, про то как заставить работать эту связку в chroot окружении с чем-то более-менее сложным, типа WordPress. Но руководствуясь этой статьёй уже можно получить вполне себе работоспособный сервер под нормальные задачи. Достаточно в /etc/php-fpm.d/www.conf закомментировать директивы chroot и chdir, а в секции location конфигурации хоста в nginx писать директивы так:
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;