Развёртывание статического на VPS вручную

Развёртывание статического на VPS вручную #

Допустим, что у вас уже есть:

  1. Виртуальная машина (VPS) с Debian GNU/Linux с известным IP адресом и доступом по SSH ключу
  2. Доменное имя, привязанное к IP адресу этой виртуальной машины

Разберём, как развернуть статический сайт с nginx на этой машине вручную.

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

Настройка Unix пользователей и доступов по ssh #

Настройка доступа под своим пользователем по ключу с sudo #

Скорее всего VPS позволяет подключится по SSH ключу из-под пользователя root, например так:

ssh root@ip-адрес-vps-сервера
  • Работать из-под root в Linux чревато проблемами — например, можно необдуманно выполнить опасную команду.
  • Мы настроим подключение под обычным пользователем, и будем использовать sudo для запуска отдельных команд, требующих привилегий root.

Если на вашем компьютере тоже Linux, выясните имя пользователя командой: id -u -n

Допустим, мы получили имя ivan.ivanov.

Создать пользователя интерактивно (лучше указать привычный и надёжный пароль):

adduser --force-badname ivan.ivanov

Добавить пользователя в группу sudo, чтобы он мог пользоваться sudo:

usermod -aG sudo ivan.ivanov

Отредактировать файл /etc/sudoers.d/ivan_ivanov

  • Мы позволим этому Unix пользователю использовать sudo без пароля
  • Пустая строка в конце файла обязательна, иначе запуск sudo сломается для всех пользователей!
ivan.ivanov ALL=(ALL:ALL) NOPASSWD: ALL

Запустить сессию bash с новым пользователем и создать каталог ~/.ssh:

sudo -uivan.ivanov bash -l
mkdir -p ~/.ssh

Отредактировать файл ~/.ssh/authorized_keys, вставив туда свой публичный ключ (~/.ssh/id_rsa.pub или ~/.ssh/id_ed25519.pub).

Пример:

ssh-rsa AAAA...BHK ivan.ivanov@home

Всё! Теперь можно входить в систему по ключу и использовать sudo:

ssh ip-адрес-vps-сервера
sudo whoami

Подготовка конфигов nginx #

На своём компьютере создадим в проекте отдельную версию конфигов nginx для production:

  1. В проекте создайте подкаталог — например, src/nginx-prod/
  2. Скопируйте в новый каталог конфиги nginx, используемые для локальной разработки — например, они могут быть в src/nginx/
  3. Внесите изменения в конфиги для production

В конфигах для production стоит сделать несколько изменений:

  1. Поменять user nginx; на user www-data; — в Debian веб-сервер принято запускать под пользователем www-data
  2. Пересмотреть worker_processes 2; — возможно, вместо 2 поставить иное число или просто auto, чтобы число процессов nginx соответствовало числу ядер в системе
  3. Поменять error_log, чтобы писать логи в файл /var/log/nginx/error.log вместо stderr
    • в контекстах server можно вместо error.log указать иное имя файла — например, atdd.ru.error.log
  4. Поменять access_log, чтобы писать логи в файл /var/log/nginx/access.log вместо stdout
    • в контекстах server можно вместо access.log указать иное имя файла — например, atdd.ru.access.log
  5. Явно определить ssl_protocols и ssl_ciphers, чтобы поддержка TLS не зависела от версии nginx в дистрибутиве Linux, установленном на VPS
  6. В контекстах server убрать локальные варианты server_name — например, убрать atdd.lan и localhost, но оставить atdd.ru
  7. Поменяйте каталог в директиве root на /var/www/html/, /usr/local/www/atdd.ru/ или любой иной каталог, куда вы планируете скопировать данные статического сайта

Пример изменений в nginx.conf:

user www-data;
worker_processes auto;

pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;

events {
    worker_connections 1024;
}

http {
    # ... другие директивы ...

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

	access_log /var/log/nginx/access.log;
}

Пример изменений в конфиге хоста (контекст server):

server {
    server_name atdd.ru;

	access_log /var/log/nginx/atdd.ru.access.log;
    error_log /var/log/nginx/atdd.ru.error.log warn;

    root /usr/local/www/atdd.ru/;

    # ... другие директивы ...
}

Установка и настройка nginx на сервере #

Сначала зайдите на сервер: ssh ip-адрес-vps-сервера

Затем установите nginx:

sudo apt-get update

sudo apt-get install nginx

# Проверка версии nginx
sudo nginx -v

# Альтернативный способ - проверка версии пакета nginx
apt-cache policy nginx

Теперь поменяйте основной конфиг nginx:

# Редактируем /etc/nginx/nginx.conf - скопируйте туда свою версию
sudo nano /etc/nginx/nginx.conf

# Проверяем корректность конфига nginx
sudo nginx -t

Установка последней версии nginx #

В дистрибутиве может быть устаревшая версия nginx, что помешает автоматизации работы с Let’s Encrypt.

  • Например, в версиях nginx ниже 1.25.1 проявится проблема HTTP2 support in Nginx plugin
  • Решение — установить последние версии nginx из официального репозитория nginx

Подробнее см. nginx: Linux packages, краткая версия ниже:

# Установка других необходимых пакетов
sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring

# Получение и проверка GPG-ключа репозитория
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg

# Добавления репозитория APT
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

# Установка приоритета для пакетов из нового репозитория
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

