Настройка сервера Linux Nginx, PHP 7, MySQL (LEMP) с поддержкой сертификата Let’s Encrypt в CentOS 7

Закончив чтение данного руководства, вы научитесь самостоятельно настраивать минималистичный сервер LEMP (Linux, Nginx, MySQL, PHP 7) для сайта без лишних компонентов. Ваш сайт будет доступен по защищенному протоколу HTTPS с поддержкой бесплатного сертификата Let’s Encrypt.

CentOS 7. Мы рекомендуем использовать данный дистрибутив CentOS, так как он будет поддерживаться до 2024 года, что позволит иметь сервер в актуальном состоянии и устанавливать на него обновления безопасности из стандартных источников. CentOS 7 — выбор для администраторов, которые приверженны дистрибутивам, основанным на RedHat Enterprise Linux, ценят непрерывность сервиса и ультрадолгий режим поддержки релизов.

MariaDB. Альтернативная реализация сервера MySQL, которая обладает лучшей производительностью и при этом полностью с ним совместима.

Требования к серверу

Для нормальной работы сайта с более-менее приличной нагрузкой вам понадобится сервер с одним или двумя ядрами, 1 GB RAM и 10 GB хранилища. Мы предполагаем, что вы уже располагаете такими или большими ресурсами и готовы приступить к установке.

Требования к DNS

Мы предполагаем, что вы уже имеете доменное имя, которое указывает на IP-сервера. Далее, будем считать, что сайт будет использовать доменное имя website.com. Везде, где фигурирует website.com вы должны сделать замену на имя своего домена. Мы будем использовать переменную WEBSITE_NAME для упрощения настройки:

# если hostname --fqdn отдает правильное доменное имя, то используйте
# 
export WEBSITE_NAME=$(hostname --fqdn)

# в противном случае

export WEBSITE_NAME=mycooldomain.com

Базовая настройка компонентов Nginx и получение сертификата

Nginx. Установка выполняется стандартным для CentOS способом:

sudo yum install -y epel-release && sudo yum install -y nginx
sudo systemctl stop nginx
sudo systemctl enable nginx

Получение сертификата Let’s Encrypt. Для установки сертификата Let’s Encrypt установим требуемое программное обеспечение:

sudo yum install -y python2-certbot-nginx nano unzip

Теперь вы можете сгенерировать сертификат Let’s Encrypt для сервера. Для этого в Nginx необходимо добавить секцию для виртуального хоста, который будет описывать ваш сайт.

Создайте файл /etc/nginx/conf.d/${WEBSITE_NAME}.conf со следующей конфигурацией, которая будет основой нашей будущей конфигурации сайта:

server {
        listen 80;
        listen [::]:80;
        server_name website.com;
}


server {
        listen 443 ssl;
        listen [::]:443 ssl;

        server_name website.com;

        root /var/www/website.com;
        index index.html;

        location / {
                try_files $uri $uri/ =404;
        }
}

Заменим website.com на имя вашего домена:

sudo sed -i "s/website.com/$WEBSITE_NAME/g" /etc/nginx/conf.d/${WEBSITE_NAME}.conf

Проверьте корректность конфигурации Nginx:

sudo nginx -t

Теперь закажем сертификат для этого виртуального хоста с помощью certbot. После запуска скрипт попросит вас указать ряд параметров:

  • адрес электронной почты, с которой будет ассоциирован сертификат;
  • домен из списка найденных в настройках Nginx доменов для которого сертификат будет получаться;
  • необходимо ли настроить автоматическое перенаправление пользователей с HTTP на HTTPS (здесь надо указать Yes).
sudo certbot --nginx
sudo pkill -9 nginx

В итоге в файле /etc/nginx/conf.d/${WEBSITE_NAME}.conf должна появиться дополнительная конфигурация, сгенерированная certbot:

server {
    if ($host = website.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;
    server_name website.com;


}


server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name website.com;

    root /var/www/website.com;
    index index.html;

    location / {
		try_files $uri $uri/ =404;
    }
    
    ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem; # managed by Certbot

}

Перезапустите nginx для активации изменений:

sudo nginx -t && sudo systemctl start nginx && sudo systemctl enable nginx

Для проверки работы SSL проведем эксперимент. Создадим файл index.html и проверим работу сертификата:

sudo mkdir -p /var/www/$WEBSITE_NAME
sudo sh -c "echo 'Hello, world' > /var/www/$WEBSITE_NAME/index.html"

