postgresql: Add a script to upgrade PostgreSQL data.

This scripts the steps given in the upgrade documentation, altered for
supporting Docker managed volumes.
This commit is contained in:
Alex Vandiver
2022-10-25 22:11:11 -04:00
parent 74aea021ce
commit cd348fb093
2 changed files with 120 additions and 61 deletions

View File

@@ -393,6 +393,13 @@ latest minor release within a major release series.
(E.g. authentication settings for `memcached` became mandatory in
the `2.1.2` release).
**Note:** Do not make any changes to the database version or
volume. If there is a difference in database version, leave those
unchanged for now, and complete that upgrade separately after the
Zulip upgrade; see [the section below][pg-upgrade].
[pg-upgrade]: #upgrading-zulipzulip-postgresql-to-14
3. Verify that your updated `docker-compose.yml` points to the desired image version,
e.g.:
```yml
@@ -447,68 +454,17 @@ Then stop and restart the container as described in the previous section.
### Upgrading zulip/zulip-postgresql to 14
These instructions assume that you have not changed the default
Postgres data path (`/opt/docker/zulip/postgresql/data`) in your
`docker-compose.yml`. If you have changed it, please replace all
occurences of `/opt/docker/zulip/postgresql/data` with your path.
The Docker Compose configuration for version 6.0-0 and higher default
to using PostgreSQL 14, as the previously-used PostgreSQL 10 is no
longer supported. Because the data is specific to the version of
PostgreSQL which is running, it must be dumped and re-loaded into a
new volume to upgrade. PostgreSQL 14 will refuse to start if provided
with un-migrated data from PostgreSQL 10.
1. Make a backup of your Zulip Postgres data dir.
2. Stop the Zulip container:
```
docker-compose stop zulip
```
3. Create a new (upgraded) Postgres container using a different data directory:
```
docker run -d \
--name postgresnew \
-e POSTGRES_DB=zulip \
-e POSTGRES_USER=zulip \
-e POSTGRES_PASSWORD=zulip \
-v /opt/docker/zulip/postgresql/new:/var/lib/postgresql/data:rw \
zulip/zulip-postgresql:14
```
4. Use `pg_dumpall` to dump all data from the existing Postgres container to the
new Postgres container, and reset the password (for SCRAM-SHA-256 auth upgrade):
```
docker-compose exec database pg_dumpall -U zulip | \
docker exec -i postgresnew psql -U zulip
echo "ALTER USER zulip WITH PASSWORD 'REPLACE_WITH_SECURE_POSTGRES_PASSWORD';" |
docker exec -i postgresnew psql -U zulip
```
5. Stop and remove both Postgres containers:
```
docker-compose rm --stop database
docker stop postgresnew
docker rm postgresnew
```
6. Edit your `docker-compose.yml` to use the `zulip/zulip-postgresql:14` image
for the `database` container.
7. Replace the old Postgres data directory with upgraded data directory:
```
sudo mv /opt/docker/zulip/postgresql/data /opt/docker/zulip/postgresql/old
sudo mv /opt/docker/zulip/postgresql/new /opt/docker/zulip/postgresql/data
```
8. Start Zulip up again:
```
docker-compose up
```
That should be it. Your Postgres data has now been updated to use the
`zulip/zulip-postgresql` image.
The provided `upgrade-postgresql` tool will dump the contents of the
`postgresql` image's volume, create a new PostgreSQL 14 volume,
perform the necessary migration, update the `docker-compose.yml`
file to match, and re-start Zulip.
### Upgrading from the old galexrt/docker-zulip

103
upgrade-postgresql Executable file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
set -eux
new_version=14
# Require the `yq` tool
if ! command -v yq >/dev/null; then
echo "You must install the 'yq' tool to use this script."
exit 1
fi
image=$(yq ".services.database.image" docker-compose.yml)
if [[ $image =~ ^zulip/zulip-postgresql:([0-9]+)$ ]]; then
old_version="${BASH_REMATCH[1]}"
else
echo "Unexpected PostgreSQL image: $image"
exit 1
fi
volume_mount=$(yq ".services.database.volumes.0" docker-compose.yml)
if [[ "$volume_mount" =~ ^([^:]+):/var/lib/postgresql/data:rw$ ]]; then
old_mountpoint="${BASH_REMATCH[1]}"
else
echo "Unexpected volume mount: $volume_mount"
exit 1
fi
if [ "$new_version" -eq "$old_version" ]; then
echo "PostgreSQL image is already version $new_version!"
exit 1
fi
# Create a new volume for the data; scope it with the current
# directory, like docker-compose
docker_compose_project="$(basename "$(pwd)")"
new_volume="postgresql-$new_version"
full_new_volume="${docker_compose_project}_${new_volume}"
docker volume create "$full_new_volume"
trap 'docker volume --force "$full_new_volume"' EXIT
# Start a new PostgreSQL container of the right version to read in the
# dumped database and write a new data dir on the new volume
temp_container=$(
docker run -d \
-e POSTGRES_DB=zulip \
-e POSTGRES_USER=zulip \
-e POSTGRES_PASSWORD=zulip \
-v "$full_new_volume:/var/lib/postgresql/data:rw" \
--health-cmd 'psql -U zulip -c "select 1"' \
--health-interval 10s \
"zulip/zulip-postgresql:$new_version"
)
trap 'docker volume rm --force "$full_new_volume"; docker rm --force "$temp_container"' EXIT
# Wait for the new PostgreSQL container to become available
tries=0
while [ "$(docker inspect --format='{{json .State.Health.Status}}' "$temp_container")" != '"healthy"' ]; do
tries=$((tries + 1))
if [ "$tries" -gt 5 ]; then
echo "PostgreSQL $new_version container failed to start!"
exit 1
fi
sleep 10
done
# Ensure database is running
docker-compose up --wait database
# Stop the zulip processes which talk to the database
zulip_is_running=$(docker-compose ps --filter status=running --services | grep zulip || true)
if [ -n "$zulip_is_running" ]; then
docker-compose stop zulip
fi
# Transfer the data to the new container
docker-compose exec database pg_dumpall -U zulip |
docker exec -i "$temp_container" psql -U zulip
if [ "$old_version" -eq "10" ]; then
# Upgrade MD5 password to SCRAM-SHA-256. We escape all 's by doubling them.
database_password=$(yq .services.database.environment.POSTGRES_PASSWORD docker-compose.yml |
perl -pe "s/'/''/g")
echo "ALTER USER zulip WITH PASSWORD '$database_password';" |
docker exec -i "$temp_container" psql -U zulip
fi
# Stop the running database
docker-compose rm --force --stop database
# Stop the temporary PostgreSQL container
docker stop "$temp_container"
docker rm "$temp_container"
trap '' EXIT
# Update the docker-compose.yml file for the new version and data path
IMAGE="zulip/zulip-postgresql:$new_version" yq -i '.services.database.image = strenv(IMAGE)' docker-compose.yml
VOLUME="$new_volume:/var/lib/postgresql/data:rw" yq -i '.services.database.volumes.0 = strenv(VOLUME)' docker-compose.yml
VOLUME="$new_volume" yq -i 'with(.volumes.[strenv(VOLUME)]; . = "" | . tag="!!null")' docker-compose.yml
# Restart zulip, and the new database container
docker-compose up --wait
echo "Old data from PostgreSQL $old_version is available in $old_mountpoint"