-
Notifications
You must be signed in to change notification settings - Fork 175
Expand file tree
/
Copy pathdocker-compose.backup.yml
More file actions
181 lines (175 loc) · 6.46 KB
/
Copy pathdocker-compose.backup.yml
File metadata and controls
181 lines (175 loc) · 6.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# Backup and recovery stack, kept out of the main compose file so a normal
# `docker compose up` does not pull backup infrastructure.
#
# Run it on its own:
#
# docker compose -f docker-compose.backup.yml up -d --wait
# ./scripts/backup.sh --type full
# ./scripts/restore-test.sh
#
# MinIO stands in for Cloudflare R2, so the whole cycle - backup, off-site copy,
# restore, verification - runs without a cloud account. The only production
# difference is the endpoint and the credentials.
#
# The database container has pgBackRest installed rather than running it as a
# sidecar: archive_command executes inside PostgreSQL's own environment, so the
# binary has to be on the database host. Production installs the package the
# same way.
services:
# Self-signed certificate for MinIO. pgBackRest always talks TLS to an S3
# endpoint and Cloudflare R2 is HTTPS-only, so the local stand-in serves HTTPS
# too; the scripts trust it with verify-tls=n rather than pretending the
# production path is plaintext.
minio-certs:
image: alpine/openssl:latest
container_name: soroban-registry-minio-certs
entrypoint:
- /bin/sh
- -c
- |
set -e
if [ -s /certs/public.crt ] && [ -s /certs/private.key ]; then
echo "certificate already present"
exit 0
fi
openssl req -new -x509 -nodes -days 825 \
-newkey rsa:2048 \
-keyout /certs/private.key \
-out /certs/public.crt \
-subj "/CN=minio" \
-addext "subjectAltName=DNS:minio,DNS:localhost,IP:127.0.0.1"
chmod 644 /certs/public.crt
chmod 600 /certs/private.key
echo "self-signed certificate generated for CN=minio"
volumes:
- minio_certs:/certs
restart: "no"
# S3-compatible object storage standing in for Cloudflare R2.
minio:
image: minio/minio:latest
container_name: soroban-registry-minio
depends_on:
minio-certs:
condition: service_completed_successfully
command: server /data --certs-dir /certs --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
# Gives the bundled mc an alias to probe, over HTTPS like the server now
# serves; without it mc falls back to a plain-HTTP localhost alias.
MC_HOST_local: https://${MINIO_ROOT_USER:-minioadmin}:${MINIO_ROOT_PASSWORD:-minioadmin}@localhost:9000
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
- minio_certs:/certs:ro
healthcheck:
test: ["CMD", "mc", "--insecure", "ready", "local"]
interval: 5s
timeout: 5s
retries: 20
# Creates the backup bucket once MinIO is up, then exits.
minio-init:
image: minio/mc:latest
container_name: soroban-registry-minio-init
depends_on:
minio:
condition: service_healthy
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
BUCKET: ${CLOUDFLARE_R2_BUCKET:-soroban-registry-backups}
entrypoint: >
/bin/sh -c "
mc --insecure alias set local https://minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD} &&
mc --insecure mb --ignore-existing local/$${BUCKET} &&
echo 'backup bucket ready: '$${BUCKET}
"
restart: "no"
# The database being protected: PostgreSQL with pgBackRest installed and
# continuous archiving turned on.
postgres-backup:
build:
context: ./database/pgbackrest
dockerfile: Dockerfile
image: soroban-registry-postgres-pgbackrest:16
container_name: soroban-registry-db-backup
depends_on:
minio-init:
condition: service_completed_successfully
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set}
POSTGRES_DB: ${POSTGRES_DB:-soroban_registry}
PGBACKREST_STANZA: ${PGBACKREST_STANZA:-soroban-registry}
# So the scripts reach the registry database without being told where.
PGDATABASE: ${POSTGRES_DB:-soroban_registry}
env_file:
- path: ./database/pgbackrest/pgbackrest.env
required: false
# Optional local override, gitignored: point repo2 at real Cloudflare R2
# instead of MinIO. Later files win, so this replaces the MinIO settings.
- path: ./database/pgbackrest/pgbackrest.local.env
required: false
ports:
- "5434:5432"
volumes:
- backup_db_data:/var/lib/postgresql/data
- pgbackrest_repo:/var/lib/pgbackrest
- pgbackrest_spool:/var/spool/pgbackrest
# The backup and verify scripts run where pgBackRest is, which is here.
- ./scripts:/opt/scripts:ro
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-soroban_registry}",
]
interval: 5s
timeout: 5s
retries: 20
# A throwaway PostgreSQL used by scripts/restore-test.sh to prove a backup can
# actually be restored. It shares the repository volume so pgBackRest can
# restore into it, and holds no data of its own.
postgres-restore-target:
build:
context: ./database/pgbackrest
dockerfile: Dockerfile
image: soroban-registry-postgres-pgbackrest:16
container_name: soroban-registry-restore-target
profiles: ["restore-test"]
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set}
POSTGRES_DB: ${POSTGRES_DB:-soroban_registry}
PGBACKREST_STANZA: ${PGBACKREST_STANZA:-soroban-registry}
env_file:
- path: ./database/pgbackrest/pgbackrest.env
required: false
# Optional local override, gitignored: point repo2 at real Cloudflare R2
# instead of MinIO. Later files win, so this replaces the MinIO settings.
- path: ./database/pgbackrest/pgbackrest.local.env
required: false
ports:
- "5435:5432"
volumes:
- restore_target_data:/var/lib/postgresql/data
# Read-only: a restore reads the repository, it must never write to it.
- pgbackrest_repo:/var/lib/pgbackrest:ro
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-soroban_registry}",
]
interval: 5s
timeout: 5s
retries: 30
volumes:
minio_data:
minio_certs:
backup_db_data:
pgbackrest_repo:
pgbackrest_spool:
restore_target_data: