Порядок действий для установки PHP 7.1 и разворачивания проекта на PHP на созданном Amazon AWS EC2 instance. Основано на этой статье, с дополнениями отсюда и из других источников. Используется более новая версия PHP, добавлены инструкции по установке HTTPS, и пример настройки проекта на Laravel. Подразумевается, что инстанс используется в режиме разработки.

Сначала приведу код полностью. Это не bash-скрипт, а просто список команд, команды выполняются вручную по отдельности, при необходимости вводятся нужные данные.

Общие настройки
# Instructions how to setup new AWS EC2 instance with Ubuntu Server 16.04 LTS and install PHP Laravel project and HTTPS
# This is not a bash script, you have to run and control all commands manually


sudo apt-get install nginx mysql-server
sudo mysql_secure_installation


sudo add-apt-repository ppa:ondrej/php && sudo apt-get update
sudo apt-get install php7.1 php7.1-cli php7.1-common php7.1-mysql php7.1-fpm php7.1-curl php7.1-gd php7.1-bz2 php7.1-mcrypt php7.1-json php7.1-tidy php7.1-mbstring php-redis php-memcached php7.1-zip php7.1-dom php7.1-gmp


# run after installation to create config directory from current user
sudo apt-get install mc
mc


sudo mcedit /etc/php/7.1/fpm/php.ini
# cgi.fix_pathinfo=0
sudo systemctl restart php7.1-fpm


sudo mcedit /etc/nginx/sites-available/default

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;
#!
    index index.php index.html index.htm index.nginx-debian.html;

    server_name _;

#!
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

#!
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.1-fpm.sock;
    }
#!
    location ~ /\.ht {
        deny all;
    }
}


sudo nginx -t
sudo systemctl reload nginx


echo "<?php phpinfo();" | sudo tee /var/www/html/info.php > /dev/null
# check http://11.22.33.44/info.php
sudo rm /var/www/html/info.php


sudo chown -R "$USER":www-data /var/www/
sudo find /var/www/ -type f -exec chmod 660 {} \; && sudo find /var/www/ -type d -exec chmod 2770 {} \;
sudo usermod -a -G www-data ubuntu




# https

sudo apt-get install software-properties-common && sudo add-apt-repository ppa:certbot/certbot && sudo apt-get update && sudo apt-get install python-certbot-nginx

sudo mcedit /etc/nginx/sites-available/default
# server_name my.domain.name;
sudo systemctl reload nginx

sudo certbot --nginx

echo -e '#!/bin/sh\n\ncertbot renew\n' | sudo tee /etc/cron.daily/certbot-renew > /dev/null
sudo chmod 0755 /etc/cron.daily/certbot-renew

sudo certbot renew --dry-run


Настройки проекта
sudo apt-get install git
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

cd /var && rm -rf www/html

# set repository URL here
git clone ... www
cd www

git checkout dev
ln -s public html
composer install


sudo chgrp -R www-data storage bootstrap/cache
sudo chmod -R ug+rwx storage bootstrap/cache
sudo chmod -R 0777 storage/framework/cache


cp .env.example .env && php artisan key:generate
# set values in .env file - APP_NAME, DB_DATABASE, and other

Настройка системы


sudo apt-get install nginx mysql-server
sudo mysql_secure_installation

Nginx и MySQL

mysql_secure_installation задает несколько вопросов по настройке.

Would you like to setup VALIDATE PASSWORD plugin? n
Change the password for root? n
Remove anonymous users? y
Disallow root login remotely? y
Remove test database and access to it? y
Reload privilege tables now? y

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

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

sudo add-apt-repository ppa:ondrej/php && sudo apt-get update
sudo apt-get install php7.1 php7.1-cli php7.1-common php7.1-mysql php7.1-fpm php7.1-curl php7.1-gd php7.1-bz2 php7.1-mcrypt php7.1-json php7.1-tidy php7.1-mbstring php-redis php-memcached php7.1-zip php7.1-dom php7.1-gmp

PHP

PHP 7.1 и 7.2 пока нет в стандартных репозиториях.

sudo apt-get install mc
mc

Midnight Commander

Устанавливать необязательно, если вам больше нравится другой редактор и способ передвижения по файловой системе.

Не стоит после установки запускать в первый раз через sudo mc, так как файлы конфигурации создадутся от пользователя root, и при запуске от обычного пользователя будет ошибка доступа.

sudo mcedit /etc/php/7.1/fpm/php.ini
# cgi.fix_pathinfo=0
sudo systemctl restart php7.1-fpm

Найти настройку cgi.fix_pathinfo, раскомментировать и поставить 0. Это закрытие уязвимости, подробнее можно почитать здесь.

sudo mcedit /etc/nginx/sites-available/default

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html;
#!
    index index.php index.html index.htm index.nginx-debian.html;

    server_name _;

#!
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

#!
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.1-fpm.sock;
    }
#!
    location ~ /\.ht {
        deny all;
    }
}

