How to use express api gateway in a docker swarm mode? - docker

I have a few simple REST api services implemented in Express.These services run in docker containers in a swarm mode.Also,I am trying to use express-api gateway with these services.The express api gateway also runs in a container as part of the docker swarm.Following is the Dockercompose file
version: "3"
services:
firstService:
image: chaitanyaw/firstrepository:first
deploy:
replicas: 3
restart_policy:
condition: on-failure
ports:
- '3000:3000'
networks:
- webnet
apiGateway:
image: firstgateway:latest
deploy:
restart_policy:
condition: on-failure
ports:
- '80:80'
networks:
- webnet
visualizer:
image: dockersamples/visualizer:latest
ports:
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
also, following is the gateway.config file
http:
port: 80
admin:
port: 9876
hostname: localhost
apiEndpoints:
api:
host: 192.168.99.100
paths: '/ip'
localApi:
host: 192.168.99.100
paths: '/'
serviceEndpoints:
httpbin:
url: 'https://httpbin.org'
services:
urls:
- 'http://192.168.99.100:3000/serviceonerequest'
- 'http://192.168.99.100:3000/servicetwo'
policies:
- basic-auth
- cors
- expression
- key-auth
- log
- oauth2
- proxy
- rate-limit
pipelines:
default:
apiEndpoints:
- api
policies:
# Uncomment `key-auth:` when instructed to in the Getting Started guide.
# - key-auth:
- proxy:
- action:
serviceEndpoint: httpbin
changeOrigin: true
customPipeline:
apiEndpoints:
- localApi
policies:
- proxy:
- action:
serviceEndpoint: services
changeOrigin: true
The ip '192.168.99.100' is the docker-machine ip.
If I run the stack using the above gateway.config file, everything works fine.
However,IF I replace
services:
urls:
- 'http://192.168.99.100:3000/serviceonerequest'
- 'http://192.168.99.100:3000/servicetwo'
with
services:
urls:
- 'http://services_firstService:3000/serviceonerequest'
- 'http://services_firstService:3000/servicetwo'
I get a bad gateway!
This docker swarm runs an overlay network "webnet".So,I must be able to use the service name as the hostname in the gateway.config file according to this link.
In the above working case with the use of IP the services become available 'outside' of the gateway which I do not desire.
What is wrong?

SO I have found where I was going wrong!Look at the compose file above!The service names are firstService (a capital S!).
Now, if I used
urls:
- 'http://services_firstService:3000/serviceonerequest'
- 'http://services_firstService:3000/servicetwo'
the API gateway would keep looking for services_firstservice (Note the small 's' in firstService) which obviously is not present in the overlay network!
Now,I changed the names to small case and it works just as expected!
Here is the new Docker-compose file
version: "3"
services:
firstservice:
image: chaitanyaw/firstrepository:first
deploy:
replicas: 3
restart_policy:
condition: on-failure
networks:
- webnet
apigateway:
image: firstgateway:latest
deploy:
restart_policy:
condition: on-failure
ports:
- '80:80'
networks:
- webnet
visualizer:
image: dockersamples/visualizer:latest
ports:
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
Also,here is the gateway.config file
http:
port: 80
admin:
port: 9876
hostname: localhost
apiEndpoints:
api:
host: 192.168.99.100
paths: '/ip'
localApi:
host: 192.168.99.100
paths: '/'
serviceEndpoints:
httpbin:
url: 'https://httpbin.org'
services:
urls:
- 'http://services_firstservice:3000/serviceonerequest'
- 'http://services_firstservice:3000/servicetwo'
policies:
- basic-auth
- cors
- expression
- key-auth
- log
- oauth2
- proxy
- rate-limit
pipelines:
default:
apiEndpoints:
- api
policies:
# Uncomment `key-auth:` when instructed to in the Getting Started guide.
# - key-auth:
- proxy:
- action:
serviceEndpoint: httpbin
changeOrigin: true
customPipeline:
apiEndpoints:
- localApi
policies:
- proxy:
- action:
serviceEndpoint: services
changeOrigin: true

From a Gateway perspective, there's nothing wrong in the configuration. As long the DNS name can be resolved correctly, the request should go through with no problem.
What I would try to do is to log in the Gateway container and try to ping the two services. If something goes wrong, then most likely the docker-compose.yml file has some problem.
I'd try to remove the network key, for example. The network is implicitly created so you do not really need that part.
Also — I do not think that the DNS name for the service is services_firstService; I'd try with firstService directly.
Cheers,
V.

