Docker Stacks
Overview
Stacks are groups of interrelated services that share dependencies and can be scaled together. Effectively, Stacks are a production-grade version of Docker Compose.
Stacks simplify application management by providing:
- desired state
- rolling updates
- scaling operations
- health checks
Define the desired state of your app in a Compose file, then deploy and manage it with the docker stack
command. You can use your existing Compose file, but it won’t recognize the build
command. Building shouldn’t happen in production (that normally happens at the CI/CD stage). You can leave the build
command, though, as it’s just ignored for Stacks. Equally, Docker Compose will ignore the deploy
key that you need for Docker Stacks - this means you can use the same file for both Compose and Stacks.
The Compose file includes the entire stack of microservices that comprise the app. It also includes all the volumes, networks, secrets, and other infrastructure required by the app.
Stacks build on top of Docker Swarm, so you get all those security and advanced features.
Containers –> Services –> Stacks
Creating a Stack File
At the highest level, the Stack file defines 4 top-level keys:
version:
- version of Compose file format (3 or higher)services:
- stack of services that comprise the appnetworks:
- required networkssecrets:
- secrets used
Here’s an example:
version: "3.2"
services:
reverse_proxy:
image: dockersamples/atseasampleshopapp_reverse_proxy
ports:
- "80:80"
- "443:443"
secrets:
- source: revprox_cert
target: revprox_cert
- source: revprox_key
target: revprox_key
networks:
- front-tier
database:
image: dockersamples/atsea_db
environment:
POSTGRES_USER: gordonuser
POSTGRES_DB_PASSWORD_FILE: /run/secrets/postgres_password
POSTGRES_DB: atsea
networks:
- back-tier
secrets:
- postgres_password
deploy:
placement:
constraints:
- 'node.role == worker'
appserver:
image: dockersamples/atsea_app
networks:
- front-tier
- back-tier
- payment
deploy:
replicas: 2
update_config:
parallelism: 2
failure_action: rollback
placement:
constraints:
- 'node.role == worker'
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
secrets:
- postgres_password
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8001:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
update_config:
failure_action: rollback
placement:
constraints:
- 'node.role == manager'
payment_gateway:
image: dockersamples/atseasampleshopapp_payment_gateway
secrets:
- source: staging_token
target: payment_token
networks:
- payment
deploy:
update_config:
failure_action: rollback
placement:
constraints:
- 'node.role == worker'
- 'node.labels.pcidss == yes'
networks:
front-tier:
back-tier:
payment:
driver: overlay
driver_opts:
encrypted: 'yes'
secrets:
postgres_password:
external: true
staging_token:
external: true
revprox_key:
external: true
revprox_cert:
external: true
If the networks don’t already exist, Docker creates them.
If secrets are defined as external, they must already exist before the stack can be deployed:
external: true
file: <filename>
It’s possible for secrets to be created on-demand when the application is deployed. However, this means the secret must exist as an unencrypted value in the host’s file system. That’s not very secure!
The environment key lets you inject environment variables into services replicas at runtime. For sensitive data, a better solution would be to pass values as secrets.
The constraints key defines a placement constraint. This ensures replicas for this service always run on Swarm worker nodes:
deploy:
placement:
constraints:
- 'node.role == worker'
You could also use Role.node.role != manager
.
When Docker stops a container, it issues a SIGTERM
to the application process with PID 1 inside the container. The application then has a 10-second grace period to perform any clean-up operations. If it doesn’t handle the signal, it’s forcibly terminated after 10 seconds with a SIGKILL
. The stop_grace_period
property overrides this 10-second grace period.
Managing Stacks
Deploying a Stack
To deploy a Stack, use:
docker stack deploy -c docker-stack.yml <stack-name>
Any created components of the Stack - e.g. networks - are prefixed with the Stack name.
Updating a Stack
There’s no specific command for updating a stack - you just deploy it again.
Removing a Stack
To remove a Stack, use:
docker stack rm <stack-name>
When using the rm
command, pre-existing secrets aren’t deleted.
Configuring Production Rollouts with Compose
Under the deploy
key, you can configure rollouts:
my-app:
deploy:
update_config:
parallelism: 3
monitor: 60s
failure_action: rollback
order: start-first
parallelism
- number of replicas that are replaced in parallel. The default is 1, so rollouts roll out one container at a time. Increasing the parallelism gives you a faster rollout and helps you find failures faster.monitor
- the time period the Swarm should wait to monitor new replicas before continuing with the rollout. You definitely need a delay if your images include health checks.failure_action
- action to take if the rollout fails because containers don’t start, or fail health checks within themonitor
period. The default is to pause the rollout. You can also set it to automatically roll back to the previous version.order
- order of replacing replicas.stop-first
is the default and it ensure there are never more replicas running than the required numbers. If your app can work with extra replicas,start-first
is better because new replicas are created and checked before the old ones are removed.
You can’t roll back a stack - only individual services can be rolled back to their previous state.