Skip to content

Commit 53bf360

Browse files
authored
Merge pull request #57 from galaxyproject/backup-remote-fixes
Fixes for remote rsync + connection options
2 parents c13f7c5 + 21fcdfc commit 53bf360

File tree

5 files changed

+69
-18
lines changed

5 files changed

+69
-18
lines changed

README.md

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,21 @@ Role Variables
9191
9292
### Backups ###
9393
94-
- `postgresql_backup_dir`: If set, enables [PITR][postgresql_pitr] backups. Set this to a directory where your database
95-
will be backed up (this can be any format supported by rsync, e.g. `user@host:/path`). The most recent backup will be
96-
in a subdirectory named `current`.
94+
This role can deploy and schedule the configuration and scripts to maintain Postgresql [PITR][postgresql_pitr] backups.
95+
96+
Full backups will be made on the configured interval, whereas write-ahead-log (WAL) segments between full backups will
97+
be archived to `{{ postgresql_backup_dir }}/wal_archive/` when instructed by the PostgreSQL server. WAL segments can be
98+
removed from this directory once the oldest backup referencing them has been removed. This is done automatically for you
99+
by the backup script if `postgresql_backup_dir` is mounted locally.
100+
101+
When `postgresql_backup_dir` is a remote rsync path (containing a "`:`"), the backup script will still maintain backups
102+
(including deleting older full backups) but cannot prune the `wal_archive/` directory automatically. If you are able to
103+
install the standard `pg_archivecleanup` utility from the PostgreSQL client package on your backup server, you can run
104+
this role's backup script with the `--clean-archive` option directly on the backup server instead.
105+
106+
- `postgresql_backup_dir`: If set, enables PITR backups. Set this to a directory where your database will be backed up
107+
(this can be any format supported by rsync, e.g. `user@host:/path`). The most recent backup will be in a subdirectory
108+
named `current`.
97109
98110
- `postgresql_backup_local_dir`: Filesystem path on the PostgreSQL server where backup scripts will be placed.
99111
@@ -122,9 +134,8 @@ Standard install: Default `postgresql.conf`, `pg_hba.conf` and default version f
122134
---
123135
124136
- hosts: dbservers
125-
remote_user: root
126137
roles:
127-
- postgresql
138+
- galaxyproject.postgresql
128139
```
129140

130141
Use the pgdg packages on a Debian-based host:
@@ -133,11 +144,10 @@ Use the pgdg packages on a Debian-based host:
133144
---
134145

135146
- hosts: dbservers
136-
remote_user: root
137147
vars:
138148
postgresql_flavor: pgdg
139149
roles:
140-
- postgresql
150+
- galaxyproject.postgresql
141151
```
142152
143153
Use the PostgreSQL 9.5 packages and set some `postgresql.conf` options and `pg_hba.conf` entries:
@@ -146,7 +156,6 @@ Use the PostgreSQL 9.5 packages and set some `postgresql.conf` options and `pg_h
146156
---
147157
148158
- hosts: dbservers
149-
remote_user: root
150159
vars:
151160
postgresql_version: 9.5
152161
postgresql_conf:
@@ -155,18 +164,44 @@ Use the PostgreSQL 9.5 packages and set some `postgresql.conf` options and `pg_h
155164
postgresql_pg_hba_conf:
156165
- host all all 10.0.0.0/8 md5
157166
roles:
158-
- postgresql
167+
- galaxyproject.postgresql
159168
```
160169

161-
Enable backups to /archive
170+
Enable backups to /archive:
162171

163172
```yaml
164173
- hosts: all
165-
remote_user: root
166174
vars:
167175
postgresql_backup_dir: /archive
168176
roles:
169-
- postgresql
177+
- galaxyproject.postgresql
178+
```
179+
180+
Enable backups to /archive on a remote server:
181+
182+
```yaml
183+
- hosts: dbservers
184+
vars:
185+
postgresql_backup_dir: backup.example.org:/archive
186+
roles:
187+
- galaxyproject.postgresql
188+
189+
- hosts: backupservers
190+
tasks:
191+
- name: Install PostgreSQL scripts
192+
ansible.builtin.apt:
193+
name: postgresql-common
194+
- name: Copy backup script
195+
ansible.builtin.copy:
196+
src: roles/galaxyproject.postgresql/files/backup.py
197+
dest: /usr/local/bin/pgbackup.py
198+
mode: "0755"
199+
- name: Schedule WAL pruning
200+
ansible.builtin.cron:
201+
name: Prune PostgreSQL Archived WALs
202+
hour: 22
203+
minute: 0
204+
job: /usr/local/bin/pgbackup.py --clean-archive /archive
170205
```
171206

172207
License

defaults/main.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ postgresql_backup_local_dir: >-
1717
'/var/lib/postgresql' if ansible_os_family == 'Debian' else '~postgres') }}/backup
1818
postgresql_create_backup_dir: true
1919

20+
# Controls whether the wal_archive directory is cleaned and whether postgresql_create_backup_dir can be used
21+
postgresql_backup_dir_is_remote: "{{ postgresql_backup_dir[0] != '/' }}"
22+
2023
# Options used for the WAL archive command - do not change this unless you have read the PITR documentation and
2124
# understand how this command must work.
2225
postgresql_archive_wal_rsync_args: '--ignore-existing -ptg --info=skip1'
@@ -31,11 +34,13 @@ postgresql_backup_keep: 30
3134

3235
__postgresql_pgdg_bin_dir: "{{ '/usr/pgsql-' ~ (postgresql_version | replace('.', '')) ~ '/bin' }}"
3336
postgresql_backup_command: >-
34-
{{ postgresql_backup_local_dir | quote }}/bin/backup.py
37+
{{ postgresql_backup_python_executable }} {{ postgresql_backup_local_dir | quote }}/bin/backup.py
3538
{{ '--rsync-connect-opts ' ~ (postgresql_backup_rsync_connect_opts | quote) if postgresql_backup_rsync_connect_opts else '' }}
3639
--rsync-backup-opts {{ postgresql_backup_rsync_backup_opts | regex_replace('^-', '\-') | quote }}
3740
--keep {{ postgresql_backup_keep | quote }}
3841
{{ '--pg-bin-dir ' ~ __postgresql_pgdg_bin_dir if ansible_os_family == 'RedHat' else '' }}
39-
--backup --clean-archive {{ postgresql_backup_dir | quote }}
42+
--backup {{ postgresql_backup_dir_is_remote | ternary('', '--clean-archive') }} {{ postgresql_backup_dir | quote }}
43+
44+
postgresql_backup_python_executable: "python"
4045

4146
postgresql_default_auth_method: "{{ (postgresql_version is version('13', '>')) | ternary('scram-sha-256', 'md5') }}"

files/backup.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
except ImportError:
3131
from pipes import quote as shlex_quote
3232

33-
import psycopg2
33+
try:
34+
import psycopg2
35+
except ImportError:
36+
psycopg2 = None
3437

3538

3639
RSYNC_EXCLUDES = (
@@ -109,7 +112,7 @@ def pg_major_version(self):
109112
def rsync_cmd(self):
110113
cmd = ['rsync']
111114
if self._rsync_opts:
112-
cmd.extend(shlex.split(rsync_opts))
115+
cmd.extend(shlex.split(self._rsync_opts))
113116
return cmd
114117

115118
@property
@@ -147,6 +150,8 @@ def parse_args(argv):
147150
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Verbose output')
148151
parser.add_argument('backup_path', help='Backup to location (rsync-compatible string)')
149152
args = parser.parse_args(argv)
153+
if args.backup and psycopg2 is None:
154+
parser.error('--backup specified but psycopg2 could not be imported')
150155
if args.clean_archive and ':' in args.backup_path:
151156
parser.error('--clean-archive cannot be used with remote backup directories')
152157
return args

tasks/backup.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
mode: 0750
1919
state: directory
2020
path: "{{ postgresql_backup_dir }}"
21-
when: postgresql_backup_dir[0] == '/' and postgresql_create_backup_dir
21+
when: not postgresql_backup_dir_is_remote and postgresql_create_backup_dir
2222

2323
- name: Install backup script templates
2424
template:

templates/archive_wal.sh.j2

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@ wal_archive_dir={{ (postgresql_backup_dir ~ '/wal_archive') | quote }}
1313
file_path="$1"
1414
file_name="$2"
1515

16+
{% if postgresql_backup_dir_is_remote %}
17+
empty=$(mktemp -d -t ansible-postgresql-empty.XXXXXX)
18+
rsync {{ postgresql_backup_rsync_connect_opts }} "${empty}/" "$wal_archive_dir"
19+
rmdir "$empty"
20+
{% else %}
1621
mkdir -p "$wal_archive_dir"
22+
{% endif %}
1723

1824
# If rsync outputs anything to stdout, the destination already existed, which should not happen
19-
if [ -n "$(rsync {{ postgresql_archive_wal_rsync_args }} "$file_path" "$wal_archive_dir")" ]; then
25+
if [ -n "$(rsync {{ postgresql_backup_rsync_connect_opts }} {{ postgresql_archive_wal_rsync_args }} "$file_path" "$wal_archive_dir")" ]; then
2026
echo "ERROR: ${wal_archive_dir}/${file_name} already exists, overwriting is not allowed!"
2127
exit 1
2228
fi

0 commit comments

Comments
 (0)