====== 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.