Восклицательными знаками отмечены места, где надо поменять.

index — добавить index.php
try_files — убрать =404, добавить /index.php?$query_string
location ~ \.php$ — раскомментировать секцию, поменять название файла с сокетом
location ~ /\.ht — раскомментировать секцию для файлов *.htaccess

sudo nginx -t
sudo systemctl reload nginx

Проверить правильность конфигурации, если все нормально, перезагрузить ее.

echo "<?php phpinfo();" | sudo tee /var/www/html/info.php > /dev/null
# check http://11.22.33.44/info.php
sudo rm /var/www/html/info.php

Создать тестовый файл, проверить работу PHP, потом удалить. Выполнять необязательно. 11.22.33.44 обозначает IP-адрес инстанса.

sudo chown -R "$USER":www-data /var/www/
sudo find /var/www/ -type f -exec chmod 660 {} \; && sudo find /var/www/ -type d -exec chmod 2770 {} \;
sudo usermod -a -G www-data ubuntu

Сейчас в веб-каталоге все создано от рута, надо изменить на обычного пользователя. Nginx запускает PHP от пользователя www-data из группы www-data, SSH подключается с пользователем ubuntu. Надо добавить ubuntu в эту группу, иначе могут быть проблемы с доступом. Например, когда какая-то консольная команда создает папку, куда будет идти запись и при открытии сайта через веб.

Настройка HTTPS


Настройка HTTPS часто делается после настройки проекта, когда он уже есть и работает по HTTP. Но действия общие, от проекта не зависят, поэтому рассмотрим ее раньше.
Используются бесплатные сертификаты от Let's Encrypt.

sudo apt-get install software-properties-common && sudo add-apt-repository ppa:certbot/certbot && sudo apt-get update && sudo apt-get install python-certbot-nginx

Устанавливаем certbot, он обеспечивает настройку и обновление сертификатов.

sudo mcedit /etc/nginx/sites-available/default
# server_name my.domain.name;
sudo systemctl reload nginx

Прописываем доменное имя. Оно уже должно быть доступно и указывать на IP сервера. Для этого в настройках домена у регистратора нужно добавить DNS-запись типа A.

sudo certbot --nginx

Запускаем бота. Он автоматически определит домен по настройкам Nginx, но запросит подтверждение, а также задаст несколько других вопросов. Затем он устанавливает сертификат и обращается по доменному имени для проверки. Настройки Nginx для HTTPS он меняет сам.

echo -e '#!/bin/sh\n\ncertbot renew\n' | sudo tee /etc/cron.daily/certbot-renew > /dev/null
sudo chmod 0755 /etc/cron.daily/certbot-renew

Обновление сертификата надо добавить в cron для ежедневного запуска.

sudo certbot renew --dry-run

Есть специальная команда для проверки, пройдет ли обновление без ошибок.

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


sudo apt-get install git
curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer

Git и Composer

Composer лучше не ставить через apt-get, там старая версия. На сайте Composer есть скрипт для более безопасной установки с проверкой хеша. Можно использовать его, или просто взять оттуда текущий хеш и проверить вручную.

cd /var && rm -rf www/html

# set repository URL here
git clone ... www
cd www

git checkout dev
ln -s public html
composer install

Клонируем проект и устанавливаем зависимости. Вставьте свой репозиторий и название ветки.
Nginx настроен на папку html, надо ее убрать и сделать симлинк на папку где в проекте находится index.php. В Laravel это папка public.

sudo chgrp -R www-data storage bootstrap/cache
sudo chmod -R ug+rwx storage bootstrap/cache
sudo chmod -R 0777 storage/framework/cache

Устанавливаем права на папки специально для Laravel. Подробнее здесь.

cp .env.example .env && php artisan key:generate

Создаем рабочий файл с настройками окружения. Далее все как обычно — создаем, настраиваем, прописываем.

