Of course there are other great tools out there like e.g. traefik with a lot of add-on functionality but this post quickly shows how you can use a regular Nginx Docker image as a kind of ingress controller to your Docker Swarm stacks and its services.
If you have one or more Swarm nodes and Nginx services running on the same Swarm with ports 80
and 443
exposed and mapped to the Docker host you can then use Docker DNS service on 127.0.0.11
in the Nginx resolver
parameter for dynamically resolving upstream Docker stack services connected to the same user-defined overlay network (backend services can be separated on further dedicated networks).
Nginx Config
For an upstream service Nginx configuration upstream_web_service.tempplate
all it takes is this:
server {
<...>
location / {
<...>
resolver 127.0.0.11 valid=30s ipv6=off;
set $upstream_addr <swarm_stack_service_name>:<port>;
proxy_pass https://$upstream_addr;
<...>
}
<...>
}
The <swarm_stack_service_name>
by default is prefixed with the environment variable COMPOSE_PROJECT_NAME
and after that the service name as given in the Docker compose/stack file. You can check this e.g. with docker stack ps "${COMPOSE_PROJECT_NAME}"
.
You can as well move the resolver
statement into your overall nginx.conf
into the http
parameter layer so that it is being used for any Nginx server
locations
.
http {
<...>
resolver 127.0.0.11 valid=30s ipv6=off;
<...>
}
Docker Stack Config
These are the relevant fragments from the docker-compose
/docker-stack
files:
Nginx:
version: "3.9"
services:
nginx_ingress:
networks:
- net1_ingress
ports:
- "80:80"
- "443:443"
# ports mapped to the host and incomming requests will be resolved according to Docker DNS above and routed over the net1_ingress network to upstream services on their internally exposed ports (not mapped externally to the docker host)
If you are using the <ip>:<host_port>:<container_port
notation for ports
then keep this github moby issue in mind that ports are always mapped to all network interfaces on 0.0.0.0
(and use e.g. your firewall and or load balancer rules accordingly).
Some upstream web service:
version: "3.9"
services:
upstream_web_service:
networks:
- net1_ingress
- net2_backend
# no ports mapped to the host and in both the general net1_ingress network and its own service's backend_network
upstream_db_service:
networks:
- net2_backend
# no ports mapped to the host and only in the dedicated service's backend_network so that only the upstream_web_service can access it
This basic setup shows the "Option 2: reverse proxy in Swarm" as explained here.
Further information:
https://docs.docker.com/network/#dns-services
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#resolver
https://traefik.io/solutions/docker-swarm-ingress
https://medium.com/@farren.alex/docker-swarm-ingress-how-to-route-traffic-in-production-e749306168a1
https://stackoverflow.com/questions/43896846/how-to-use-docker-swarm-dns-service-names-in-nginx-upstream/44303568#44303568
https://medium.com/@prajwal.chin/understanding-docker-dns-2ed4b070a0