73 votes

Nginx ne démarre pas si l'hôte n'est pas trouvé en amont.

J'utilise nginx comme proxy et pour maintenir des connexions persistantes avec des serveurs éloignés.

J'ai configuré une quinzaine de blocs semblables à cet exemple :

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

Le problème est que si le nom d'hôte ne peut pas être résolu dans un ou plusieurs des éléments suivants upstream blocs, nginx ne (re)démarre pas. Je ne peux pas non plus utiliser d'IP statiques, certains de ces hôtes m'ont explicitement dit de ne pas le faire parce que les IP vont changer. Toutes les autres solutions que j'ai vues pour ce message d'erreur recommandent de se débarrasser des éléments suivants upstream et faire tout dans le location bloc. Ce n'est pas possible ici car keepalive est uniquement disponible sous upstream .

Je peux temporairement me permettre de perdre un serveur mais pas les 15.

Éditer : Il s'avère que nginx n'est pas adapté à ce cas d'utilisation. Un proxy keepalive alternatif (en amont) devrait être utilisé. Une alternative Node.js personnalisée se trouve dans ma réponse . Jusqu'à présent, je n'ai pas trouvé d'autres solutions qui fonctionnent vraiment.

4 votes

Il y a deux choses que vous pouvez essayer. Changez proxy_pass https://rinu-test; a proxy_pass $proxyurl; et avant cela, vous pouvez définir la variable set $proxyurl $scheme://$host$request_uri Et la suivante est d'essayer d'utiliser une variable en amont, je n'ai pas testé la 2ème option et ne peux pas encore vérifier. Mais utiliser une variable dans proxy_pass désactive la mise en cache DNS dans nginx

0 votes

La procuration sans l'amont est inutile. Les variables ne peuvent pas être utilisées en amont.

0 votes

Je voulais dire que vous pourriez essayer quelque chose comme proxy_pass https://rinu-test$request_uri;

22voto

cnst Points 1699

Les versions antérieures de nginx (avant la 1.1.4), qui alimentaient déjà un grand nombre des sites web les plus visités au monde (et certains le font encore aujourd'hui, si l'on en croit les en-têtes du serveur), ne prenaient même pas en charge les éléments suivants keepalive sur le upstream car il n'y a que très peu d'avantages à le faire dans un centre de données, à moins que vous n'ayez une latence non négligeable entre vos différents hôtes ; cf. https://serverfault.com/a/883019/110020 pour une explication.

En gros, à moins que vous ne sachiez que vous avez spécifiquement besoin de keepalive entre votre amont et votre frontal, il y a de fortes chances que cela ne fasse que rendre votre architecture moins résiliente et moins performante.

(Notez que votre solution actuelle est également mauvaise parce qu'un changement d'adresse IP passera également inaperçu, car vous effectuez la résolution du nom d'hôte au moment du rechargement de la configuration uniquement ; ainsi, même si nginx démarre, il cessera de fonctionner dès que les adresses IP des serveurs en amont changeront).

Solutions potentielles, choisissez-en une :

  • La meilleure solution semble être de se débarrasser simplement de upstream keepalive comme probablement inutile dans un environnement de centre de données, et utiliser les variables avec des proxy_pass pour une résolution DNS à jour pour chaque requête (nginx est encore assez intelligent pour faire la mise en cache de telles résolutions)

  • Une autre option serait d'obtenir une version payante de nginx par le biais d'un abonnement commercial, qui a une resolve pour le paramètre server dans la directive upstream le contexte.

  • Enfin, vous pouvez également essayer d'utiliser un fichier set variable et/ou un map pour spécifier les serveurs dans upstream ; il n'est ni confirmé ni démenti que cela a été mis en œuvre ; par exemple, cela peut fonctionner ou non.

0 votes

Je sais ce que je fais. Grâce à Keepalive, les demandes adressées aux fournisseurs de services externes ont été accélérées d'une seconde et le gain de temps dans cette application est essentiel.

2 votes

Les variables ne peuvent pas être utilisées pour définir le serveur en amont.

0 votes

Un nginx payant est trop cher. Il est plus économique de réécrire l'application qui a besoin de ce proxy. Ce qui est déjà en cours mais j'estime qu'il faudra encore 3-4 ans pour le réaliser.

13voto

nbari Points 847

Votre scénario est très similaire à celui de l'utilisation d'aws. ELB comme des flux ascendants dans où est critique pour resolve l'IP propre du domaine défini.

La première chose que vous devez faire et vous assurer que les serveurs DNS que vous utilisez peuvent résoudre vos domaines, alors vous pouvez créer votre configuration comme ceci :

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

Remarquez le resolver 10.0.0.2 il doit s'agir de l'IP du serveur DNS qui fonctionne et répond à vos requêtes, selon votre configuration, il peut s'agir d'un service de cache local comme non consolidé . et ensuite utiliser simplement resolve 127.0.0.1

Maintenant, il est très important d'utiliser un variable pour spécifier le nom de domaine, à partir de la documentation :

Lorsque vous utilisez une variable pour spécifier le nom de domaine dans la directive proxy_pass, NGINX résout à nouveau le nom de domaine lorsque son TTL expire.

Vous pouvez vérifier votre résolveur en utilisant des outils tels que dig par exemple :

$ dig +short stackoverflow.com

En cas de besoin, il faut utiliser keepalive en amont, et s'il n'est pas possible d'utiliser Nginx +, alors vous pouvez essayer équilibreur ouvert vous devrez utiliser/implémenter lua-resty-dns

0 votes

Pourriez-vous nous parler plus en détail d'openresty, de lua et de la façon dont ils pourraient être utilisés ici ? Je n'en ai jamais entendu parler auparavant et la documentation ne montre pas clairement comment les installer ou comment les appliquer à mon cas d'utilisation.

0 votes

Salut @rinu, j'ai trouvé ce gist qui peut probablement vous donner quelques idées supplémentaires : gist.github.com/toritori0318/f9be21fb8df9e4d5768bb5f48456717‌​5 lua + nginx fonctionne très bien, on peut l'étendre et mettre en place un WAF, etc., et couvrir beaucoup de choses qui manquent dans le Nginx standard.

3voto

ruvim Points 78

Une solution possible est d'impliquer un cache de DNS local. Il peut être un serveur DNS local comme à Lier ou à Dnsmasq (avec quelques petits malins configuration, notez que nginx pouvez également utiliser le serveur dns spécifié à la place de la valeur par défaut du système), ou tout simplement maintenir le cache en hosts le fichier.

Il semble que l'utilisation d' hosts le fichier avec certains de script est assez simple. Le fichier hosts doit être divisé en parties statiques et dynamiques (c - cat hosts.static hosts.dynamic > hosts), et la partie dynamique doit être généré (et mis à jour automatiquement par un script.

Peut-être il judicieux de vérifier de temps en temps les noms d'hôte pour changer les IPs, et de mettre à jour le fichier d'hôtes et recharger la configuration de nginx sur les changements. Dans le cas de certains nom d'hôte ne peut pas être résolu à l'ancienne adresse IP ou certaines IP par défaut (comme 127.0.1.9) doit être utilisé.

Si vous n'avez pas besoin de les noms d'hôte dans le fichier de configuration de nginx (c'est à dire, IPs sont assez), l' upstream section avec IPs (résolu les noms d'hôtes) peut être généré par un script et inclus dans la config nginx - et pas besoin de toucher au fichier hosts dans de tels cas.

0 votes

Nous avons déjà eu l'idée du cache DNS et elle sera bientôt étudiée. Mon administrateur système a bien rigolé à propos de l'idée générée hosts fichier. Je suppose que ça ne se produit pas. Mais j'aime bien votre réponse de toute façon, jusqu'à présent c'est la seule qui pourrait réellement fonctionner.

0 votes

@rinu, pourriez-vous s'il vous plaît mentionner un argument constructif en quoi l'utilisation du fichier hosts est mauvaise ?

0 votes

Rien de mal à cela, c'est pourquoi j'ai voté pour vous. Gérer cela dans tous les serveurs est tout simplement trop de travail. Des alternatives à nginx ou un meilleur service DNS général sont beaucoup plus faciles à gérer.

1voto

Bruno Paiuca Points 57

J'ai mis le paramètre resolve sur le serveur et vous devez définir le résolveur Nginx dans nginx.conf comme ci-dessous :

/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;
}

0 votes

Je pense que c'est pour Nginx+, mais si vous définissez la localisation /foo { set $foo_backend_servers foo_backends.example.com ; proxy_pass http://$foo\_backend\_servers ; } cela fonctionnera. Si ce n'est pas le cas, postez votre configuration et vos journaux

0 votes

nginx.org/r/resolver est une directive standard, elle n'est pas du tout limitée à nginx plus ; c'est la directive resolve mot-clé dans le contexte amont, c'est Plus-seulement ; cependant, cette question ignore complètement le problème de keepalive que l'OP mentionne spécifiquement qu'ils ont besoin.

0 votes

Vous avez raison, le résolveur est ouvert et le paramètre resolve de la commande serveur est une option de reconfiguration à la volée. Si vous utilisez la variable, vous perdrez le keepalive et si vous utilisez l'upstream, vous perdrez une vérification DNS qui ne sera vérifiée qu'au démarrage.

-5voto

rinu Points 537

Une alternative est d'écrire un nouveau service qui ne fait que ce que je veux. Ce qui suit remplace nginx pour l'acheminement des connexions https en utilisant 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();
        })
    );
}

Exemple d'utilisation : curl http://localhost:3000/request_uri -H "Host: test.rinu.test" ce qui est équivalent à : curl https://test.rinu.test/request_uri

7 votes

Cette solution n'entre pas dans le cadre de la question, puisque celle-ci porte uniquement sur nginx (c'est-à-dire comment résoudre le problème avec nginx). Il est nécessaire de reformuler la question pour la rendre conforme à cette solution :)

0 votes

Dans sa forme actuelle, la question n'a pas de réponse. Ce que je demande n'est tout simplement pas possible. Je suis également d'accord pour dire que cette réponse ne fait pas exception. Mais en fin de compte, c'est ainsi que j'ai résolu mon problème et c'est pourquoi je l'ai incluse ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X