Немного воды
Всем читателям, привет! Хочется поделиться своим опытом по созданию Nginx reverse proxy для интересного кейса. Не судите строго но критике и предложениям буду рад.
Начало
Поступил вызов о необходимости реализации следующего кейса:
Есть 1 ip и на него нацелено n доменов
Есть n серверов (за NAT)
Когда пользователь заходит на домен_1 попадает на сервер_1
Когда пользователь заходит на домен_2 попадает на сервер_2
Когда пользователь заходит на домен_n попадает на сервер_n
Казалось бы, тривиальная задача... Но не с инфраструктурой которая есть
Немного о инфраструктуре
Из за отсутствия необходимости реверсивного прокси схема инфраструктуры выглядела так как на рис.1.

Естественно, для реализации кейса, необходимо было встроить Nginx в эту схему. Итоговая схема представлена на рис.2

В целом такая конфигурация не предоставляет собой ничего сложного. Однако если istio настроен и присутствуют политики авторизации будут тонкости но об этом дальше.
Настройка Nginx reverse proxy
Немного вводных:
ОС виртуализации Centos7
Iptables настроен на полный запрет иных портов кроме 443
Istio нацелен на доменное имя которое соответствует ВМ
Так как у меня ОС Centos7 конфиги http и server будут совмещены. Но важно понимать что если у вас раздельные конфиги как в Ubuntu это так же будет работать. Главное поместить нужный код в нужные места ;)
Итоговый конфиг для nginx выглядит так:
worker_processes 10; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #ВМ_1 server { server_name test1.domain.ru; listen 443 ssl; listen [::]:443 ssl; ssl_certificate /home/CERT.crt; ssl_certificate_key /home/CERT.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_protocols TLSV1.1 TLSV1.2 TLSV1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass https://192.168.0.21; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; } #ВМ_2 server { server_name test2.domain.ru; listen 443 ssl; listen [::]:443 ssl; ssl_certificate /home/CERT.crt; ssl_certificate_key /home/CERT.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_protocols TLSV1.1 TLSV1.2 TLSV1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass https://192.168.0.21; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; } } #ВМ_n server { server_name testn.domain.ru; listen 443 ssl; listen [::]:443 ssl; ssl_certificate /home/CERT.crt; ssl_certificate_key /home/CERT.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_protocols TLSV1.1 TLSV1.2 TLSV1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass https://192.168.0.x; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; } } }
Теперь давайте немного разберем основные параметры.
Общий блок:
worker_processes - Колличество возможных рабочих процессов при подключении к одному IP. Как правило, на ядро запускается 1 рабочий процесс;
worker_processes - Колличество пользователей могут одновременно обслуживаться Nginx;
Блок http {
keepalive_timeout - Максимальное время поддержания keepalive-соединения, в случае, если пользователь по нему ничего не запрашивает.
Блок http { server {
server_name - Ожидаемое доменное имя по которому будет доступен сервер. Например testn.domain.ru
listen - Указывает на порт по которому будет доступен сервер. В случае с https/443 необходимо контролировать наличие подписи ssl в строке.
ssl_certificate/ssl_certificate_key - доменнные сертификаты
ssl_session_cache - Кеширование сессий. Необходимо для повторного использования ключей, чтобы не повторять хэндшейк.
ssl_protocols - Определение возможных TLS протоколов для работы с сервером.
Блок http { server { location / {
ВАЖНО
В случае если у вас есть фронт с url testn.domain.ru/front не получится использовать другие location кроме как корневой (/). Связано с передачей url от nginx на сервер во время proxy. istio будет отбивать такие запросы и rewrite не поможет
proxy_pass - Сервер на которое осуществляется проксирование домена, указанного в server_name блока server.
proxy_http_version - Позволяет использовать HTTP/1.1 вместо дефолтного параметра HTTP/1.0
proxy_cache_bypass - В нашем случае не отдаются данные об изменении http запроса ($http_upgrade)
proxy_set_header - Записывает заголовки для передачи на сервер назначения proxy_pass
Подробнее можете почитать в доках nginx.
Должен отметить что по незнанию к такому конфигу я шел достаточно долго. 3 рабочих дня если быть точным.
Я долго не мог заставить дружить istio и nginx и перелопатил кучу информации. И что в итоге? Самый важный параметр всего этого конфига - это proxy_cache_bypass.
В чем же суть этого proxy_cache_bypass?
proxy_cache_bypass — Задает условия, при которых nginx не станет брать ответ из кэша, а сразу перенаправит запрос на бэкенд. Если хотя бы один из параметров не пустой и не равен “0”. Подробнее почитайте вот тут
В итоге у нас получается достаточно гибкая конфигурация прокси сервера которую легко аадминистрировать. Основной изменяемый/удаляемый/добавляемый блок - это блок http { server {
#ВМ_n server { server_name testn.domain.ru; listen 443 ssl; listen [::]:443 ssl; ssl_certificate /home/CERT.crt; ssl_certificate_key /home/CERT.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_protocols TLSV1.1 TLSV1.2 TLSV1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass https://192.168.0.x; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; } }
Итоги
На мой взгляд получилась достаточно гибкая конфигурация отвечающая всем условиям кейса. Надеюсь будет полезно. Спасибо за внимание.
