Деплой приложения на Laravel 7 на Ubuntu & Nginx

Деплой приложения на Laravel 7 на Ubuntu & Nginx

Решил я тут своё портфолио сделать на Laravel 7. Чтобы главная страница была лендингом, а всю информацию на ней можно было менять с помощью админки. Не суть. Дело дошло до деплоя. Нашел пару хороших туториалов, как это сделать на полноценном сервере со всеми заморочками. В деплое я не очень силен, я вообще больше фронт, чем фулстек. И, если писать и тестить на PHP я еще могу, то до управления сервером и т.п. я еще не дорос. Но пришлось разбираться.

Сейчас пройдемся по всем шагам, начиная с запуска через SSH и заканчивая рабочим сайтом. Постараемся обойти все подводные камни.

Возможно, вы сможете найти аналогичные инструкции в интернете. Ведь я же в конце концов нашел. Правда не в одном месте, не без помощи StackOverflow и вряд ли на русском. Я помучился. Поэтому решил вам жизнь упростить.

Делать мы всё будем с дроплетом на DigitalOcean. Это, конечно, необязательно, выбирайте любой хостинг. Дойдете до рабочего сервера на Ubuntu, возвращайтесь. Для тех, кто все же решил делать на DigitalOcean, будут еще советы по настройке домена. А также реферальная ссылка на 100$.

Все специфические для DigitalOcean шаги будут даны в подобных сносках.

Начнем.

TL;DR (только основные команды)

Создаем пользователя

  • ssh root@[IP-адрес вашего дроплета]
  • adduser laravel
  • usermod -aG sudo laravel
  • su laravel

Добавляем ему SSH

  • mkdir ~/.ssh
  • chmod 700 ~/.ssh
  • vim ~/.ssh/authorized_keys
  • Вставляем публичный ключ
  • chmod 600 ~/.ssh/authorized_keys

Фаервол

  • sudo ufw allow OpenSSH
  • sudo ufw enable
  • sudo ufw status

Nginx

  • sudo apt update
  • sudo apt install -y nginx
  • sudo ufw allow 'Nginx HTTP'
  • sudo ufw status

MySQL

  • sudo apt install -y mysql-server
  • sudo mysql_secure_installation, NYNNY
  • sudo mysql
  • ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '<Ваш пароль для MySQL>';
  • SELECT user,authentication_string,plugin,host FROM mysql.user;
  • FLUSH PRIVILEGES;
  • exit

PHP

  • sudo apt update

  • sudo apt install -y curl wget gnupg2 ca-certificates lsb-release apt-transport-https

  • sudo apt-add-repository ppa:ondrej/php

  • sudo apt update

  • 7.3: sudo apt install -y php7.3-fpm php7.3-mysql

  • 7.4: sudo apt install -y php7.4-fpm php7.4-mysql

  • sudo vim /etc/nginx/sites-available/<Ваш домен>

Базовая настройка:

server {
        listen 80;
        root /var/www/html;
        index index.php index.html index.htm index.nginx-debian.html;
        server_name <Ваш домен или IP>;

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

        location ~ .php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        }

        location ~ /.ht {
                deny all;
        }
}

Только HTTP настройка под Laravel:

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

    root /var/www/html/<Имя проекта>/public;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name <Ваш домен или IP>;

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

    location ~ .php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /.ht {
        deny all;
    }
}

HTTPS настройка под Laravel:

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

    server_name <Ваш домен> www.<Ваш домен>;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name <Ваш домен> www.<Ваш домен>;
    root /var/www/html/<Имя проекта>/public;

    ssl_certificate /etc/letsencrypt/live/<Ваш домен>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<Ваш домен>/privkey.pem;

    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php index.html index.htm index.nginx-debian.html;

    charset utf-8;

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

    location ~ .php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /.ht {
            deny all;
    }

    location ~ /.well-known {
            allow all;
    }
}

  • sudo ln -s /etc/nginx/sites-available/<Ваш домен> /etc/nginx/sites-enabled/
  • sudo unlink /etc/nginx/sites-enabled/default
  • sudo nginx -t
  • sudo systemctl reload nginx

Laravel

  • 7.3: sudo apt install -y php7.3-mbstring php7.3-xml composer unzip

  • 7.4: sudo apt install -y php7.4-mbstring php7.4-xml composer unzip

  • mysql -u root -p

  • CREATE DATABASE laravel DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

  • GRANT ALL ON laravel.* TO 'root'@'localhost' IDENTIFIED BY '<Ваш пароль от MySQL>';

  • FLUSH PRIVILEGES;

  • exit

  • cd /var/www/html

  • sudo mkdir -p <Имя проекта>

  • sudo chown laravel:laravel <Имя проекта>

  • cd ./<Имя проекта>

  • git clone <ссылка на проект> . / git clone -b <имя ветки> --single-branch <ссылка на проект> .

  • composer install

  • vim .env

APP_NAME=Laravel
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://<Ваш домен>

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=<Ваш пароль от MySQL>

  • php artisan migrate

  • php artisan key:generate

  • sudo chown -R $USER:www-data storage

  • sudo chown -R $USER:www-data bootstrap/cache

  • chmod -R 775 storage

  • chmod -R 775 bootstrap/cache

HTTPS

  • sudo add-apt-repository ppa:certbot/certbot

  • sudo apt install -y python-certbot-nginx

  • sudo certbot certonly --webroot --webroot-path=/var/www/html/<Имя проекта>/public -d <Ваш домен> -d www.<Ваш домен>

  • sudo nginx -t

  • sudo ufw allow 'Nginx HTTPS'

  • sudo ufw status

  • sudo systemctl reload nginx

Создаем дроплет на DigitalOcean и регистрируем новый SSH-ключ

Я искренне верю, что вы сами разберетесь с регистрацией на DigitalOcean. Она непростая, с кучей верификаций и прочей байды. Если у вас при верификации с помощью документов постоянно возникает ошибка сети, попробуйте всё сделать через VPN, должно помочь.

В меню сверху нажимаем Create->Droplets. Выбираем Ubuntu.

Как только зарегистировались, вы получите 100$ на счет. Но не обольщайтесь. У вас есть всего 60 дней, чтобы их потратить. А это очень мало. Вы можете, как и я, захотеть использовать план подороже, чтобы потом, когда уже пойдут реальные деньги, пересесть на подешевле. Сразу говорю, не получится. Увеличивать можно, уменьшать нельзя. Такие дела. Я выбираю Standard->$5.

Регион я выбираю ближайший к нам Frankfurt. VPC Network->default-fra1

Аутентификацию сразу сделаем через SSH. Нажимаем New SSH Key. Если у вас нет SSH, справа есть очень простая инструкция. Открываем bash-терминал, вставляем ssh-keygen. Потом заходим в файл с публичным ключом /Users/<Ваше имя пользователя>/.ssh/id_rsa.pub (или просто cat ~/.ssh/id_rsa.pub), копируем содержимое и вставляем в окно слева. Имя любое.

Придумываем для дроплета хостнейм.

Нажимаем Create Droplet

Создаем нового пользователя

  • ssh root@[IP-адрес вашего дроплета]
  • Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
  • Введите ваш пароль от SSH
  • Создаем пользователя laravel: adduser laravel
  • Введите пароль и другую информацию (я ввожу только Full Name)
  • Добавляем пользователя в группу sudo: usermod -aG sudo laravel

SSH для нового пользователя

  • Переключаемся на нового пользователя: su laravel

Все действия далее, до конца статьи, мы проводим от имени пользователя laravel. Поэтому, если вы вдруг прервались, перезайдите и введите su laravel

  • mkdir ~/.ssh
  • chmod 700 ~/.ssh
  • vim ~/.ssh/authorized_keys

Мы открыли файл в Vim. Если вы с ним не знакомы вообще, можете работать в Nano, ваше право.

Самые базовые команды Vim

Для того, чтобы по ходу статьи пользоваться редактором Vim, вам достаточно знать следующее.

  • В Vim есть разные режимы: обычный (Normal mode), в котором вы вводите команды и выбираете режимы и остальные.
  • Чтобы выйти из любого режима и попасть в обычный режим достаточно нажать Esc
  • Передвигаться: можно просто стрелками
  • Выйти без сохранения <Normal mode>: :q!
  • Выйти и сохранить <Normal mode>: :wq
  • Перейти в режим ввода текста <Normal mode>: i (от англ. insert)
  • Вставляем наш публичный ключ (который мы делали выше)
  • Защищаем от изменений: chmod 600 ~/.ssh/authorized_keys

Устанавливаем фаервол

  • Смотрим все доступные настройки: sudo ufw app list
  • Разрешаем OpenSSH (иначе нас залочит): sudo ufw allow OpenSSH
  • Запускаем фаервол: sudo ufw enable, y
  • Проверяем: sudo ufw status

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)

Все в порядке.

Устанавливаем Nginx

При установке вас иногда будет спрашивать "Вы уверены?". Отвечайте y (ну только, если уверены).

  • sudo apt update
  • sudo apt install nginx

Добавляем Nginx в настройки фаервола

  • sudo ufw app list
  • sudo ufw allow 'Nginx HTTP'
  • sudo ufw status

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx HTTP                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx HTTP (v6)            ALLOW       Anywhere (v6)

