borgmatic-collective / docker-borgmatic

Borgmatic in Docker
GNU General Public License v3.0
358 stars 93 forks source link

Using docker in docker to stop and restart containers before/after backup #86

Closed fschrempf closed 3 years ago

fschrempf commented 3 years ago

I'm using docker-borgmatic to backup volumes of several other containers. To get consistent data during the backup, I would like to stop the containers in the before_backup hook and restart them in the after_backup hook.

To do this I need to run docker in docker which shouldn't be a problem if the docker engine would be installed in the image. Is this something that could be added or are there any objections?

toastie89 commented 3 years ago

Hi Frieder, backup is quite a sensible topic, so I would prefer to keep the number of binaries in this image to an absolute minimum.

Possibly you could run another container with docker installed for managing start /stop of your services. The hooks could communicate with this container.

grantbevis commented 3 years ago

I think adding docker to this image is slightly out of scope. Id suggest looking if portainer or some other docker engine interface could accept a webhook to stop/start your containers

fschrempf commented 3 years ago

@toastie89 @b3vis Ok, got it. After looking at the capabilities of the Docker Engine API, the easiest way would probably be to mount the docker socket from the host into the backup container and do API requests to the socket. The problem here is that alpine has none of the common CLI utils like curl, nc -U or socat to send requests to a socket. Would adding curl be in scope? :wink: I guess that could come in handy for other cases too where hooks need to send requests to some REST API.

toastie89 commented 3 years ago

@fschrempf, from what I see nc is part of busybox, would this do the job?

docker run -it --rm b3vis/borgmatic nc
BusyBox v1.31.1 () multi-call binary.

Usage: nc [OPTIONS] HOST PORT  - connect
nc [OPTIONS] -l -p PORT [HOST] [PORT]  - listen
fschrempf commented 3 years ago

@fschrempf, from what I see nc is part of busybox, would this do the job?

Hm, at first I thought that it doesn't work, as the busybox netcat is missing the -U option to connect to a unix socket. But after some more investigation I found out that using local: does the same. This made me come up with the following commands to stop/start my containers from a hook without any additional tools:

echo -ne "POST /v1.41/containers/<container_id_or_name>/stop HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc local:/var/run/docker.sock 80
echo -ne "POST /v1.41/containers/<container_id_or_name>/start HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc local:/var/run/docker.sock 80

As this seems to do the job quite well, I consider this solved. Thanks for the help!

toastie89 commented 3 years ago

@fschrempf, nice! Possibly this example could be added to the README?

fschrempf commented 3 years ago

@fschrempf, nice! Possibly this example could be added to the README?

See #88 for a short documentation of my approach.

gjgress commented 7 months ago

Sorry to revive an old issue, but I was hoping to get some additional guidance on setting up these hooks. For security reasons, I am using a docker socket proxy container. After I connect borgmatic to the same network, how would I go about sending the POST signal to the docker socket on the socket-proxy container?

Minimal docker-compose setup:

networks:
  socket_proxy:
    name: socket_proxy
services:
  app:
  socket-proxy:
    container_name: socket-proxy
    image: tecnativa/docker-socket-proxy
    restart: always
    networks:
      - socket_proxy
    ports:
      - 127.0.0.1:2375:2375
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - LOG_LEVEL=info # debug,info,notice,warning,err,crit,alert,emerg
      ## Variables match the URL prefix (i.e. AUTH blocks access to /auth/* parts of the API, etc.).
      # 0 to revoke access.
      # 1 to grant access.
      ## Granted by Default
      - EVENTS=1
      - PING=1
      - VERSION=1
      ## Revoked by Default
      # Security critical
      - AUTH=0
      - SECRETS=0
      - POST=1 # Watchtower
      # Not always needed
      - ALLOW_START=1
      - ALLOW_STOP=1
      - BUILD=0
      - COMMIT=0
      - CONFIGS=0
      - CONTAINERS=1 # Traefik, portainer, etc.
      - DISTRIBUTION=0
      - EXEC=0
      - IMAGES=1 # Portainer
      - INFO=1 # Portainer
      - NETWORKS=1 # Portainer
      - NODES=0
      - PLUGINS=0
      - SERVICES=1 # Portainer
      - SESSION=0
      - SWARM=0
      - SYSTEM=0
      - TASKS=1 # Portainer
      - VOLUMES=1 # Portainer
  borgmatic:
    image: ghcr.io/borgmatic-collective/borgmatic
    container_name: borgmatic
    volumes:
      - ${VOLUME_SOURCE}:/mnt/source:ro
      - ${VOLUME_TARGET}:/mnt/borg-repository      # backup target
      - ${VOLUME_ETC_BORGMATIC}:/etc/borgmatic.d/  # borgmatic config file(s) + crontab.txt
      - ${VOLUME_BORG_CONFIG}:/root/.config/borg   # config and keyfiles
      - ${VOLUME_SSH}:/root/.ssh                   # ssh key for remote repositories
      - ${VOLUME_BORG_CACHE}:/root/.cache/borg     # checksums used for deduplication
    environment:
      - TZ=${TZ}
      - BORG_PASSPHRASE=${BORG_PASSPHRASE}
      - DOCKER_HOST=socket-proxy
    networks:
      - socket_proxy  
fschrempf commented 7 months ago

@gjgress Did you try the example in the README and put in your proxy as host and tcp socket?

gjgress commented 6 months ago

I don't see anything in the README which discusses connecting a proxy as host and tcp socket. I've never used a proxy for tcp socket so I'm not even sure where to begin.

I ended up running borgmatic on the host system anyway, which I think makes more sense for my use-case. So my issue is now moot I suppose