ทำ nginx เพื่อส่งชื่อโฮสต์ของ upstream เมื่อทำการย้อนหน้า


89

ฉันเรียกใช้คอนเทนเนอร์นักเทียบท่าที่มีชื่อโฮสต์:

web1.local web2.local web3.local

การกำหนดเส้นทางไปยังสิ่งเหล่านี้ทำได้ตามชื่อโฮสต์โดย nginx ฉันมีพร็อกซีที่หน้าการตั้งค่านี้ (บนเครื่องที่แตกต่างกันที่เชื่อมต่อกับอินเทอร์เน็ต) ที่ฉันกำหนด upstream เป็น:

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

ขณะนี้เนื่องจากคอนเทนเนอร์ได้รับชื่อโฮสต์ "main" แทน "web1.local" จึงไม่ตอบสนองต่อคำขออย่างถูกต้อง

คำถาม: ฉันจะบอก nginx ให้ส่งชื่อของเซิร์ฟเวอร์ upstream แทนที่จะเป็นชื่อของกลุ่ม upstream ของเซิร์ฟเวอร์ใน Host: header เมื่อร้องขอการมอบฉันทะได้อย่างไร


3
ฉันไม่คิดว่าคุณจะทำได้ ทำไมคุณไม่ตั้งค่าเซิร์ฟเวอร์แบ็คเอนด์ให้ตอบสนองต่อ main หรือ example.com มันไม่เหมือนกับว่าแบ็คเอนด์ไม่รู้ว่ามันคือใคร การย้อนกลับเป็นไปได้อย่างง่ายดาย: proxy_set_header Host $ host; จะแทนที่ตัวแปรโฮสต์ใด ๆ ที่กลับมาจากอัปสตรีมด้วยชื่อโฮสต์จากคำขอต้นฉบับ
Andrew Domaszek

สิ่งที่ต้องทำคือแก้ไขแอปพลิเคชัน
Michael Hampton

คำตอบ:


109

ที่จริงคุณสามารถทำได้ผ่าน proxy_set_header

สำหรับรายละเอียดเพิ่มเติมดูที่นี่: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_headerหรือดูตัวอย่างการใช้งานกรณีที่นี่: https://stackoverflow.com/questions/12847771/configure-nginx- กับพร็อกซีผ่าน

ฉันได้รวมวิธีแบบไดนามิกไว้ในการกำหนดค่าที่คุณโพสต์ไว้ด้านบน:

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

7
proxy_set_header X-Forwarded- สำหรับ $ proxy_add_x_forwarded_for; ดูเหมือนจะดีกว่า
sivann

1
@pavel: เข้าใจแล้ว จริงๆแล้วฉันได้ทำการวิจัยและทดสอบบ้างแล้ว ดูเหมือนว่าไม่มีวิธีการตรงเพื่อตอบสนองความต้องการของคุณ ดังนั้นแม้แต่โซลูชัน "bastardized" ก็เป็นโซลูชัน ฉันไม่ชอบที่จะถามว่าทำไมคุณถึงต้องการทำเช่นนี้ ฉันค่อนข้างแน่ใจว่าคุณมีเหตุผลของคุณ :-)
Jens Bradler

@JensBradler คุณดูเหมือนจะมีความเชี่ยวชาญมากกว่าฉันดังนั้นคุณช่วยบอกฉันได้ไหมว่าคุณคิดอย่างไรกับวิธีแก้ปัญหาของฉัน ผมต้องการที่จะทำเช่นเดียวกันเพราะผมทำงานสองสำเนาของเว็บไซต์ของฉันจากสองบัญชีของฉัน ISP: site1.myisp.comและsite2.myisp.comและพวกเขาตอบสนองต่อชื่อของตน ตอนนี้ฉันเป็นเจ้าของชื่อโดเมนของฉันและฉันต้องการใช้เว็บไซต์ ISP ของฉันเพื่อโหลดเซิร์ฟเวอร์ของฉัน นั่นเป็นเหตุผลที่ดีใช่ไหม ขอบคุณมาก;)
ncenerar

1
@ncenerar คุณสามารถทำได้ แต่สิ่งนี้จะนำคุณไปสู่จุดที่ล้มเหลวเพียงจุดเดียว: load balancer หากใช้สำหรับการทำโหลดบาลานซ์ (ไม่ใช่การทำซ้ำซ้อน) คุณสามารถใช้การทำโหลดบาลานซ์บน DNS ร่วมกับ DNS ล้มเหลวได้
Jens Bradler

