Skip to content

Commit

Permalink
Support exposing multiple ports
Browse files Browse the repository at this point in the history
  • Loading branch information
BobSilent committed Mar 3, 2020
1 parent 941fd63 commit 745ea7b
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 58 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ If your container exposes multiple ports, nginx-proxy will default to the servic
[1]: https://github.com/jwilder/docker-gen
[2]: http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/
`VIRTUAL_PORT` supports a comma seperated list of key value pair syntax `hostPort:containerPort` or `containerPort` only. E.g. `VIRTUAL_PORT=443:80,3000,4000:8086`, this will expose containter port 80 to host port 443, 3000 to 3000 and container port 8086 to host port 4000.
### Multiple Hosts
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.
Expand Down
134 changes: 76 additions & 58 deletions nginx.tmpl
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}

{{ $external_http_port := coalesce $.Env.HTTP_PORT "80" }}
{{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }}

{{ 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 }}
server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
# {{ .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 }} down;
{{ else }}
server 127.0.0.1 down;
server 127.0.0.1 down;
{{ end }}
{{ end }}

Expand Down Expand Up @@ -141,9 +138,9 @@ proxy_set_header Proxy "";
{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen {{ $external_http_port }};
listen 80;
{{ if $enable_ipv6 }}
listen [::]:{{ $external_http_port }};
listen [::]:80;
{{ end }}
access_log /var/log/nginx/access.log vhost;
return 503;
Expand All @@ -152,9 +149,9 @@ server {
{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
listen {{ $external_https_port }} ssl http2;
listen 443 ssl http2;
{{ if $enable_ipv6 }}
listen [::]:{{ $external_https_port }} ssl http2;
listen [::]:443 ssl http2;
{{ end }}
access_log /var/log/nginx/access.log vhost;
return 503;
Expand All @@ -170,37 +167,6 @@ server {

{{ $host := trim $host }}
{{ $is_regexp := hasPrefix "~" $host }}
{{ $upstream_name := when $is_regexp (sha1 $host) $host }}

# {{ $host }}
upstream {{ $upstream_name }} {

{{ range $container := $containers }}
{{ $addrLen := len $container.Addresses }}

{{ 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) }}
{{ end }}
{{ else }}
# Cannot connect to network of this container
server 127.0.0.1 down;
{{ end }}
{{ end }}
{{ end }}
{{ end }}
}

{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
Expand Down Expand Up @@ -239,14 +205,62 @@ upstream {{ $upstream_name }} {

{{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}

{{/* Define an upstream name either by host or if is regex by sha1 */}}
{{ $upstream_name := when $is_regexp (sha1 $host) $host }}

{{/* Set a variablename for mapping host:port to an upstream name */}}
{{ $proxypass_variable := printf "%s%s" "$upstream_" (replace $upstream_name "." "_" -1) }}

{{ $default_port := when (or (not $is_https) (eq $https_method "noredirect")) "80" "443" }}

# {{ $host }}
{{ $hostPortContainersMap := groupByMultiKeyValuePairs $containers "Env.VIRTUAL_PORT" "," ":" $default_port }}
map $host:$server_port {{ $proxypass_variable }} {
{{ range $port, $containers := $hostPortContainersMap }}
{{ $host }}:{{ $port }} {{ $upstream_name }}_{{ $port }};
{{ end }}
}

{{ range $port, $portContainers := $hostPortContainersMap }}
upstream {{ $upstream_name }}_{{ $port }} {
{{ range $container := $portContainers }}
{{ $portHostPortContainerMap := splitKeyValuePairs $container.Env.VIRTUAL_PORT "," ":" $default_port }}
{{ $addrLen := len $container.Addresses }}

{{ 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 }}
{{ $containerPort := coalesce (index $portHostPortContainerMap $port) "80" }}
{{ $address := where $container.Addresses "Port" $containerPort | first }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{ end }}
{{ else }}
# Cannot connect to network of this container knownNetwork={{ $knownNetwork.Name }} containerNetwork={{ $containerNetwork.Name }}
server 127.0.0.1 down;
{{ end }}
{{ end }}
{{ end }}
{{ end }}
}
{{ end }}


{{ if $is_https }}

{{ if eq $https_method "redirect" }}
server {
server_name {{ $host }};
listen {{ $external_http_port }} {{ $default_server }};
listen 80 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_http_port }} {{ $default_server }};
listen [::]:80 {{ $default_server }};
{{ end }}
access_log /var/log/nginx/access.log vhost;

Expand All @@ -267,10 +281,12 @@ server {

server {
server_name {{ $host }};
listen {{ $external_https_port }} ssl http2 {{ $default_server }};
{{ range $port, $containers := $hostPortContainersMap }}
listen {{ $port }} ssl http2 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
listen [::]:{{ $port }} ssl http2 {{ $default_server }};
{{ end }}
{{ end }}
access_log /var/log/nginx/access.log vhost;

{{ if eq $network_tag "internal" }}
Expand Down Expand Up @@ -310,15 +326,15 @@ server {
location / {
{{ if eq $proto "uwsgi" }}
include uwsgi_params;
uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
uwsgi_pass {{ trim $proto }}://{{ trim $proxypass_variable }};
{{ else if eq $proto "fastcgi" }}
root {{ trim $vhost_root }};
include fastcgi_params;
fastcgi_pass {{ trim $upstream_name }};
fastcgi_pass {{ trim $proxypass_variable }};
{{ else if eq $proto "grpc" }}
grpc_pass {{ trim $proto }}://{{ trim $upstream_name }};
grpc_pass {{ trim $proto }}://{{ trim $proxypass_variable }};
{{ else }}
proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
proxy_pass {{ trim $proto }}://{{ trim $proxypass_variable }};
{{ end }}

{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
Expand All @@ -339,10 +355,12 @@ server {

server {
server_name {{ $host }};
listen {{ $external_http_port }} {{ $default_server }};
{{ range $port, $containers := $hostPortContainersMap }}
listen {{ $port }} {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:80 {{ $default_server }};
listen [::]:{{ $port }} {{ $default_server }};
{{ end }}
{{ end }}
access_log /var/log/nginx/access.log vhost;

{{ if eq $network_tag "internal" }}
Expand Down Expand Up @@ -384,9 +402,9 @@ server {
{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name {{ $host }};
listen {{ $external_https_port }} ssl http2 {{ $default_server }};
listen 443 ssl http2 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
listen [::]:443 ssl http2 {{ $default_server }};
{{ end }}
access_log /var/log/nginx/access.log vhost;
return 500;
Expand All @@ -397,4 +415,4 @@ server {
{{ end }}

{{ end }}
{{ end }}
{{ end }}

0 comments on commit 745ea7b

Please sign in to comment.