Browse Source

support stopping containers before snapshot

master
Sean Johnson 7 months ago
parent
commit
54f358312b
  1. 3
      resource/Dockerfile
  2. 54
      resource/tools/backup-volumes.py

3
resource/Dockerfile

@ -46,6 +46,7 @@ ADD tools /tools
RUN apk add --no-cache bash curl jq lvm2-libs python3 && \
curl https://bootstrap.pypa.io/get-pip.py | python3 - && \
pip install /wheel/*.whl
pip install /wheel/*.whl && \
rm -rf /wheel
ENTRYPOINT ["/bin/convoy"]

54
resource/tools/backup-volumes.py

@ -10,8 +10,11 @@ import re
import shutil
import sys
import traceback
from contextlib import asynccontextmanager
from datetime import datetime
from typing import Iterator, List, Optional, Sequence, T, Type, Union
from typing import AsyncContextManager, Iterator, List, Optional, Sequence, T, Type, Union
import aiodocker
_CONVOY_DATE_FMT = "%a %b %d %H:%M:%S %z %Y"
BACKUP_DESTINATION = os.getenv("BACKUP_DESTINATION", None)
@ -19,10 +22,13 @@ BACKUP_INTERVAL = int(os.getenv("BACKUP_INTERVAL", 86400))
CONCURRENT_SNAPSHOTS = int(os.getenv("CONCURRENT_SNAPSHOTS", 4))
CONVOY_BIN = shutil.which("convoy") or os.getenv("CONVOY_BIN")
CONVOY_SOCKET = os.getenv("CONVOY_SOCKET", "/var/run/convoy/convoy.sock")
DOCKER_URL = os.getenv("DOCKER_URL", "unix:///var/run/docker.sock")
KEEP_LAST_BACKUPS = int(os.getenv("KEEP_LAST_BACKUPS", 7))
KEEP_LAST_SNAPSHOTS = int(os.getenv("KEEP_LAST_SNAPSHOTS", 7))
ONLY_MATCHING = re.compile(os.getenv("ONLY_MATCHING", r"^.*$"))
_docker = aiodocker.Docker(url=DOCKER_URL)
def timestamp_filename() -> str:
return datetime.now().strftime("%Y-%m-%dT%H-%M-%SZ%z")
@ -151,9 +157,13 @@ async def backup_all_volumes():
async def backup_volume(vol_name: str, vol_data: str):
log_stdout(f"Starting backup for {vol_name}", vol_name=vol_name)
# Find any containers this volume is attached to.
attached_containers = await load_attached_containers(vol_name)
try:
# Create a new snapshot
snapshot_name = await perform_volume_snapshot(vol_name, vol_data)
async with stopped_containers(attached_containers):
# Create a new snapshot
snapshot_name = await perform_volume_snapshot(vol_name, vol_data)
# Remove old snapshots if there are more than KEEP_LAST_SNAPSHOTS
await clean_older_snapshots(vol_data)
@ -304,6 +314,44 @@ async def clean_older_snapshots(vol_data: dict):
log_stdout(f"Done cleaning snapshots for volume {vol_name}", vol_name=vol_name)
async def load_attached_containers(vol_name: str) -> List[aiodocker.containers.DockerContainer]:
""" Returns a list of container dictionaries that have attached `vol_name`.
"""
containers = []
for container in await _docker.containers.list():
mounts = (await container.show())["Mounts"]
for mount in mounts:
if (mount["Type"] == "volume" and
mount.get("Driver") == "convoy" and
mount.get("Name") == vol_name):
containers.append(container)
break
return containers
@asynccontextmanager
async def stopped_containers(containers: Sequence[aiodocker.containers.DockerContainer]) -> AsyncContextManager:
""" Stops any containers annotated with the `convoy-tools.backup.stop-before-snapshot`,
starting them back up after yielding to the captured context.
"""
stopped = []
for container in containers:
labels = (await container.show())["Config"]["Labels"]
stop_before_snapshot = labels.get("convoy-tools.backup.stop-before-snapshot", "false").lower()
if stop_before_snapshot in ["true", "yes", "t", "y", "1", "on"]:
await container.stop()
stopped.append(container)
yield
for container in stopped:
await container.start()
async def main():
sys.excepthook = excepthook

Loading…
Cancel
Save