Установка и оптимальная настройка Nginx + LAMP (CentOS 7)

в 10:01, , рубрики: Apache, centos 7, mysql, nginx, php, vds, Серверное администрирование
Комментарии к записи Установка и оптимальная настройка Nginx + LAMP (CentOS 7) отключены

Недавно приняли решение переехать с хостинга на VPS, будем использовать: CentOS 7, Nginx, Apache, PHP, MySQL. Несмотря на большое количество статей на эту тему, многие аспекты не упоминаются, поэтому выкладываем эту статью чтобы услышать мнение знающих и опытных людей. Настраивать сервер как Вы уже поняли будем первый раз, поэтому о актуальности статьи можно будет судить из комментариев.

Подготовка.

Все настройки будем применять на рабочем сервере нашего проекта с конфигурацией сервера: CPU — 2 × 2000 МГц и RAM — 2048 МБ.

Для начала работы находим подходящий VPS с предустановленной CentOS 7, к серверу будем подключаться по SSH через PuTTY.

Вводим название хоста и порт, нажимаем Open:

image

Далее вводим логин [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

image

Временно останавливаем:

[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

image
Временно останавливаем:

[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 10;
 client_body_timeout 25;
 send_timeout 8;
 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;

Читать заголовок запроса клиента не более 10 секунд:

client_header_timeout 10;

Читать тело запроса клиента не более 25 секунд — интервал устанавливается не на всю передачу тела запроса, а только между двумя последовательными операциями чтения:

client_body_timeout 25;

Если клиент не принимает ответ более 8 секунд, сбрасываем соединение:

send_timeout 8;

Держим соединение открытым не более пяти секунд:

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://localhost: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;
  
  proxy_set_header Range "";
  proxy_set_header Request-Range "";
 }

 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://localhost: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;

Защита от killapache:

proxy_set_header Range "";
proxy_set_header Request-Range "";

Статику будет отдавать 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 Off

Apache статику отдавать не будет, поэтому отключаем:

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 = 128M
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

Меняем порт на любой свободный:

Port 54139

Запрещаем попытку входа с пустым паролем:

PermitEmptyPasswords no

Запретим вход в терминал root:

PermitRootLogin no

Разрешим логиниться в терминале только новому пользователю —
newuser:

AllowUsers newuser

Перезагружаем ssh:

[root@test ~]# service sshd restart

P.S. Можно использовать Nginx с php-fpm, но есть такое мнение, что при правильно настроенном Apache особой разницы в производительности не наблюдается.

Особое внимание хотелось обратить на безопасность и производительность сервера, поэтому если вы нашли ошибки или недочёты, просим написать это в комментариях и в случае необходимости мы внесём изменения в статью.

Автор: XAKPM

Источник

Поделиться новостью