Зробіть nginx для передачі імені хоста висхідного потоку при зворотній проксі


89

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

web1.local web2.local web3.local

Маршрутизація до них робиться на основі імені хоста від nginx. Я маю проксі перед цим налаштуванням (на різних машинах, підключених до Інтернету), де я визначаю вище:

    upstream main {
      server web1.local:80;
      server web2.local:80;
      server web3.local:80;
    }

І власне опис віртуального хоста:

    server {
      listen 80;
      server_name example.com;
      location / {
        proxy_pass http://main;
      }
    }

Тепер, оскільки контейнери отримують ім'я хоста "main" замість "web1.local", вони не відповідають належним чином на запит.

Питання: як я можу сказати nginx передавати ім'я верхнього сервера замість імені групи серверів вище в потоці в заголовку Host: при зверненні до запиту?


3
Я не думаю, що ти можеш. Чому б вам не встановити сервери, які відповідають на головний або example.com? Це не так, як якщо бэкенд не знає, хто це . Реверс легко можливий: proxy_set_header Хост $ хост; замінить будь-яку змінну хосту, що повертається з висхідного потоку, на ім'я хоста з оригінального запиту.
Андрій Домашек

Треба правильно зафіксувати додаток.
Майкл Хемптон

Відповіді:


109

Насправді це можна зробити через proxy_set_header.

Детальніше дивіться тут: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header або подивіться приклад використання тут: https://stackoverflow.com/questions/12847771/configure-nginx- з-проксі-пропуск

Я включив динамічний підхід до вашої вище розміщеної конфігурації:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

Ось приклад зі статичним іменем хоста:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            www.example.com;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

7
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; здається краще
Сіванн

1
@pavel: отримав. Насправді я також робив деякі дослідження та деякі тести. Здається, немає прямого підходу до виконання вашої вимоги. Тож навіть «бастардний» розчин - це рішення. Мені не подобається питати, чому ти хотів би це зробити. Я впевнений, що у вас є свої причини. :-)
Єнс Бредлер

@JensBradler Ви здаєтесь більш експертом, ніж я, чи не могли б ви сказати мені, що ви думаєте про моє рішення? Я хочу зробити те ж саме , тому що я запустити дві копію мого сайту з двох рахунків на моєму провайдера: site1.myisp.comі site2.myisp.comвони реагують тільки на їх відповідну назву. Зараз я маю своє доменне ім’я, і я хотів би використовувати свій веб-сайт провайдера, щоб завантажити баланс моїх серверів. Це не вагома причина? Дуже дякую;)
ncenerar

1
@ncenerar Ви можете це зробити, але це призведе до єдиної точки відмови: балансира навантаження. Якщо це для балансування навантаження (а не надмірності), ви також можете використовувати балансування навантаження на основі DNS у поєднанні з відмовою від DNS.
Єнс Бредлер

2
Ця відповідь відображає поради офіційного блогу .
Бернард Россет

28

У мене була така ж проблема, і я нарешті вирішив її, використовуючи два рівні проксі. Ось як ви могли зробити для своєї ситуації (я думаю):

server {
  listen      8001 default_server;
  server_name web1.example.com;
  location / {
    proxy_pass       http://web1.local:80;
    proxy_set_header Host web1.local:80;
  }
}

server {
  listen      8002 default_server;
  server_name web2.example.com;
  location / {
    proxy_pass       http://web2.local:80;
    proxy_set_header Host web2.local:80;
  }
}

server {
  listen      8003 default_server;
  server_name web3.example.com;
  location / {
    proxy_pass       http://web3.local:80;
    proxy_set_header Host web3.local:80;
  }
}

upstream main {
  server 127.0.0.1:8001;
  server 127.0.0.1:8002;
  server 127.0.0.1:8003;
}

server {
  listen      80;
  server_name example.com;
  location / {
    proxy_pass http://main;
  }
}

Як бачите, хитрість полягає в тому, щоб створити локальний сервер у відповідь на певний порт, який буде проксі-сервер, переписавши правильний хост для кожного сервера. Потім ви можете використовувати ці локальні сервери у своєму поточному і, нарешті, використати це в реальному проксі.


Спочатку я використовував підхід Lua, але тепер повністю перейшов на HAProxy, що дозволяє робити тільки те, що я хотів зі стандартною конфігурацією.
pavel_karoukin

3

Тож, прочитавши всю документацію для nginx (я не міг реально розібрати код для upstream module = (), я придумав це бастардизоване рішення. На жаль, це рішення не відслідковує помилки хостів, а просто вибирає випадковий і перенаправляє запит на нього. Тому я маю налаштувати якийсь моніторинг, щоб переконатися, що всі запущені програми.

server {
        listen 80;
        server_name example.com;
        resolver 127.0.0.1;

        location / {
                set $upstream "";
                rewrite_by_lua '
                        local upstreams = {
                                "http://web1.dokku.localdomain",
                                "http://web2.dokku.localdomain",
                                "http://web3.dokku.localdomain",
                                "http://web4.dokku.localdomain"
                        }
                        ngx.var.upstream = upstreams[ math.random( #upstreams ) ] 
                ';
                proxy_pass $upstream;
        }
}

2

Ми передаємо у висхідний addr як окремий заголовок, як це

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Upstream      $upstream_addr;
  }
}

Що робити, якщо ви спробували?

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $upstream_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Host          $host;
  }
}

2

Хоча мета здається логічною, nginx не збирається змінювати заголовок Host: на відповідність висхідній лінії . Натомість він розглядає upstreamдоменні імена, як CNAMEу DNS - як спосіб дістатися до IP-адреси.

Заголовки запиту (та тіло) фіксуються перед вибором висхідного потоку. Верхній потік може змінити середину запиту, якщо виявиться, що конкретний верхній потік не відповідає, але запит не змінюється.


0

Хм. У мене є аналогічна установка, в якій я просто робив

location / {
    ... 
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_pass ...;
}

Використання тут $http_host(заголовка хоста HTTP від ​​вхідного запиту), а не $host(конфігурація імені хоста сервера) призводить до того, що той самий заголовок хоста, який передається клієнтом, передається вгору по течії в моєму тестуванні.

Дивіться також https://stackoverflow.com/questions/14352690/change-host-header-in-nginx-reverse-proxy .


0

Оскільки інші люди вже розміщували за допомогою змінної скрипту (наприклад, $ upstream), ви можете встановити її як завгодно, і це виправить проблему без додаткового злому заголовка.

Змінні сценарію загрози обробника проксі-сервера по-іншому, якщо значення не є умовним (не має $ в імені), повертається до висхідного потоку на фазі конфігурації та використовувати пізніше.

Простий спосіб опустити цю проблему та отримати найбільш переваги (безкоштовної версії) вище за течією, буде використовувати щось на кшталт Split_Clients:

split_clients $request_uri $my_upstream {
              33%          server1.domainX.com;
              33%          server2.domainX.com;
# Always use DOT at end entry if you wonder why, read the SC code.
              *            server3.domainX.com;  
}
location / {
    ... 
    proxy_pass http://$my_upstream;
}

Наведений вище приклад виглядає майже так само, як і вище за течією. Існують інші модулі, які виконують відображення, тобто chash_map_module , але, оскільки вони знаходяться поза деревом, вам потрібно буде створити їх самостійно, що неможливо для деяких випадків використання /

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.