From 3c661ed4d7df6cabea814197ffab14a26dc60f9f Mon Sep 17 00:00:00 2001 From: Tim Birkett Date: Tue, 14 Jun 2016 17:47:06 +0100 Subject: [PATCH 1/2] Modify template and add tests to allow an "app_key" environment variable to ping containers to different proxies. --- nginx.tmpl | 34 ++++++++++++++++--------- test/app-env.bats | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 test/app-env.bats diff --git a/nginx.tmpl b/nginx.tmpl index eb00afe..7d3edf1 100644 --- a/nginx.tmpl +++ b/nginx.tmpl @@ -1,4 +1,5 @@ {{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} +{{ $proxyAppKey := coalesce $CurrentContainer.Env.APP_KEY "" }} {{ define "upstream" }} {{ if .Address }} @@ -81,18 +82,23 @@ upstream {{ $host }} { {{ range $knownNetwork := $CurrentContainer.Networks }} {{ range $containerNetwork := $container.Networks }} {{ if eq $knownNetwork.Name $containerNetwork.Name }} - ## Can be connect with "{{ $containerNetwork.Name }}" network + {{/* If the APP_KEY matches */}} + {{ $appKey := coalesce $container.Env.APP_KEY "" }} - {{/* 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 }} + {{ if eq $proxyAppKey $appKey }} + ## Can be connect 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 }} + {{ end }} {{ end }} {{ end }} {{ end }} @@ -165,6 +171,9 @@ server { location / { proxy_pass {{ trim $proto }}://{{ trim $host }}; + {{ if (not (eq "" $proxyAppKey)) }} + add_header X-App-Key {{ $proxyAppKey }}; + {{ end }} {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} auth_basic "Restricted {{ $host }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; @@ -194,6 +203,9 @@ server { location / { proxy_pass {{ trim $proto }}://{{ trim $host }}; + {{ if (not (eq "" $proxyAppKey)) }} + add_header X-App-Key {{ $proxyAppKey }}; + {{ end }} {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} auth_basic "Restricted {{ $host }}"; auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; diff --git a/test/app-env.bats b/test/app-env.bats new file mode 100644 index 0000000..2d57656 --- /dev/null +++ b/test/app-env.bats @@ -0,0 +1,63 @@ +#!/usr/bin/env bats +load test_helpers +SUT_CONTAINER=bats-nginx-proxy-${TEST_FILE} + +function setup { + # make sure to stop any web container before each test so we don't + # have any unexpected contaiener running with VIRTUAL_HOST or VIRUTAL_PORT set + stop_bats_containers web +} + + +@test "[$TEST_FILE] start a nginx-proxy container" { + # GIVEN + run nginxproxy $SUT_CONTAINER -v /var/run/docker.sock:/tmp/docker.sock:ro -e APP_KEY=green + assert_success + docker_wait_for_log $SUT_CONTAINER 9 "Watching docker events" +} + + +@test "[$TEST_FILE] VIRTUAL_HOST=green.app.bats APP_KEY=green" { + # WHEN + prepare_web_container bats-app-env-1 80 -e VIRTUAL_HOST=green.app.bats -e APP_KEY=green + dockergen_wait_for_event $SUT_CONTAINER start bats-app-env-1 + sleep 1 + + # THEN + assert_200 green.app.bats + assert_output -p "X-App-Key: green" +} + +@test "[$TEST_FILE] VIRTUAL_HOST=blue.app.bats APP_KEY=blue" { + # WHEN + prepare_web_container bats-app-env-2 80 -e VIRTUAL_HOST=blue.app.bats -e APP_KEY=blue + dockergen_wait_for_event $SUT_CONTAINER start bats-app-env-2 + sleep 1 + + # THEN + assert_503 blue.app.bats + refute_output -p "X-App-Key: blue" +} + +@test "[$TEST_FILE] stop all bats containers" { + stop_bats_containers +} + + +# assert that querying nginx-proxy with the given Host header produces a `HTTP 200` response +# $1 Host HTTP header to use when querying nginx-proxy +function assert_200 { + local -r host=$1 + + run curl_container $SUT_CONTAINER / --head --header "Host: $host" + assert_output -l 0 $'HTTP/1.1 200 OK\r' +} + +# assert that querying nginx-proxy with the given Host header produces a `HTTP 503` response +# $1 Host HTTP header to use when querying nginx-proxy +function assert_503 { + local -r host=$1 + + run curl_container $SUT_CONTAINER / --head --header "Host: $host" + assert_output -l 0 $'HTTP/1.1 503 Service Temporarily Unavailable\r' +} From 8ec98e25fa1a6a729900d6f503047fc961dbc558 Mon Sep 17 00:00:00 2001 From: Tim Birkett Date: Tue, 14 Jun 2016 18:03:34 +0100 Subject: [PATCH 2/2] Add docs for APP_KEY variable --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index b12c9c2..cfc7236 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,32 @@ $ 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) +### Multiple nginx-proxy Containers + +If you need to run multiple nginx-proxy containers to load balance across +different sets of backend containers, you can set an environment variable +APP_KEY on both the nginx-proxy container and the backend containers. +``` +$ docker run -d -p 8080:80 -p 8443:443 \ + -e APP_KEY=api-green \ + -v /var/run/docker.sock:/tmp/docker.sock:ro \ + jwilder/nginx-proxy + +$ docker run -e VIRTUAL_HOST=foo.bar.com \ + -e APP_KEY=api-green ... + +$ docker run -d -p 9090:80 -p 9443:443 \ + -e APP_KEY=api-blue \ + -v /var/run/docker.sock:/tmp/docker.sock:ro \ + jwilder/nginx-proxy + + $ docker run -e VIRTUAL_HOST=foo.bar.com \ + -e APP_KEY=api-blue ... + +``` +This could be used in the orchestration of blue-green style deployments of +containers onto a single Docker host. + ### Custom Nginx Configuration If you need to configure Nginx beyond what is possible using environment variables, you can provide custom configuration files on either a proxy-wide or per-`VIRTUAL_HOST` basis.