Skip to main content

Docker Compose Networking

Victoria Harbour, Hong Kong

I previously created 3 apps that are proxied by NGINX - all together 4 docker container waiting to be deployed. I now want to create a Docker Compose file to be able to start all of them up with a single command.

Docker Compose

My first attempt to accomplish this looks like that - every wiki container runs the hapi web server and serves the content of wiki/public which is mapped to a folder on my host system that contains the static web content from my app (that is source controlled by Gitlab and build in a Gitlab CI Pipeline). The NGINX proxy is then used to help out with the domain assignment and TLS certification:

version: '3.8'
services:
wiki_en:
image: my.gitlab.com:12345/wiki/wiki_container:latest
container_name: wiki_en
ports:
- '127.0.0.1:7777:8888'
restart: unless-stopped
volumes:
- /opt/wiki/wiki-en/public:/wiki/public

wiki_fr:
image: my.gitlab.com:12345/wiki/wiki_container:latest
container_name: wiki_fr
ports:
- '127.0.0.1:7778:8888'
restart: unless-stopped
volumes:
- /opt/wiki/wiki-fr/public:/wiki/public

wiki_de:
image: my.gitlab.com:12345/wiki/wiki_container:latest
container_name: wiki_de
ports:
- '127.0.0.1:7779:8888'
restart: unless-stopped
volumes:
- /opt/wiki/wiki-de/public:/wiki/public

ingress:
image: nginx:stable-alpine
container_name: ingress
network_mode: host
restart: unless-stopped
volumes:
- /opt/wiki/docker_ingress:/etc/nginx/conf.d

Note that I am running into an issue where NGINX has to access the public dir of each app (long story). This directory lies outside of the app container on my host system. This is why I have to forward ports for each app to my host network where NGINX is running. To prevent my apps from leaking onto the external network I will bind them to 127.0.0.1 on my host system.

When I start this composite it will automatically create a virtual network for me - named after the folder that contains my docker-compose.yml file (wiki):

docker network ls
NETWORK IDNAMEDRIVERSCOPE
d61bea58e174bridgebridgelocal
c8cc528dc050hosthostlocal
a982d8cfae58nonenulllocal
545b0454b47fwiki_defaultbridgelocal

Inspecting the network shows me that all applications have been attached to it at start up:

docker inspect network wiki_default
[
{
"Name": "wiki_default",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"Ingress": false,
"Containers": {
"44665": {
"Name": "wiki_de",
"MacAddress": "02:42:ac:12:00:04",
"IPv4Address": "172.18.0.4/16",
"IPv6Address": ""
},
"bca6e": {
"Name": "wiki_fr",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
},
"e98fdb": {
"Name": "wiki_en",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
}
}
]

Manual Network Override

You can override the default network name by adding a project name:

docker-compose -p wiki_project up -d

You can also specify the network in your docker-compose.yml file. But before I start with that - let's figure out a way how I can remove the requirement for my NGINX ingress to be attached to my host network.

For example we can try to mount the public directory of each app into the NGINX container and serve static content from there. I then also have to bring the NGINX container into my custom network and have it access my apps through the Docker DNS service.

The NGINX configuration file I use together the NGINX container will then look like this:

server {
listen 80;
listen [::]:80;
server_name localhost;

charset koi8-r;

# Gzip Compression
gzip on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_proxied no-cache no-store private expired;
gzip_buffers 16 8k;
gzip_comp_level 6;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
gzip_vary on;

location / {
rewrite ^/(.*)$ /en/$1 permanent;
}

root /opt/wiki/wiki-en/public;

location /en/ {
add_header Cache-Control "public, must-revalidate, proxy-revalidate, max-age=0";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto http;
proxy_hide_header X-Frame-Options;
proxy_set_header Accept-Encoding "";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_max_temp_file_size 0;
proxy_redirect off;
proxy_read_timeout 240s;
proxy_pass http://wiki_en:7777/;
}

location /fr/ {
add_header Cache-Control "public, must-revalidate, proxy-revalidate, max-age=0";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto http;
proxy_hide_header X-Frame-Options;
proxy_set_header Accept-Encoding "";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_max_temp_file_size 0;
proxy_redirect off;
proxy_read_timeout 240s;
proxy_pass http://wiki_fr:7778/;
root /opt/wiki/wiki-fr/public;
}

location /de/ {
add_header Cache-Control "public, must-revalidate, proxy-revalidate, max-age=0";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto http;
proxy_hide_header X-Frame-Options;
proxy_set_header Accept-Encoding "";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_max_temp_file_size 0;
proxy_redirect off;
proxy_read_timeout 240s;
proxy_pass http://wiki_de:7779/;
root /opt/wiki/wiki-de/public;
}

error_page 404 /de/404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

Note that now, instead of having a proxy pass to http://127.0.0.1:7777/, http://127.0.0.1:7778/, http://127.0.0.1:7779/ I am using the container names http://wiki_en:8888, http://wiki_fr:8888, http://wiki_de:8888.

The root directories are left as they were:

/opt/wiki/wiki-en/public
/opt/wiki/wiki-fr/public
/opt/wiki/wiki-de/public

But now they are pointing inside the NGINX container and have to be mounted in from my host system. And I now need to expose the port 80 for the NGINX service:

version: '3.8'
services:
wiki_en:
image: my.gitlab.com:12345/wiki/wiki_container:latest
container_name: wiki_en
networks:
- gateway
restart: unless-stopped
volumes:
- /opt/wiki/wiki-en/public:/wiki/public

wiki_fr:
image: my.gitlab.com:12345/wiki/wiki_container:latest
container_name: wiki_fr
networks:
- gateway
restart: unless-stopped
volumes:
- /opt/wiki/wiki-fr/public:/wiki/public

wiki_de:
image: my.gitlab.com:12345/wiki/wiki_container:latest
container_name: wiki_de
networks:
- gateway
restart: unless-stopped
volumes:
- /opt/wiki/wiki-de/public:/wiki/public

ingress:
image: nginx:stable-alpine
container_name: ingress
networks:
- gateway
ports:
- '80:80'
restart: unless-stopped
volumes:
- /opt/wiki/docker_ingress:/etc/nginx/conf.d
- /opt/wiki/wiki-en:/opt/wiki/wiki-en
- /opt/wiki/wiki-fr:/opt/wiki/wiki-fr
- /opt/wiki/wiki-de:/opt/wiki/wiki-de

networks:
gateway: {}