Какие существуют стратегии восстановления базы данных postgres, в которой работали два экземпляра postgres в Docker, указывающих на одну и ту же точку монтирования?

Вопрос или проблема

Предыстория

Я управляю сервером для своей организации, основное использование которого — это частная инстанция gitea, хотя он выполняет и другие функции.

Несколько месяцев назад я организовывал файлы docker compose на своем VPS и упрощал добавление сервисов. В рамках этого процесса некоторые конфигурации docker compose были переименованы и перемещены. Что я не знал в тот момент, так это то, что это означало, что использование docker compose -f [filename] down фактически не остановит контейнеры, запущенные с оригинальным docker compose, хотя все имена сервисов и т.д. оставались теми же. Спустя месяцы, после активного использования сервера, я вернулся к серверу, чтобы добавить еще один сервис, и увидел, что docker ps показывал:

$ docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS                  PORTS                                                                          NAMES
845b1e2b0725   ##################-website   "/usr/bin/supervisor…"   4 месяца назад    Работает 4 месяца (нормально)   0.0.0.0:8081->8080/tcp, :::8081->8080/tcp                                      ####################-website-1
46cd2f360104   gitea/act_runner:latest         "/sbin/tini -- /opt/…"   6 месяцев назад    Работает 4 месяца                                                                                            docker-runner-1
2173badc8b07   postgres:14                     "docker-entrypoint.s…"   6 месяцев назад    Работает 4 месяца             5432/tcp                                                                       docker-giteadb-1
0990a6a53465   gitea/act_runner:latest         "/sbin/tini -- /opt/…"   7 месяцев назад    Работает 4 месяца                                                                                            docker-compose-configs-runner-1
34fd2b4b3cd9   gitea/gitea:latest              "/usr/bin/entrypoint…"   7 месяцев назад    Работает 4 месяца             0.0.0.0:22->22/tcp, :::22->22/tcp, 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   docker-compose-configs-gitea-1
84a8132cc11d   postgres:14                     "docker-entrypoint.s…"   7 месяцев назад    Работает 4 месяца             5432/tcp                                                                       docker-compose-configs-giteadb-1
bda89d4a5cfe   176399451347                    "docker-entrypoint.s…"   18 месяцев назад   Работает 4 месяца             5432/tcp                                                                       docker-compose-configs-odoodb-1

Запуск docker compose -f [filename] down удалил только некоторые процессы, оставив работающими процессы docker-compose-configs-*. Я затем вручную завершил каждый из них по отдельности, используя docker kill. После перезапуска этих сервисов я обнаружил, что сервер gitea недоступен и контейнер postgres зацикливается с следующими журналами:

$ docker logs 8377a9155386
Директория базы данных PostgreSQL, похоже, содержит базу данных; Пропускаем инициализацию

2024-10-15 02:07:25.301 UTC [1] LOG:  запускаем PostgreSQL 14.11 (Debian 14.11-1.pgdg120+2) на x86_64-pc-linux-gnu, скомпилированный gcc (Debian 12.2.0-14) 12.2.0, 64-бит
2024-10-15 02:07:25.302 UTC [1] LOG:  прослушиваем на IPv4 адресе "0.0.0.0", порт 5432
2024-10-15 02:07:25.302 UTC [1] LOG:  прослушиваем на IPv6 адресе "::", порт 5432
2024-10-15 02:07:25.309 UTC [1] LOG:  прослушиваем на Unix сокете "/var/run/postgresql/.s.PGSQL.5432"
2024-10-15 02:07:25.319 UTC [27] LOG:  система базы данных была остановлена 2024-10-14 23:25:46 UTC
2024-10-15 02:07:25.319 UTC [27] LOG:  недействительная запись контрольной точки
2024-10-15 02:07:25.319 UTC [27] PANIC:  не удалось найти действительную запись контрольной точки
2024-10-15 02:07:25.706 UTC [1] LOG:  процесс запуска (PID 27) был завершен сигналом 6: Прервано
2024-10-15 02:07:25.707 UTC [1] LOG:  прерывание запуска из-за сбоя процесса запуска
2024-10-15 02:07:25.736 UTC [1] LOG:  система базы данных отключена

“PANIC: не удалось найти действительную запись контрольной точки” было достаточно легко найти, и я нашел много источников о том, что делать. Этот пост на stack overflow, в частности, был очень полезен. Следуя шагам из двух верхних ответов, я смог восстановить сервер gitea и базу данных.

