Вопрос или проблема
Предыстория
Я управляю сервером для своей организации, основное использование которого — это частная инстанция 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 и базу данных.
Конкретно, вот шаги, которые я выполнил:
- Я скопировал папку с данными postgres в резервную
cp -r ./db ./db_backup
. - Затем я сменил владельца папки на пользователя docker
chmod -R systemd_coredump:docker ./db_backup
. - Затем я смонтировал контейнер docker, чтобы получить доступ к полезным утилитам postgres
docker run -it -v /srv/#########/gitea/db_backup:/var/lib/postgresql/data postgres:14 /bin/bash
- Внутри контейнера я выполнил
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
- Я затем выполнил это без
-n
$ gosu postgres pg_resetwal /var/lib/postgresql/data/
Сброс журнала с записью вперед
- Наконец, я изменил свою конфигурацию 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 и обеспечить дальнейшую стабильность сервера.