Комментарии (29)


  1. Shtucer
    17.12.2017 20:16

    Далее все как обычно — создаем, настраиваем, прописываем.

    Собственно, и перед этим все как обычно.


    1. michael_vostrikov Автор
      17.12.2017 20:39

      Да, для людей, знакомых с администрированием Linux, ничего нового здесь нет.
      Под «обычно» я имел в виду то, что и локально делается, на машине для разработки.


      1. Shtucer
        17.12.2017 20:45

        Не обязательно. Тыщи этих мануалов. Только в вашем заголовке упоминается AWS(кстати, зачем? ничего AWS специфичного в статье не заметил) и это всё отличие.


        1. michael_vostrikov Автор
          17.12.2017 20:58

          В принципе да, подойдет для любой системы, если есть Ubuntu и публичный IP. Просто я эти инструкции составил для себя при работе с AWS.
          2 статьи, указанные до ката, немного устарели, пришлось кое-что отдельно искать. Вот и решил собрать все в одном месте.


  1. yu-hritsaiy
    17.12.2017 20:34

    Конец 2017, уже все на полную используют контейнеры, может ещё 2 года назад было б актуально


    1. YaRobot
      17.12.2017 20:43

      На продакшене тоже контейнерами оперируете?
      У нас как бы HL++ и контейнеры явно лишнее занятие.


      1. WebProd
        17.12.2017 20:51

        Почему бы и нет? Контейнеры очень удобны в production, не в условиях HL, конечно.


        1. P6i
          18.12.2017 13:17

          … не в условиях HL, конечно.

          Ой, да ладно


      1. yu-hritsaiy
        17.12.2017 20:59

        да в проде на aws контейнеры. у кого HL++ тем подобные статьи наверное не нужны, квалификации им хватает, извините, но давайте смотреть трезво на ситуацию.


      1. VolCh
        17.12.2017 23:06

        Они привносят небольшой оверхед в целом, но, субъективно, для подавляющего большинства применений PHP он не значим.


  1. 027
    17.12.2017 20:37

    Обновление сертификата надо добавить в cron для ежедневного запуска.

    Везде пишут, что надо прописывать в крон руками.
    Два раза ставил certbot, оба раза он сделал это сам. ЧЯДНТ?


    1. Finesse
      18.12.2017 08:46

      Я ставил certbot на Ubuntu, он после установки попросил меня добавить себя в cron.


  1. Crysdd
    17.12.2017 20:37

    Не помешало бы осветить добавление второго домена на сервер. Я полагаю, certbot читает не только дефолтный конфиг?


    1. michael_vostrikov Автор
      17.12.2017 20:53

      Я с такими не работал, но вроде можно запустить certbot повторно и выбрать другой домен, то есть будет 2 сертификата. Можно один сертификат на 2 имени сделать, но я так делал только для алиасов основного.


      1. 027
        17.12.2017 21:07

        Можно запускать повторно, многократно и даже несколько доменов за один раз через пробел.


  1. Hixon10
    17.12.2017 22:27

    А если хочется nginx (или аналог) + docker + https в пару строк, то:
    github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion
    github.com/umputun/nginx-le
    traefik.io
    caddyserver.com


    1. shizo
      18.12.2017 12:25

      Да, хороший вариант. О нем же от/для Google Cloud cloud.google.com/community/tutorials/nginx-reverse-proxy-docker


  1. arround
    18.12.2017 04:27

    Запускать certbot раз в сутки…


    1. grossws
      18.12.2017 15:07

      Но ничего особо страшного в этом нет, он проверяет срок действия сертификата и не дёргает сервер, если ещё достаточно времени осталось.


      1. arround
        19.12.2017 20:21

        Просто нет смысла, раз в месяц вполне адекватно


        1. grossws
          19.12.2017 22:53

          Поэтому я и поставил плюс вашему комментарию, что полностью согласен.


        1. sav1812
          20.12.2017 00:18

          Читаем рекомендацию на сайте Cerbot:

          Note:

          if you're setting up a cron or systemd job, we recommend running it twice per day (it won't do anything until your certificates are due for renewal or revoked, but running it regularly would give your site a chance of staying online in case a Let's Encrypt-initiated revocation happened for some reason). Please select a random minute within the hour for your renewal tasks.


    1. michael_vostrikov Автор
      18.12.2017 17:17

      В документации вообще 2 раза в день советуют запускать.

      Note:
      if you're setting up a cron or systemd job, we recommend running it twice per day (it won't do anything until your certificates are due for renewal or revoked, but running it regularly would give your site a chance of staying online in case a Let's Encrypt-initiated revocation happened for some reason). Please select a random minute within the hour for your renewal tasks.


  1. Timonych
    18.12.2017 19:40

    Ваша неправда, касательно наличия DNS записи с A.

    У меня основной домен hms.fun, в котором есть только запись CNAME, с маской *.


    1. michael_vostrikov Автор
      18.12.2017 20:06

      Сервис проверки DNS говорит, что CNAME hms.fun указывает на запись timonych.mykeenetic.ru типа A. Понятно, что алиасы и поддомены можно по-разному создавать, но изначальная запись A должна где-то быть. Или если у заказчика уже есть mydomain.com с блогом или landing page, то для api.mydomain.com тоже нужна запись A.


      1. Timonych
        18.12.2017 20:34

        Начальная запись однозначно где-то должна быть, чтобы ссылаться на конкретный IP, но в данном случае, у меня белый динамический, и CNAME в данном случае идеальный выход, при условии, что не надо заморачиваться с поддоменами в DNS записях.


        1. grossws
          19.12.2017 22:56

          CNAME на верхнем уровне сильно не рекомендуется, многие DNS-провайдеры её явно запрещают. AFAIK, это просто не разрешено стандартами, но проверять RFC сейчас лень.


          1. VolCh
            20.12.2017 12:32

            Что вы называете верхним уровнем? Второй?


            1. grossws
              20.12.2017 13:21

              Причём здесь второй? Может быть любой по уровню, вопрос в наличии soa и ns rr.