On a local server I started several instances of gunicorn each running a different web app on a unique port as a service to the rest of the internal network. Now I want to put each instance of gunicorn behind its own nginx reverse-proxy server for better load balancing.
I decided to try to deploy each (nginx, gunicorn) pair in its own Docker container, exposing only its unique port to the outside world. I know that's easy enough with port-forwarding - for example "5555:80". However, each app also accesses external services, such as databases, that run on the same host independently of Docker.
Through trial and error, I found that a Docker container can access an external service (e.g. MySQL, or MongoDB) only if I run it as "docker run --network=host ...". This has the effect letting the container share the the host network, but it also exposes any ports gunicorn opens, which means that it leaves a way for a client to circumvent the reverse-proxy. While this is all running on a local server on a secure network, it doesn't seem like a good security practice, as it leaves the back-end open to denial-of-service attacks.
So I guess I want the best of both worlds - I want each (proxy, gunicorn) pair to talk with one another over a private network that only they use, while I expose one port (e.g. "5555") to the network, and the web app running under gunicorn can still access other services on the same host.
My nginx.conf would look something like this:
http {
upstream app {
server network1_app;
}
server {
location / {
proxy_pass http://app;
}
}
}
And my docker-compose.yml might look something like:
version: "3"
services:
proxy:
image: nginx:alpine
networks:
- host
- network1
volumes:
./nginx.conf:/etc/nginx/nginx.conf:ro
app:
build: ./app
networks:
- network1
ports: "5555:80" # Do I have to associate this with the "host" network somehow?
networks:
network1:
Then deploy this with
docker stack deploy network1 -c docker-compose.yml
Am I on the right path, or am I making it too complicated? Would it be more straightforward not to use Docker at all for this? Instead, I could create named sockets for this.
I like using docker-compose if I can, because it encapsulates some of the details and makes management easier (if it works). It also leaves less surface for a security breach.