curl https://$WEBSITE_NAME/

Hello, world

Теперь, если вы откроете в браузере http://website.com, то Nginx должен вас перебросить на https://website.com и отобразить текст Hello, world.

Настройка автопродления сертификата. Сертификаты Let’s Encrypt надо продлевать раз в 90 дней, иначе они устаревают. Добавим в CRON задачу вызова автопродления. Для этого в каталоге /etc/cron.monthly создайте скрипт по имени le-renew следующего содержания:

#/bin/bash
certbot renew

Выдайте ему права на исполнение и протестируйте:

sudo chmod 755 /etc/cron.monthly/le-renew
sudo /etc/cron.monthly/le-renew

На этом пока что настройку Nginx закончим и установим оставшиеся компоненты.

Установка и настройка MariaDB

Произведем базовую установку MariaDB 10.3, рекомендованную поставщиком для CentOS 7. Сначала необходимо добавить репозиторий пакетов MariaDB 10.3 в Yum:

sudo nano /etc/yum.repos.d/mariadb.repo

со следующим содержимым:

# MariaDB 10.3 CentOS repository list - created 2019-03-28 19:13 UTC
# http://downloads.mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

Теперь можно выполнить установку пакетов:

sudo yum install -y MariaDB-server MariaDB-client apg && 
      sudo systemctl enable mariadb && 
      sudo systemctl start mariadb

Проверьте корректность установки, соединившись с MariaDB с помощью клиента командной строки:

mysql -uroot -e 'SELECT version();'
+-----------------+
| version()       |
+-----------------+
| 10.3.13-MariaDB |
+-----------------+

Соединение работает, если вы получили вывод похожий на приведенный выше.

Если ваш сервер располагает внушительным запасом RAM и ядрами, а сайт будет работать под существенной нагрузкой, вы должны выполнить дополнительные настройки MySQL, чтобы оптимизировать его призводительность, однако, в рамках данной статьи эти настройки не рассматриваются.

Создадим базу данных для сайта и настроим доступ пользователя к ней. В настройке используется пользователь siteuser с паролем secret. Вы должны задать безопасный пароль, который можете сгенерировать с помощью команды apg.

export DB_NAME=website
export DB_USER=siteuser
export DB_PASSWORD=secret

mysql -uroot -e "CREATE DATABASE IF NOT EXISTS $DB_NAME;"

mysql -uroot -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASSWORD'";                                                                                    

mysql -uroot -e "FLUSH PRIVILEGES;"

mysql -u$DB_USER -p$DB_PASSWORD $DB_NAME -e 'select version();'
+-----------------+
| version()       |
+-----------------+
| 10.3.14-MariaDB |
+-----------------+

Запишите выбранные имя пользователя, пароль и название базы данных, чтобы не забыть. На этом настройка MariaDB завершена.

Установка и настройка PHP7

PHP версии 7.2 отсутствует в стандартной поставке CentOS 7. Установим нужные пакеты из репозитория remirepo:

sudo yum -y install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
sudo yum-config-manager -y --enable remi-php72
sudo yum install -y php72 php72-php-fpm php72-php-mysqlnd php72-php-opcache php72-php-xml php72-php-xmlrpc php72-php-gd php72-php-mbstring php72-php-json php72-php-zip php72-php-xml

sudo ln -s /usr/bin/php72 /usr/bin/php
sudo systemctl start php72-php-fpm
sudo systemctl enable php72-php-fpm

# поправим настройки PHP для режима FPM

sudo sed -i "s/memory_limit = .*/memory_limit = 256M/" /etc/opt/remi/php72/php.ini
sudo sed -i "s/upload_max_filesize = .*/upload_max_filesize = 128M/" /etc/opt/remi/php72/php.ini
sudo sed -i "s/zlib.output_compression = .*/zlib.output_compression = on/" /etc/opt/remi/php72/php.ini
sudo sed -i "s/max_execution_time = .*/max_execution_time = 18000/" /etc/opt/remi/php72/php.ini

Менеджер процессов PHP-FPM может работать в трех режимах — статическом, динамическом и «по требованию». По умолчанию, используется динамический режим. Мы изменим режим на режим «по требованию». Для этого мы перенесем базовую конфигурацию в файл www.conf.orig, а новую конфигурацию заполним данными из листинга ниже:

