Jelajahi Sumber

Merge pull request #1609 from pini-gh/pini-1132

Honor VIRTUAL_PORT + DEBUG flag + fallback entry
Nicolas Duchon 4 tahun lalu
induk
melakukan
49b2b5cd97

+ 37 - 6
README.md

@@ -82,16 +82,20 @@ NginX does not support scoped IPv6 resolvers. In [docker-entrypoint.sh](./docker
 
 By default, docker uses IPv6-to-IPv4 NAT. This means all client connections from IPv6 addresses will show docker's internal IPv4 host address. To see true IPv6 client IP addresses, you must [enable IPv6](https://docs.docker.com/config/daemon/ipv6/) and use [ipv6nat](https://github.com/robbertkl/docker-ipv6nat). You must also disable the userland proxy by adding `"userland-proxy": false` to `/etc/docker/daemon.json` and restarting the daemon.
 
-### Multiple Ports
+### Multiple Hosts
 
-If your container exposes multiple ports, nginx-proxy will default to the service running on port 80.  If you need to specify a different port, you can set a VIRTUAL_PORT env var to select a different one.  If your container only exposes one port and it has a VIRTUAL_HOST env var set, that port will be selected.
+If you need to support multiple virtual hosts for a container, you can separate each entry with commas.  For example, `foo.bar.com,baz.bar.com,bar.com` and each host will be setup the same.
 
-  [1]: https://github.com/jwilder/docker-gen
-  [2]: http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/
+### Virtual Ports
 
-### Multiple Hosts
+When your container exposes only one port, nginx-proxy will default to this port, else to port 80.
 
-If you need to support multiple virtual hosts for a container, you can separate each entry with commas.  For example, `foo.bar.com,baz.bar.com,bar.com` and each host will be setup the same.
+If you need to specify a different port, you can set a `VIRTUAL_PORT` env var to select a different one. This variable cannot be set to more than one port.
+
+For each host defined into `VIRTUAL_HOST`, the associated virtual port is retrieved by order of precedence:
+1. From the `VIRTUAL_PORT` environment variable
+1. From the container's exposed port if there is only one
+1. From the default port 80 when none of the above methods apply
 
 ### Wildcard Hosts
 
@@ -424,6 +428,33 @@ will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRT
 #### Per-VIRTUAL_HOST `server_tokens` configuration
 Per virtual-host `servers_tokens` directive can be configured by passing appropriate value to the `SERVER_TOKENS` environment variable. Please see the [nginx http_core module configuration](https://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens) for more details.
 
+### Troubleshooting
+
+In case you can't access your VIRTUAL_HOST, set `DEBUG=true` in the client container's environment and have a look at the generated nginx configuration file `/etc/nginx/conf.d/default`:
+
+```
+$ docker exec <nginx-proxy-instance> cat /etc/nginx/conf.d/default
+```
+Especially at `upstream` definition blocks which should look like:
+
+```
+# foo.example.com
+upstream foo.example.com {
+	## Can be connected with "my_network" network
+	# Exposed ports: [{   <exposed_port1>  tcp } {   <exposed_port2>  tcp } ...]
+	# Default virtual port: <exposed_port|80>
+	# VIRTUAL_PORT: <VIRTUAL_PORT>
+	# foo
+	server 172.18.0.9:<Port>;
+	# Fallback entry
+	server 127.0.0.1 down;
+}
+```
+
+The effective `Port` is retrieved by order of precedence:
+1. From the `VIRTUAL_PORT` environment variable
+1. From the container's exposed port if there is only one
+1. From the default port 80 when none of the above methods apply
 
 ### Contributing
 

+ 25 - 21
nginx.tmpl

@@ -2,24 +2,25 @@
 
 {{ $external_http_port := coalesce $.Env.HTTP_PORT "80" }}
 {{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }}
+{{ $debug_all := $.Env.DEBUG }}
 
 {{ define "upstream" }}
 	{{ if .Address }}
 		{{/* 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 .Address.HostPort }}
-			# {{ .Container.Node.Name }}/{{ .Container.Name }}
+	# {{ .Container.Node.Name }}/{{ .Container.Name }}
 			server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
 		{{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}}
 		{{ else if .Network }}
-			# {{ .Container.Name }}
-			server {{ .Network.IP }}:{{ .Address.Port }};
+	# {{ .Container.Name }}
+	server {{ .Network.IP }}:{{ .Address.Port }};
 		{{ end }}
 	{{ else if .Network }}
-		# {{ .Container.Name }}
+	# {{ .Container.Name }}
 		{{ if .Network.IP }}
-			server {{ .Network.IP }} down;
+	server {{ .Network.IP }}:{{ .VirtualPort }};
 		{{ else }}
-			server 127.0.0.1 down;
+	# /!\ No IP for this network!
 		{{ end }}
 	{{ end }}
 
@@ -180,29 +181,32 @@ server {
 upstream {{ $upstream_name }} {
 
 {{ range $container := $containers }}
-	{{ $addrLen := len $container.Addresses }}
-
+	{{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }}
+	{{/* 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 }}
 	{{ range $knownNetwork := $CurrentContainer.Networks }}
 		{{ range $containerNetwork := $container.Networks }}
 			{{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
-				## Can be connected with "{{ $containerNetwork.Name }}" network
-
-				{{/* If only 1 port exposed, use that */}}
-				{{ if eq $addrLen 1 }}
-					{{ $address := index $container.Addresses 0 }}
-					{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
-				{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
-				{{ else }}
-					{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
-					{{ $address := where $container.Addresses "Port" $port | first }}
-					{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
+	## Can be connected with "{{ $containerNetwork.Name }}" network
+				{{ $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }}
+				{{ $address := where $container.Addresses "Port" $port | first }}
+				{{ if $debug }}
+	# Exposed ports: {{ $container.Addresses }}
+	# Default virtual port: {{ $defaultPort }}
+	# VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }}
+					{{ if not $address }}
+	# /!\ Virtual port not exposed
+					{{ end }}
 				{{ end }}
+				{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork "VirtualPort" $port) }}
 			{{ else }}
-				# Cannot connect to network of this container
-				server 127.0.0.1 down;
+	# Cannot connect to network '{{ $containerNetwork.Name }}' of this container
 			{{ end }}
 		{{ end }}
 	{{ end }}
+	{{/* nginx-proxy/nginx-proxy#1105 */}}
+	# Fallback entry
+	server 127.0.0.1 down;
 {{ end }}
 }
 

+ 12 - 0
test/test_debug/test_proxy-debug-flag.py

@@ -0,0 +1,12 @@
+import pytest
+import re
+
+def test_debug_info_is_present_in_nginx_generated_conf(docker_compose, nginxproxy):
+    conf = nginxproxy.get_conf().decode('ASCII')
+    assert re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \}\]", conf) or \
+           re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \}\]", conf)
+    assert re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+82\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+83\s+tcp \}\]", conf) or \
+           re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+83\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+82\s+tcp \}\]", conf)
+    assert "# Default virtual port: 80" in conf
+    assert "# VIRTUAL_PORT: 82" in conf
+    assert conf.count("# /!\ Virtual port not exposed") == 1

