Продолжаем тему обновления контейнеров PostgreSQL между мажорными версиями, в прошлой заметке мы рассмотрели миграцию при помощи утилиты pg_dumpall.
Это простой и надежный способ, но у него есть один существенный недостаток: pg_dumpall – однопоточен, использует для выгрузки и загрузки только одно ядро и выполняет все операции последовательно, как они записаны в файле дампа.
На крупных и даже средних базах такой подход может привести к неконтролируемому времени простоя, что неприемлемо. Поэтому сегодня мы рассмотрим миграцию при помощи другой утилиты - pg_upgrade.
Преимущество pg_upgrade в том, что он не делает экспорт/импорт, не пересоздает всю структуру СУБД, а работает на уровне файлов базы данных.
Итак, приступим, допустим мы изначально имеем:
services:
db:
container_name: postgres-db
В нашем случае:
▫️ db — имя сервиса Compose
▫️ postgres-db — реальное имя контейнера Docker
1️⃣ Остановим весь стек и запустим обратно только контейнер с СУБД для создания резервной копии, это исключит возможную запись в БД во время выгрузки:
docker compose stop
docker compose up -d db
Теперь выполним выгрузку дампа всего кластера:
docker exec -t postgres-db pg_dumpall -U postgres > full_backup.sql
Проверяем, что файл дампа создан и останавливаем контейнер с БД:
docker compose down
2️⃣ Следующие шаги будут несколько различаться в отличие от того, используем ли мы Named Volumes или Bind mount.
🔹 При Named Volumes создаем дополнительный именованный том для нового кластера СУБД:
docker volume create pg_data_18
И запускаем специальный контейнер для апгрейда:
docker run --rm \
-e POSTGRES_USER=postgres \
-v pg_data:/var/lib/postgresql/17/data \
-v pg_data_18:/var/lib/postgresql/18/data \
tianon/postgres-upgrade:17-to-18
Данный контейнер НЕ является официальным образом PostgreSQL, это community image от Tianon Gravi, но он де-факто является стандартом в мире Docker.
Обратите внимание на тег контейнера, он указывает версии, с которой и на которую мы переходим, в нашем случае это 17-to-18, в других случаях выбирайте контейнер с нужным тегом.
Затем меняем Compose, было:
services:
db:
image: postgres:17
volumes:
- pg_data:/var/lib/postgresql/data
volumes:
pg_data:
Стало:
services:
db:
image: postgres:18
volumes:
- pg_data_18:/var/lib/postgresql/data
volumes:
pg_data_18:
🔹 Для Bind mount создаем новую директорию:
mkdir ./data/postgres-data-18
И запускаем служебный контейнер:
docker run --rm \
-e POSTGRES_USER=postgres \
-v ./data/ postgres-data:/var/lib/postgresql/17/data \
-v ./data/postgres-data-18:/var/lib/postgresql/18/data \
tianon/postgres-upgrade:17-to-18
Затем меняем Cpmpose, было:
services:
db:
image: postgres:17
volumes:
- ./postgres-data:/var/lib/postgresql/data
Стало:
services:
db:
image: postgres:18
volumes:
- ./postgres-data-18:/var/lib/postgresql/data
3️⃣ Запускаем стек:
docker compose up -d
И выполняем оптимизацию базы:
docker compose exec -T db vacuumdb -U postgres --all --analyze-in-stages
Это не мешает работе и можно запускать уже на работающем стеке. Старые тома лучше всего оставить на некоторое время, пока вы не убедитесь, что обновление прошло нормально.
