Настройка балансирующего прокси Nginx с сертификатом Let’s Encrypt в Ubuntu 18.04 Bionic или Debian 9 Stretch

Вы научитесь настраивать прокси-сервер Nginx с поддержкой SSL Let’s Encrypt для балансировки между двумя upstream-серверами. Данное руководство для ОС семейства Debian — Ubuntu Linux, Debian Linux и других, которые используют схожие пакеты и методы их установки.

Nginx часто используется не как самостоятельный Web-сервер, а как обратный прокси, который, который распределяет трафик на обрабатывающие серверы (в общей терминологии — upstream), которые уже выполняют всю работу. Эта схема часто применяется даже там, где вы ее не ожидаете, например, для взаимодействия с PHP-FPM или для передачи обработки на сервер Apache2.

Говоря о роли Nginx как об обратном прокси необходимо отметить несколько функций, которые Nginx выполняет:

  • Маршрутизация трафика. В рамках этой функции Nginx решает какой трафик на какой сервер отправить, в этот процесс может входить и балансировка по доступным серверам и видоизменение запроса, добавление в него дополнительной информации.
  • Обработка SSL. Прокси размещается на границе сети, позволяя внутренним серверам использовать HTTP без шифрования, а задачи шифрования трафика решаются Nginx. Соответственно, уменьшается количество необходимых мест управления сертификатами и упрощается инфраструктура.
  • Аутентификация и поддержка SSO. Опциональная роль, в рамках которой Nginx решает задачи пропуска запросов на обрабатывающие серверы в зависимости от того, прошли ли их отправители аутентификацию.

Чем более сложные задачи приходится решать на уровне прокси-сервера тем более часто он называется API Gateway. Существуют продукты, которые построены на Nginx, но решают задачи, требующие продвинутой обработки, которые Nginx «из коробки» не умеет. Эти задачи решаются с помощью скриптов на Lua. Два известных проекта — Kong и OpenResty.

Демонстрационный Upstream-сервер

Для демонстрации будем использовать тривиальный HTTP-сервер, реализованный на Python:

sudo apt update
sudo apt install -y python3

python3 -m http.server <PORT> --bind 127.0.0.1

Эта команда создает тривиальный HTTP-сервер, который просто выводит структуру каталога, в котором запущен. Вы можете запустить его на локальной машине, например, на 8000 порту и проверить его работу через браузер или curl:

curl http://localhost:8000/

Для целей урока на сервере, где будет настраиваться Nginx, мы так же запустим два сервера — один на порту 8081, второй на порту 8082.

Будем использовать различные каталоги, чтобы нагляднее видеть как меняется поведение. Эти серверы не будут доступны извне, но будут использоваться в качестве Upstream-серверов, между которых будет распределять трафик Nginx:

cd /tmp; python3 -m http.server 8081 --bind 127.0.0.1 &
cd /usr; python3 -m http.server 8082 --bind 127.0.0.1 &

Обратите внимание на символ ‘&‘ в конце строки — это означает, что процесс будет запущен в фоновом режиме. Вы не должны закрывать терминал, в котором запустили эти процессы до окончания урока.

Проверьте работоспособность обоих серверов с помощью curl, соединившись с ними:

curl http://localhost:8081/
curl http://localhost:8082/

Настройка Let’s Encrypt

Для настройки Let’s Encrypt (далее LE) необходимо установить программное обеспечение CertBot:

sudo apt update
sudo apt install -y certbot

Теперь запросим сертификат Let’s Encrypt. Вам понадобится e-mail, и имя домена:

Обратите внимание, ваш сервер должен быть доступен по доменному имени извне перед началом запуска команды получения сертификата. Не забывайте везде заменять домен ngx.cs2.netpoint-dc.com на ваш домен.

sudo certbot certonly --standalone -m user@site.com --agree-tos -d ngx.cs2.netpoint-dc.com

В примере выше имя домена — ngx.cs2.netpoint-dc.com, а e-mail: user@site.com. В момент запуска этой команды у вас не должно быть сервисов, занимающих порты 80 и 443. Если вы уже установили Nginx, остановите его с помощью service nginx stop.

После успешного завершения работы приложения вы получите сообщения следующего вида:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/ngx.cs2.netpoint-dc.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/ngx.cs2.netpoint-dc.com/privkey.pem
   Your cert will expire on 2019-06-27. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

