Buat nginx untuk memberikan nama host dari upstream saat melakukan reverseeproxying

89

Saya menjalankan beberapa wadah buruh pelabuhan dengan nama host:

web1.local web2.local web3.local

Routing ke ini dilakukan berdasarkan hostname oleh nginx. Saya memiliki proxy di depan pengaturan ini (pada mesin yang berbeda terhubung ke internet) di mana saya mendefinisikan hulu sebagai:

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

Dan deskripsi host virtual aktual:

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

Sekarang, karena kontainer menerima nama host "utama" alih-alih "web1.local", mereka tidak menanggapi permintaan dengan benar.

Pertanyaan: bagaimana saya bisa memberi tahu nginx untuk memberikan nama server upstream alih-alih nama grup server upstream di Host: header ketika meminta proxy?

pavel_karoukin
sumber
3
Saya pikir Anda tidak bisa. Mengapa Anda tidak mengatur server backend Anda untuk menanggapi main atau example.com? Bukannya backend tidak tahu siapa itu . Kebalikannya sudah memungkinkan: Host $ host proxy_set_header; akan mengganti variabel Host apa pun yang kembali dari hulu dengan nama host dari permintaan awal.
Andrew Domaszek
Yang harus dilakukan adalah memperbaiki aplikasi.
Michael Hampton

Jawaban:

109

Sebenarnya Anda bisa melakukannya melalui proxy_set_header.

Untuk lebih jelasnya lihat di sini: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header atau lihat contoh kasus penggunaan di sini: https://stackoverflow.com/questions/12847771/configure-nginx- dengan-proxy-pass

Saya telah memasukkan pendekatan dinamis ke dalam konfigurasi yang Anda posting di atas:

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

Berikut ini adalah contoh dengan nama host statis:

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;
  }
}
Jens Bradler
sumber
7
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; tampaknya lebih baik
sivann
1
@ Pavel: mengerti. Sebenarnya saya juga melakukan beberapa penelitian dan beberapa tes. Tampaknya tidak ada pendekatan langsung untuk memenuhi kebutuhan Anda. Jadi, bahkan solusi "bastardized" adalah solusi. Saya tidak suka bertanya mengapa Anda ingin melakukan ini. Saya cukup yakin Anda punya alasan. :-)
Jens Bradler
@JensBradler Anda sepertinya lebih ahli daripada saya, bisakah Anda memberi tahu saya apa pendapat Anda tentang solusi saya? Saya ingin melakukan hal yang sama karena saya menjalankan dua salinan dari website saya dari dua account pada ISP saya: site1.myisp.comdan site2.myisp.comdan mereka hanya menanggapi nama mereka masing-masing. Saya, sekarang, memiliki nama domain saya dan saya ingin menggunakan situs web ISP saya untuk memuat saldo server saya. Bukankah itu alasan yang bagus? Terima kasih banyak;)
ncenerar
1
@ncenerar Anda bisa melakukan itu tetapi ini akan membawa Anda ke satu titik kegagalan: penyeimbang beban. Jika ini untuk penyeimbangan beban (bukan redundansi), Anda juga dapat menggunakan penyeimbangan beban berbasis DNS bersama dengan failover DNS.
Jens Bradler
2
Jawaban ini mencerminkan saran dari blog resmi .
Bernard Rosset
28

Saya memiliki masalah yang sama dan saya akhirnya menyelesaikannya dengan menggunakan dua tingkat proxy. Inilah yang dapat Anda lakukan untuk situasi Anda (saya pikir):

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

Seperti yang Anda lihat, triknya adalah membuat server lokal merespons port tertentu yang akan mem-proxy server dengan menulis ulang Host yang tepat untuk setiap server. Kemudian, Anda dapat menggunakan server lokal ini di hulu Anda dan akhirnya menggunakannya hulu di proksi nyata.

ncenerar
sumber
Saya awalnya menggunakan pendekatan Lua, tetapi sekarang beralih sepenuhnya ke HAProxy yang memungkinkan untuk melakukan apa yang saya inginkan dengan konfigurasi standar.
pavel_karoukin
3

Jadi dari membaca semua dokumentasi untuk nginx (saya tidak dapat benar-benar menguraikan kode untuk modul hulu = () Saya datang dengan solusi bastardized ini. Sayangnya solusi ini tidak melacak host yang gagal, tetapi cukup pilih satu acak dan arahkan permintaan ke sana. Jadi saya harus mengatur semacam pemantauan untuk memastikan semua backend berjalan.

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;
        }
}
pavel_karoukin
sumber
2

Kami meneruskan addr upstream sebagai header terpisah seperti ini

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

Bagaimana jika Anda mencobanya?

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;
  }
}
dalore
sumber
2

Meskipun tujuannya tampak logis, nginx tidak akan mengubah header Host: agar sesuai dengan hulu . Sebaliknya, ia memperlakukan upstreamnama domain seperti CNAMEdi DNS - sebagai cara untuk mendapatkan alamat IP.

Tajuk permintaan (dan isi) diperbaiki sebelum hulu dipilih. Hulu dapat mengubah pertengahan permintaan jika hulu tertentu ternyata tidak responsif, tetapi permintaan tidak berubah.

GreenReaper
sumber
0

Hmm. Saya memiliki pengaturan yang sama, di mana saya hanya selesai

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

Penggunaan $http_host(tajuk Host HTTP dari permintaan masuk) di sini alih-alih $host(konfigurasi nama host server) menyebabkan tajuk Host yang sama yang dilewatkan oleh klien untuk diteruskan ke hulu, dalam pengujian saya.

Lihat juga https://stackoverflow.com/questions/14352690/change-host-header-in-nginx-reverse-proxy .

lyngvi
sumber
0

Karena orang lain sudah memposting menggunakan variabel skrip (seperti $ hulu), Anda dapat mengaturnya sesuka Anda, dan itu akan memperbaiki masalah, tanpa peretasan header tambahan.

Variabel skrip ancaman penangan Proxy Pass dengan cara yang berbeda, jika nilai tidak bersyarat (tidak memiliki $ pada nama) didukung ke hulu pada tahap konfigurasi dan digunakan nanti.

Cara sederhana untuk menghilangkan masalah ini, dan memiliki kelebihan (versi gratis) hulu akan menggunakan sesuatu seperti 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;
}

Contoh di atas terlihat hampir sama dengan hulu. Ada modul-modul lain yang melakukan pemetaan, yaitu chash_map_module , tetapi karena mereka keluar dari pohon, Anda harus membuatnya sendiri, yang tidak mungkin dilakukan untuk beberapa kasus penggunaan /

Mazeryt
sumber