Configurer nginx pour ne pas planter si l'hôte en amont n'est pas trouvé

117

Nous avons plusieurs applications de rails sous un domaine commun dans Docker et nous utilisons nginx pour diriger les demandes vers des applications spécifiques.

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

La configuration ressemble à ceci:

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...
}

Si l'une de ces applications n'est pas démarrée, nginx échoue et s'arrête:

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

Nous n'avons pas besoin de tous pour être en place, mais nginx échoue autrement. Comment faire en sorte que Nginx ignore les échecs en amont?

Morozov
la source
1
Liez-vous les conteneurs d'applications aux conteneurs Nginx ou les exécutez-vous séparément les uns des autres? Si l'hôte dans le upstreambloc ne résout pas, au moment de l'exécution, Nginx se fermera avec l'erreur ci-dessus ...
Justin
1
Si vous pouvez utiliser une adresse IP, le démarrage sera parfait. L'utilisation de resolver( nginx.org/en/docs/http/ngx_http_core_module.html#resolver ) fonctionnerait-elle dans votre cas?
Justin le
@Justin, nous avons chaque application dans un conteneur séparé, nginx aussi. Liez-les avec docker
Morozov
L'ordre de démarrage @Justin est correct, nginx démarre après d'autres applications. Nous voulons juste n'en exécuter que quelques-uns :)
Morozov
1
J'ai une configuration similaire (conteneur Nginx avec conteneur (s) d'application) . Nous avons créé une image Nginx qui comprend un proxy.shscript qui lit les variables d'environnement et ajoute dynamiquement des upstreamentrées pour chacune, puis démarre Nginx. Cela fonctionne très bien en ce que lorsque nous exécutons notre conteneur proxy, nous pouvons transmettre les flux amont nécessaires au moment de l'exécution. Vous pouvez faire quelque chose de similaire pour activer / désactiver certains amonts au lancement (ou comme ma configuration, ajoutez simplement ceux nécessaires à l'exécution)
Justin

Réponses:

90
  1. Si vous pouvez utiliser une adresse IP statique, utilisez-la simplement, elle démarrera et retournera simplement 503si elle ne répond pas.

  2. Utilisez la resolverdirective pour pointer vers quelque chose qui peut résoudre l'hôte, qu'il soit actuellement actif ou non.

  3. Résolvez-le au locationniveau, si vous ne pouvez pas faire ce qui précède (cela permettra à Nginx de démarrer / s'exécuter) :

    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;
    }
    
Justin
la source
1
votre option 3 fonctionne très bien pour moi. Si je ne spécifie pas de résolveur, savez-vous combien de temps nginx mettra en cache l'IP qu'il résout?
Riley Lark
14
Merci! Le simple fait d'utiliser une variable semble empêcher nginx d'être intelligent à ce sujet
Blanka
1
J'ai trouvé qu'un groupe de capture de regex m'a permis de sauter la variable:location ~ ^/foo/(.*)$ { proxy_pass http://foo/$1; }
Danny Kirchmeier
2
Comment cela fonctionne-t-il pour un proxy TCP? Il semble qu'il n'y ait aucun moyen d'essayer l'option 3 pour le proxy TCP.
krish7919
1
@Charlie ce genre d'erreurs dans nginx est presque toujours lié à l' absence de ";" signe en fin de ligne :)
SteveB
18

Pour moi, l'option 3 de la réponse de @ Justin / @ duskwuff a résolu le problème, mais j'ai dû changer l'adresse IP du résolveur en 127.0.0.11 (serveur DNS de 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;
}

Mais comme @ Justin / @ duskwuff l'a mentionné, vous pouvez utiliser n'importe quel autre serveur DNS externe.

Neumann
la source
15

Le principal avantage de l'utilisation upstreamest de définir un groupe de serveurs pouvant écouter sur différents ports et de configurer l'équilibrage de charge et le basculement entre eux .

Dans votre cas, vous ne définissez qu'un seul serveur principal par amont, il doit donc être opérationnel .

À la place, utilisez des variables pour votre proxy_pass( vos ) et n'oubliez pas de gérer les erreurs possibles (404, 503) que vous pourriez obtenir lorsqu'un serveur cible est arrêté.

danielgpm
la source
1
> Au lieu de cela, utilisez des variables pour votre ou vos proxy_pass et n'oubliez pas de gérer les erreurs possibles (404, 503) que vous pourriez obtenir lorsqu'un serveur cible est arrêté. Pouvez-vous expliquer comment procéder? Si je fais set $variable http://fooet proxy_pass $variableet que je garde le toto "en amont" (pour conserver les avantages que vous avez mentionnés), alors je rencontre toujours le problème mentionné par OP.
Tibor Vass
6
Comme vous pouvez le voir dans d'autres exemples, ce sera set $variable fooetproxy_pass http://$variable
danielgpm
2
@danielgpm Comme vous l'avez dit, l'utilisation de la variable pour proxy_pass fonctionne parfaitement et a résolu mon problème. Cela aiderait les autres si vous pouviez mettre à jour votre réponse et le mentionner à titre d'exemple
Nitb
3
Que faire si j'en ai plusieurs et que je souhaite ignorer ceux qui ne peuvent pas être résolus?
talabes le
0

J'ai eu le même problème «Hôte introuvable» car une partie de mon hôte était mappée en utilisant $uriau lieu de $request_uri:

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

Et lorsque la demande est devenue la sous-demande d'authentification, le a $uriperdu sa valeur initiale. Changer le mappage à utiliser $request_uriau lieu de $urirésoudre mon problème:

map $request_uri $kubernetes {
    # ...
}
Washington Guedes
la source
-8

Vous ne pouvez pas utiliser d' --linkoption, à la place, vous pouvez utiliser le mappage de port et lier nginx à l'adresse hôte.

Exemple: exécutez votre premier conteneur docker avec -p 180:80option, deuxième conteneur avec -p 280:80option.

Exécutez nginx et définissez ces adresses pour le proxy:

proxy_pass http://192.168.1.20:180/; # first container
proxy_pass http://192.168.1.20:280/; # second container
kvaps
la source