Конкретно, вот шаги, которые я выполнил:

  1. Я скопировал папку с данными postgres в резервную cp -r ./db ./db_backup.
  2. Затем я сменил владельца папки на пользователя docker chmod -R systemd_coredump:docker ./db_backup.
  3. Затем я смонтировал контейнер docker, чтобы получить доступ к полезным утилитам postgres docker run -it -v /srv/#########/gitea/db_backup:/var/lib/postgresql/data postgres:14 /bin/bash
  4. Внутри контейнера я выполнил gosu postgres pg_resetwal /var/lib/postgresql/data/ -n с флагом -n, что сделало это пробным запуском. Честно говоря, я не знаю, является ли какая-либо из этой информации проблемной, но наличие резервной копии дало мне уверенность продолжить.
$ gosu postgres pg_resetwal /var/lib/postgresql/data/ -n
Текущие значения pg_control:

номер версии pg_control:            1300
номер версии каталога:               202107181
идентификатор системы базы данных:           7210922407513952295
Идентификатор временной линии последней контрольной точки:       1
Полные записи последней контрольной точки: включены
Следующий XID последней контрольной точки:          0:4470579
Следующий OID последней контрольной точки:          26153
Следующий MultiXactId последней контрольной точки:  1
Следующий MultiOffset последней контрольной точки:  0
Старейший XID последней контрольной точки:        727
Старейший XID последней контрольной точки:   1
Старейший ActiveXID последней контрольной точки:  0
Старейший MultiXid последней контрольной точки:   1
Старейший Multi' базы последней контрольной точки: 1
Старейший CommitTsXid последней контрольной точки:0
Самый новый CommitTsXid последней контрольной точки:0
Максимальное выравнивание данных:               8
Размер блока базы данных:                  8192
Блоков на сегмент большого отношения: 131072
Размер блока WAL:                       8192
Байты на сегмент WAL:                16777216
Максимальная длина идентификаторов:        64
Максимум столбцов в индексе:          32
Максимальный размер TOAST чанка:        1996
Размер чанка большого объекта:         2048
Хранилище типа даты/времени:               64-битные целые числа
Передача аргументов Float8:              по значению
Версия контрольной суммы страницы данных:           0


Значения, которые нужно изменить:

Первый лог сегмента после сброса:        000000010000000400000004
  1. Я затем выполнил это без -n
$ gosu postgres pg_resetwal /var/lib/postgresql/data/
Сброс журнала с записью вперед
  1. Наконец, я изменил свою конфигурацию docker compose, чтобы указать на очищенную папку базы данных, и снова запустил контейнеры. Для справки, вот этот файл docker compose с единственным отличием — это скрытием информации и монтированием тома на db_backup вместо db.
version: "3.9"
services:
  gitea:
    image: gitea/gitea:latest
    restart: always
    hostname: ###.###.###.###
    environment:
      - USER=git
      - USER_UID=1000
      - USER_GID=998
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database___HOST=giteadb:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=###################
    networks:
      - gitea
    ports:
      - 3000:3000
      - 22:22
    volumes:
      - /mnt/###################/gitea/data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    shm_size: 256m
    depends_on:
      - giteadb

  giteadb:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=###################
      - POSTGRES_DB=gitea
    networks:
      - gitea
    volumes:
      - /srv/###################/gitea/db_backup:/var/lib/postgresql/data

  runner:
    image: gitea/act_runner:latest
    restart: always
    depends_on:
      - gitea
    volumes:
      - /mnt/###################/gitea/data/act_runner:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - GITEA_INSTANCE_URL=https://git.###################.com
      # При использовании Docker Secrets также возможно использовать
      # GITEA_RUNNER_REGISTRATION_TOKEN_FILE для передачи местоположения.
      # Переменная окружения имеет приоритет.
      # Необходимо только для первого запуска.
      - GITEA_RUNNER_REGISTRATION_TOKEN=###################
networks:
  gitea:
    external: false

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

На этом сервере намного больше пользователей, чем указано в списке. Как минимум 30 пользователей. Кроме того, организация отсутствует, и несколько репозиториев также не перечислены. К счастью, проверка директории данных, где фактически хранятся git-репозитории, показывает, что они в порядке. У нас также есть резервные копии для них, так что я не сильно беспокоюсь о потере целых репозиториев.

Завершив работу всех контейнеров docker и заменив папку ./db_backup новой копией ./db, я изучил реальные файлы. Я нашел странным, например, то, что в ./db_backup/pg_wal содержатся два файла, которые были отредактированы примерно в то время, когда я выключил два разных контейнера postgres.