В данном сообщении содержится важная информация о месте хранения данных сертификата. Мы будем использовать это в дальнейшем.

Следующий шаг — настроить автопродление сертификата. Сертификаты LE требуется продлевать каждые 90 дней. Мы просто настроим ежедневную задачу CRON, которая будет это делать. Для этого создайте файл /etc/cron.daily/le-renew со следующим содержимым:

#/bin/bash
certbot renew

Пометьте его как исполняемый и проверьте, что он работает:

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

Установка Nginx

В этой части урока мы установим Nginx и настроим его для балансировки трафика между двумя upstream-серверами, запущенными ранее с поддержкой сертификата LE. Установка из пакетов выполняется стандартным способом:

sudo apt update
sudo apt install -y nginx

Теперь настроим конфигурацию для сайта, создав новый файл настроек для виртуального хоста — /etc/nginx/sites-available/ngx.cs2.netpoint-dc.com. Добавим в него следующее содержимое:

# переадресация с HTTP на HTTPS
#
server {
    if ($host = ngx.cs2.netpoint-dc.com) {
        return 301 https://$host$request_uri;
    }
    
    listen 80;
    listen [::]:80;
    server_name ngx.cs2.netpoint-dc.com;
}

# upstream-серверы для балансировки (python)
#
upstream dirlist {
        ip_hash; # липкая балансировка по IP-клиента
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
}

# обработка SSL и соединение с upstream-серверами
#
server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name ngx.cs2.netpoint-dc.com;
  ssl_certificate /etc/letsencrypt/live/ngx.cs2.netpoint-dc.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/ngx.cs2.netpoint-dc.com/privkey.pem;
  access_log /var/log/nginx/website_access.log;
  error_log /var/log/nginx/website_error.log;
 
  # все запросы отправляем на upstream
  location / {
    proxy_pass http://dirlist/;
  }
}

Теперь активируем созданный виртуальный хост:

sudo ln -s /etc/nginx/sites-{available,enabled}/ngx.cs2.netpoint-dc.com
sudo nginx -t && sudo service nginx restart

Теперь при заходе через браузер на https://ngx.cs2.netpoint-dc.com/ вы будете видеть содержимое каталога, который отдается одним из upstream-серверов. В нашей настройке мы использовали ключевой слово ip_hash, что говорит Nginx делать липкую баансировку. Это означает, что до тех пор пока upstream-сервер, на который были распределены запросы от вашего IP работает, вы будете обслуживаться на нем.

Nginx поддерживает и другие способы, кроме ip_hash, например, least_conn, при этом Nginx будет выбирать наименее загруженный upstream-сервер.

Имитация отказа Upstream-сервера

Убедимся в том, что Nginx переключает трафик на второй upstream-сервер при падении первого. Для этого вы должны идентифицировать вывод какого каталога вы видите. В моем случае — это каталог ‘/usr’, а значит, это отдает сервер, работающий на порту 8082; отключим его.

Для этого в терминале, где вы запускали upstream-серверы python, выполните следующие команды:

$ cd; jobs

[1]-  Запущен          python3 -m http.server 8081 --bind 127.0.0.1 &  (рабочий каталог: /tmp)
[2]+  Запущен          python3 -m http.server 8082 --bind 127.0.0.1 & (рабочий каталог: /usr)

# у меня отдается трафик с 8082, значит я буду останавливать процесс %2
# активирую %2

$ kill %2
python3 -m http.server 8082 --bind 127.0.0.1

Теперь, если вы обновите страницу в браузере, вы должны увидеть, что Nginx отдает контент с оставшегося сервера. Запустите сервер опять — в моем случае сервер на порту 8082:

cd /usr; python3 -m http.server 8082 --bind 127.0.0.1 &

Обоновите страницу в браузере. Вы можете повторить этот опыт еще несколько раз, чтобы проверить корректное переключение.

Заключение

Вы научились создавать базовый проксирующий сервер Nginx с поддержкой сертификата Let’s Encrypt, освоили настройку секции upstream Nginx, которая позволяет балансировать нагрузку между несколькими серверами, познакомились с директивой балансировщика ip_hash, предназначенной для реализации липкой балансировки.

Вам могут быть интересны следующие статьи:

Если вам понравилась статья, поделитесь ей.