sudo mv /etc/opt/remi/php72/php-fpm.d/www.{conf,conf.orig}
sudo nano /etc/opt/remi/php72/php-fpm.d/www.conf
[www]
user = nginx
group = nginx
listen = /var/run/php72-fpm.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0666
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 200
chdir = /

Теперь перезапустим PHP-FPM для активации изменений:

sudo systemctl restart php72-php-fpm

Настройка сайта

Настройку начнем с того, что завершим конфигурацию Nginx для работы с PHP-FPM. Для этого изменим конфигурационный файл сайта /etc/nginx/conf.d/${WEBSITE_NAME}.conf.

Добавим перед секцией server следующий фрагмент конфигурации, который задает способ связи с PHP-FPM:

upstream php {
        server unix:/var/run/php72-fpm.sock;
}

# то, что ниже не добавлять в настройку, 
# приведено для примера куда вставить секцию
# upstream
#
server {
        listen 443 ssl;
        listen [::]:443 ssl;

...

Удалим следующие строки конфигурации:

       index index.html;

       location / {
                try_files $uri $uri/ =404;
       }

Добавим следующие строки конфигурации вместо них:

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        index index.php;

        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ .php$ {
                include fastcgi.conf;
                fastcgi_intercept_errors on;
                fastcgi_pass php;
        }

        location ~* .(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        }

Пример полной конфигурации:

server {
    if ($host = website.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;
        server_name website.com;


}

upstream php {
        server unix:/var/run/php72-fpm.sock;
}

server {
	listen 443 ssl;
	listen [::]:443 ssl;

	server_name website.com;

	root /var/www/website.com;

        ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem; # managed by Certbot

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        index index.php;

        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ .php$ {
                include fastcgi.conf;
                fastcgi_intercept_errors on;
                fastcgi_pass php;
        }

        location ~* .(js|css|png|jpg|jpeg|gif|ico)$ {
                expires max;
                log_not_found off;
        }
}

Проверим корректность настроек Nginx и перезапустим его:

sudo nginx -t && sudo systemctl restart nginx

Убедимся, что PHP-скрипты работают корректно. Для этого создадим файл index.php в пути /var/www/$WEBSITE_NAME/index.php со следующим содержимым:

<?php 
echo "Hello\n";

Проверим работоспособность скрипта:

# локально
php /var/www/$WEBSITE_NAME/index.php
Hello

# через web
curl https://$WEBSITE_NAME/index.php
Hello

# удалим скрипт
sudo rm /var/www/$WEBSITE_NAME/index.{php,html}

Настройка сервера LEMP завершена. Теперь вы можете выполнить развертывание сайта в каталоге /var/www/$WEBSITE_NAME и начать его использование, открыв в браузере https://$WEBSITE_NAME/.

Организация доступа к серверу для разработчика

Если настраиваемый сервер планируется для разработки, возможно, требуется настроить FTP-сервер для работы с файлами сайта.

Для настройки мы воспользуемся возможностью Linux создавать нескольких пользователей с одним и тем же UID. В этом случае все учетные записи являются синонимами, но им можно назначать разные пароли и оболочки.

Дело в том, что файлы, которые обслуживаются Nginx хранятся под пользователем nginx. Если мы создадим нового уникального пользователя, то придется для установить на файлы и каталоги широкие права для того, чтобы он мог манипулировать файлами Nginx, кроме того, вероятно, будет возникать путаница с правами и периодическая недоступность ресурсов, связанная с этим.

Вместо этого, мы создадим пользователя developer с таким же UID как и у www-data, соответственно, он сможет получить нормальный доступ к файлам и права будут корректными.

Сначала узнаем UID nginx:

id nginx                                                                                                         
uid=996(nginx) gid=993(nginx) группы=993(nginx)                                                                                        

Теперь добавим нового пользователя с повторяющимся UID:

sudo useradd -o -u 996 -d /var/www/$WEBSITE_NAME -g nginx developer
sudo passwd developer

Проверим, что пользователь может нормально войти в систему по ssh с локальной машины:

ssh developer@$WEBSITE_NAME

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

Резервное копирование данных

Важно настроить резервное копирование сразу же, как только вы настроили сервер, не откладывая это действие на потом. Воспользуйтесь нашим пошаговым руководством по настройке инкрементного резервного копирования файлов и баз данных для MySQL и PostgreSQL с помощью Duplicity.