ls -l ./db/pg_wal
total 32772
-rw------- 1 systemd-coredump systemd-coredump 16777216 Oct 14 23:25 000000010000000400000002
-rw------- 1 systemd-coredump systemd-coredump 16777216 Oct 14 18:07 000000010000000400000003
drwx------ 2 systemd-coredump systemd-coredump     4096 Mar 15  2023 archive_status

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

больше учетных записей, но все еще не все из них

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

Что теперь?

На этом этапе моя нехватка знаний по диагностике postgres мешает мне, и я не знаю, чего не знаю, чтобы лучше продвинуться вперед. Я не знаю, почему удаление одного из этих WAL файлов позволило /больше/ данных появиться.

Я точно понимаю, как я попал в это состояние, а именно, что было 2 инстанции docker postgres, смонтированные к одному и тому же тому. Что я не знаю, так это мои варианты восстановления. Судя по моим манипуляциям, очевидно, что есть некоторые действия, которые я могу предпринять, чтобы вернуть больше данных. Это дает мне надежду, что, возможно, есть еще вещи, которые я могу попробовать для восстановления из этого плохого состояния.

Ответ или решение

Восстановление базы данных PostgreSQL после конфликта с Docker-контейнерами

Проблема, с которой вы столкнулись, укладывается в распространённый сценарий работы с контейнерами Docker и платформами баз данных, такими как PostgreSQL, где возможность доступа к одной и той же точке монтирования от нескольких контейнеров может привести к серьезным повреждениям данных. Рассмотрим стратегии и методы, которые помогут вам восстановить базу данных PostgreSQL в данной ситуации.

1. Понимание проблемы

Использование двух экземпляров PostgreSQL, указывающих на одну и ту же точку монтирования, может вызывать конфликты, поскольку оба контейнера могут пытаться одновременно писать в одни и те же файлы журналов транзакций (WAL). Это приводит к несовместимым состояниям базы данных и, как следствие, к ошибкам при запуске.

2. Подход к восстановлению

Для успешного восстановления базы данных необходимо следовать структурированному подходу:

  • Создание резервной копии: Во-первых, продолжайте делать резервные копии данных, прежде чем предпринимать какие-либо изменения. Вы уже сохранили копию каталога с базой данных, что является важным шагом.

  • Использование утилиты pg_resetwal: Как вы уже упомянули, использование pg_resetwal может помочь в случае отсутствия корректного контрольного записа. Но будьте осторожны: этот метод может привести к потере данных. Рекомендуется использовать его только после резервного копирования.

  • Анализ WAL файлов: Разберитесь с журналами транзакций. Ваша практика удаления старых файлов WAL помогла восстановить часть данных. Правильный анализ и удаление ненужных или конфликтующих WAL файлов, по всей вероятности, является одной из стратегии для восстановления данных. Однако не удаляйте WAL файлы без уверенности в их роли.

3. Стратегии восстановления

На основе вашего описания и стандартных практик восстановления базы данных PostgreSQL, вот некоторые дополнительные стратегии:

  • Восстановление из последней рабочей резервной копии: Если у вас есть недавние резервные копии базы данных, и если они работают корректно, восстановите базу данных из резервной копии. Это обеспечит целостность данных.

  • Логирование действий: Используйте ваши серверные журналы для анализа действий, происходивших вокруг времени, когда произошел сбой. Это может помочь вам выявить, какие операции выполнялись и какие данные могли быть потеряны.

  • Проверка состояния данных: Например, используйте команды SQL для проверки наличия таблиц и записей, которые кажутся отсутствующими. Вы можете использовать SELECT * FROM pg_catalog.pg_tables; для проверки структуры базы данных.

  • Работа с модулями для диагностики: Инструменты, такие как pg_dump, могут помочь вам сохранить уже восстановленные элементы базы данных, чтобы предотвратить дальнейшие потери данных.

4. Обратитесь к сообществу

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

5. Профилактические меры

Чтобы избегать подобных ситуаций в будущем:

  • Используйте отдельные тома для каждой базы данных: Не допускайте, чтобы несколько экземпляров PostgreSQL указывали на одну и ту же точку монтирования.

  • Регулярные резервные копии: Настройте автоматические резервные копии ваших баз данных, чтобы минимизировать риск потерь.

  • Мониторинг и уведомления: Настройте мониторинг состояния контейнеров и баз данных для получения уведомлений о критических ситуациях.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...