Перейдите по вашему IP. Если все идет хорошо, вы должны увидеть следующее.

Деплой приложения на Laravel 7 на Ubuntu & Nginx

Устанавливаем MySQL

  • sudo apt install mysql-server
  • Запускаем автоматический скрипт по защите sudo mysql_secure_installation

Ответьте на поставленные вопросы. Если вы не знаете, что отвечать, вот рекомендуемые варианты:

  • Validate password plugin — N

  • Remove anonymous users? — Y

  • Disallow root login remotely? — N

  • Remove test database and access to it? — N

  • Reload privilege tables now? — Y

  • Заходим в MySQL: sudo mysql

  • Смотрим методы доступа: SELECT user,authentication_string,plugin,host FROM mysql.user;

  • Устанавливаем пароль для root: ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '<Ваш пароль для MySQL>';

  • Смотрим методы доступа снова: SELECT user,authentication_string,plugin,host FROM mysql.user;

  • Применяем изменения и выходим из MySQL: FLUSH PRIVILEGES; и exit

  • Теперь, чтобы войти в MySQL нужно использовать mysql -u root -p и вводить пароль

Устанавливаем PHP

Воспользуемся сторонним репозиторием от Ondřej Surý

  • sudo apt update
  • sudo apt install -y curl wget gnupg2 ca-certificates lsb-release apt-transport-https
  • sudo apt-add-repository ppa:ondrej/php
  • sudo apt update

Теперь выбираем. Для Laravel 7 можно выбрать PHP 7.3 или 7.4. Отличие будет лишь в цифрах 3 и 4.

  • 7.3: sudo apt install -y php7.3-fpm php7.3-mysql
  • 7.4: sudo apt install -y php7.4-fpm php7.4-mysql

PHP FastCGI Process Manager (fpm) работает с PHP запросами. mysql, естественно, для работы с MySQL.

Дальше я все буду делать на 7.4.

Настраиваем Nginx

  • sudo vim /etc/nginx/sites-available/<Ваш домен>

Вместо "<Ваш домен>" впишите домен (например, mysite.ru), который хотите использовать в будущем. Если у вас такого пока нет, пишите любой, потом просто повторите действия в этой главе для своего домена, когда выберите его.

Вписываем следующее:

server {
        listen 80;
        root /var/www/html;
        index index.php index.html index.htm index.nginx-debian.html;
        server_name <Ваш домен или IP>;

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

        location ~ .php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        }

        location ~ /.ht {
                deny all;
        }
}

Если Вы выбрали версию 7.3 вместо php7.4-fpm.sock впишите php7.4-fpm.sock.

Слушаем 80 порт на server_name, когда приходим запрос в корне /var/www/html берем index-файл. Если после server_name что-то есть, ищем такой файл. Не находим, выбрасываем 404. Если заканчивается на .php, прогоняем через fpm. Если есть .ht, запрещаем (403).

  • Делаем ссылку из sites-available в sites-enabled: sudo ln -s /etc/nginx/sites-available/<Ваш домен> /etc/nginx/sites-enabled/
  • Удаляем ссылку на default: sudo unlink /etc/nginx/sites-enabled/default
  • Проверяем на ошибки: sudo nginx -t
  • Перезагружаем: sudo systemctl reload nginx

Проверяем работу:

  • sudo vim /var/www/html/info.php
  • Пишем: <?php phpinfo();
  • Переходим на <Ваш IP>/info.php

Вы должны увидеть что-то подобное:

Деплой приложения на Laravel 7 на Ubuntu & Nginx

Теперь этот файл можно удалить: sudo rm /var/www/html/info.php

Ставим Laravel

  • 7.3: sudo apt install php7.3-mbstring php7.3-xml composer unzip

  • 7.4: sudo apt install php7.4-mbstring php7.4-xml composer unzip

  • Заходим в MySQL: mysql -u root -p

  • Создаем БД с именем laravel: CREATE DATABASE laravel DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

  • Предоставляем root доступ к laravel: GRANT ALL ON laravel.* TO 'root'@'localhost' IDENTIFIED BY '<Ваш пароль от MySQL>';

  • FLUSH PRIVILEGES;

  • exit

  • cd /var/www/html

  • Создаем папку для проекта: sudo mkdir -p <Имя проекта>

  • Предоставляем пользователю laravel права на проект: sudo chown laravel:laravel <Имя проекта>

Далее нужно перенести проект. Например, клонированием с Github.

  • cd ./<Имя проекта>
  • git clone <ссылка на проект> .

