From 7ca1da8358879fe54b3861f590c1c43af7abde5e Mon Sep 17 00:00:00 2001 From: Niek <100143256+SchoNie@users.noreply.github.com> Date: Tue, 21 Mar 2023 07:49:27 +0100 Subject: [PATCH] feat: Add support for HTTP load balancing between the proxy and upstream server groups (#2173) Add initial tests Newlines Remove unused variable Co-authored-by: Richard Hansen Change comment value Co-authored-by: Richard Hansen add missing services line Co-authored-by: Richard Hansen Use deploy.replicas Remove details about choosing a load balancing method Feedback note Co-authored-by: Nicolas Duchon --- README.md | 36 ++++++++++++++++++++++++++++++++++++ nginx.tmpl | 5 +++++ test/test_loadbalancing.py | 16 ++++++++++++++++ test/test_loadbalancing.yml | 27 +++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 test/test_loadbalancing.py create mode 100644 test/test_loadbalancing.yml diff --git a/README.md b/README.md index 1ff960b..4e0d9d4 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,42 @@ docker run -d -p 80:80 -p 443:443 \ You'll need apache2-utils on the machine where you plan to create the htpasswd file. Follow these [instructions](http://httpd.apache.org/docs/2.2/programs/htpasswd.html) +### Upstream (Backend) Server HTTP Load Balancing Support + +> **Warning** +> This feature is experimental. The behavior may change (or the feature may be removed entirely) without warning in a future release, even if the release is not a new major version. If you use this feature, or if you would like to use this feature but you require changes to it first, please [provide feedback in #2195](https://github.com/nginx-proxy/nginx-proxy/discussions/2195). Once we have collected enough feedback we will promote this feature to officially supported. + +If you have multiple containers with the same `VIRTUAL_HOST` and `VIRTUAL_PATH` settings, nginx will spread the load across all of them. To change the load balancing algorithm from nginx's default (round-robin), set the `com.github.nginx-proxy.nginx-proxy.loadbalance` label on one or more of your application containers to the desired load balancing directive. See the [`ngx_http_upstream_module` documentation](https://nginx.org/en/docs/http/ngx_http_upstream_module.html) for available directives. + +> **Note** +> * Don't forget the terminating semicolon (`;`). +> * If you are using Docker Compose, remember to escape any dollar sign (`$`) characters (`$` becomes `$$`). + +Docker Compose example: + +```yaml +services: + nginx-proxy: + image: nginxproxy/nginx-proxy + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + environment: + HTTPS_METHOD: nohttps + myapp: + image: jwilder/whoami + expose: + - "8000" + environment: + VIRTUAL_HOST: myapp.example + VIRTUAL_PORT: "8000" + labels: + com.github.nginx-proxy.nginx-proxy.loadbalance: "hash $$remote_addr;" + deploy: + replicas: 4 +``` + ### Upstream (Backend) Server HTTP Keep-Alive Support > **Warning** diff --git a/nginx.tmpl b/nginx.tmpl index e35258f..98ab38e 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -219,6 +219,11 @@ {{- define "upstream" }} upstream {{ .Upstream }} { {{- $server_found := false }} + {{- $loadbalance := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.loadbalance")) }} + {{- if $loadbalance }} + # From the container's loadbalance label: + {{ $loadbalance }} + {{- end }} {{- range $container := .Containers }} # Container: {{ $container.Name }} {{- $args := dict "globals" $.globals "container" $container }} diff --git a/test/test_loadbalancing.py b/test/test_loadbalancing.py new file mode 100644 index 0000000..4b43aa5 --- /dev/null +++ b/test/test_loadbalancing.py @@ -0,0 +1,16 @@ +import pytest +import re + +def test_loadbalance_hash(docker_compose, nginxproxy): + conf = nginxproxy.get_conf().decode('ASCII') + r1 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld") + r2 = nginxproxy.get("http://loadbalance-enabled.nginx-proxy.tld") + assert re.search(r"hash \$remote_addr\;", conf) + assert r1.status_code == 200 + assert r2.text == r1.text + +def test_loadbalance_roundrobin(docker_compose, nginxproxy): + r1 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld") + r2 = nginxproxy.get("http://loadbalance-disabled.nginx-proxy.tld") + assert r1.status_code == 200 + assert r2.text != r1.text diff --git a/test/test_loadbalancing.yml b/test/test_loadbalancing.yml new file mode 100644 index 0000000..b8f42eb --- /dev/null +++ b/test/test_loadbalancing.yml @@ -0,0 +1,27 @@ +services: + loadbalance-hash: + image: web + expose: + - "81" + environment: + WEB_PORTS: 81 + VIRTUAL_HOST: loadbalance-enabled.nginx-proxy.tld + labels: + com.github.nginx-proxy.nginx-proxy.loadbalance: "hash $$remote_addr;" + deploy: + replicas: 2 + + loadbalance-roundrobin: + image: web + expose: + - "82" + environment: + WEB_PORTS: 82 + VIRTUAL_HOST: loadbalance-disabled.nginx-proxy.tld + deploy: + replicas: 2 + + sut: + image: nginxproxy/nginx-proxy:test + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro