Introduction

Docker is a containerization tool that enables the hosting of container-based workloads. Within Docker, Swarm functions as a grouping mechanism, allowing multiple containers to operate collectively in a coordinated manner. To external observers, the workload service appears as a single container, yet it efficiently manages and load balances requests across the multiple containers operating behind the scenes.

In this article, we will discuss docker swarm and learn to host our containerized applications on a swarm instance. We will make use of different servers that are running as a part of a docker swarm to run our workloads.

Swarm is a valuable stepping stone for those considering a transition to Kubernetes. While Kubernetes offers greater capabilities, the core principles and behaviour between the two are similar. Thus, skills acquired in Docker Swarm are transferable, making it an ideal platform for acquainting yourself with the Kubernetes ecosystem.

Basics of Swarm Mode

First, let us enable swarm mode on our docker container.

docker swarm init --advertise-addr ,<127.0.0.1> or private network ip

Once executed, a token will be provided in the output. That token can be used to connect other swarm instances with this swarm instance. This Swarm will be the main node, while the other nodes will act as secondaries.

To connect another server with this swarm network, simply run the following command:

docker swarm join --address <token>

Basic Commands of Swarm

docker stack ls = list swarm stacks

docker service = work with services

docker stack up/deploy = deploy/update workloads

docker stack rm = remove workloads

docker stack config = validate configuration

Anatomy of a Swarm Declarative Configuration

Swarm uses a declarative configuration, i.e. a YAML file. Similar to a docker-compose file, the swarm config is not much different. Only differing in a few parts where the features from compose don't carry over to Swarm.

Listed below are the config syntaxes unique to docker swarm.

# This configuration specifies the deployment strategy of the specific service.
deploy:
      replicas: 2
      update_config:
        parallelism: 1
        order: start-first
        failure_action: rollback
        delay: 10s
      rollback_config:
        parallelism: 0
        order: stop-first
      restart_policy:
        condition: "on-failure"
        delay: 5s
        max_attempts: 3
        window: 120s

depends_on:
	- service_name # specified in list rather than key value pairs

# for using secrets
secrets:
  secret-name:
    file: ./.env.api

# for using config, these configs are base64 encoded during creation and decoded by swarm when mounted
config:
	config-name:
    	file: ./config.conf

Different Swarm Configs

Deploying Workloads In Our Docker Swarm Network

Here is an example swarm configuration that we will use for this demo. Go ahead and save it into a file.

version: '3.9'

services:
  api:
    image: api:latest
    ports:
      - 127.0.0.1:3000:3000
    secrets:
      - source: env-api
        target: /usr/src/app/.env
    networks:
      - swarm-net
    volumes:
      - files:/usr/src/app/files/
    depends_on:
      - postgres

  postgres:
    image: postgres:15.3-alpine3.18
    ports:
      - 127.0.0.1:5432:5432
    env_file:
      - .env.db
    volumes:
      - postgres-db:/var/lib/postgresql/data
    networks:
      - swarm-net
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "pg_isready -U ${POSTGRES_USER:-db-user} -d ${POSTGRES_DB:-db-name}"
        ]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7.2.1-alpine
    env_file:
      - .env.db
    command: /bin/sh -c "redis-server --requirepass $$REDIS_PASSWORD"
    networks:
      - swarm-net
    healthcheck:
      test: [ "CMD-SHELL", "redis-cli -h 127.0.0.1 -p 6379 PING" ]
      interval: 10s
      timeout: 5s
      retries: 5


volumes:
  postgres-db:
    name: postgres-db
  files:
    name: files

networks:
  swarm-net:
    name: swarm-net
    driver: overlay

secrets:
  env-api:
    file: ./.env.api

Example of a Swarm Compose File

⚠️
Please note that the env variable parser for docker swarm works differently than docker compose's parser. Do not use "" in your env variables.

To summarize, what we're doing above is creating a few resources on a custom docker network, separating volumes and injecting environment variables into our docker container.

To deploy the stack, run the command,

docker stack up --compose-file stack.yml demo-stack1
⚠️
Note: the stack name is necessary when creating workloads in a docker swarm.

Updating Services and Handling Rollbacks In Docker Swarm

To update services in the docker swarm, run the commands below:

docker service update service_name --image image:tag
docker service rollback service_name <version_number>

Secrets In Docker Swarm

To create docker secrets, run the command given below:

docker secret create secret_name secret_file

The secret can be mounted into a docker container and used by said docker container. The default path is /run/secrets. It can be changed by changing the config a bit like follows:

service:
	service_name: abcd
    secret:
    	source: secret123
        target: /app/
....
secret:
	secret123:
    	file: ./secret.txt

Secrets in Docker Swarm

Configs In Docker Swarm

To create and import config in Swarm for applications like nginx, apache, etc, where you don't want to bind mount the file but rather use the config feature provided by Swarm, do as follows.

service:
	service_name: nginx
    image: nginx
    config:
    	source: nginx-conf
        target: /etc/nginx/nginx.conf
....
config:
	nginx-conf:
    	file: ./nginx/nginx-config.conf

Docker Swarm Config

💡
Note: If you inspect the config using docker config inspect <config_name> and decode the provided base64 hash, then your config will be visible. This is because the config option hashes the config in base64 format.

Conclusion


In this article, we learned about docker swarm, learned to set up a swarm network, deployed containerized workloads to docker swarm, played with secrets and utilized rollbacks and other important features provided to us by docker swarm.

Please comment below if you have any queries, I try to regularly update my articles to ensure legibility.