Related

Traefic Routing for FireflyIII

Greetings Stack Overflow,
I have a RespberryPi4B, on which I installed Ubuntu 20.
(Linux ubuntu 5.4.0-1034-raspi aarch64 - Ubuntu 20.04.1 LTS)
On this Pi I want to install several Applications for my local use only.
To be able to have multiple Applications exposed, I use Traefik as a Proxy.
To easier deploy the Applications, I use Docker and Docker-Compose
Already up and running I have a Nextcloud instance, which works just fine.
Now I want to add FireflyIII as an Application, but the routing does not comply, and greets me with "Bad Gateway".
Here's what I have
The following docker-compose.yml for my Nextcloud works like a charm:
version: '3.3'
services:
nextcloud-db:
image: mariadb
container_name: nextcloud-db
command: --transaction-isolation=READ-COMMITTED --log-bin=ROW
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- /opt/containers/nextcloud/database:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD={supersecret}
- MYSQL_PASSWORD={supersecret}
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloudusr
- MYSQL_INITDB_SKIP_TZINFO=1
networks:
- default
nextcloud-redis:
image: redis:alpine
container_name: nextcloud-redis
hostname: nextcloud-redis
networks:
- default
restart: unless-stopped
command: redis-server --requirepass {supersecret}
nextcloud-app:
image: nextcloud
container_name: nextcloud-app
restart: unless-stopped
depends_on:
- nextcloud-db
- nextcloud-redis
environment:
REDIS_HOST: nextcloud-redis
REDIS_HOST_PASSWORD: {supersecret}
volumes:
- /opt/containers/nextcloud/app:/var/www/html
- /opt/containers/nextcloud/daten:/var/www/html/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextcloud-app.entrypoints=http"
- "traefik.http.routers.nextcloud-app.rule=Host(`nextcloud.mydomain.com`)"
- "traefik.http.middlewares.nextcloud-app-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.nextcloud-app.middlewares=nextcloud-app-https-redirect"
- "traefik.http.routers.nextcloud-app-secure.entrypoints=https"
- "traefik.http.routers.nextcloud-app-secure.rule=Host(`nextcloud.mydomain.com`)"
- "traefik.http.routers.nextcloud-app-secure.tls=true"
- "traefik.http.routers.nextcloud-app-secure.tls.certresolver=http"
- "traefik.http.routers.nextcloud-app-secure.service=nextcloud-app"
- "traefik.http.services.nextcloud-app.loadbalancer.server.port=80"
- "traefik.docker.network=proxy"
- "traefik.http.routers.nextcloud-app-secure.middlewares=nextcloud-dav,secHeaders#file"
- "traefik.http.middlewares.nextcloud-dav.replacepathregex.regex=^/.well-known/ca(l|rd)dav"
- "traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement=/remote.php/dav/"
networks:
- proxy
- default
networks:
proxy:
external: true
While knowing that this configuration, and these traefik labels work, I would assume, that the following docker-compose.yml, now for FireflyIII would work as well. And while the container spins up without any visible issues - I can see in the container logs, that the applications connects to the database and prepares everything - the access via browser is not possible - I get a Bad Gateway.
This is my FireflyIII's docker-compose.yml
version: '3.3'
services:
fireflydb:
image: mariadb
container_name: fireflydb
environment:
- MYSQL_RANDOM_ROOT_PASSWORD=yes
- MYSQL_USER=fireflyuser
- MYSQL_PASSWORD={supersecret}
- MYSQL_DATABASE=fireflydb
volumes:
- firefly_db:/var/lib/mysql
networks:
- default
firefly:
image: jc5x/firefly-iii:latest
container_name: firefly
volumes:
- firefly_upload:/var/www/html/storage/upload
depends_on:
- fireflydb
env_file: .env
labels:
- "traefik.enable=true"
- "traefik.http.routers.firefly-app.entrypoints=http"
- "traefik.http.routers.firefly-app.rule=Host(`firefly.mydomain.com`)"
- "traefik.http.middlewares.firefly-app-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.firefly-app.middlewares=firefly-app-https-redirect"
- "traefik.http.routers.firefly-app-secure.entrypoints=https"
- "traefik.http.routers.firefly-app-secure.rule=Host(`firefly.mydomain.com`)"
- "traefik.http.routers.firefly-app-secure.tls=true"
- "traefik.http.routers.firefly-app-secure.tls.certresolver=http"
- "traefik.http.routers.firefly-app-secure.service=firefly-app"
- "traefik.http.services.firefly-app.loadbalancer.server.port=80"
- "traefik.docker.network=proxy"
networks:
- proxy
- default
volumes:
firefly_upload:
firefly_db:
networks:
proxy:
external: true
The only difference between the Traefik Labels is, that I don't need the replacepathregex labels for firefly, and that I changed the Hosts and application names:
nextcloud.mydomain.com -> firefly.mydomain.com
nextcloud-app/nextcloud-app-secure -> firefly-app/firefly-app-secure
I just don't understand yet, why the "same" configuration behaves differently.
The Traefik container logs don't throw any errors.
A note about the SSL Certificates, since the applications are run in my local network, and I edit my local hostfile to access the application via browser, the SSL renewal isn't possible, I am aware of that. I currently work around it by manually renewing on a different server and copying the cert to my Pi. Quick and dirty, but works for now.
For completeness, here's my Traefik's traefik.yml, docker-compose.yml and dynamic_conf.yml:
traefik.yml:
api:
dashboard: true
entryPoints:
http:
address: ":80"
https:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: "/dynamic_conf.yml"
certificatesResolvers:
http:
acme:
email: mymail#mydomain.com
storage: acme.json
httpChallenge:
entryPoint: http
docker-compose.yml:
version: '3'
services:
traefik:
image: traefik:latest
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/acme.json:/acme.json
- ./data/dynamic_conf.yml:/dynamic_conf.yml
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.entrypoints=http"
- "traefik.http.routers.traefik.rule=Host(`traefik.mydomain.com`)"
- "traefik.http.middlewares.traefik-auth.basicauth.users=user:secret"
- "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
- "traefik.http.routers.traefik-secure.entrypoints=https"
- "traefik.http.routers.traefik-secure.rule=Host(`traefik.mydomain.com`)"
- "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
- "traefik.http.routers.traefik-secure.tls=true"
- "traefik.http.routers.traefik-secure.tls.certresolver=http"
- "traefik.http.routers.traefik-secure.service=api#internal"
- "providers.file.filename=/dynamic_conf.yml"
networks:
proxy:
external: true
dynamic_conf.yml
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
sniStrict: true
http:
middlewares:
secHeaders:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
sslRedirect: true
#HSTS Configuration
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
customFrameOptionsValue: "SAMEORIGIN"
The only thing I could think of, might be a problem with the internal Ports.
But I don't know enough about traefik and docker yet, to say for sure.
I appreciate any hints and suggestions for improvement.
At first glance I'd recommend to change the port to 8080:
version: '3.3'
services:
# [...]
firefly:
labels:
#
- "traefik.http.services.firefly-app.loadbalancer.server.port=8080"
# [...]
The firefly-iii-Image you're using is based on their BaseImage which's README.md says:
Basically, I use the 7.4 Apache image with some minor changes.
And one change is:
Switch to port 8080

