Skip to content

Instantly share code, notes, and snippets.

@joshbrooks
Created August 12, 2021 02:41
Show Gist options
  • Save joshbrooks/918c6c093114002c97225894f632ff5e to your computer and use it in GitHub Desktop.
Save joshbrooks/918c6c093114002c97225894f632ff5e to your computer and use it in GitHub Desktop.
This is a single file class to do a backup; rsync that backup over to a local dir; and load that as initial data in a postgis container
#!/usr/bin/env python
import docker
import logging
from docker import DockerClient
import time
import subprocess
import sys
from dataclasses import dataclass
logger = logging.getLogger()
logger.setLevel(logging.INFO)
@dataclass
class ContainerRebuild:
# Server config
user: str
host: str
db: str
tmpdir: str
# Local container config
# Note these are for local container and
# django setting, not for host
postgres_user: str
postgres_db: str
postgres_password: str
# Container config
image: str
tag: str
def rsync(self):
"""
Runs pg_dump on the server, and then 'rsyncs' the result over
"""
logger.info("Calling pg_dump on the server")
subprocess.call(
F"ssh {self.user}@{self.host} pg_dump --format=custom --compress=0 postgresql:///{self.db} --format=directory -f {self.tmpdir}".split()
)
logger.info("Calling rsync")
subprocess.call(
F"rsync -avz {self.user}@{self.host}:{self.tmpdir} .".split()
)
logger.info("Remove temp backup dir")
subprocess.call(F"ssh {self.user}@{self.host} rm -rf {self.tmpdir}".split())
logger.info('Rsync complete')
def start_container(self, client):
"""
Run a container to load the data
This will run a postgis container with the specified data
and wait until it is ready to connect
"""
logger.info("Starting container")
container = client.containers.run(
image = "postgis/postgis:12-3.1",
environment = {
"POSTGRES_USER": self.postgres_user,
"POSTGRES_DB": self.postgres_db,
"POSTGRES_PASSWORD": self.postgres_password
},
detach=True,
)
logger.info("Awaiting postgres, this may take some time...")
ready_command = ["pg_isready", "-h", "localhost", "-U", self.postgres_user]
logger.info('Checking for postgres')
response = container.exec_run(ready_command)
while response[0] != 0:
logger.info(".")
time.sleep(2)
response = container.exec_run(ready_command)
logger.info("Postgres reported ready")
return container
def write_archive(self, container):
"""
Write content of the "/var/lib/postgresql/data" as 'pg_data_dir.tar'
"""
logger.info('Copy content of the "/var/lib/postgresql/data" as pg_data_dir.tar')
bits, stat = container.get_archive("/var/lib/postgresql/data")
with open("./pg_data_dir.tar", "wb") as volume_data:
for chunk in bits:
volume_data.write(chunk)
def run(self):
self.rsync()
client = docker.from_env() # type: DockerClient
container = self.start_container(client)
container.exec_run(cmd=f"pg_restore -u {self.postgres_user} -d {self.postgres_db} /source", user="postgres")
self.write_archive(container)
container.stop()
container.remove()
client.images.build(path=".", tag=self.tag)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment