Недавно приняли решение переехать с хостинга на VPS, будем использовать: CentOS 7, Nginx, Apache, PHP, MySQL. Несмотря на большое количество статей на эту тему, многие аспекты не упоминаются, поэтому выкладываем эту статью чтобы услышать мнение знающих и опытных людей. Настраивать сервер как Вы уже поняли будем первый раз, поэтому о актуальности статьи можно будет судить из комментариев. Nginx будет отдавать статику, а динамику Apache (скрипты PHP), чтобы снизить нагрузку на сервер.
Подготовка.
Все настройки будем применять на рабочем сервере нашего проекта с конфигурацией сервера: CPU — 2 × 2000 МГц и RAM — 2048 МБ.
Для начала работы находим подходящий VPS с предустановленной CentOS 7, к серверу будем подключаться по SSH через PuTTY.
Вводим название хоста и порт, нажимаем Open:
Далее вводим логин [Enter], потом пароль (обратите внимание, ввод пароля не отображается) [Enter]:
Обновить систему, при этом сохранить устаревшие версии пакетов:
Или обновить все пакеты, старые пакеты будут удалены:
Устанавливаем файловый менеджер с текстовым интерфейсом — Midnight Commander:
Устанавливаем текстовый редактор — Nano:
Проверяем сколько на сервере оперативной памяти и сколько доступно, а также наличие SWAP. Когда заканчивается оперативная память, данные перемещаются на диск, что замедляет работу сервера, работа SWAP нежелательна, но позволяет подстраховать себя:
Создаём файловую структуру и пользователей под сайты.
Создаём каталог (папку) для файлов под все сайты:
Под каждый отдельный сайт выполните такие действия.
Содержимое каждого сайта будет находиться в собственном каталоге, поэтому создаём нового пользователя и отдельный каталог для разграничения прав доступа:
-b папка в которой будет создан каталог пользователя
-m создать каталог
-U создаём группу с таким же именем как у пользователя
-s /bin/false отключаем пользователю shell
Делаем каталоги для данных сайта (файлы сайта, логи и временные файлы):
Изменяем владельца и группу на каталог, включая вложенные папки:
Изменяем права доступа на каталог — name.site:
Устанавливаем Nginx.
Инструкции по установке приведены на официальном сайте Nginx.
Для настройки репозитория yum в CentOS создаём файл /etc/yum.repos.d/nginx.repo:
Открываем файл nginx.repo:
Вставляем такое содержимое и сохраняем файл:
Для проверки подписи загружаем ключ и импортируем его в менеджер пакетов rpm:
Устанавливаем Nginx:
Запускаем:

Временно останавливаем:
Устанавливаем Apache и PHP.
Устанавливаем Apache (в CentOS — httpd):
Устанавливаем PHP:
Запускаем Apache:

Временно останавливаем:
Настраиваем Nginx.
Добавляем в автозагрузку:
Открываем основной конфигурационный файл:
Редактируем и сохраняем:
От какого пользователя запускаем Nginx:
Указываем количество рабочих процессов (зависит от количества ядер процессора и количества жёстких дисков, потому что головка диска быстрее перемещаться не сможет):
Идентификатор процесса запущенного сервера:
Секция events:
Внутри блока events, максимальное количество одновременных соединении с сервером (worker_processes × worker_connections):
Внутри блока events, принимать соединения сколько будет возможно:
Секция http, остальное будет находиться внутри неё:
Записывать ошибки (уровня: warn, error, crit, alert) по указанному пути:
Отключаем запись журнала доступа (жёсткий диск скажет спасибо):
Установим кодировку по умолчанию:
Отключим показ версии Nginx:
Подключить mimetypes:
Если MIME-тип файла не удастся определить, то по умолчанию файл будет бинарным:
Закрывать соединение если клиент не отвечает:
Читать заголовок запроса клиента не более 15 секунд:
Читать тело запроса клиента не более 30 секунд — интервал устанавливается не на всю передачу тела запроса, а только между двумя последовательными операциями чтения:
Если клиент не принимает ответ более 15 секунд, сбрасываем соединение:
Держим соединение открытым не более пяти секунд:
Максимальное количество запросов с открытым соединением от одного клиента:
Запросы больше 8 мегабайт принимать не будем:
Чтобы один пользователь не занял весь свободный канал трафика, после 30 Мб наложим ограничение на скорость отдачи данных:
Максимальная скорость с клиентом в рамках одного соединения после наложенных ограничений будет не более 500 Кб/с:
Устанавливаем максимальное количество файлов, информация о которых будет содержаться в кеше и удаляться, если файл не будет запрошен повторно в течении 3 минут:
Если файл будет запрошен более 2 раз, поместить в кэш:
Проверять актуальность кэша каждую минуту:
Отдавать статику без посредников:
Не буферизировать данные:
Отправлять заголовки одним пакетом:
Подключить конфиги:
Для каждого сайта создаём виртуальный хост Nginx.
Чтобы Nginx получил доступ к файлам сайта, добавим пользователя nginx в группу name.site:
Затем создаём конфигурационный файл:
Открываем файл:
Редактируем и сохраняем:
Сервер слушает на 80 порту:
Имя сервера, определяет в каком блоке будет выполнен запрос, указывается имя домена:
Путь к журналу ошибок Nginx конкретного сайта:
Перенаправить запрос Apache:
Обрывать коннект через 300 секунд, если превышен таймаут при чтении ответа с сервера Apache:
Передать заголовки:
Передать IP клиента:
Передать список серверов по которым прошёл запрос и добавить свой:
Отключить буферизацию проксируемого сервера:
Статику будет отдавать Nginx:
Настраиваем Apache.
Посмотрите какой именно модуль Apache у вас установлен. У меня — apache2-mpm-prefork (один процесс с одним потоком будет обрабатывать одно соединение, рекомендуется как безопасный совместно с PHP):
Открываем httpd.conf:
Редактируем и сохраняем:
Устанавливаем корневой каталог Apache:
Каталог где будут храниться файлы сайтов:
Подгружаем конфигурационные файлы:
От какого пользователя запускаем сервер:
От какой группы запускаем сервер:
Указываем IP и порт с которых будем принимать запросы, снаружи этот сервер видно не будет:
Имя хоста и порт для определения самого себя:
Адрес электронной почты который отправляется клиенту в случае ошибок:
Отключаем отправку информации версии системы и сервера Apache:
Отключаем отправку клиенту в заголовке информацию о Apache:
Ограничиваем использование памяти 750 мегабайтами:
Максимальное время приёма запроса, обработки, отправки контента серверу Nginx:
Устанавливаем кодировку:
Задаём язык содержимого:
Отключаем обработку большого количества запросов в одном соединении:
Выключить генерацию Content-MD5 заголовков HTTP:
Apache статику отдавать не будет, поэтому отключаем:
Записываем ошибки Apache по указанному пути /etc/httpd/logs/error_log:
Указываем c какого уровня записывать ошибки:
Подключить mimetypes:
Секция Directory:
Внутри блока Directory, в случае указания пути до каталога, по умолчанию отдавать index.php::
Внутри блока Directory, запретить переопределение информации доступа в .htaccess:
Внутри блока Directory, запретить доступ к файлам сервера:
Секция mpm_prefork_module:
Внутри блока mpm_prefork_module, после запуска Apache создать 5 процессов:
Внутри блока mpm_prefork_module, минимальное количество неиспользуемых процессов (если все процессы будут заняты, то запустятся новые свободные процессы):
Внутри блока mpm_prefork_module, максимальное количество неиспользуемых (запасных) процессов:
Внутри блока mpm_prefork_module, максимальное количество дочерних процессов которые можно запустить одновременно, остальные встают в очередь (с увеличением дочерних процессов, увеличивается потребление памяти):
Внутри блока mpm_prefork_module, после указанного числа обработанных запросов, процесс перезапускается (необходимо при переполнении — утечки памяти):
Закрываем доступ к .htaccess:
Подгружаем конфигурационные файлы:
Для каждого сайта создаём виртуальный хост Apache.
Добавляем пользователя apache в группу каждого сайта:
Создаём каталог под конфигурационные файлы виртуальных хостов Apache:
Создаём конфигурационный файл:
Открываем файл:
Редактируем и сохраняем:
Блок VirtualHost, указывается какой порт слушать:
Имя домена:
Зеркало домена:
Каталог где будут храниться файлы этого сайта:
Открыть доступ к файлам сайта:
Если путь указан до каталога, по умолчанию открывать:
Путь к журналу ошибок Apache конкретного сайта:
Путь к журналу доступа:
Проверка Nginx и Apache.
Добавляем Apache в автозагрузку:
Создаём, редактируем и сохраняем файл:
Копируем конфиг php:
Запускаем Nginx и Apache:
Настраиваем PHP.
Открываем php.ini:
Редактируем и сохраняем:
Включаем интерпретатор PHP, по необходимости можно выключить на конкретном сайте:
Отключаем заголовки отправляемые клиенту о PHP:
Отключаем короткую запись тегов PHP <?… ?>:
Выключаем сжатие страниц:
Отключаем опасные функции:
Не выводить на экран ошибки возникшии во время старта PHP:
Не показывать ошибки:
Логируем ошибки, после выключения их вывода на экран:
Файл в который будут записываться ошибки:
Не записывать одинаковые ошибки, которые проиcходят в конкретном файле и строке (ignore_repeated_source — нужно выключить):
При включении не записывает одинаковые ошибки, которые могут происходить в разных файлах и строках, поэтому выключаем:
Выключить HTML теги при просмотре сообщений об ошибках:
Складываем данные в буфер:
Буферизация вывода для всех файлов, максимальное количество:
Используем кэш realpath, тем самым уменьшая количество вызовов stat():
Установим время хранения кэша 30 минут:
Включаем сборщик циклических ссылок:
Указываем максимальное время в течении которого будут приниматься данные на сервер (POST, GET, HEAD), время измеряется с запуска PHP до момента выполнения скрипта:
Указываем максимальное время выполнения скрипта (значение не больше чем в Apache — Timeout) — вместо set_time_limit:
Разрешить загрузку файлов на сервер:
Максимальный размер памяти, который можно использовать скрипту:
Максимальный размер данных, отправляемых методом POST (должно быть меньше — memory_limit):
Максимальный размер закачиваемого файла (должно быть меньше — post_max_size):
Количество файлов, которые можно передать за один запрос:
Путь до каталога с модулями расширения:
Устанавливаем временную зону:
Тип данных:
Устанавливаем кодировку UTF-8:
Порядок обработки переменных — $_COOKIE, $_GET, $_POST, $_SERVER:
Не объявлять переменные argv и argc:
Переменные SERVER и ENV будут создаваться в момент использования, что приводит к увеличению производительности:
Выключаем динамическую подгрузку, влияет на безопасность:
Разрешаем работу с внешними файлами по URL:
Отключаем использование внешних файлов:
Устанавливаем и настраиваем MySQL.
Отрываем my.cnf:
Количество параллельных процессов, обрабатывающих конкурентные запросы к MySQL (количество ядер умноженных на два):
Устанавливаем кодировку по умолчанию для новых таблиц:
Будем использовать таблицы InnoDB:
Устанавливаем размер буфера индексов таблиц в оперативной памяти (актуально для таблиц MyISAM):
Буфер данных и индексов таблиц — InnoDB:
Максимальный размер оперативной памяти, выделяемой для временных таблиц, создаваемых MySQL:
Максимальное количество открытых таблиц, которые будут находиться в кэше:
Буфер данных, который используется для записи информации на диск — InnoDB:
Отключаем кэширование запросов:
Размер буфера который используется для сортировки (ORDER BY) или группировки GROUP BY) данных в каждом потоке:
Выделяем для каждого потока память на каждую таблицу, при увеличении этого значения может пострадать скорость выполнения запроса:
Влияет на скорость сортировки, для запросов с — ORDER BY:
Размер буфера с использованием JOIN, если не используются индексы в этих запросах:
Размер стека, место для хранения списка задач (открыть или закрыть таблицу, выполнить запрос и т.п.):
Выделяем память для буфера соединения и его результатов, может быть увеличено до max_allowed_packet:
Максимальный размер данных, которые можно передать за один запрос:
Максимальное количество одновременных соединений:
Количество соединений, которые могут стоять в очереди:
Слушать только localhost:
Не использовать TCP/IP соединения, передавать данные через сокет:
Чтобы рассчитать примерное потребление оперативной памяти на сервере MySQL (насколько я понял) можно воспользоваться следующей формулой (не забывайте, что серверу Apache уже выделено 750 МБ и ещё нужно оставить серверу Nginx):
Немного про безопасность.
Под root заходить нежелательно, поэтому создаём нового пользователя:
Задаём пароль пользователю — newuser:
Устанавливаем пакет sudo:
Заносим нового пользователя в sudo:
Если вы подключаетесь к SSH через 22 порт, то нужно его изменить, открываем sshd_config:
Меняем порт на любой свободный (не забываем через установленный у вас firewall закрыть 22 порт и открыть новый, например 54139):
Запрещаем попытку входа с пустым паролем:
Запретим вход в терминал root:
Разрешим логиниться в терминале только новому пользователю —
newuser:
Перезагружаем ssh:
P.S. Можно использовать Nginx с php-fpm, но есть такое мнение, что при правильно настроенном Apache особой разницы в производительности не наблюдается.
Особое внимание хотелось обратить на безопасность и производительность сервера, поэтому если вы нашли ошибки или недочёты, просим написать это в комментариях и в случае необходимости мы внесём изменения в статью.
Подготовка.
Все настройки будем применять на рабочем сервере нашего проекта с конфигурацией сервера: CPU — 2 × 2000 МГц и RAM — 2048 МБ.
Для начала работы находим подходящий VPS с предустановленной CentOS 7, к серверу будем подключаться по SSH через PuTTY.
Вводим название хоста и порт, нажимаем Open:
Далее вводим логин [Enter], потом пароль (обратите внимание, ввод пароля не отображается) [Enter]:
Обновить систему, при этом сохранить устаревшие версии пакетов:
[root@test ~]# yum updateИли обновить все пакеты, старые пакеты будут удалены:
[root@test ~]# yum upgradeУстанавливаем файловый менеджер с текстовым интерфейсом — Midnight Commander:
[root@test ~]# yum install mcУстанавливаем текстовый редактор — Nano:
[root@test ~]# yum install nanoПроверяем сколько на сервере оперативной памяти и сколько доступно, а также наличие SWAP. Когда заканчивается оперативная память, данные перемещаются на диск, что замедляет работу сервера, работа SWAP нежелательна, но позволяет подстраховать себя:
[root@test ~]# free -mСоздаём файловую структуру и пользователей под сайты.
Создаём каталог (папку) для файлов под все сайты:
[root@test ~]# cd /
[root@test ~]# mkdir -m 755 websiteПод каждый отдельный сайт выполните такие действия.
Содержимое каждого сайта будет находиться в собственном каталоге, поэтому создаём нового пользователя и отдельный каталог для разграничения прав доступа:
-b папка в которой будет создан каталог пользователя
-m создать каталог
-U создаём группу с таким же именем как у пользователя
-s /bin/false отключаем пользователю shell
[root@test ~]# useradd name.site -b /website/ -m -U -s /bin/falseДелаем каталоги для данных сайта (файлы сайта, логи и временные файлы):
[root@test ~]# mkdir -p -m 754 /website/name.site/www
[root@test ~]# mkdir -p -m 754 /website/name.site/logs
[root@test ~]# mkdir -p -m 777 /website/name.site/tmpИзменяем владельца и группу на каталог, включая вложенные папки:
[root@test ~]# chown -R name.site:name.site /website/name.site/Изменяем права доступа на каталог — name.site:
[root@test ~]# chmod 755 /website/name.siteУстанавливаем Nginx.
Инструкции по установке приведены на официальном сайте Nginx.
Для настройки репозитория yum в CentOS создаём файл /etc/yum.repos.d/nginx.repo:
[root@test ~]# cd /etc/yum.repos.d
[root@test ~]# touch nginx.repoОткрываем файл nginx.repo:
[root@test ~]# nano /etc/yum.repos.d/nginx.repoВставляем такое содержимое и сохраняем файл:
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=1
enabled=1Для проверки подписи загружаем ключ и импортируем его в менеджер пакетов rpm:
[root@test ~]# rpm --import http://nginx.org/keys/nginx_signing.keyУстанавливаем Nginx:
[root@test ~]# yum install nginxЗапускаем:
[root@test ~]# systemctl start nginx.service
Временно останавливаем:
[root@test ~]# systemctl stop nginx.serviceУстанавливаем Apache и PHP.
Устанавливаем Apache (в CentOS — httpd):
[root@test ~]# yum install httpdУстанавливаем PHP:
[root@test ~]# yum install phpЗапускаем Apache:
[root@test ~]# systemctl start httpd.service
Временно останавливаем:
[root@test ~]# systemctl stop httpd.serviceНастраиваем Nginx.
Добавляем в автозагрузку:
[root@test ~]# systemctl enable nginx.serviceОткрываем основной конфигурационный файл:
[root@test ~]# nano /etc/nginx/nginx.confРедактируем и сохраняем:
/etc/nginx/nginx.conf
user nginx;
worker_processes 2;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
}
http {
error_log /var/log/nginx/error.log warn;
access_log off;
charset utf-8;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
reset_timedout_connection on;
client_header_timeout 15;
client_body_timeout 30;
send_timeout 15;
keepalive_timeout 5;
keepalive_requests 30;
client_max_body_size 8m;
limit_rate_after 30M;
limit_rate 500K;
open_file_cache max=10000 inactive=3m;
open_file_cache_min_uses 2;
open_file_cache_valid 1m;
sendfile on;
tcp_nodelay on;
tcp_nopush on;
include /etc/nginx/conf.d/*.conf;
}От какого пользователя запускаем Nginx:
user nginx;Указываем количество рабочих процессов (зависит от количества ядер процессора и количества жёстких дисков, потому что головка диска быстрее перемещаться не сможет):
worker_processes 2;Идентификатор процесса запущенного сервера:
pid /var/run/nginx.pid;Секция events:
events {
#
}Внутри блока events, максимальное количество одновременных соединении с сервером (worker_processes × worker_connections):
worker_connections 1024;Внутри блока events, принимать соединения сколько будет возможно:
multi_accept on;Секция http, остальное будет находиться внутри неё:
http {
#
}Записывать ошибки (уровня: warn, error, crit, alert) по указанному пути:
error_log /var/log/nginx/error.log warn;Отключаем запись журнала доступа (жёсткий диск скажет спасибо):
access_log off;Установим кодировку по умолчанию:
charset utf-8;Отключим показ версии Nginx:
server_tokens off;Подключить mimetypes:
include /etc/nginx/mime.types;Если MIME-тип файла не удастся определить, то по умолчанию файл будет бинарным:
default_type application/octet-stream;Закрывать соединение если клиент не отвечает:
reset_timedout_connection on;Читать заголовок запроса клиента не более 15 секунд:
client_header_timeout 15;Читать тело запроса клиента не более 30 секунд — интервал устанавливается не на всю передачу тела запроса, а только между двумя последовательными операциями чтения:
client_body_timeout 30;Если клиент не принимает ответ более 15 секунд, сбрасываем соединение:
send_timeout 15;Держим соединение открытым не более пяти секунд:
keepalive_timeout 5;Максимальное количество запросов с открытым соединением от одного клиента:
keepalive_requests 30;Запросы больше 8 мегабайт принимать не будем:
client_max_body_size 8m;Чтобы один пользователь не занял весь свободный канал трафика, после 30 Мб наложим ограничение на скорость отдачи данных:
limit_rate_after 30M;Максимальная скорость с клиентом в рамках одного соединения после наложенных ограничений будет не более 500 Кб/с:
limit_rate 500K;Устанавливаем максимальное количество файлов, информация о которых будет содержаться в кеше и удаляться, если файл не будет запрошен повторно в течении 3 минут:
open_file_cache max=10000 inactive=3m;Если файл будет запрошен более 2 раз, поместить в кэш:
open_file_cache_min_uses 2;Проверять актуальность кэша каждую минуту:
open_file_cache_valid 1m;Отдавать статику без посредников:
sendfile on;Не буферизировать данные:
tcp_nodelay on;Отправлять заголовки одним пакетом:
tcp_nopush on;Подключить конфиги:
include /etc/nginx/conf.d/*.conf;Для каждого сайта создаём виртуальный хост Nginx.
Чтобы Nginx получил доступ к файлам сайта, добавим пользователя nginx в группу name.site:
[root@test ~]# usermod -a -G name.site nginxЗатем создаём конфигурационный файл:
[root@test ~]# touch /etc/nginx/conf.d/name.site.confОткрываем файл:
[root@test ~]# nano /etc/nginx/conf.d/name.site.confРедактируем и сохраняем:
/etc/nginx/conf.d/name.site.conf
server {
listen 80;
server_name name.site www.name.site;
#access_log /website/name.site/logs/nginx_access.log;
error_log /website/name.site/logs/nginx_error.log;
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_read_timeout 300s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
}
location ~* \.(css|js|png|gif|jpg|jpeg|ico)$ {
root /website/name.site/www;
expires 1d;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}Сервер слушает на 80 порту:
listen 80;Имя сервера, определяет в каком блоке будет выполнен запрос, указывается имя домена:
server_name name.site www.name.site;Путь к журналу ошибок Nginx конкретного сайта:
error_log /serves/name.site/logs/nginx_error.log;Перенаправить запрос Apache:
proxy_pass http://127.0.0.1:8080/;Обрывать коннект через 300 секунд, если превышен таймаут при чтении ответа с сервера Apache:
proxy_read_timeout 300s;Передать заголовки:
proxy_set_header Host $host;Передать IP клиента:
proxy_set_header X-Real-IP $remote_addr;Передать список серверов по которым прошёл запрос и добавить свой:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Отключить буферизацию проксируемого сервера:
proxy_buffering off;Статику будет отдавать Nginx:
location ~* \.(css|js|png|gif|jpg|jpeg|ico)$ {
root /serves/name.site/www;
expires 1d;
}Настраиваем Apache.
Посмотрите какой именно модуль Apache у вас установлен. У меня — apache2-mpm-prefork (один процесс с одним потоком будет обрабатывать одно соединение, рекомендуется как безопасный совместно с PHP):
[root@test ~]# apachectl -VОткрываем httpd.conf:
[root@test ~]# nano /etc/httpd/conf/httpd.confРедактируем и сохраняем:
httpd.conf
ServerRoot "/etc/httpd"
DocumentRoot "/website"
Include conf.modules.d/*.conf
User apache
Group apache
Listen 127.0.0.1:8080
ServerName 127.0.0.1:8080
ServerAdmin root@localhost
ServerSignature Off
ServerTokens Prod
RLimitMEM 786432000
TimeOut 250
AddDefaultCharset utf-8
DefaultLanguage ru
KeepAlive Off
ContentDigest Off
EnableSendfile off
ErrorLog "logs/error_log"
LogLevel error
<IfModule mime_module>
TypesConfig /etc/mime.types
</IfModule>
<Directory />
DirectoryIndex index.php
AllowOverride none
Require all denied
</Directory>
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 30
MaxRequestsPerChild 2500
</IfModule>
<Files ".ht*">
Require all denied
</Files>
IncludeOptional sites-enabled/*.conf
Устанавливаем корневой каталог Apache:
ServerRoot "/etc/httpd"Каталог где будут храниться файлы сайтов:
DocumentRoot "/website"Подгружаем конфигурационные файлы:
Include conf.modules.d/*.confОт какого пользователя запускаем сервер:
User apacheОт какой группы запускаем сервер:
Group apacheУказываем IP и порт с которых будем принимать запросы, снаружи этот сервер видно не будет:
Listen 127.0.0.1:8080Имя хоста и порт для определения самого себя:
ServerName 127.0.0.1:8080Адрес электронной почты который отправляется клиенту в случае ошибок:
ServerAdmin root@localhostОтключаем отправку информации версии системы и сервера Apache:
ServerSignature OffОтключаем отправку клиенту в заголовке информацию о Apache:
ServerTokens ProdОграничиваем использование памяти 750 мегабайтами:
RLimitMEM 786432000Максимальное время приёма запроса, обработки, отправки контента серверу Nginx:
TimeOut 250Устанавливаем кодировку:
AddDefaultCharset utf-8Задаём язык содержимого:
DefaultLanguage ruОтключаем обработку большого количества запросов в одном соединении:
KeepAlive OffВыключить генерацию Content-MD5 заголовков HTTP:
ContentDigest OffApache статику отдавать не будет, поэтому отключаем:
EnableSendfile offЗаписываем ошибки Apache по указанному пути /etc/httpd/logs/error_log:
ErrorLog "logs/error_log"Указываем c какого уровня записывать ошибки:
LogLevel errorПодключить mimetypes:
<IfModule mime_module>
TypesConfig /etc/mime.types
</IfModule>
Секция Directory:
<Directory />
...
</Directory>
Внутри блока Directory, в случае указания пути до каталога, по умолчанию отдавать index.php::
DirectoryIndex index.phpВнутри блока Directory, запретить переопределение информации доступа в .htaccess:
AllowOverride noneВнутри блока Directory, запретить доступ к файлам сервера:
Require all deniedСекция mpm_prefork_module:
<IfModule mpm_prefork_module>
...
</IfModule>Внутри блока mpm_prefork_module, после запуска Apache создать 5 процессов:
StartServers 5Внутри блока mpm_prefork_module, минимальное количество неиспользуемых процессов (если все процессы будут заняты, то запустятся новые свободные процессы):
MinSpareServers 5Внутри блока mpm_prefork_module, максимальное количество неиспользуемых (запасных) процессов:
MaxSpareServers 10Внутри блока mpm_prefork_module, максимальное количество дочерних процессов которые можно запустить одновременно, остальные встают в очередь (с увеличением дочерних процессов, увеличивается потребление памяти):
MaxClients 30Внутри блока mpm_prefork_module, после указанного числа обработанных запросов, процесс перезапускается (необходимо при переполнении — утечки памяти):
MaxRequestsPerChild 2500Закрываем доступ к .htaccess:
<Files ".ht*">
Require all denied
</Files>Подгружаем конфигурационные файлы:
IncludeOptional sites-enabled/*.confДля каждого сайта создаём виртуальный хост Apache.
Добавляем пользователя apache в группу каждого сайта:
[root@test ~]# usermod -a -G name.site apacheСоздаём каталог под конфигурационные файлы виртуальных хостов Apache:
[root@test ~]# mkdir /etc/httpd/sites-enabledСоздаём конфигурационный файл:
[root@test ~]# touch /etc/httpd/sites-enabled/name.site.confОткрываем файл:
[root@test ~]# nano /etc/httpd/sites-enabled/name.site.confРедактируем и сохраняем:
/etc/httpd/sites-enabled/name.site.conf
<VirtualHost *:8080>
ServerName name.site
ServerAlias www.name.site
DocumentRoot /website/name.site/www
<Directory "/website/name.site">
AllowOverride None
Require all granted
</Directory>
DirectoryIndex index.php
ErrorLog /website/name.site/logs/error.log
CustomLog /website/name.site/logs/requests.log combined
</VirtualHost>
Блок VirtualHost, указывается какой порт слушать:
<VirtualHost *:8080>
...
</VirtualHost>
Имя домена:
ServerName name.siteЗеркало домена:
ServerAlias www.name.siteКаталог где будут храниться файлы этого сайта:
DocumentRoot /website/name.site/wwwОткрыть доступ к файлам сайта:
Require all grantedЕсли путь указан до каталога, по умолчанию открывать:
DirectoryIndex index.phpПуть к журналу ошибок Apache конкретного сайта:
ErrorLog /website/name.site/logs/error.logПуть к журналу доступа:
CustomLog /website/name.site/logs/requests.log combinedПроверка Nginx и Apache.
Добавляем Apache в автозагрузку:
[root@test ~]# systemctl enable httpd.service
Создаём, редактируем и сохраняем файл:
[root@test ~]# touch /website/name.site/www/index.php
[root@test ~]# nano /website/name.site/www/index.php
Копируем конфиг php:
[root@test ~]# cp /etc/httpd/conf.d/php.conf /etc/httpd/sites-enabled/php.conf
Запускаем Nginx и Apache:
[root@test ~]# systemctl start nginx.service
[root@test ~]# systemctl start httpd.service
Настраиваем PHP.
Открываем php.ini:
[root@test ~]# nano /etc/php.iniРедактируем и сохраняем:
/etc/php.ini
engine = On
expose_php = Off
short_open_tag = Off
zlib.output_compression = Off
disable_functions = exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec, parse_ini_file, show_source, etc
display_startup_errors = Off
display_errors = Off
log_errors = On
error_log = "/usr/local/zend/var/log/php.log"
ignore_repeated_errors = Off
ignore_repeated_source = Off
html_errors = On
implicit_flush = Off
output_buffering = 4K
realpath_cache_size = 2M
realpath_cache_ttl = 1800
zend.enable_gc = On
max_input_time = 200
max_execution_time = 30
file_uploads = On
memory_limit = 256M
post_max_size = 8M
upload_max_filesize = 2M
max_file_uploads = 4
extension_dir = "/usr/local/zend/lib/php_extensions"
date.timezone = Europe/Moscow
default_mimetype = "text/html"
default_charset = "UTF-8"
variables_order = "CGPS"
register_argc_argv = Off
auto_globals_jit = On
enable_dl = Off
allow_url_fopen = On
allow_url_include = OffВключаем интерпретатор PHP, по необходимости можно выключить на конкретном сайте:
engine = OnОтключаем заголовки отправляемые клиенту о PHP:
expose_php = OffОтключаем короткую запись тегов PHP <?… ?>:
short_open_tag = OffВыключаем сжатие страниц:
zlib.output_compression = OffОтключаем опасные функции:
disable_functions = exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec, parse_ini_file, show_source, etcНе выводить на экран ошибки возникшии во время старта PHP:
display_startup_errors = OffНе показывать ошибки:
display_errors = OffЛогируем ошибки, после выключения их вывода на экран:
log_errors = OnФайл в который будут записываться ошибки:
error_log = "/usr/local/zend/var/log/php.log"Не записывать одинаковые ошибки, которые проиcходят в конкретном файле и строке (ignore_repeated_source — нужно выключить):
ignore_repeated_errors = OffПри включении не записывает одинаковые ошибки, которые могут происходить в разных файлах и строках, поэтому выключаем:
ignore_repeated_source = OffВыключить HTML теги при просмотре сообщений об ошибках:
html_errors = OnСкладываем данные в буфер:
implicit_flush = OffБуферизация вывода для всех файлов, максимальное количество:
output_buffering = 4KИспользуем кэш realpath, тем самым уменьшая количество вызовов stat():
realpath_cache_size = 2MУстановим время хранения кэша 30 минут:
realpath_cache_ttl = 1800Включаем сборщик циклических ссылок:
zend.enable_gc = OnУказываем максимальное время в течении которого будут приниматься данные на сервер (POST, GET, HEAD), время измеряется с запуска PHP до момента выполнения скрипта:
max_input_time = 200Указываем максимальное время выполнения скрипта (значение не больше чем в Apache — Timeout) — вместо set_time_limit:
max_execution_time = 30Разрешить загрузку файлов на сервер:
file_uploads = OnМаксимальный размер памяти, который можно использовать скрипту:
memory_limit = 256MМаксимальный размер данных, отправляемых методом POST (должно быть меньше — memory_limit):
post_max_size = 8MМаксимальный размер закачиваемого файла (должно быть меньше — post_max_size):
upload_max_filesize = 2MКоличество файлов, которые можно передать за один запрос:
max_file_uploads = 4Путь до каталога с модулями расширения:
extension_dir = "/usr/local/zend/lib/php_extensions"Устанавливаем временную зону:
date.timezone = Europe/MoscowТип данных:
default_mimetype = "text/html"Устанавливаем кодировку UTF-8:
default_charset = "UTF-8"Порядок обработки переменных — $_COOKIE, $_GET, $_POST, $_SERVER:
variables_order = "CGPS"Не объявлять переменные argv и argc:
register_argc_argv = OffПеременные SERVER и ENV будут создаваться в момент использования, что приводит к увеличению производительности:
auto_globals_jit = OnВыключаем динамическую подгрузку, влияет на безопасность:
enable_dl = OffРазрешаем работу с внешними файлами по URL:
allow_url_fopen = OnОтключаем использование внешних файлов:
allow_url_include = OffУстанавливаем и настраиваем MySQL.
Отрываем my.cnf:
[root@test ~]# nano /etc/mysql/my.cnf Количество параллельных процессов, обрабатывающих конкурентные запросы к MySQL (количество ядер умноженных на два):
thread_concurrency = 4Устанавливаем кодировку по умолчанию для новых таблиц:
default-character-set = utf8Будем использовать таблицы InnoDB:
default-storage-engine = InnoDBУстанавливаем размер буфера индексов таблиц в оперативной памяти (актуально для таблиц MyISAM):
key_buffer_size = 5MБуфер данных и индексов таблиц — InnoDB:
innodb_buffer_pool_size = 300MМаксимальный размер оперативной памяти, выделяемой для временных таблиц, создаваемых MySQL:
tmp_table_size = 50MМаксимальное количество открытых таблиц, которые будут находиться в кэше:
table_open_cache = 64Буфер данных, который используется для записи информации на диск — InnoDB:
innodb_log_buffer_size = 0MОтключаем кэширование запросов:
query_cache_size = 0Размер буфера который используется для сортировки (ORDER BY) или группировки GROUP BY) данных в каждом потоке:
sort_buffer_size = 512KВыделяем для каждого потока память на каждую таблицу, при увеличении этого значения может пострадать скорость выполнения запроса:
read_buffer_size = 512KВлияет на скорость сортировки, для запросов с — ORDER BY:
read_rnd_buffer_size = 1MРазмер буфера с использованием JOIN, если не используются индексы в этих запросах:
join_buffer_size = 2MРазмер стека, место для хранения списка задач (открыть или закрыть таблицу, выполнить запрос и т.п.):
thread_stack = 1MВыделяем память для буфера соединения и его результатов, может быть увеличено до max_allowed_packet:
net_buffer_length = 30KМаксимальный размер данных, которые можно передать за один запрос:
max_allowed_packet = 5MМаксимальное количество одновременных соединений:
max_connections = 75Количество соединений, которые могут стоять в очереди:
back_log = 250Слушать только localhost:
bind-address = 127.0.0.1Не использовать TCP/IP соединения, передавать данные через сокет:
skip-networkingЧтобы рассчитать примерное потребление оперативной памяти на сервере MySQL (насколько я понял) можно воспользоваться следующей формулой (не забывайте, что серверу Apache уже выделено 750 МБ и ещё нужно оставить серверу Nginx):
key_buffer_size + innodb_buffer_pool_size + tmp_table_size + ((sort_buffer_size + read_buffer_size + read_rnd_buffer_size + join_buffer_size + thread_stack) × max_connections) = ?Немного про безопасность.
Под root заходить нежелательно, поэтому создаём нового пользователя:
[root@test ~]# adduser newuserЗадаём пароль пользователю — newuser:
[root@test ~]# passwd newuserУстанавливаем пакет sudo:
[root@test ~]# yum install sudoЗаносим нового пользователя в sudo:
[root@test ~]# gpasswd -a newuser wheelЕсли вы подключаетесь к SSH через 22 порт, то нужно его изменить, открываем sshd_config:
[root@test ~]# nano /etc/ssh/sshd_configМеняем порт на любой свободный (не забываем через установленный у вас firewall закрыть 22 порт и открыть новый, например 54139):
Port 54139Запрещаем попытку входа с пустым паролем:
PermitEmptyPasswords noЗапретим вход в терминал root:
PermitRootLogin noРазрешим логиниться в терминале только новому пользователю —
newuser:
AllowUsers newuserПерезагружаем ssh:
[root@test ~]# service sshd restartP.S. Можно использовать Nginx с php-fpm, но есть такое мнение, что при правильно настроенном Apache особой разницы в производительности не наблюдается.
Особое внимание хотелось обратить на безопасность и производительность сервера, поэтому если вы нашли ошибки или недочёты, просим написать это в комментариях и в случае необходимости мы внесём изменения в статью.
