Сложилось так исторически, что мы долго не обновляли PostgreSQL и застряли на версии 12. Но пришло время обновлять зависимости проекта и оказалось, что Django 5.1 версию 12 PostgreSQL уже не поддерживает и это мотивировало меня провести обновление до новейшей 16 версии.

На момент написания у нас всё организовано достаточно просто — все необходимые компоненты запускаются в Docker-контейнерах через Docker Compose.

У меня было два варианта, как произвести миграцию:

  1. Сделать полный дамп, загрузить новый образ postgres:16 и накатить дамп

  2. Использовать утилиту pg_upgrade, которая позволяет мигрировать данные между версиями PostgreSQL

Поскольку первый метод требует больше времени на восстановление данных, я решил воспользоваться вторым вариантом — pg_upgrade. Плюс, этот вариант давал возможность попробовать что-то новое.

Сначала было непонятно как применить pg_upgrade в docker, но после непродолжительного гуглежа я нашёл решение: https://github.com/tianon/docker-postgres-upgrade

По ходу изучения того, как работать с docker-postgres-upgrade был составлен такой план:

  1. остановить сервисы, зависимые от БД

  2. сделать бэкап БД

  3. остановить БД

  4. произвести миграцию

  5. запустить и проверить БД на новой версии PostgreSQL

  6. запустить сервисы

  7. обновить docker-compose.yml

Примечание: хотя pg_upgrade и позволяет произвести миграцию на горячую, но я решил не рисковать и сделать это на холодную

Ниже я кратко опишу процесс миграции.

Для начала нужно понять какой volume используется нашим PostgreSQL

% cat docker-compose.yml
services:
...
	postgres:
		image: postgres:12
		container_name: postgres
		command: postgres -c "config_file=/etc/postgresql/postgresql.conf"
		environment:
			POSTGRES_PASSWORD: xxxxxx
		volumes:
			- ./postgresql/postgresql.conf:/etc/postgresql/postgresql.conf
			- postgres:/var/lib/postgresql/data
		ports:
			- "5432:5432"
...

volumes:
	postgres:
% docker volume ls
DRIVER    VOLUME NAME
local     1b7ffcbd614f280ab577370912c625135ef84fe8cf721953206ba0142bed8cad
local     6b82aa6ecc19fbea9eb91b8d68af0d6a18d04911717953935cff006a4220e123
local     7c2b74aa668a47d2fe15615b8f4a614ebcf3065ab101cdaf487da6b5e1c95ffd
local     16ea96ec68eeb0134f0eebd27ec983143e9bdd43d527d3c6e1d3f1cd2bacc516
local     180aeeeadba249e991c22bb77c53c9b2a50612a3d181598a8014b667d920762d
local     625ebc83103e8c223395fb7b5e7e31a714110400f6f70f499bee13f3d9b86bce
local     803c056ecb2c83b31e5d0dbb089689ae1b44996fb84aee1e12c8d224b583f8e7
local     a1ccdb17c4cc50333ec2c417cf24553e89e4f5aa1e402c1985a8fec2f52c00dd
local     a40c9d1b958169dda6db47e7493dacf7c05e6e0acd678954ea87f7ac3ffb8a16
local     backend_media
local     backend_postgres
local     backend_postgres-14
local     backend_postgres-16
local     backend_static
local     c73a013579341af8a05646a501f4b4a9595a3e2b1167543889d1c26a21e4fda3
local     cebd974e3e2f2a5bd0b2cf16a4ef293696e30d547ccfd59bad44d155541da16b
local     d393b6d74083d4ac5a679a7ed62cf364663a66fa41f113e1b60395ec369e26bb
local     dev_badger
local     dev_media
local     dev_postgres
local     dev_static
local     edb6be7221e82a9f9c4ba47ac1cae104765bc8d4d29020fef1397aa4d22e9472
local     minikube
local     minikube-m02
local     postgres
local     postgres16
local     qa-automation_badger
local     qa-automation_media
local     qa-automation_postgres
local     qa-automation_static

Нам нужен volume backend_postgres, потому что Docker Compose по умолчанию именует volumes в формате "имя проекта + имя volume". Поскольку проект называется backend (по-умолчанию имя каталога, где запускается Docker Compose, если не задано другое ключом -p), наш целевой volume будет называться backend_postgres. Для новой версии PostgreSQL volume будет называться backend_postgres16.