+ 27 - 0
test/test_debug/test_proxy-debug-flag.yml

@@ -0,0 +1,27 @@
+web1:
+  image: web
+  expose:
+    - "80"
+    - "81"
+  environment:
+    WEB_PORTS: "80 81"
+    VIRTUAL_HOST: "web1.nginx-proxy.tld"
+    VIRTUAL_PORT: "82"
+
+web2:
+  image: web
+  expose:
+    - "82"
+    - "83"
+  environment:
+    WEB_PORTS: "82 83"
+    VIRTUAL_HOST: "web2.nginx-proxy.tld"
+    VIRTUAL_PORT: "82"
+
+sut:
+  image: nginxproxy/nginx-proxy:test
+  volumes:
+    - /var/run/docker.sock:/tmp/docker.sock:ro
+    - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
+  environment:
+    DEBUG: "true"

+ 8 - 0
test/test_debug/test_server-debug-flag.py

@@ -0,0 +1,8 @@
+import pytest
+import re
+
+def test_debug_info_is_present_in_nginx_generated_conf(docker_compose, nginxproxy):
+    conf = nginxproxy.get_conf().decode('ASCII')
+    assert re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \}\]", conf) or \
+           re.search(r"# Exposed ports: \[\{\d+\.\d+\.\d+\.\d+\s+81\s+tcp \} \{\d+\.\d+\.\d+\.\d+\s+80\s+tcp \}\]", conf)
+    assert conf.count("# Exposed ports: [{") == 1

+ 26 - 0
test/test_debug/test_server-debug-flag.yml

@@ -0,0 +1,26 @@
+web1:
+  image: web
+  expose:
+    - "80"
+    - "81"
+  environment:
+    WEB_PORTS: "80 81"
+    VIRTUAL_HOST: "web1.nginx-proxy.tld"
+    VIRTUAL_PORT: "82"
+    DEBUG: "true"
+
+web2:
+  image: web
+  expose:
+    - "82"
+    - "83"
+  environment:
+    WEB_PORTS: "82 83"
+    VIRTUAL_HOST: "web2.nginx-proxy.tld"
+    VIRTUAL_PORT: "82"
+
+sut:
+  image: nginxproxy/nginx-proxy:test
+  volumes:
+    - /var/run/docker.sock:/tmp/docker.sock:ro
+    - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro

+ 8 - 0
test/test_multiple-ports/test_VIRTUAL_PORT-single-different-from-single-port.py

@@ -0,0 +1,8 @@
+import pytest
+import re
+
+
+def test_answer_is_served_from_virtual_port_which_is_ureachable(docker_compose, nginxproxy):
+    r = nginxproxy.get("http://web.nginx-proxy.tld/port")
+    assert r.status_code == 502
+    assert re.search(r"\n\s+server \d+\.\d+\.\d+\.\d+:90;\n", nginxproxy.get_conf().decode('ASCII'))

+ 15 - 0
test/test_multiple-ports/test_VIRTUAL_PORT-single-different-from-single-port.yml

@@ -0,0 +1,15 @@
+web:
+  image: web
+  expose:
+    - "81"
+  environment:
+    WEB_PORTS: "81"
+    VIRTUAL_HOST: "web.nginx-proxy.tld"
+    VIRTUAL_PORT: "90"
+
+
+sut:
+  image: nginxproxy/nginx-proxy:test
+  volumes:
+    - /var/run/docker.sock:/tmp/docker.sock:ro
+    - ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro