web-dev-qa-db-fra.com

Nginx ne commencera pas avec l'hôte non trouvé en amont

J'utilise nginx pour créer des proxy et conserver des connexions persistantes sur des serveurs distants pour moi.

J'ai configuré environ 15 blocs similaires à 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 blocs upstream, nginx ne démarrera pas. Je ne peux pas non plus utiliser d'IP statiques, certains de ces hôtes ont explicitement dit de ne pas le faire car les IP changeront. Toutes les solutions que j'ai rencontrées avec ce message d'erreur demandent de se débarrasser de upstream et de tout faire dans le bloc location. Cela n'est pas possible ici parce que keepalive n'est disponible que sous upstream.

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

Éditer: il s'avère que nginx n'est pas adapté à ce cas d'utilisation. Un autre proxy keepalive (en amont) devrait être utilisé. Une alternative personnalisée à Node.js est in ma réponse . Jusqu'à présent, je n'ai trouvé aucune autre solution efficace.

29
rinu

Les versions antérieures de nginx (antérieures à 1.1.4), qui exploitaient déjà un grand nombre des sites Web les plus visités dans le monde (et certains le sont encore de nos jours, si l’on en croit les en-têtes du serveur), ne prenaient même pas en charge keepalive du côté upstream , car cela présente très peu d'avantages dans le centre de données paramètre, sauf si vous avez une latence non négligeable entre vos différents hôtes; voir https://serverfault.com/a/883019/110020 pour obtenir des explications.

Fondamentalement, à moins que vous ne sachiez que vous avez spécifiquement besoin de keepalive entre votre amont et votre front-end, il est probable que cela rendra votre architecture moins résiliente et moins bien lotie.

(Notez que votre solution actuelle est également incorrecte, car une modification de l'adresse IP restera également non détectée, car vous ne résolvez que le nom d'hôte lors du rechargement de la configuration uniquement; ainsi, même si nginx démarre, il ne fonctionnera pratiquement plus une fois que les adresses IP des serveurs en amont changent.)

Solutions possibles, choisissez-en une:

  • La meilleure solution semblerait simplement se débarrasser de upstreamkeepalive probablement inutile dans un environnement de centre de données et utiliser des variables avec proxy_pass pour la mise à jour. résolution DNS actuelle pour chaque demande (nginx est toujours assez intelligent pour continuer à mettre en cache de telles résolutions)

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

  • Enfin, essayez d’utiliser une variable set et/ou une map pour spécifier les serveurs dans upstream; cela n'est ni confirmé ni nié avoir été mis en œuvre; Par exemple, cela peut ou peut ne pas fonctionner.

7
cnst

Une alternative est d'écrire un nouveau service qui ne fait que ce que je veux. Ce qui suit remplace nginx pour le proxy des connexions https à l’aide de 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" qui équivaut à: curl https://test.rinu.test/request_uri

2
rinu

Une solution possible consiste à impliquer un cache DNS local. Il peut s'agir d'un serveur DNS local comme Bind ou Dnsmasq (avec une configuration astucieuse, notez que nginx peut également utiliser serveur DNS spécifié à la place de la valeur par défaut du système), ou tout simplement en conservant le cache dans hosts fichier.

Il semble que l'utilisation du fichier hosts avec certains scripts est un moyen assez simple. Le fichier hosts doit être ajouté aux parties statique et dynamique (cat hosts.static hosts.dynamic > hosts), et la partie dynamique doit être générée (et mise à jour) automatiquement par un script.

Il serait peut-être judicieux de vérifier de temps en temps les noms d’hôte pour changer d’adresse IP, de mettre à jour le fichier hosts et de recharger la configuration dans nginx en cas de modification. Si un nom d’hôte ne peut pas être résolu, l’ancienne IP ou une adresse IP par défaut (telle que 127.0.1.9) doit être utilisée.

Si vous n'avez pas besoin des noms d'hôte dans le fichier de configuration nginx (c'est-à-dire, les adresses IP suffisent), la section upstream avec les adresses IP (noms d'hôtes résolus) peut être générée par un script et inclus dans nginx config - et pas besoin de toucher le fichier hosts dans un tel cas.

2
ruvim

Je mets le paramètre de résolution 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;
}
1
Bruno Paiuca