2
0
Эх сурвалжийг харах

fix: Generate at most one `server` directive per container

Richard Hansen 2 жил өмнө
parent
commit
6162427c45

+ 19 - 4
nginx.tmpl

@@ -118,6 +118,7 @@ upstream {{ .Upstream }} {
     # Container: {{ $container.Name }}
         {{- /* If only 1 port exposed, use that as a default, else 80 */}}
         {{- $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }}
+        {{- $ip := "" }}
         {{- $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }}
         {{- $addr_obj := where $container.Addresses "Port" $port | first }}
     #     Exposed ports:{{ range $container.Addresses }} {{ .Port }}/{{ .Proto }}{{ else }} (none){{ end }}
@@ -141,23 +142,37 @@ upstream {{ .Upstream }} {
     #         {{ $containerNetwork.Name }} (unreachable)
                 {{- continue }}
             {{- end }}
+            {{- /*
+                 * Do not emit multiple `server` directives for this container
+                 * if it is reachable over multiple networks.  This avoids
+                 * accidentally inflating the effective round-robin weight of
+                 * this container due to the redundant upstreams that nginx sees
+                 * as belonging to distinct servers.
+                 */}}
+            {{- if $ip }}
+    #         {{ $containerNetwork.Name }} (ignored; reachable but redundant)
+                {{- continue }}
+            {{- end }}
     #         {{ $containerNetwork.Name }} (reachable)
             {{- /*
                  * If we got the containers from swarm and this container's
                  * port is published to host, use host IP:PORT.
                  */}}
             {{- if and $container.Node.ID $addr_obj $addr_obj.HostPort }}
-                {{- $server_found = true }}
-    server {{ $container.Node.Address.IP }}:{{ $addr_obj.HostPort }};
+                {{- $ip = $container.Node.Address.IP }}
+                {{- $port = $addr_obj.HostPort }}
             {{- else if and $containerNetwork $containerNetwork.IP }}
-                {{- $server_found = true }}
-    server {{ $containerNetwork.IP }}:{{ $port }};
+                {{- $ip = $containerNetwork.IP }}
             {{- else }}
     #             /!\ No IP for this network!
             {{- end }}
         {{- else }}
     #         (none)
         {{- end }}
+        {{- if $ip }}
+            {{- $server_found = true }}
+    server {{ $ip }}:{{ $port }};
+        {{- end }}
     {{- end }}
     {{- /* nginx-proxy/nginx-proxy#1105 */}}
     {{- if not $server_found }}

+ 16 - 3
test/test_multiple-networks.py

@@ -1,15 +1,28 @@
+import re
+
 import pytest
 
+
 def test_unknown_virtual_host(docker_compose, nginxproxy):
     r = nginxproxy.get("http://nginx-proxy/")
     assert r.status_code == 503
 
 def test_forwards_to_web1(docker_compose, nginxproxy):
     r = nginxproxy.get("http://web1.nginx-proxy.local/port")
-    assert r.status_code == 200   
+    assert r.status_code == 200
     assert r.text == "answer from port 81\n"
 
 def test_forwards_to_web2(docker_compose, nginxproxy):
     r = nginxproxy.get("http://web2.nginx-proxy.local/port")
-    assert r.status_code == 200   
-    assert r.text == "answer from port 82\n"
+    assert r.status_code == 200
+    assert r.text == "answer from port 82\n"
+
+def test_multipath(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://web3.nginx-proxy.test/port")
+    assert r.status_code == 200
+    assert r.text == "answer from port 83\n"
+    cfg = nginxproxy.get_conf().decode()
+    lines = cfg.splitlines()
+    web3_server_lines = [l for l in lines
+                         if re.search(r'(?m)^\s*server\s+[^\s]*:83;\s*$', l)]
+    assert len(web3_server_lines) == 1

+ 15 - 0
test/test_multiple-networks.yml

@@ -3,6 +3,8 @@ version: '2'
 networks:
   net1: {}
   net2: {}
+  net3a: {}
+  net3b: {}
 
 services:
   nginx-proxy:
@@ -12,6 +14,8 @@ services:
     networks:
       - net1
       - net2
+      - net3a
+      - net3b
 
   web1:
     image: web
@@ -32,3 +36,14 @@ services:
       VIRTUAL_HOST: web2.nginx-proxy.local
     networks:
       - net2
+
+  web3:
+    image: web
+    expose:
+      - "83"
+    environment:
+      WEB_PORTS: 83
+      VIRTUAL_HOST: web3.nginx-proxy.test
+    networks:
+      - net3a
+      - net3b