Configuring Nginx with Traefik

I'm new to Linux in general, and even newer to Traefik, and have been trying for some time to setup a nginx container to serve some AngularJS web pages, using Traefik as reverse-proxy. I have a small Ubuntu 20.4 server hosted on DigitalOcean (if that's of any relevance).
I have tried following a few different nginx tutorials, and messing with the settings on my own, to no success. It works if I run a simple command like:
$ docker run –name some-nginx -d -p 8888:80 nginx
But then I can only access it through myip:8888, and I would like to access it with a subdomain, i.e. docs.domain.com.
I managed to setup Traefik using a traefik.yml like that:
api:
dashboard: true
entryPoints:
http:
address: ":80"
https:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
certificatesResolvers:
http:
acme:
email: email#email.com
storage: acme.json
httpChallenge:
entryPoint: http
And then docker-compose this file:
version: '3'
services:
traefik:
image: traefik:v2.0
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./acme.json:/acme.json
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.entrypoints=http"
- "traefik.http.routers.traefik.rule=Host(`monitor.domain.com`)"
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:secret_password"
- "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
- "traefik.http.routers.traefik-secure.entrypoints=https"
- "traefik.http.routers.traefik-secure.rule=Host(`monitor.domain.com`)"
- "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
- "traefik.http.routers.traefik-secure.tls=true"
- "traefik.http.routers.traefik-secure.tls.certresolver=http"
- "traefik.http.routers.traefik-secure.service=api#internal"
networks:
proxy:
external: true
I also successfully set up a portainer:
version: '3'
services:
portainer:
image: portainer/portainer:latest
container_name: portainer
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.entrypoints=http"
- "traefik.http.routers.portainer.rule=Host(`manage.domain.com`)"
- "traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.portainer.middlewares=portainer-https-redirect"
- "traefik.http.routers.portainer-secure.entrypoints=https"
- "traefik.http.routers.portainer-secure.rule=Host(`manage.domain.com`)"
- "traefik.http.routers.portainer-secure.tls=true"
- "traefik.http.routers.portainer-secure.tls.certresolver=http"
- "traefik.http.routers.portainer-secure.service=portainer"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
- "traefik.docker.network=proxy"
networks:
proxy:
external: true
This way I can access both portainer and traefik subdomains, with SSL, no problems.
I tried using docker-compose files for nginx similar to the portainer one, using the volumes "./data:/usr/share/nginx/html:ro" and "./nginx.conf:/etc/nginx/nginx.conf:ro", as well some different nginx.conf options as exemplified in the Beginner's Guide from the official docs.
I would appreciate if someone could point ou which labels I should use on the docker-compose file and how to properly interface Traefik with the nginx config.

