Skip to content
Docker Swarm

Docker Swarm
Since v1.0.0

Introduction

The Docker Swarm provider scales replicated services to zero replicas when their session expires, and back up when a request needs them. Sablier runs as a service on a manager node and calls the Docker API through the node’s Docker socket.

Install Sablier

Deploy Sablier as a stack from a manager node:

docker stack deploy -c sablier.yml sablier
# Sablier as a Swarm service:
#   docker stack deploy -c sablier.yml sablier
services:
  sablier:
    image: sablierapp/sablier:1.19.0 # x-release-please-version
    command:
      - start
      - --provider.name=docker_swarm
    environment:
      # sablier.running-hours windows use Sablier's local time.
      TZ: Europe/Paris
    volumes:
      # The Docker socket of the manager node Sablier runs on.
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - sablier
    deploy:
      placement:
        constraints:
          # Only manager nodes serve the service API.
          - node.role == manager

networks:
  # Attach your reverse proxy to this network to reach http://sablier:10000.
  sablier:
    name: sablier
    driver: overlay
    attachable: true
  • Run Sablier on a manager node. Only managers serve the service API, so the placement constraint keeps Sablier there.
  • Mount the manager’s Docker socket. Sablier lists, inspects and scales services through it.

Reverse proxy plugins reach Sablier at http://sablier:10000. Attach your reverse proxy to the sablier overlay network to resolve that name.

Usage example

Opt a service in with the sablier.enable service label, and give it a group:

# A service Sablier scales to zero when idle and back up on demand:
#   docker stack deploy -c whoami.yml demo
services:
  whoami:
    image: sablierapp/mimic:v0.3.3
    command: ["-healthy=true"]
    # Sablier reports the service ready once its tasks pass this healthcheck.
    healthcheck:
      test: ["CMD", "/mimic", "healthcheck"]
      interval: 2s
    deploy:
      # Service labels, not container labels: Sablier reads deploy.labels.
      labels:
        - sablier.enable=true
        - sablier.group=whoami

Deploy it with docker stack deploy -c whoami.yml demo. Then configure your reverse proxy to call Sablier for the whoami group in front of the service. On the first request, Sablier scales the service to one replica and holds the request, or shows a waiting page, until its task passes its healthcheck. When the session expires, Sablier scales it back to zero.

Sablier scales opted-in services to zero when it starts. A service you deploy afterwards keeps running until its first session expires, unless you stop externally-started instances.

Provider reference

Configuration

OptionValueDescription
--provider.namedocker_swarm or swarmSelects the Docker Swarm provider.

The provider has no options of its own. Sablier connects to Docker with the standard Docker environment variables, such as DOCKER_HOST. Without them, it uses the socket at /var/run/docker.sock.

Every --provider.* option that is not provider-specific also applies; see the CLI reference.

Workloads

Replicated services

Sablier scales replicated services by updating their replica count:

Start ignores deploy.replicas. To run several replicas, set sablier.active.replicas to the same count:

services:
  web:
    deploy:
      replicas: 3
      labels:
        - sablier.enable=true
        - sablier.group=web
        - sablier.active.replicas=3 # without it, Sablier starts one replica

Instance names

A reverse proxy can target one service by name instead of a group. The whoami service of the usage example, deployed in the demo stack, is named:

demo_whoami

The instance name is the exact service name, as docker service ls lists it. docker stack deploy prefixes each service with the stack name and _.

Prefer groups: a name ties your proxy configuration to the stack name.

Labels

On Docker Swarm, Sablier settings are sablier.* service labels, under deploy.labels:

services:
  team-api:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=team-a,team-b
  • Put the labels under deploy.labels. Sablier reads the service’s labels only. The service-level labels key sets container labels, which Sablier ignores on Swarm.
  • sablier.enable must be exactly true. Sablier finds services with a sablier.enable=true label filter.

The label reference lists every key.

Services created without a stack file

With docker service create, set the keys with --label, which sets service labels. --container-label sets container labels, which Sablier ignores.

Workload settings

Each excerpt below is the start of a complete stack file in examples/docker-swarm.

Enable
Since v1.4.0

sablier.enable=true opts the service in. It is required, must be a service label, and any other value is ignored. See the usage example.

Group
Since v1.4.0

sablier.group puts the service in one or more comma-separated groups. A session for any of them starts it. Without it, the service is in the default group.

services:
  team-api:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=team-a,team-b

Learn more about groups.

Ready after
Since v1.13.0

sablier.ready-after keeps reporting the service as not ready for a settling delay after all its tasks first run. Use it when a task passes its healthcheck before it can serve traffic.

services:
  slow-starter:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=slow-starter
        - sablier.ready-after=30s

Learn more about delaying readiness.

Ready on start
Since v1.15.0

sablier.ready-on-start=true reports the service ready as soon as Sablier sends the scale-up, without waiting for its tasks.

services:
  fast-starter:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=fast-starter
        - sablier.ready-on-start=true

Learn more about marking ready on start.

Running hours
Since v1.13.0

  • sablier.running-hours keeps the service running during a daily window.
  • sablier.running-days limits that window to some weekdays, since v1.15.0.
services:
  office-app:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=office-app
        - sablier.running-hours=09:00-18:00
        - sablier.running-days=Mon,Tue,Wed,Thu,Fri

The window uses Sablier’s local time. Set the time zone with the TZ environment variable of the Sablier service:

    environment:
      # sablier.running-hours windows use Sablier's local time.
      TZ: Europe/Paris

Learn more about running hours.

Anti-affinity
Since v1.15.0

sablier.anti-affinity lists groups the service backs off from: Sablier keeps it idle while any of them has an active session.

services:
  batch-worker:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=batch
        - sablier.anti-affinity=streaming

Learn more about anti-affinity.

Scale instead of stop
Since v1.13.0

Keep the service running when idle, optionally throttled, instead of scaling it to zero:

  • idle.replicas set to 1 or more keeps that many replicas when idle.
  • idle.cpu and idle.memory set the CPU and memory limits while idle.
  • The matching active.* labels restore them on the next session.

CPU is in decimal cores, such as 0.5. Memory uses Docker units, such as 64m or 1g.

services:
  warm-api:
    deploy:
      labels:
        - sablier.enable=true
        - sablier.group=warm-api
        - sablier.idle.replicas=1
        - sablier.idle.cpu=0.1
        - sablier.idle.memory=64m
        - sablier.active.cpu=2
        - sablier.active.memory=512m
Changing limits updates the service’s task template, so Swarm replaces its tasks with a rolling update. Sablier changes limits, never reservations. An idle limit stays in place on the next session unless you set its active counterpart.

Learn more about scaling instead of stopping.

Readiness

Sablier compares the desired and running tasks of a service, and reports it:

  • stopped when it has no desired task: it is scaled to 0.
  • ready when all desired tasks are running.
  • starting otherwise.

Swarm marks a task running only once its container passes its healthcheck. Give your services one: without it, a task runs as soon as its container starts.

Limitations

  • Sablier must reach the Docker API of a manager node: worker nodes do not serve the service API.
  • Only replicated services are supported, not global services or jobs.
  • Start always scales to sablier.active.replicas, one replica by default, whatever deploy.replicas says.
  • A service is ready only when all its desired tasks run: no option accepts the first ready task.
  • Sablier does not start depends_on services. Put dependencies in the same group instead.
  • Block I/O throttling is not available on Docker Swarm.
  • docker stack deploy sets each service back to its deploy.replicas, or one replica when unset. Redeploying a stack starts its stopped services, and Sablier treats them as started externally.

Examples