Настройте nginx, чтобы он не падал, если хост в восходящем потоке не найден

118

У нас есть несколько приложений rails под общим доменом в Docker, и мы используем nginx для направления запросов к конкретным приложениям.

our_dev_server.com/foo # proxies to foo app
our_dev_server.com/bar # proxies to bar

Конфиг выглядит так:

upstream foo {
  server foo:3000;
}

upstream bar {
  server bar:3000;
}

# and about 10 more...

server {
  listen *:80 default_server;

  server_name our_dev_server.com;

  location /foo {
      # this is specific to asset management in rails dev
      rewrite ^/foo/assets(/.*)$ /assets/$1 break;
      rewrite ^/foo(/.*)$ /foo/$1 break;
      proxy_pass http://foo;
  }

  location /bar {
      rewrite ^/bar/assets(/.*)$ /assets/$1 break;
      rewrite ^/bar(/.*)$ /bar/$1 break;
      proxy_pass http://bar;
  }

  # and about 10 more...
}

Если одно из этих приложений не запущено, nginx завершится ошибкой и остановится:

host not found in upstream "bar:3000" in /etc/nginx/conf.d/nginx.conf:6

Нам не нужно, чтобы они все работали, но в противном случае nginx выйдет из строя. Как заставить nginx игнорировать неудачные апстримы?

Морозов
источник
1
Связываете ли вы контейнеры приложений с контейнерами Nginx или запускаете их отдельно друг от друга? Если хост в upstreamблоке не разрешается во время выполнения, Nginx завершит работу с указанной выше ошибкой ...
Джастин
1
Если вы можете использовать IP-адрес, все будет нормально. Будет ли работать в вашем случае использование resolver( nginx.org/en/docs/http/ngx_http_core_module.html#resolver )?
Джастин
@Justin у нас есть каждое приложение в отдельном контейнере, nginx тоже. Свяжите их с докером
Морозов
@Justin Порядок запуска в порядке, nginx запускается после других приложений. Мы просто хотим запустить только некоторые из них :)
Морозов
1
У меня аналогичная установка (контейнер Nginx с контейнером (ами) приложений) . Мы создали образ Nginx, который включает proxy.shскрипт, который считывает переменные среды и динамически добавляет upstreamзаписи для каждой, а затем запускает Nginx. Это прекрасно работает в том смысле, что, когда мы запускаем наш прокси-контейнер, мы можем передавать необходимые восходящие потоки во время выполнения. Вы могли бы сделать что-то подобное, чтобы включить / отключить определенные восходящие потоки при запуске (или, как моя установка, просто добавьте те, которые нужны во время выполнения)
Джастин

Ответы:

90
  1. Если вы можете использовать статический IP-адрес, просто используйте его, он запустится и просто вернет 503, если не ответит.

  2. Используйте resolverдирективу, чтобы указать на что-то, что может разрешить хост, независимо от того, работает он в данный момент или нет.

  3. Разрешите это на locationуровне, если вы не можете сделать это (это позволит запускать / запускать Nginx) :

    location /foo {
      resolver 127.0.0.1 valid=30s;
      # or some other DNS (you company/internal DNS server)
      #resolver 8.8.8.8 valid=30s;
      set $upstream_foo foo;
      proxy_pass http://$upstream_foo:80;
    }
    
    location /bar {
      resolver 127.0.0.1 valid=30s;
      # or some other DNS (you company/internal DNS server)
      #resolver 8.8.8.8 valid=30s;
      set $upstream_bar foo;
      proxy_pass http://$upstream_bar:80;
    }
    
Джастин
источник
1
ваш вариант 3 отлично подходит для меня. Если я не укажу преобразователь, знаете ли вы, как долго nginx будет кэшировать IP-адрес, который он разрешает?
Райли Ларк,
14
Спасибо! Похоже, что простое использование переменной мешает nginx в этом разбираться
Бланка
1
Я обнаружил, что группа захвата регулярных выражений позволяет мне пропустить переменную:location ~ ^/foo/(.*)$ { proxy_pass http://foo/$1; }
Дэнни Кирчмайер,
2
Как это работает для TCP-прокси? Кажется, нет возможности попробовать вариант 3 для tcp-прокси.
krish7919
1
@Charlie: подобные ошибки в nginx почти всегда связаны с отсутствием ";" знак в конце строки :)
SteveB
18

Для меня вариант 3 ответа от @ Justin / @ duskwuff решил проблему, но мне пришлось изменить IP-адрес преобразователя на 127.0.0.11 (DNS-сервер Docker):

location /foo {
  resolver 127.0.0.11 valid=30s;
  set $upstream_foo foo;
  proxy_pass http://$upstream_foo:80;
}

location /bar {
  resolver 127.0.0.11 valid=30s;
  set $upstream_bar foo;
  proxy_pass http://$upstream_bar:80;
}

Но, как упоминал @ Justin / @ duskwuff, вы можете использовать любой другой внешний DNS-сервер.

Нейман
источник
15

Основным преимуществом использования upstreamявляется определение группы серверов, которые могут прослушивать разные порты и настраивать балансировку нагрузки и аварийное переключение. между ними. .

В вашем случае вы определяете только 1 первичный сервер для каждого восходящего потока, поэтому он должен быть включен .

Вместо этого используйте переменные для своих proxy_pass(ов) и не забудьте обработать возможные ошибки (404, 503), которые могут возникнуть, когда целевой сервер не работает.

Дэниэл ГПМ
источник
1
> Вместо этого используйте переменные для ваших proxy_pass (ов) и не забудьте обработать возможные ошибки (404, 503), которые могут возникнуть, когда целевой сервер не работает. Не могли бы вы рассказать, как это сделать? Если я это сделаю set $variable http://fooи proxy_pass $variableсохраню foo «вверх по течению» (чтобы сохранить упомянутые вами преимущества), то я все равно столкнусь с проблемой, упомянутой OP.
Tibor Vass
6
Как вы можете видеть в других примерах, это будет set $variable fooиproxy_pass http://$variable
danielgpm 01
2
@danielgpm Как вы сказали, использование переменной для proxy_pass отлично работает и решает мою проблему. Другим было бы
полезно,
3
Что, если у меня их несколько, и я хочу игнорировать те, которые не могут быть решены?
Talabes
0

У меня была та же проблема «Хост не найден», потому что часть моего хоста отображалась с использованием $uriвместо $request_uri:

proxy_pass http://one-api-service.$kubernetes:8091/auth;

И когда запрос изменился на подзапрос auth, он $uriпотерял свое первоначальное значение. Изменение сопоставления для использования $request_uriвместо $uriрешения моей проблемы:

map $request_uri $kubernetes {
    # ...
}
Вашингтон Гедес
источник
-8

Вы можете не использовать --linkопцию, вместо этого вы можете использовать сопоставление портов и привязать nginx к адресу хоста.

Пример: запустите свой первый контейнер докеров с -p 180:80опцией, второй контейнер с -p 280:80опцией.

Запустите nginx и установите эти адреса для прокси:

proxy_pass http://192.168.1.20:180/; # first container
proxy_pass http://192.168.1.20:280/; # second container
квапс
источник