Nginx не запускается с хостом, который не найден в восходящем потоке

Я использую nginx для прокси-сервера и сохраняю постоянные подключения к удаленным серверам для меня.

Я сконфигурировал около 15 блоков, подобных этому примеру:

upstream rinu-test {
    server test.rinu.test:443;
    keepalive 20;
}
server {
    listen 80;
    server_name test.rinu.test;
    location / {
        proxy_pass https://rinu-test;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $http_host;
    }
}

Проблема в том, что если имя хоста не может быть разрешено в одном или нескольких из upstream блоков, nginx не запустится (повторно). Я также не могу использовать статические IP-адреса, некоторые из этих ящиков явно не говорили об этом, потому что IP-адреса будут меняться. Каждое другое решение, которое я видел в этом сообщении об ошибке, говорит, чтобы избавиться от upstream и сделать все в блоке location. Это невозможно здесь, потому что keepalive доступен только под upstream.

Я могу временно позволить себе потерять один сервер, но не все 15.

Edit: Оказывается, nginx не подходит для этого варианта использования. Следует использовать альтернативный бэкэнд (upstream) keepalive proxy. В моем ответе есть альтернатива Node.js. До сих пор я не нашел других альтернатив, которые действительно работают.

Ответы

Ответ 1

Более ранние версии nginx (до 1.1.4), которые уже приводили в действие огромное количество самых посещаемых веб-сайтов во всем мире (а некоторые до сих пор работают даже в наши дни, если верить заголовкам серверов), даже не поддерживали поддержку keepalive в upstream сторона, потому что в настройке центра обработки данных это очень мало выгодно, если у вас нет нетривиальной задержки между вашими различными хостами; см. https://serverfault.com/a/883019/110020 для некоторых объяснений.

По сути, если вы не знаете, что вам конкретно нужен keepalive между вашими вышестоящим и внешним интерфейсом, скорее всего, это только сделает вашу архитектуру менее устойчивой и ухудшающей.

(Обратите внимание, что ваше текущее решение также неверно, потому что изменение IP-адреса также останется незамеченным, потому что вы выполняете разрешение имени хоста только при перезагрузке конфигурации; поэтому, даже если nginx запускается, он в основном перестанет работать после того, как IP-адреса из вышестоящих серверов меняются.)

Потенциальные решения, выберите одно:

  • Лучшее решение, по-видимому, состоит в том, чтобы просто избавиться от поддержки keepalive в upstream как, вероятно, ненужной в среде центра обработки данных, и использовать переменные с proxy_pass для современного разрешения DNS для каждого запроса (nginx все еще достаточно умен, чтобы все еще выполнять кэширование таких резолюции)

  • Другой вариант - получить платную версию nginx через коммерческую подписку, в которой есть параметр resolve для директивы server в контексте upstream.

  • Наконец, еще одна вещь, которую можно попробовать, это использовать переменную set и/или map для определения серверов в upstream; это ни подтверждено, ни опровергнуто не было выполнено; например, это может или не может работать.

Ответ 2

Одним из возможных решений является использование локального кэша DNS. Это может быть локальный DNS-сервер, такой как Bind или Dnsmasq (с некоторой хитрой конфигурацией, обратите внимание, что nginx также может использовать указанный DNS-сервер вместо системы по умолчанию) или просто поддерживать кеш в файле hosts.

Похоже, что использование файла hosts с некоторыми скриптами довольно просто. Файл hosts должен быть разделен на статические и динамические части (то есть cat hosts.static hosts.dynamic > hosts), а динамическая часть должна автоматически генерироваться (и обновляться) скриптом.

Возможно, имеет смысл время от времени проверять имена хостов для изменения IP-адресов и обновлять файлы хостов и перезагружать конфигурацию в nginx при изменениях. В случае, если какое-либо имя хоста не может быть разрешено, должен использоваться старый IP-адрес или некоторый IP-адрес по умолчанию (например, 127.0.1.9).

Если вам не нужны имена хостов в файле конфигурации nginx (т.е. Достаточно IP-адресов), upstream раздел с IP-адресами (разрешенными именами хостов) может быть сгенерирован скриптом и включен в конфигурацию nginx - и нет необходимости касаться файла hosts в таком случае.

Ответ 3

Альтернативой является создание новой службы, которая только делает то, что я хочу. Следующее заменяет nginx для проксирования соединений https с помощью Node.js

const http = require('http');
const https = require('https');

const httpsKeepAliveAgent = new https.Agent({ keepAlive: true });

http.createServer(onRequest).listen(3000);

function onRequest(client_req, client_res) {
    https.pipe(
        protocol.request({
            host: client_req.headers.host,
            port: 443,
            path: client_req.url,
            method: client_req.method,
            headers: client_req.headers,
            agent: httpsKeepAliveAgent
        }, (res) => {
            res.pipe(client_res);
        }).on('error', (e) => {
            client_res.end();
        })
    );
}

Пример использования: curl http://localhost:3000/request_uri -H "Host: test.rinu.test" который эквивалентен: curl https://test.rinu.test/request_uri

Ответ 4

Я установил параметр разрешения на сервер, и вам нужно установить Nginx Resolver в nginx.conf, как показано ниже:

/etc/nginx/nginx.conf:

http {
    resolver 192.168.0.2 ipv6=off valid=40s;  # The DNS IP server
} 

Site.conf:

upstream rinu-test {
    server test.rinu.test:443;
    keepalive 20;
}

Ответ 5

(новый для nginx) В моем случае это было неправильное имя папки

Для конфигурации

upstream serv {
    server ex2_app_1:3000;
}

убедитесь, что папка приложения находится в папке ex2:

ех2/приложение/...

Ответ 6

Ваш сценарий очень похож на тот, который используется при использовании aws ELB как uptreams, где критически важно resolve правильный IP-адрес определенного домена.

Первое, что вам нужно сделать и обеспечить, - это то, что DNS-серверы, которые вы используете, могут разрешать ваши домены, тогда вы можете создать свою конфигурацию следующим образом:

resolver 10.0.0.2 valid=300s;
resolver_timeout 10s;

location /foo {
    set $foo_backend_servers foo_backends.example.com;
    proxy_pass http://$foo_backend_servers;
 }

location /bar {
    set $bar_backend_servers bar_backends.example.com;
    proxy_pass http://$bar_backend_servers;
 }

Обратите внимание, что resolver 10.0.0.2 должен быть IP-сервером DNS-сервера, который работает и отвечает на ваши запросы, в зависимости от вашей установки это может быть служба локального кеша, такая как unbound. а затем просто используйте resolve 127.0.0.1

Теперь очень важно использовать переменную для указания имени домена из документов:

Когда вы используете переменную для указания имени домена в директиве proxy_pass, NGINX повторно разрешает доменное имя, когда истекает его TTL.

Вы можете проверить свой резольвер, используя такие инструменты, как dig например:

$ dig +short stackoverflow.com

В случае необходимости использовать keepalive в восходящем потоке, и если вы не можете использовать Nginx +, тогда вы можете попробовать открыть резервный балансир, вам нужно будет использовать/реализовать lua-resty-dns