A declarative GitOps and Continuous Deployment tool for Docker Swarm.
Inspired by ArgoCD.
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.yamlAnd 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:roRun this on a swarm manager node:
docker stack deploy --compose-file docker-compose.yaml swarm-cdThis will start SwarmCD, it will periodically check the stack repo for new changes, pulling them and updating the stack.
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.keyThen you need to set the SOPS environment variables that are required to decrypt the files. Depending on the backend you used for sops encryption, the configuration can be a little different:
- If you used age
to encrypt, you have to mount the age key file to SwarmCD
and set the environment variable SOPS
SOPS_AGE_KEY_FILEto the path of the key file. - If you used gpg, you have to mount the file containing your gpg private
key in the container, and set the environment variable
SOPS_GPG_PRIVATE_KEY_FILEto the path of the gpg private key file. It is also possible to directly provide the gpg key in theSOPS_GPG_PRIVATE_KEYenvironment variable.
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 # or /secrets/private.gpg
environment:
- SOPS_AGE_KEY_FILE=/secrets/age.key
# or
- SOPS_GPG_PRIVATE_KEY_FILE=/secrets/private.gpg
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.keyThis way, SwarmCD will decrypt the files each time before it updates the stack.
Instead of specifying the paths of every single secrets you need to decrypt,
you can use the sops_secrets_discovery: true option:
- in the
config.yamlfile to enable it globally - in the
stacks.yamlfile for the individual stacks.
Please note that:
- if the global setting is set to
true, it ignores individual stacks overrides. - if the stack-level setting is set to
true, it ignores thesops_filessetting altogether.
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:latest
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.yamlYou 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' | base64Then 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.jsonSee docs.