2
คำตอบนี้สะท้อนให้เห็นถึงคำแนะนำของบล็อกอย่างเป็นทางการ
Bernard Rosset

28

ฉันมีปัญหาเดียวกันและในที่สุดฉันก็แก้ไขมันโดยใช้พร็อกซีสองระดับ นี่คือวิธีที่คุณสามารถทำได้สำหรับสถานการณ์ของคุณ (ฉันคิดว่า):

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

อย่างที่คุณเห็นเคล็ดลับคือการสร้างเซิร์ฟเวอร์ภายในที่ตอบสนองต่อพอร์ตเฉพาะที่จะใช้พร็อกซีเซิร์ฟเวอร์โดยเขียนโฮสต์ที่เหมาะสมสำหรับแต่ละเซิร์ฟเวอร์ใหม่ จากนั้นคุณสามารถใช้เซิร์ฟเวอร์ท้องถิ่นนี้ในอัพสตรีมของคุณและในที่สุดก็ใช้อัพสตรีมนั้นในพร็อกซีจริง


ตอนแรกฉันใช้วิธี Lua แต่ตอนนี้เปลี่ยนเป็น HAProxy อย่างสมบูรณ์ซึ่งอนุญาตให้ทำสิ่งที่ฉันต้องการด้วยการกำหนดค่ามาตรฐาน
pavel_karoukin

3

ดังนั้นจากการอ่านเอกสารทั้งหมดสำหรับ nginx (ฉันไม่สามารถแยกวิเคราะห์รหัสสำหรับ upstream module = () ฉันมาพร้อมกับวิธีแก้ปัญหา bastardized โชคไม่ดีที่วิธีนี้ไม่ติดตามโฮสต์ที่ล้มเหลว แต่เพียงแค่เลือกสุ่ม ดังนั้นฉันต้องตั้งค่าการตรวจสอบบางอย่างเพื่อให้แน่ใจว่าแบ็กเอนด์ทั้งหมดทำงานอยู่

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

2

เราส่งผ่านแอดอัปสตรีมเป็นส่วนหัวแยกเช่นนี้

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

2

ในขณะที่เป้าหมายที่ดูเหมือนว่าตรรกะNginx จะไม่เปลี่ยนโฮสต์: หัวเพื่อให้ตรงกับต้นน้ำ แต่จะปฏิบัติต่อupstreamชื่อโดเมนเช่นCNAMEใน DNS - เพื่อไปยังที่อยู่ IP

ส่วนหัวคำขอ (และเนื้อหา) ได้รับการแก้ไขก่อนที่จะเลือกอัปสตรีม อัปสตรีมอาจเปลี่ยนการร้องขอกลางหากพบว่าอัพสตรีมเฉพาะนั้นไม่ตอบสนอง แต่คำขอไม่เปลี่ยนแปลง


0

อืมมม ฉันมีการตั้งค่าที่คล้ายกันซึ่งฉันเพิ่งทำไป

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

การใช้$http_host(ส่วนหัวโฮสต์ HTTP จากคำขอขาเข้า) ที่นี่แทน$host(การกำหนดค่าชื่อโฮสต์เซิร์ฟเวอร์) ทำให้ส่วนหัวโฮสต์เดียวกันที่ส่งผ่านโดยไคลเอนต์จะถูกส่งผ่านไปยังอัปสตรีมในการทดสอบของฉัน

ดูเพิ่มเติมhttps://stackoverflow.com/questions/14352690/change-host-header-in-nginx-reverse-proxy


0

ในขณะที่คนอื่นโพสต์โดยใช้ตัวแปรสคริปต์ (เช่น $ upstream) คุณสามารถตั้งค่าได้ตามต้องการและจะแก้ไขปัญหาได้โดยไม่ต้องแฮ็คส่วนหัวเพิ่มเติม

ตัวแปรสคริปต์การคุกคาม Proxy Pass handler ในวิธีอื่นหากค่าไม่เป็นไปตามเงื่อนไข (ไม่มี $ ในชื่อ) จะถูกสำรองข้อมูลไปยัง upstream บนเฟสการกำหนดค่าและใช้ในภายหลัง

วิธีง่ายๆในการละเว้นปัญหานี้และมีข้อดีที่สุดของ (เวอร์ชันฟรี) 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แต่เนื่องจากโมดูลเหล่านี้อยู่นอกต้นไม้คุณจะต้องสร้างมันด้วยตัวคุณเองซึ่งเป็นไปไม่ได้สำหรับบางกรณี /

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.