Стоит учитывать, что, если вы не сохраняли статические файлы (например, из /public) на Github, то их у вас, естественно, не будет. Я, например, для решения этого создал отдельную ветку deploy, из которой уже и клонировал: git clone -b <имя ветки> --single-branch <ссылка на проект> ..

  • Устанавливаем зависимости: composer install
  • Создаем .env: vim .env

Базовая версию его выглядит так:

APP_NAME=Laravel
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://<Ваш домен>

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=<Ваш пароль от MySQL>

Если вы копируете свой .env, замените APP_ENV на production, APP_DEBUG на false и впишите правильные настройки для MySQL.

  • Мигрируем БД: php artisan migrate
  • Генерируем код: php artisan key:generate

Меняем разрешения:

  • sudo chown -R $USER:www-data storage
  • sudo chown -R $USER:www-data bootstrap/cache
  • chmod -R 775 storage
  • chmod -R 775 bootstrap/cache

Последнее, что осталось, – перенастроить Nginx под Laravel:

sudo vim /etc/nginx/sites-available/<Ваш домен>

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

    root /var/www/html/<Имя проекта>/public;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name <Ваш домен или IP>;

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

    location ~ .php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /.ht {
        deny all;
    }
}

Как и в прошлый раз, если Вы выбрали версию 7.3 вместо php7.4-fpm.sock впишите php7.4-fpm.sock.

Настройка домена на DigitalOcean

Всё, на самом деле, очень просто. Вы покупаете домен (где угодно), переходите на DigitalOcean в Create->Domains/DNS. В поле Add a domain вы вписываете этот домен, нажимаете добавить. Затем переходите в настройки домена и в поле HOSTNAME вписываете @. Выбираете проект и нажимаете Create record.
Теперь переходите на сайт, где покупали домен, находите там "DNS Серверы" (или что-то подобное) и вписываете серверы DigitalOcean (а именно ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com). Теперь надо немного (или много) подождать, пока эти настройки будут приняты. Готово!
Единственная проблема – у вас сайт будет открываться только как HTTP. Чтобы был HTTPS, переходим к следующей части.

Настроиваем HTTPS

Устанавливаем certbot и передаем ему имя домена (формата mysite.ru) и имя домена с www (www.mysite.ru).

  • sudo add-apt-repository ppa:certbot/certbot
  • sudo apt install python-certbot-nginx
  • sudo certbot certonly --webroot --webroot-path=/var/www/html/<Имя проекта>/public -d <Ваш домен> -d www.<Ваш домен>

Теперь надо перенастроить Nginx (не забудьте подставить свои значения):

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

    server_name <Ваш домен> www.<Ваш домен>;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name <Ваш домен> www.<Ваш домен>;
    root /var/www/html/<Имя проекта>/public;

    ssl_certificate /etc/letsencrypt/live/<Ваш домен>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<Ваш домен>/privkey.pem;

    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php index.html index.htm index.nginx-debian.html;

    charset utf-8;

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

    location ~ .php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~ /.ht {
            deny all;
    }

    location ~ /.well-known {
            allow all;
    }
}

Думаю, вы уже поняли, что нужно поменять для PHP 7.3.

Тут, на самом деле, все просто. Мы просто перенаправляем все запросы с HTTP (порт 80) на HTTPS (порт 443). А там делаем все тоже самое, что и раньше, но уже с шифрованием.

Осталось поставить разрешения в фаерволе:

  • sudo nginx -t
  • sudo ufw app list
  • sudo ufw allow 'Nginx HTTPS'
  • sudo ufw status
  • sudo systemctl reload nginx

Теперь все должно работать, как надо.

[Дополнительно] Установка Node.js

Если вдруг вам понадобилось запускать npm-команды прямо на сервере, вам нужно установить Node.js.

  • sudo apt update
  • sudo apt install -y nodejs npm
  • nodejs -v

Всё, я остановился на этом этапе. В принципе, результат меня устраивает. Возможно, я перейду с DigitalOcean куда-нибудь поближе к России и подешевле. Но так как я уже прошел все круги верификации на сайте и всё делал там, я показывал на их примере. К тому же их стартовые 100 долларов – отличный плацдарм для тренировок.

P.S. Отдельная благодарность автору вот этого гиста, который послужил основой всех вышеперечисленных действий. Он в некоторых моментах не работает для Laravel 7, я это поправил.

P.P.S. Если вы вдруг топовый инженер, который думает командами из bash, пожалуйста, не судите строго. Возможно, вам уровень этой статьи покажется низковатым, но я был бы рад найти такую, когда она мне была нужна. Если есть предложения по улучшению, я обеими руками "за".

Источник: habr.com