I’ve been asked a few times over the last few weeks about how you can have multiple services (for example, Munki and Sal) running on the same port on the same server - how we used to do Virtual Hosting when we ran our apps on the host OS. My usual four word answer has been ‘use a proxy container’. How you actually do that has been undocumented - this post hopes to recitfy that.
Let’s consider a usual setup that has Sal and Munki running on the same box. We have two hostnames that both resolve to our Docker server - sal.example.com
and munki.example.com
, and we have our containers running in a manner similar to the below:
Each of our containers is linked to it’s parent:
# Munki server
docker run -d \
-v /usr/local/docker/munki:/munki_repo \
--restart="always" \
--name="munki" \
macadmins/munki
# Postgres container for Sal
docker run -d --name="postgres-sal" \
-v /usr/local/docker/sal/db:/var/lib/postgresql/data \
-e DB_NAME=sal \
-e DB_USER=admin \
-e DB_PASS=password \
--restart="always" \
grahamgilbert/postgres
# Sal Container
docker run -d \
-e ADMIN_PASS=pass \
-e DB_NAME=sal \
-e DB_USER=admin \
-e DB_PASS=password \
--name="sal" \
--link postgres-sal:db \
--restart="always" \
macadmins/sal
# Proxy
docker run -d --name="proxy" \
--link sal:sal \
--link munki:munki \
-v /usr/local/docker/proxy/sites-templates:/etc/nginx/sites-templates \
-v /usr/local/docker/proxy/keys:/etc/ssl/keys \
-p 443:443 \
-p 80:80 \
--restart="always" \
grahamgilbert/proxy
So how does the proxy
container know about the other containers? When you link a Docker container to another, a few environment variables are made available to you - there are two we’re interested in:
CONTAINERNAME_PORT_EXPOSEDPORTNUMBER_TCP_ADDR
CONTAINERNAME_PORT_EXPOSEDPORTNUMBER_TCP_PORT
So in the case of the Sal container, it would be SAL_PORT_8000_TCP_ADDR
and SAL_PORT_8000_TCP_PORT
. The grahamgilbert/proxy
image will simply run over any file named *.tmpl
in /etc/nginx/sites-templates
and replace the placeholders with the value that’s in the environment variable - here’s my nginx configuration file template for Sal:
{% codeblock nginx /etc/nginx/sites-templates/sal.example.com.tmpl %} server { listen 80; server_name sal.example.com; rewrite ^ https://$server_name$request_uri? permanent; }
server { listen 443 ssl; server_name sal.example.com; ssl_certificate /etc/ssl/keys/ssl-unified.crt; ssl_certificate_key /etc/ssl/keys/sal.key;
location / {
proxy_pass http://${SAL_PORT_8000_TCP_ADDR}:${SAL_PORT_8000_TCP_PORT}; # set to your own port
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
}
{% endcodeblock %}
Using this technique, you can have as many containers as you like all serving content on the same port number - just change line 14 above to reflect the exposed ports and names of your Docker containers.