m-adawi / swarm-cd

SwarmCD: Declarative GitOps and Continuous Deployment tool for Docker Swarm.
GNU General Public License v3.0
68 stars 2 forks source link

SwarmCD

A declarative GitOps and Continuous Deployment tool for Docker Swarm.

Inspired by ArgoCD.

SwarmCD UI

Usage

In this example, we use SwarmCD to deploy the stack in the repo swarm-cd-example to a docker swarm cluster.

First we add the repo to the file repos.yaml

# repos.yaml
swarm-cd-example:
  url: "https://github.com/m-adawi/swarm-cd-example.git"

Then we define the stack in stacks.yaml

# stacks.yaml
nginx:
  repo: swarm-cd-example
  branch: main
  compose_file: nginx/compose.yaml

And finally, we deploy SwarmCD to the cluster using the following docker-compose file:

# docker-compose.yaml
version: '3.7'
services:
  swarm-cd:
    image: ghcr.io/m-adawi/swarm-cd:latest
    deploy:
      placement:
        constraints:
          - node.role == manager
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./repos.yaml:/app/repos.yaml:ro
      - ./stacks.yaml:/app/stacks.yaml:ro

Run this on a swarm manager node:

docker stack deploy --compose-file docker-compose.yaml swarm-cd

This will start SwarmCD, it will periodically check the stack repo for new changes, pulling them and updating the stack.

Manage Encrypted Secrets Using SOPS

You can use sops to encrypt secrets in git repos and have SwarmCD decrypt them before deploying or updating your stacks.

The stack nginx-ssl in the example repo has two secret files under nginx-ssl/secrets/ directory. You can configure SwarmCD files to decrypt them by setting the propertysops_files in a stack defenition.

# stacks.yaml
nginx-ssl:
    repo: swarm-cd-example
    branch: main
    compose_file: nginx-ssl/compose.yaml
    sops_files: 
      - nginx-ssl/secrets/www.example.com.crt
      - nginx-ssl/secrets/www.example.com.key

Then you need to set the SOPS environment variables that are required to decrypt the files. For example, if you used age to encrypt them, you have to mount the age key file to SwarmCD and set the environment variable SOPS SOPS_AGE_KEY_FILE to the path of the key file. See the following docker-compose example

version: '3.7'
services:
  swarm-cd:
    image: ghcr.io/m-adawi/swarm-cd:latest
    deploy:
      placement:
        constraints:
          - node.role == manager
    secrets:
      - source: age
        target: /secrets/age.key
    environment:
      - SOPS_AGE_KEY_FILE=/secrets/age.key
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./repos.yaml:/app/repos.yaml:ro
      - ./stacks.yaml:/app/stacks.yaml:ro
secrets:
  age:
    file: age.key

This way, SwarmCD will decrypt the files each time before it updates the stack.

Automatic SOPS secrets detection

Instead of specifying the paths of every single secrets you need to decrypt, you can use the sops_secrets_discovery: true option:

Please note that:

Connect SwarmCD to a remote docker socket

You can use the DOCKER_HOST environment variable to point SwarmCD to a remote docker socket, be it in the same swarm or a different host.

In the following example docker-socket-proxy talks directly to the host socket proxy, and SwarmCD connects to it:

version: '3.7'

services:
  socket_proxy:
    image: tecnativa/docker-socket-proxy:0.2.0
    deploy:
      placement:
        constraints: 
          - node.role == manager
    volumes: 
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      TZ: Europe/Rome
      INFO: 1
      SERVICES: 1
      NETWORKS: 1
      SECRETS: 1
      CONFIGS: 1
      POST: 1

  swarm-cd:
    image: ghcr.io/m-adawi/swarm-cd:1.1.0
    depends_on:
      - socket_proxy
    environment:
      DOCKER_HOST: tcp://socket_proxy:2375
    configs:
      - source: stacks
        target: /app/stacks.yaml
        mode: 0400
      - source: repos
        target: /app/repos.yaml
        mode: 0400

configs:
  stacks:
    file: ./stacks.yaml
  repos:
    file: ./repos.yaml

Give SwarmCD access to private registries

You can pass the authentication to private container registries via the ~/.docker/config.json file.

First, encode your credentials with base64 (here we use printf to avoid the trailing newline):

printf 'username:password' | base64

Then create the docker config file like this:

// docker-config.json
{
    "auths": {
        "my.registry.example": {
            "auth": "(base64 output here)"
        }
    }
}

Lastly, add the config file as secret and mount it to /root/.docker/config.json:

# docker-compose.yaml
version: '3.7'
services:
  swarm-cd:
    image: ghcr.io/m-adawi/swarm-cd:latest
    deploy:
      placement:
        constraints:
          - node.role == manager
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./repos.yaml:/app/repos.yaml:ro
      - ./stacks.yaml:/app/stacks.yaml:ro
    secrets:
      - source: docker-config
        target: /root/.docker/config.json
secrets:
  docker-config:
    file: docker-config.json

Documentation

See docs.