Express gateway always return Bad Gateway when I run with docker-compose

I tried to use Express Gateway with Docker as a Container for each microservice. So far it has been very good at local testing. There was a problem using the Docker by implementing EG. I don't know where the problem is. There is no complete description at LOG_LEVEL = debug. Previously I used the docker without EG and then communicating with other microservices just fine. Please help.
gateway.config.yml
http:
port: 8080
admin:
port: 9876
apiEndpoints:
authAPI:
host: '*' # i tried with egateway, localhost, 127.0.0.1 but still not working
paths:
- '/users/signin'
- '/users/signup'
- '/users/oauth/facebook'
- '/users/auth/google'
- '/users/auth/google/callback'
- '/users/secret'
- '/users/get-user-google'
- '/dashboard'
- '/test'
crudAPI:
host: '*'
paths:
- '/users/get-user-data'
- '/users/delete-user-data'
- '/users/add-user-data'
- '/users/get-one-user-data/*'
- '/users/update-user-data'
- '/users/update-pass-user-data'
serviceEndpoints:
authService:
url: 'http://localhost:3003'
crudService:
url: 'http://localhost:3004'
policies:
- log
- proxy
- jwt
- cors
- jwt-custom1
pipelines:
crud:
apiEndpoints:
- crudAPI
policies:
- cors:
- action:
origin: '*'
methods: 'GET, POST, PUT, DELETE'
allowedHeaders: 'X-Requested-With,content-type,Authorization'
credentials: true
- proxy:
- action:
serviceEndpoint: crudService
auth:
apiEndpoints:
- authAPI
policies:
- proxy:
- action:
serviceEndpoint: authService
docker-compose.yml
version: '3'
services:
egateway:
hostname: egateway
container_name: egateway
build: './test-eg2'
ports:
- "8080:8080"
depends_on:
- mongo
environment:
- LOG_LEVEL=debug
api-auth:
container_name: api-auth
build: './api-auth'
ports:
- "3003:3003"
links:
- mongo
- egateway
api-crud:
container_name: api-crud
build: './api-crud'
ports:
- "3004:3004"
links:
- mongo
- egateway
mongo:
container_name: mongo-dbx
image: mongo
ports:
- "27017:27017"
Results from log level = debug
I need your help.
Thank you for reviewing this issue.
Your URL for the auth-service and crud-service shouldn't be localhost. It should be:
serviceEndpoints:
authService:
url: 'http://api-auth:3003'
crudService:
url: 'http://api-crud:3004'
api-auth and api-crud would be the hostnames for those 2 containers in your docker-compose network.

How to map specific port inside docker container when using traefik?