миграция

% docker run --platform linux/amd64 --rm -v backend_postgres:/var/lib/postgresql/12/data -v backend_postgres16:/var/lib/postgresql/16/data tianon/postgres-upgrade:12-to-16

На MacOS с архитектурой Apple Silicon (M1/M2/M3) возникла необходимость добавить флаг --platform linux/amd64, так как образ tianon/postgres-upgrade по умолчанию не поддерживает ARM-архитектуру. Это позволяет принудительно использовать x86_64-образ для успешного запуска.

После завершения процедуры миграции делаем пробный запуск:

% docker run -dit --name postgres16 -p 5432:5432 -e POSTGRESS_PASSWORD=postgres -v backend_postgres16:/var/lib/postgresql/data -v ./postgresql/postgresql.conf:/var/lib/postgresql/data/postgresql.conf -v ./postgresql/pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf  postgres:16

У меня кастомные postgresql.conf и pg_hba.conf поэтому прокидываем их внутрь контейнера.

И проверяем по логам, что всё хорошо

% docker logs -f postgres16

Нужно проверить логи на наличие ошибок или предупреждений. Особое внимание стоит уделить сообщениям о несовместимости версий или проблемах с доступом к данным.

Если тестовый запуск прошёл успешно, можно закрепить изменения в основном файле docker-compose.yml, заменив старую версию PostgreSQL на новую. Важно убедиться, что все зависимости обновлены и настроены правильно перед запуском в продакшн.

Новый docker-compose.yml

services:
...
    postgres:
        image: postgres:16
        container_name: postgres
        environment:
            POSTGRES_PASSWORD: xxxxxx
        volumes:
            - ./postgresql/postgresql.conf:/var/lib/postgresql/data/postgresql.conf
            - ./postgresql/pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf
            - postgres16:/var/lib/postgresql/data
        ports:
            - "5432:5432"
...

volumes:
    postgres16:

Заключение

Благодаря docker-postgres-upgrade процесс миграции на новую версию PostgreSQL прошёл быстро и гладко. Это отличное решение для тех, кто ищет простой и надёжный способ обновления PostgreSQL в Docker-среде. Рекомендую!

Комментарии (9)


  1. Kill_Voice
    23.08.2024 07:47

    Зачем вы продуктивную БД в докер запихали?)


    1. jonic
      23.08.2024 07:47

      А почему нет?


      1. turbidit
        23.08.2024 07:47

        Потому что docker по своей природе stateless?


      1. Kill_Voice
        23.08.2024 07:47
        +6

        В целом для маленьких баз с низкой нагрузкой, это может быть и норм. Но для продуктивной эксплуатации не один вендор не рекомендует использовать виртуализацию или контейнеризацию.

        Если кратко то есть проблемы:

        • Латентность файловой системы которую использует докер

        • Сетевые задержки на визуальных итерфейсах

        • Миграция и обновление вызывает сложности

        • Есть особенности резервного копирования

        • Сложности мониторинга и профайлинга, многие тулзы нацелены только на голую OS, а не контейнеры

        Просто весьма странно запихивать в контейнер то что из коробки требует большого железного кластера


        1. jonic
          23.08.2024 07:47

          Как то не убедительно звучит, если честно…


          1. Kill_Voice
            23.08.2024 07:47
            +1

            Если деградация производительности для вас не убедительно, то как бы дальше обсуждать нечего


            1. jonic
              23.08.2024 07:47

              Что то я не вижу фактов деградации производительности. А docker на Linux хосте проседает на 1-2% от нативной. Это в целом в рамках погрешностей. Очень кстати интересно, а как работают все эти managed postgresql? Не уж то baremetal везут в шкаф и бегают гномики быстро раскатывают? Или все таки через виртуализацию? То то и оно.


              1. jonic
                23.08.2024 07:47

                Сам себя поправлю, конечно я имел ввиду контейнеризацию. А все эти байки про попа боль появилось от virtual box под Mac/windows где действительно ухудшается производительность, но для дев пойдет.


  1. same_one
    23.08.2024 07:47
    +1

    Есть кстати набор готовых образов-миграторов как раз для таких случаев.

    https://hub.docker.com/r/tianon/postgres-upgrade/tags