Скажу сразу, что я — разработчик, и, думаю многие коллеги меня поддержат, в наших проектах часто не хватает системных администраторов, где-то сначала, где-то всегда. Нам приходится изучать новые для себя материалы в данной области, а делиться друг с другом накопленным опытом, по-моему, правильно. Данная статья не претендует на лавры завершенного руководства по настройке CentOS, и да, в ней отключается SELinux, и не создаются дополнительные пользователи MySQL, она описывает лишь то, что сказано в заголовке при чем в минимальной рабочей конфигурации. В конфигурации, которую затем всегда может дополнить специалист, но которая позволит развернуть требуемый проект за максимально короткое время. Я буду благодарен гуру за их советы и замечания, всегда за конструктивную критику, на основании которой буду вносить правки, если потребуется. Для остальных же хочу процитировать AntonShevchuk: «Если Вы не согласны с автором статьи — опишите свою точку зрения, зачем же злорадно понижать ему карму?». Спасибо, поехали…
В данном посте я приведу конкретные шаги по установке и настройке связки Nginx + MySQL + PHP7 на CentOS 7. Стоит отметить, что в данной статье будет рассказано про настройку системы для одного домена. В качестве площадки будет использоваться инстанс на Google Cloud Platform, с создания которого и начну:
Предлагаемый образ загрузочного диска изменяем на CentOS 7, минимальный объем диска в 10Гб вполне подойдет для развертывания системы, всех необходимых библиотек и зависимостей.
Для доступа к создаваемому серверу по SSH необходимо сгенерировать соответствующий ключ, у пользователей MacOS X или Linux для этого существует команда
ssh-keygen -t rsa -f __FILE__ -C __USER__
где __FILE__ — путь к сохраняемому ключу с именем файла, а __USER__ — имя пользователя под которым вы будете логинититься в систему. При выполнения данной команды система запросит вас указать ключевую фразу, которая будет служить паролем при использовании данного ключа, ее ввод необязателен, для пропуска ничего не вводите и нажмите Enter, далее подтвердите предыдущее действие. После выполнения данной команды система создаст указанный в __FILE__ приватный ключ, а рядом с ним — открытый с именем __FILE__.pub
Последнее, что остается сделать с ключом пользователям MacOS X и Linux — это запретить доступ на запись для всех, кроме владельца ключа, делается это командой
chmod 400 __FILE__
Пользователи Windows могут вопользоваться программой PuTTYgen. Откройте программу, нажмите Generate и следуйте инструкциям, установленные по-умолчанию параметры подходит для большинства случаев, однако Google настаивает на 2048-битных ключах, не забудьте установить данный параметр. Поле «Key comment» служит для задания имени пользователя, а для задания ключевой фразы — «Key passphrase». После завершения генерации программа отобразит публичный ключ. Чтобы сохранить приватный ключ с расширением .ppk нажмите «Save private key».
Скопируйте содержимое открытого ключа (из файла __FILE__.pub для MacOS X / Linux или из окна PuTTYgen для Windows) и вставьте его в соответствующее поле на вкладке «Безопасность»:
Не забываем разрешить трафик HTTP и HTTPS, поставив галочки в соответствующих полях. На данном этапе все готово, нажимаем «Создать».
После создания система отобразит присвоенный экземпляру внешний IP-адрес, подключимся к нему по SSH используя приватный ключ:
ssh -i __FILE__ __USER__@__IP__
Приступим непосредственно к установке LEMP. Под правами текущего пользователя создайте переменные для будущего использования:
export USER_NAME=__ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__
export DOMAIN_NAME=__ВАШЕ_ДОМЕННОЕ_ИМЯ__
Перейдите в режим суперпользователя, все приведенные действия будут выполняться от его имени, если явно не указано иное.
Обновляем систему и удаляем «осиротевшие» пакеты:
yum clean all
yum update
yum autoremove
Добавляем поддержку репозиториев Fedora, установим текстовый редактор nano и утилиту wget для возможности получения файлов с удаленных серверов. Для развертывания приложений на Symfony (и не только), я использую клонирование git-репозитория, для реализации данного метода установим поддержку git, также Symfony необходима поддержка zip (опция -y означает автоматический ответ «Y» на все возникающие у инсталлятора вопросы):
yum install epel-release nano wget git-core zip unzip -y
Нужно указать корректное имя хоста, для этого отредактируем конфигурационный файл сети:
cat << EOF > /etc/sysconfig/network
HOSTNAME=${DOMAIN_NAME}
EOF
Сохраняем файл и устанавливаем его же с помощью утилиты hostnamectl:
hostnamectl set-hostname ${DOMAIN_NAME}
Устанавливаем nginx:
yum install nginx -y
Запускаем nginx:
systemctl start nginx
Добавляем nginx в автозагрузку:
systemctl enable nginx
Устанавливаем MySQL, запускаем и добавляем в автозагрузку:
yum install mariadb mariadb-server -y
systemctl start mariadb
systemctl enable mariadb
Проведем начальную настройку MySQL, на все вопросы скрипта ответьте «Y» и укажите пароль суперпользователя:
mysql_secure_installation
Добавим поддержку Let's Encrypt:
yum install certbot -y
Так как к моменту написания данной статьи PHP 7 нет ни в репозиториях CentOS, ни в Fedora, его мы установим из репозитория REMI:
yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm yum-utils -y
yum-config-manager --enable remi-php72
yum --enablerepo=remi,remi-php72 install php-fpm php-common php-opcache php-cli php-pear php-pdo php-mysqlnd php-gd php-mcrypt php-xml php-zip -y
Весь необходимый софт установлен, перейдем к его настройке, первоначально отредактируем конфигурационный файл php-fpm:
nano /etc/php-fpm.d/www.conf
В данном файле необходимо указать имя пользователя и группу, под которым вы будете запускать приложение, в нашей минимальной конфигурации это пользователь, под кем вы изначально логинились в систему, одноименная группа для него создается системой автоматически.
user = __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__
group = __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__
listen.owner = __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__
listen.group = __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__
Далее, укажем сокет — необходимо под строкой listen = 127.0.0.1:9000 добавить следующее:
listen = /var/run/php-fpm/php-fpm.sock
Сохраните изменения, затем запустите и добавьте php-fpm в автозагрузку:
systemctl start php-fpm
systemctl enable php-fpm
Отключим SELINUX:
setenforce 0
Данной командой вы отключаете SELINUX только в текущем сеансе пользователя, чтобы после перезагруки настройки сохранились необходимо отредактировать его конфигурационный файл:
nano /etc/selinux/config
где указать SELINUX=disabled
Сохраните изменения и выйдете из режима суперпользователя. Далее мы создадим директорию для проекта в домашней директории и поместим туда индексный файл — заглушку для проверки работоспособности создаваемого стека:
cd
mkdir -p ${DOMAIN_NAME}/public
cd ${DOMAIN_NAME}/public
nano index.php
Пусть содержимое файла будет следующим (замените __YOUR_ROOT_PASSWORD__ на пароль суперпользователя MySQL, указанный при его настройке):
<html>
<head>
<h2>LEMP test</h2>
</head>
<body>
<?php echo '<p>Hello!</p>';
$host = "localhost";
$username = "root";
$password = "__YOUR_ROOT_PASSWORD__";
$connection = mysqli_connect($host, $username, $password);
if (!$connection)
print '<p>DB connect failed with error: ' . mysqli_connect_error() . '</p>';
else
print '<p>DB connection established</p>';
?>
</body>
</html>
Снова авторизуемся под правами суперпользователя и откроем на редактирование конфигурационный файл nginx:
nano /etc/nginx/nginx.conf
Изменим имя пользователя подобно конфигурации php-fpm:
user __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__;
Конфигурацию директивы server, созданную по-уполчанию, измените следующим образом:
server {
set $USER_NAME __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__;
server_name $hostname www.$hostname;
root /home/$USER_NAME/$hostname/public;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/$hostname.error.log;
access_log /var/log/nginx/$hostname.access.log;
}
Сохраните изменения и дайте команду nginx перечитать конфигурацию:
nginx -s reload
По доменному имени __ВАШЕ_ДОМЕННОЕ_ИМЯ__ должна открываться созданная нами страница — заглушка.
Приступим к созданию сертификата:
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
certbot certonly --webroot -w /home/${USER_NAME}/${DOMAIN_NAME}/public -d ${DOMAIN_NAME}
certbot certonly --webroot -w /home/${USER_NAME}/${DOMAIN_NAME}/public -d www.${DOMAIN_NAME}
После генерации ключей, изменим конфигурацию nginx для поддержки HTTPS, в данной конфигурации «главным» идет домен без www и настроено перенаправление на защищенное соединение. И так:
nano /etc/nginx/nginx.conf
Меняем созданную нами 2 шага назад директиву server на следующие:
server {
set $USER_NAME __ИМЯ_ПОЛЬЗОВАТЕЛЯ_СИСТЕМЫ__;
listen 443 ssl;
server_name $hostname;
ssl_certificate /etc/letsencrypt/live/__ВАШЕ_ДОМЕННОЕ_ИМЯ__/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/__ВАШЕ_ДОМЕННОЕ_ИМЯ__/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
root /home/$USER_NAME/$hostname/public;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
internal;
}
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/$hostname.error.log;
access_log /var/log/nginx/$hostname.access.log;
}
server {
listen 443 ssl;
server_name www.$hostname;
ssl_certificate /etc/letsencrypt/live/www.__ВАШЕ_ДОМЕННОЕ_ИМЯ__/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.__ВАШЕ_ДОМЕННОЕ_ИМЯ__/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
return 301 https://$hostname$request_uri;
}
server {
server_name $hostname www.$hostname;
return 301 https://$hostname$request_uri;
}
Сохраняем результат и говорим nginx перечитать конфигурацию:
nginx -s reload
Так как Let's Encrypt необходимо обновлять раз в три месяца, настроим автоматическое продление в планировщике, к примеру так, в ночь на понедельник
crontab -e
15 4 * * 1 /usr/bin/certbot renew --quiet
18 4 * * 1 /usr/bin/systemctl reload nginx
Чтобы у разворачиваемого приложения не было проблем с правами, меняем владельца следующих папок:
chown -R ${USER_NAME}:${USER_NAME} /var/lib/nginx
chown -R ${USER_NAME}:${USER_NAME} /var/lib/php/session/
Все, система готова. Перезагрузитесь, чтобы удостовериться, что весь необходимый софт сконфигурирован правильно и после перезагрузки не будет вести себя иначе, далее можно удалить созданную нами папку проекта в директории пользователя и клонировать в ней необходимый рабочий репозиторий.
UPD:
По совету Amet13 в командах используются переменные + объединение пакетов в последовательную «бесшумную» установку
Комментарии (24)
jehy
03.08.2018 10:37Для тех, кого, как и меня, смутило LEMP вместо LAMP или LNMP:
We go with LEMP due to the pronunciation for Nginx: Engine-X (en-juhn-ecks). Think of how in English, the article an is used instead of a for hour even though it begins with a consonant. The importance is the sound of the first letter rather than its written representation. Besides, LEMP is actually pronounceable and doesn’t sound like reciting the alphabet.
iAmWeb Автор
03.08.2018 10:49Да, LEMP — это связка Linux, Nginx, MySQL и PHP. Вариация знакомого всем LAMP, где вместо Apache используется Nginx.
VolCh
03.08.2018 11:36заглушку для проверки работоспособности создаваемого стека:
Пусть содержимое файла будет следующим (замените YOUR_ROOT_PASSWORD на пароль суперпользователя MySQL, указанный при его настройке):И если где-то в конфигах немного ошиблись, то показываем всему миру пароль рута. Какой-нибудь бот может вполне проиндексировать и опять будут какую-нибудь поисковую систему обвинять, что палит данные секретные.
Нет мелочей в безопасности. Даже для тестирования нужно пароли помещать, как минимум, в отдельный файл, вне докрута.
Кстати, для тестирования симфони+доктрина я бы проверял не через mysqli, а через pdo.
iAmWeb Автор
03.08.2018 11:44Нет мелочей в безопасности
Все так. Вполне можно создать отдельного пользователя и все манипуляции проводить с ним, никто не мешает, я могу дополнить статью, если пожелаете. Я хотел показать именно минимальную конфигурацию. Пароль суперпользователя MySQL изначально пуст, это знают все и многие этим пренебрегают. Мы же при mysql_secure_installation устанавливаем пароль и запрещаем внешний коннект под рутом.jenki
03.08.2018 15:17Я хотел показать именно минимальную конфигурацию.
Таких статей пруд пруди, на разных языках. В чём исключительное отличие от остальных? Установка дополнительных репозиториев или отключение мандатной системы контроля доступа?
Статья просто в радость для майнеров.iAmWeb Автор
03.08.2018 15:21А что майнеры не люди? Будет полезна им и хорошо. Я еще раз повторюсь, что собирал эту информацию по кускам и немалое время, всей информации в одном месте я не встречал, потому и решил ей поделиться.
VJean
03.08.2018 17:16+1Статья просто в радость для майнеров.
А что майнеры не люди? Будет полезна им и хорошо.
Спасибо, вы сделали мой день.
tnt4brain
03.08.2018 19:49+2Увы, после команды отключения SELinux начал прокручивать остаток текста до комментариев. Интересуюсь для собственного понимания, стоит ли писать статью — а что именно помешало воспользоваться штатными инструментами ОС (audit2why, audit2allow) и сразу создать недостающие политики?
jenki
06.08.2018 00:06Утилиты audit2why, audit2allow не сильно подходят для написания политики (именно политики), но и с разрешающими правилами надо быть внимательным, audit2allow часто слишком фривольные правила выдаёт. Другой момент — для данного стэка все политики уже давно идут по умолчанию и ничего не надо писать дополнительно.
Amet13
03.08.2018 20:16+1Немного замечаний по Linux.
Не нужно каждый раз после изменения nginx перезагружать его, достаточно указать, чтобы перечитал конфиг:
nginx -s reload
Даунтайма сервиса в таком случае не будет, конкретно в этом случае это не критично, но лучше сразу привыкать делать правильно.
Вместо того чтобы писать:
yum install package1 yum install package2
как по мне лучше использовать команду
yum install package1 package2 -y
для удобства чтения.
Перезагружать сервер после всех настроек также не самая лучшая практика, достаточно проверить сервисы systemd, что они в состоянии enabled.
Если есть какие-то переменные, то лучше вместо этого:
__USER__
и<domain.name>
использовать:
export USER_NAME=vasya export DOMAIN_NAME=example.com chown -R ${USER_NAME}:${USER_NAME} /var/www/${DOMAIN_NAME}
Вместо
cd ~
достаточно простоcd
Вместо
# wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm # rpm -Uvh remi-release-7.rpm
лучше юзать:
yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
Вместо:
# hostname <domain.name> # /etc/init.d/network restart
лучше использовать:
hostnamectl set-hostname $YOUR_HOSTNAME
Вместо того чтобы писать:
nano file.txt и добавляем сюда строчки foo=bar
лучше использовать конструкцию:
cat << EOF > file.txt foo=bar ... EOF
По поводу обновления сертификатов, проверьте в
/etc/cron.d/
возможно там уже есть задание на обновление, по крайней мере в убунту они вместе с установкой пакета добавляется.VJean
03.08.2018 20:49Перезагружать сервер после всех настроек также не самая лучшая практика, достаточно проверить сервисы systemd, что они в состоянии enabled.
Спорно, перезагрузкой как раз проверяется что всё будет работать в случае ребута через веб-морду или запуска после выключения. В дальнейшем, когда давным давно подняты все нужные сервисы, уже смотреть на состояние systemd после правок.
Таки быстрее открыть редактор с подсветкой синтаксиса и написать что нужно, чем вспоминать очередность кавычек в консоле и прописывать EOF.cat << EOF > file.txt
Amet13
03.08.2018 20:54Про перезагрузку, на данном этапе, когда это чистая установка, вполне допустимо, но лучше к этому не привыкать, в дальнейшей перспективе когда уже будет рабочая инсталляция, перезагружать сервер явно не будете после изменения настроек. Но это дело привычки скорее.
Таки быстрее открыть редактор с подсветкой синтаксиса и написать что нужно, чем вспоминать очередность кавычек в консоле и прописывать EOF.
Это да, но для меня лучше смотрится в мануале например, когда нет лишнего, а только то, что нужно сделать.OnYourLips
04.08.2018 23:27+1Что-то менять руками на рабочем сервере — не самая лучшая идея. Для этого есть инструменты автоматизации.
VolCh
04.08.2018 23:37Если есть кому автоматизировать.
OnYourLips
05.08.2018 10:40+1Так если некому (нет ни админов, ни программистов), то не будет и задачи устанавливать что-то на сервер.
VolCh
05.08.2018 11:12Есть и программисты, и админы, но в их обязанности не входит автоматизация процессов разработки и деплоя. Админы ручками на сервера ходят и необходимости в автоматизации не видят, да и опасаются, что они ненужными станут. А программисты бизнес-задачи решают.
denaspireone
а что не так с lemp стеком? все вроде стандартно и типично
ничего нового и прям таки отличного от стандартной натсройки и установки нет, разве что можно было заиспользовать MySQL от самого GCE — чего здесь нет кстати…
iAmWeb Автор
В данном случае настройка для хоста в nginx подойдет именно для приложений Symfony, для которого и писалась. Эта статья задумывалась как туториал для тех, кому нужны конкретные шаги для сборки системы в минимальной конфигурации под Symfony проект, с самого начала. Я собирал по кускам, обновлял и дописывал информацию из различных источников, которую и решил оформить в виде данного руководства, мало ли кому то поможет.
denaspireone
На официальном сайте symfony всегда конфиг для apache2/nginx всегда имеется в наличии, даже на сайте nginx есть конфиги для symfony ранних версий, до 4ки тожно.
PS: при этом Вы не указали пример для production server и test installation. Так же не првиели ссылок на оф сайты с доками. Я вот редко когда доверяю отсебятине, т.к. всегда делаю сам и перепроверяю.
PSS: положите все готовые конфиги на github — это сделает более полезной этот пост к.м.к.