# Установка новой версии nginx
sudo apt update
sudo apt install nginx

# Проверка версии nginx
sudo nginx -v

Получение сертификата Let’s Encrypt #

Временный конфиг nginx #

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

  • Создайте конфиг своего сайта в каталоге /etc/nginx/conf.d — например, используя редактор nano: sudo nano /etc/nginx/conf.d/atdd_ru.conf
  • Скопируйте конфигурацию ниже, поменяв atdd.ru на свой домен
  • Обработка URL, начинающихся с /.well-known, нужна для получения сертификата Let’s Encrypt
server {
    listen 80;
    listen [::]:80;

    server_name atdd.ru;

    root /usr/share/nginx/html;

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

    location / {
        index index.html;
    }
}

Установка и запуск Certbot #

Certbot — инструмент для автоматизации получения сертификатов Let’s Encrypt

  • Сертификаты Let’s Encrypt выдаются на конкретный домен автоматизированно на 90 дней любому, кто докажет владение данным доменом
  • Для выдачи сертификатов предусмотрен определённый протокол (ACME) и два способа доказательства владения доменом
  • Мы применим один из способов доказательства владения доменом, реализованный в Certbot — отдача определённого файла с сервера, обслуживающего домен

Поддержка различных веб-серверов в Certbot реализована плагинами, и мы установим Certbot с плагином для nginx:

# Установка в Debian 12:
sudo apt install certbot python3-certbot-nginx

Далее запускаем Certbot:

sudo certbot --nginx

Результат #

После запуска Certbot файл конфигурации nginx изменится.

  • Обратите внимание на строки с суффиксом “# managed by Certbot”
  • Эти строки не следует редактировать, т.к. их редактирует сам Certbot
  • Однако их можно скопировать в локальную версию production конфигов в каталоге src/nginx-prod/conf.d/

Вывести конфиг можно командой cat /etc/nginx/conf.d/<ваш-сайт>.conf. В моём случае он выглядит так:

server {
    server_name atdd.ru;

    root /usr/share/nginx/html;

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

    location / {
        index index.html;
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/atdd.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/atdd.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

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

    listen 80;
    listen [::]:80;

    server_name atdd.ru;
    return 404; # managed by Certbot
}

Применяем финальный конфиг #

В собственный конфиг nginx не забудьте внести правки от Let’s Encrypt:

  • Скопировать все строки “# managed by Certbot”
  • Добавить обработку /.well-known

Пример:

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

    server_name atdd.ru;

    location ~ /.well-known {
        root /usr/share/nginx/html;
        allow all;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    server_name atdd.ru;

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/atdd.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/atdd.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

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

    # ... другие параметры конфигурации
}

Сохраним финальную версию и перезагрузим конфигурацию nginx

# Редактируем конфиг своего сайта - например, atdd.ru
sudo nano /etc/nginx/conf.d/atdd_ru.conf

# Проверяем корректность конфига nginx,
#  затем выполняем чтение конфига сервисом nginx
sudo nginx -t && sudo systemctl reload nginx

Развёртываем статический сайт через rsync #

Утилита rsync предназначена для копирования файлов на сервер или между серверами при наличии SSH доступов. Для облачных платформ есть похожая утилита rclone, способная работать с S3-совместимыми хранилищами и с десятками других API облачных хранилищ.

Перед первым использованием нужно установить rsync как локально, так и на сервере:

sudo apt-get install rsync

Допустим, что мы хотим скопировать статический сайт с такими ограничениями:

  • Локально статика находится в каталоге public/ проекта
  • На сервере nginx настроен на раздачу статики с каталога /usr/local/www/atdd.ru/
  • Пусть удалённый сервер доступен по домену atdd.ru или по какому-то IP адресу
  • Мы не хотим копировать определённые файлы, например файл .gitignore
  • Вход по SSH на сервере будет из-под root, но владельцем скопированных файлов будет www-data

Перед первым копированием на сервере надо создать каталог и поменять его владельца на www-data:

sudo mkdir -p /usr/local/www/atdd.ru
sudo chown www-data:www-data /usr/local/www/atdd.ru

Затем для каждого развёртывания можно использовать rsync:

# Проверка
rsync --dry-run --archive --progress --delete --exclude=.gitignore \
    --chown=www-data:www-data \
    public/ atdd.ru:/usr/local/www/atdd.ru/

# Выполнение
rsync --archive --progress --delete --exclude=.gitignore \
    --chown=www-data:www-data \
    public/ root@atdd.ru:/usr/local/www/atdd.ru/

Объяснение флагов rsync:

ФлагЗачем он нужен
–archiveАрхивный режим: копировать рекурсивно с сохранением владельца, группы, прав на файлы и времени изменения
–progressВыводить прогресс
–deleteУдалять файлы в целевом каталоге, если их нет в источнике
–exclude=.gitignoreИсключить файлы по указанному шаблону
–chown=www-data:www-dataПоменять владельца и группу файлов на указанного

Можно добавить в проект bash скрипт bin/deploy для копирования на сервер:

#!/usr/bin/env bash

set -o errexit

SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
PROJECT_DIR=$(dirname "$SCRIPT_DIR")

rsync --archive --stats --delete --exclude=.gitignore \
    --chown=www-data:www-data \
    "$PROJECT_DIR/public/" \
    root@atdd.ru:/usr/local/www/atdd.ru/

Сайт atdd.ru — блог разработчика.