Here's my docker-compose.yml:
version: '3'
services:
website:
build: ./website
expose: [3000]
labels:
- "traefik.frontend.rule=Host:localhost"
blog:
build: ./blog
expose: [4000]
labels:
- "traefik.frontend.rule=Host:localhost;PathPrefix:/blog"
docs:
build: ./docs
expose: [3000]
labels:
- "traefik.frontend.rule=Host:localhost;PathPrefix:/docs"
proxy:
image: traefik
command: --api.insecure=true --providers.docker
networks:
- webgateway
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
webgateway:
driver: bridge
What I want is access three different node.js websites via different routes. But these three node.js websites actually expose different ports. Now my treafik is running. I can config via localhost:8080 But localhost localhost/blog and localhost/docs are all 404 page not found
P.S: I'm not sure whether port is the issue I should investigate, because changing one node.js service to port 80 doesn't solve the puzzle. And I saw on traefik dashboard the rule is Host(blog-dev)
PathPrefix:/blog
When you have this as a routing rule, traefix won't automatically remove the prefix when sending to the container.
So unless you have a route /blog inside your container you will get a 404.
So what you normally do is also add a middleware to strip this ->
https://docs.traefik.io/middlewares/stripprefix/
Also you appear not to be setting your rules based on your service.
So as an example for your first service blog,
try->
labels:
- "traefik.http.routers.blog.rule=Host(`localhost`) && PathPrefix(`/blog`)"
- "traefik.http.routers.blog.middlewares=strip-blog"
- "traefik.http.middlewares.strip-blog.stripprefix.prefixes=/blog"
And then do the same for your other routes, don't forget to replace routers.blog with routers.docs etc..
labels:
- traefik.http.services.<YOUR-SERVICE-NAME>.loadbalancer.server.port=9763
EG:
services:
wso:
image: "my-custom-wso-image"
volumes:
- .....
labels:
- "traefik.enable=true"
- "traefik.http.routers.wso.tls=true"
- "traefik.http.routers.wso.rule=Host(`my.nice.url`)"
- "traefik.http.services.wso.loadbalancer.server.port=9763" #<-----
Thanks to #Keith I found the solution
version: '3'
services:
website:
build: ./website
expose: [3000]
networks: # It's essential to specify the same network in every service
- webgateway
labels:
- "traefik.http.routers.website.rule=Host(`localhost`)" # Use the right format
- "traefik.port=3000" # Let traefik find the right port
blog:
build: ./blog
expose: [4000]
networks:
- webgateway
labels:
- "traefik.http.routers.blog.rule=Host(`localhost`) && PathPrefix(`/blog`)" # blog has a root as `/blog` so no need to strip otherwise too many redirects
- "traefik.port=4000"
docs:
build: ./docs
expose: [3000]
networks:
- webgateway
labels:
- "traefik.http.routers.docs.rule=Host(`localhost`) && PathPrefix(`/docs`)"
- "traefik.http.routers.docs.middlewares=strip-docs" # Necessary as Keith mentioned
- "traefik.http.middlewares.strip-docs.stripprefix.prefixes=/docs"
- "traefik.port=3000"
proxy:
image: traefik
command: --api.insecure=true --providers.docker
networks:
- webgateway
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
webgateway:
driver: bridge

Docker Swarm connection between containers refused for some containers

simplified swarm:
manager1 node
- consul-agent
worker1 node
- consul-client1
- web-app:80
- web-network:9000
database1 node
- consul-client2
- redis:6379
- mongo:27017
The web-app and web-network services can connect to redis and mongo through their service names correctly, e.g redis.createClient('6379', 'redis') and mongoose.connect('mongodb://mongo').
However, container web-app cannot connect to web-network, I'm trying to make a request like so:
request('http://web-network:9000')
But get the error:
errorno: ECONNREFUSED
address: 10.0.1.9
port: 9000
Request to web-network using a private IP does work:
request('http://11.22.33.44:9000')
What am I missing? Why can they connect to redis and mongo but not between each container? When moving redis/mongo to the same node as web-app, it will still work, so I don't think the issue comes because the services cannot talk to a service on the same server node.
Can we make docker network use private IP instead of the pre-configured subnet?
docker stack deploy file
version: '3'
services:
web-app:
image: private-repo/private-image
networks:
- swarm-network
ports:
- "80:8080"
deploy:
placement:
constraints:
- node.role==worker
web-network:
image: private-repo/private-image2
networks:
- swarm-network
ports:
- "9000:8080"
deploy:
placement:
constraints:
- node.role==worker
redis:
image: redis:latest
networks:
- swarm-network
ports:
- "6739:6739"
deploy:
placement:
constraints:
- engine.labels.purpose==database
mongo:
image: mongo:latest
networks:
- swarm-network
ports:
- "27017:27017"
deploy:
placement:
constraints:
- engine.labels.purpose==database
networks:
swarm-network:
driver: overlay
docker stack deploy app -c docker-compose.yml

Resources