web-dev-qa-db-ja.com

リバースプロキシするときにnginxがアップストリームのホスト名を渡すようにする

ホスト名でいくつかのDockerコンテナーを実行します。

web1.local web2.local web3.local

Nginxによるホスト名に基づいて行われるこれらへのルーティング。このセットアップの前にプロキシがあり(インターネットに接続された別のマシン上)、アップストリームを次のように定義します。

    upstream main {
      server web1.local:80;
      server web2.local:80;
      server web3.local:80;
    }

そして実際の仮想ホストの説明:

    server {
      listen 80;
      server_name example.com;
      location / {
        proxy_pass http://main;
      }
    }

現在、コンテナは「web1.local」ではなく「main」というホスト名を受け取るため、リクエストに適切に応答しません。

質問:リクエストをプロキシするときに、Host:ヘッダーで上流のサーバーグループの名前ではなく上流のサーバーの名前を渡すようにnginxに指示するにはどうすればよいですか?

94
pavel_karoukin

実際には、proxy_set_headerを介してそれを行うことができます。

詳細についてはこちらをご覧ください: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header またはこちらの使用例をご覧ください: https://stackoverflow.com/questions/12847771/configure-nginx-with-proxy-pass

上記の投稿された構成に動的なアプローチを含めました:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $Host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

以下は、静的ホスト名の例です。

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            www.example.com;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}
115
Jens Bradler

同じ問題があり、2つのレベルのプロキシを使用して最終的に解決しました。これがあなたの状況にどう対処できるかです(私は思う):

server {
  listen      8001 default_server;
  server_name web1.example.com;
  location / {
    proxy_pass       http://web1.local:80;
    proxy_set_header Host web1.local:80;
  }
}

server {
  listen      8002 default_server;
  server_name web2.example.com;
  location / {
    proxy_pass       http://web2.local:80;
    proxy_set_header Host web2.local:80;
  }
}

server {
  listen      8003 default_server;
  server_name web3.example.com;
  location / {
    proxy_pass       http://web3.local:80;
    proxy_set_header Host web3.local:80;
  }
}

upstream main {
  server 127.0.0.1:8001;
  server 127.0.0.1:8002;
  server 127.0.0.1:8003;
}

server {
  listen      80;
  server_name example.com;
  location / {
    proxy_pass http://main;
  }
}

ご覧のとおり、トリックは、各サーバーの適切なホストを書き換えることにより、サーバーをプロキシする特定のポートに応答するローカルサーバーを作成することです。次に、このローカルサーバーをアップストリームで使用し、最後にそのアップストリームを実際のプロキシで使用できます。

28
ncenerar

したがって、nginxのすべてのドキュメントを読むことから(上流モジュールのコードを実際に解析できなかった=()私はこの粗雑なソリューションを思いつきました。残念ながら、このソリューションは失敗したホストを追跡せず、ランダムなホストを選択してリクエストをリダイレクトします。そのため、すべてのバックエンドが実行されていることを確認するために、何らかの監視をセットアップする必要があります。

server {
        listen 80;
        server_name example.com;
        resolver 127.0.0.1;

        location / {
                set $upstream "";
                rewrite_by_lua '
                        local upstreams = {
                                "http://web1.dokku.localdomain",
                                "http://web2.dokku.localdomain",
                                "http://web3.dokku.localdomain",
                                "http://web4.dokku.localdomain"
                        }
                        ngx.var.upstream = upstreams[ math.random( #upstreams ) ] 
                ';
                proxy_pass $upstream;
        }
}
3
pavel_karoukin

次のように、上流のaddrを個別のヘッダーとして渡します

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $Host;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Upstream      $upstream_addr;
  }
}

試してみたら?

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $upstream_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Host          $Host;
  }
}
3
dalore

目標は理にかなっているように見えますが、 nginxはHost:ヘッダーをアップストリームと一致するように変更しません 。代わりに、DNSでupstreamドメイン名をCNAMEのように扱います-IPアドレスにアクセスする方法として。

要求ヘッダー(および本文)は、アップストリームが選択される前に修正されます。特定のアップストリームが応答しないことが判明した場合、アップストリームは要求の途中で変更される可能性がありますが、要求は変更されません。

2
GreenReaper

うーん。私は同様の設定をしています

location / {
    ... 
    proxy_set_header X-Forwarded-Host $http_Host;
    proxy_pass ...;
}

の用法 $http_Host(着信リクエストのHTTPホストヘッダー)ではなく$Host(サーバーのホスト名の構成)を使用すると、テストでは、クライアントから渡された同じHostヘッダーがアップストリームに渡されます。

https://stackoverflow.com/questions/14352690/change-Host-header-in-nginx-reverse-proxy も参照してください。

2
lyngvi

他の人々はすでにスクリプト変数($ upstreamなど)を使用して投稿しているので、好きなように設定できます。これにより、追加のヘッダーハッキングなしで問題が修正されます。

値が条件付きでない場合(名前に$が含まれていない場合)プロキシパスハンドラーの脅威スクリプト変数は別の方法で設定フェーズで上流に戻され、後で使用されます。

この問題を省略し、(無料バージョン)アップストリームの最大の利点を得る簡単な方法は、Split_Clientsのようなものを使用することです。

split_clients $request_uri $my_upstream {
              33%          server1.domainX.com;
              33%          server2.domainX.com;
# Always use DOT at end entry if you wonder why, read the SC code.
              *            server3.domainX.com;  
}
location / {
    ... 
    proxy_pass http://$my_upstream;
}

上記の例は、アップストリームとほとんど同じです。 chash_map_module など、マッピングを行う他のモジュールが存在しますが、それらはツリーの外にあるため、独自に構築する必要があります。これは、一部のユースケースでは不可能です/

0
Mazeryt