====== Upgrading PostgreSQL 9.2 to 9.6 ======
This is a tutorial for upgrading PostgreSQL database on CentOS 7 server. Upgrading your database is necessary to have it properly patched, secure and to have all DB features your applications need. Upgrade process in this article installs another PostgreSQL engine alongside the old one and migrates data. Since original data are untouched during the upgrade, this howto is also suitable for production deployments.
In this howto we upgrade from PostgreSQL 9.2 (with systemd unit ''postgresql.service'' and base directory ''/data/pgsql/9.2'') to PostgreSQL 9.6 (with systemd unit ''postgresql-9.6.service'' and base directory ''/data/pgsql/9.6''). However, this tutorial is suitable for migrating between other versions too, assuming you take proper precautions.
This tutorial is written for CentOS 7 and may need adjustments for other Linux distributions.
==== Risks and considerations ====
When you upgrade the PostgreSQL database where the IdM repository is located, you should take care of a few things.
* CzechIdM application must be stopped during whole PostgreSQL upgrade.
* Upgrading the database may potentially damage your data.
* It is vital to have backups of database before upgrade.
* It is vital to have a procedure for confirming data was not corrupted during the DB upgrade.
* We advise to make another backup right after the upgrade, after you verify that data is OK.
==== Preparation and prerequisites====
Before you start the upgrade, check that you have all necessary thing prepared:
* Check that you have enough space on the disk.
* Space for backup that is made with ''pg\_dump''. Can be located on any volume.
* Enough space to hold raw copy of databases (basically at least ''du -sh /data/pgsql/9.2'' space). The free space should be located/available for the ''/data/pgsql/9.6'' directory.
* Check you have rollback plan for situation when upgrade doesn't go as planned.
* Create those temporary directories for upgrade (we usually have one mountpoint ''/data'' that is big enough to conatins everything we need or create during the upgrade).
mkdir /data/postgres_upgrade
mkdir /data/postgres_upgrade/backup_postgres92
mkdir /data/postgres_upgrade/backup_postgres96
chown postgres:root /data/postgres_upgrade/backup_postgres92
chown postgres:root /data/postgres_upgrade/backup_postgres96
* List all configuration options of old PostgreSQL that you use in ''postgresql.conf'' and ''pg\_hba.conf'' . Save this configs into ''/data/postgres\_upgrade/postgresql.conf.new'' and ''/data/postgres\_upgrade/pg\_hba.conf.new''.
* If you are using certificate and key - backup them from old postgres.
cp /data/pgsql/9.2/data/server.crt /data/postgres_upgrade/
cp /data/pgsql/9.2/data/server.key /data/postgres_upgrade/
===== Upgrade process =====
==== Turn off applications and change listening port of old postgres ====
Check that you have turned off all applications that use postgres(Tomcat - IdM).
During the upgrade, the PostgreSQL needs to be running, but we also have to make sure that no daemon accesses the database. We ensure this by changing the TCP port the database listens on.
In file ''/etc/systemd/system/postgresql.service'' set:
Environment=PGPORT=6000
Then restart postgres and check it's listening on port ''6000'':
systemctl daemon-reload
systemctl restart postgresql
systemctl status postgresql
netstat -nlpt | grep 6000
#expected output:
#tcp 0 0 127.0.0.1:6000 0.0.0.0:* LISTEN 1815/postmaster
netstat -nlpt | grep 5432
#expected output: nothing
==== Backup all databases on old postgres ====
Under user ''postgres'', create dumps of all databases you want to preserve (in this example is database czechidm and postgres).
cd /data/postgres_upgrade/backup_postgres92
su - postgres
pg_dump --compress=9 --create --port=6000 czechidm > czechidm_backup.sql.gz
pg_dump --compress=9 --create --port=6000 postgres > postgres_backup.sql.gz
==== Stop old postgres ====
systemctl stop postgresql
systemctl disable postgresql
==== Install new postgres ====
Install new postgres(example 9.6) and, create new data directory:
yum install -y postgresql96-server postgresql96-contrib pgstat2_96 pg_top96 postgresql96-libs
mkdir -p /data/pgsql/9.6/data/
chown -R postgres:postgres /data/pgsql/9.6/
chmod 700 /data/pgsql/9.6/
cp /usr/lib/systemd/system/postgresql-9.6.service /etc/systemd/system/
During the installation ''yum'' will complain that it cannot create symlinks to postgres binaries. This is because there are still binaries from the 9.2 version. Save the list of symlinks that could not be created so you can create them manually after uninstalling the 9.2 version.
Set up new data directory for newly installed postgres.
In file ''/etc/systemd/system/postgresql-9.6.service '' set:
Environment=PGDATA=/data/pgsql/9.6/data/
Environment=PGPORT=7000
In file ''~postgres/.bash_profile'' set:
PGDATA=/data/pgsql/9.6/data/
Reload setting and initialize new database (run all following commands as ''root''):
systemctl daemon-reload
/usr/pgsql-9.6/bin/postgresql96-setup initdb
systemctl start postgresql-9.6.service
systemctl status postgresql-9.6.service
==== Upgrade the database ====
Stop both postgres engines:
systemctl stop postgresql-9.6.service
systemctl stop postgresql
systemctl disable postgresql-9.6.service
systemctl disable postgresql
In postgres 9.2 there is bug in ''pg_ctl'' so you need do this workaround:
Backup ''pg_ctl'':
cp /usr/bin/pg_ctl /data/postgres_upgrade/pg_ctl
Modify the binary and set correct permitions:
mv /usr/bin/pg_ctl /usr/bin/pg_ctl-orig
echo '#!/bin/bash' > /usr/bin/pg_ctl
echo '"$0"-orig "${@/unix_socket_directory/unix_socket_directories}"' >> /usr/bin/pg_ctl
chmod +x /usr/bin/pg_ctl
=== Run the upgrade ===
Run ''pg\_upgrade'' binary from the new postgres and add path to both old and new binaries and data directories. You also need to add old and new port number:
su - postgres
/usr/pgsql-9.6/bin/pg_upgrade -v --old-datadir="/data/pgsql/9.2/data/" --new-datadir="/data/pgsql/9.6/data/" --old-bindir="/usr/bin/" --new-bindir="/usr/pgsql-9.6/bin/" --old-port=6000 --new-port=7000
This command will migrate the database for you and it takes some time to complete. After it finishes, the database has been upgraded and datafiles under new postgres base have been populated.
Do **NOT** delete old data directory yet.
Revert the changes you made to ''pg_ctl'' (if you had to do them).
mv -f /usr/bin/pg_ctl-orig /usr/bin/pg_ctl
Backup default configuration of new postgres. Then move prepared configuration options from ''/data/postgres\_upgrade/pg\_hba.conf.new'' and ''/data/postgres\_upgrade/postgresql.conf.new'' to new postgres's configuration with the necessary changes:
cp -v /data/pgsql/9.6/data/pg_hba.conf{,-orig}
cp -v /data/pgsql/9.6/data/postgresql.conf{,-orig}
cp /data/postgres_upgrade/pg_hba.conf.new /data/pgsql/9.6/data/pg_hba.conf
cp /data/postgres_upgrade/postgresql.conf.new /data/pgsql/9.6/data/postgresql.conf
Adjust settings for the new version of the database (some settings may be missing now and some may have become obsolete).
If you are using postgres certificate and key. Copy it from old postgres and set correct permissions.
cp /data/postgres_upgrade/server.crt /data/pgsql/9.6/data/
cp /data/postgres_upgrade/server.key /data/pgsql/9.6/data/
chown postgres:postgres /data/pgsql/9.6/data/server.crt
chown postgres:postgres /data/pgsql/9.6/data/server.key
==== Start and check postgres 9.6 ====
Start ''postgresql-9.6.service'':
systemctl start postgresql-9.6.service
systemctl enable postgresql-9.6.service
systemctl status postgresql-9.6.service
Check that you can connect to the database.
su - postgres
/usr/pgsql-9.6/bin/psql -p 7000
#print all databases
\l
#Go to the czechidm DB
\c czechidm
#Print all roles and check that you can read from database.
select * from idm_role;
#than exit
\q
# also try connect as app user who will use the database
psql -h localhost -U czechidm
Set default port of new postgres to 5432 (the port the 9.2 postgres originally ran on)
In file ''/etc/systemd/system/postgresql-9.6.service'' set:
Environment=PGPORT=5432
Restart postgres and check that it's running on port ''5432'':
systemctl daemon-reload
systemctl restart postgresql-9.6
systemctl status postgresql-9.6
netstat -nlpt | grep 7000
#expected output: nothing
netstat -nlpt | grep 5432
#expected output:
#tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 1815/postmaster
Then reboot system and check if postgres and other services started after reboot:
reboot
systemctl status postgresql-9.6
# the old postgresql should remain stopped
systemctl status postgresql
==== Start IdM ====
Start IdM or other application that uses postgres and check if all data are correct.
==== Backup database on postgres 9.6 ====
Create "after-upgrade" backup of migrated databases.
cd /data/postgres_backup/backup_postgres96
su - postgres
/usr/pgsql-9.6/bin/pg_dump --compress=9 --create --dbname=czechidm > czechidm_backup.sql.gz
/usr/pgsql-9.6/bin/pg_dump --compress=9 --create --dbname=postgres > postgres_backup.sql.gz
==== Uninstall postgres 9.2 ====
Stop applications using postgres - just to be sure:
systemctl stop tomcat
systemctl disable tomcat
Uninstall old postgres and move old data directory.
mv /data/pgsql/9.2 /data/postgres_upgrade/
yum remove postgresql.x86_64
yum remove postgresql-libs.x86_64
Check that new postgres starts up correctly after the uninstallation of the old one is complete. Just to be sure.
systemctl stop postgresql-9.6
systemctl start postgresql-9.6
systemctl status postgresql-9.6
If new postgres started correctly remove the backup of the old data directory.
rm -rf /data/postgres_upgrade/9.2
Finally, create symlinks to new postgres binaries (use list of symlink the ''yum'' complained about when you were installing new postgres version):
ln -s /etc/alternatives/pgsql-psql /usr/bin/psql
ln -s /etc/alternatives/pgsql-clusterdb /usr/bin/clusterdb
ln -s /etc/alternatives/pgsql-createdb /usr/bin/createdb
ln -s /etc/alternatives/pgsql-createlang /usr/bin/createlang
ln -s /etc/alternatives/pgsql-createuser /usr/bin/createuser
ln -s /etc/alternatives/pgsql-dropdb /usr/bin/dropdb
ln -s /etc/alternatives/pgsql-droplang /usr/bin/droplang
ln -s /etc/alternatives/pgsql-dropuser /usr/bin/dropuser
ln -s /etc/alternatives/pgsql-pg_basebackup /usr/bin/pg_basebackup
ln -s /etc/alternatives/pgsql-pg_dump /usr/bin/pg_dump
ln -s /etc/alternatives/pgsql-pg_dumpall /usr/bin/pg_dumpall
ln -s /etc/alternatives/pgsql-pg_restore /usr/bin/pg_restore
ln -s /etc/alternatives/pgsql-reindexdb /usr/bin/reindexdb
ln -s /etc/alternatives/pgsql-vacuumdb /usr/bin/vacuumdb
Start IdM or your application and check if it started correctly.
The upgrade is complete.
==== Post upgrade notes and checks ====
After upgrade, you should check that automatic backup of database are executing properly. If they are using ''pg_dump'' there shouldn't be any issue.
If you monitor that postgres is running implemented with ''systemctl status ...'', note the change of unit name from ''postgresql.service'' to ''postgresql-9.6.service''.
You can either change your monitoring or change the unit name.
===== Recovery guide =====
==== Automatic upgrade fails or new PostgreSQL installation gets broken ====
In case the ''pg\_upgrade'' fails or the upgrade process corrupts data, you can try to import the new database from the backup. PostgreSQL backups made on older versions of RDBMS can be imported to its newer versions. Such recovery is also usable when your new PostreSQL installation (already containing data) gets corrupted. You can then use old or new backup, depending on which you already have at hand and which you trust it contains correct data.
If you do not have such backups and have to return back to old PostgreSQL installation, follow the steps below:
Turn off the application which is using postgres.
Under user postgres, drop all damaged databases on new postgres:
su - postgres
/usr/pgsql-9.6/bin/psql -p 7000
drop database [DB name]
#when you are done exit with \q
Then in ''/data/postgres\_upgrade/backup\_postgres92'', decompress all backups you need to restore and restore them to new postgres.
cd /data/postgres_upgrade/backup_postgres92
gunzip czechidm_backup.sql.gz
/usr/pgsql-9.6/bin/psql -p 7000 -d postgres -f /data/postgres_upgrade/backup_postgres92/czechidm_backup.sql
# ... and similarly for other database dumps ...
Then refer back to [[priv/postgres_upgrade_tutorial#run_upgrade|]] section of this document and continue as if the ''pg\_upgrade'' command completed successfully.
==== Recovery to old postgres ====
When upgrade fails and you don't have time to try it again use this guide to revert to old postgres.
Turn off the application which using postgres (if it is already running).
Then set default port on postgres. If you postgres is running on that port already - skip this step:
In file ''/etc/systemd/system/postgresql.service'' set:
Environment=PGPORT=5432
Restart postgres and check that it's running on port ''5432'':
systemctl daemon-reload
systemctl restart postgresql.service
systemctl status postgresql.service
netstat -nlpt | grep 6000
#expected output: nothing
netstat -nlpt | grep 5432
#expected output:
#tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 1815/postmaster
Start your application and check if all data is correct.