背景故事

我为我的组织运行一个服务器,主要用途是私有 gitea 实例,尽管它还有其他用途。

几个月前,我正在整理 VPS 上的 docker compose 文件,以便更轻松地添加服务。作为此过程的一部分,一些 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 months ago    Up 4 months (healthy)   0.0.0.0:8081->8080/tcp, :::8081->8080/tcp                                      ####################-website-1
46cd2f360104   gitea/act_runner:latest         "/sbin/tini -- /opt/…"   6 months ago    Up 4 months                                                                                            docker-runner-1
2173badc8b07   postgres:14                     "docker-entrypoint.s…"   6 months ago    Up 4 months             5432/tcp                                                                       docker-giteadb-1
0990a6a53465   gitea/act_runner:latest         "/sbin/tini -- /opt/…"   7 months ago    Up 4 months                                                                                            docker-compose-configs-runner-1
34fd2b4b3cd9   gitea/gitea:latest              "/usr/bin/entrypoint…"   7 months ago    Up 4 months             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 months ago    Up 4 months             5432/tcp                                                                       docker-compose-configs-giteadb-1
bda89d4a5cfe   176399451347                    "docker-entrypoint.s…"   18 months ago   Up 4 months             5432/tcp                                                                       docker-compose-configs-odoodb-1

运行docker compose -f [filename] down仅删除了几个进程,剩下的docker-compose-configs-*进程仍在运行。然后我使用手动杀死每个进程docker kill。重新启动这些服务后,我发现 gitea 服务器不可用,并且 postgres 容器正在引导循环,并显示以下日志:

$ docker logs 8377a9155386
PostgreSQL Database directory appears to contain a database; Skipping initialization

2024-10-15 02:07:25.301 UTC [1] LOG:  starting PostgreSQL 14.11 (Debian 14.11-1.pgdg120+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
2024-10-15 02:07:25.302 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2024-10-15 02:07:25.302 UTC [1] LOG:  listening on IPv6 address "::", port 5432
2024-10-15 02:07:25.309 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2024-10-15 02:07:25.319 UTC [27] LOG:  database system was shut down at 2024-10-14 23:25:46 UTC
2024-10-15 02:07:25.319 UTC [27] LOG:  invalid primary checkpoint record
2024-10-15 02:07:25.319 UTC [27] PANIC:  could not locate a valid checkpoint record
2024-10-15 02:07:25.706 UTC [1] LOG:  startup process (PID 27) was terminated by signal 6: Aborted
2024-10-15 02:07:25.707 UTC [1] LOG:  aborting startup due to startup process failure
2024-10-15 02:07:25.736 UTC [1] LOG:  database system is shut down

“PANIC:无法找到有效的检查点记录”很容易搜索,我找到了很多解决方法的来源。尤其有用。按照前两个答案中的步骤,我能够让 gitea 服务器和数据库恢复运行。

具体来说,以下是我执行的步骤:

  1. 我将 postgres 数据文件夹复制到了备份cp -r ./db ./db_backup
  2. 然后,我将文件夹的所有者更改为 docker 用户chmod -R systemd_coredump:docker ./db_backup。 组和用户的原因systemd_coredump是主机系统(我在其中执行许多这些命令)的组 ID 和用户 ID 与 docker 容器中的 ID 的映射不同。systemd_coredump映射到postgres容器内的用户。
  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
Current pg_control values:

pg_control version number:            1300
Catalog version number:               202107181
Database system identifier:           7210922407513952295
Latest checkpoint's TimeLineID:       1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID:          0:4470579
Latest checkpoint's NextOID:          26153
Latest checkpoint's NextMultiXactId:  1
Latest checkpoint's NextMultiOffset:  0
Latest checkpoint's oldestXID:        727
Latest checkpoint's oldestXID's DB:   1
Latest checkpoint's oldestActiveXID:  0
Latest checkpoint's oldestMultiXid:   1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Maximum data alignment:               8
Database block size:                  8192
Blocks per segment of large relation: 131072
WAL block size:                       8192
Bytes per WAL segment:                16777216
Maximum length of identifiers:        64
Maximum columns in an index:          32
Maximum size of a TOAST chunk:        1996
Size of a large-object chunk:         2048
Date/time type storage:               64-bit integers
Float8 argument passing:              by value
Data page checksum version:           0


Values to be changed:

First log segment after reset:        000000010000000400000004
  1. 然后我运行了这个,没有-n
$ gosu postgres pg_resetwal /var/lib/postgresql/data/
Write-ahead log reset
  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
      # When using Docker Secrets, it's also possible to use
      # GITEA_RUNNER_REGISTRATION_TOKEN_FILE to pass the location.
      # The env var takes precedence.
      # Needed only for the first start.
      - 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 文件会允许 /more/ 数据显示出来。

我确实知道我是如何陷入这种状态的,因为有 2 个 postgres docker 实例挂载到同一个卷。我不知道恢复的选项。根据我的操作,很明显我可以做一些事情来恢复更多数据。这让我希望也许我可以尝试更多方法来从这种糟糕的状态中恢复过来。

編輯

结果psql -c "show archive_mode" -U gitea

 $ psql -c "show archive_mode" -U gitea
 archive_mode
--------------
 off
(1 row)

结果file 000000010000000400000002

db_backup2/pg_wal/000000010000000400000002: data ```

6

  • 2
    如果数据库出现问题,您永远都不应该删除 WAL 文件。您能否file 000000010000000400000002告诉我们这是什么类型的文件?我发现该文件的所有者是 ,这很不寻常systemd-coredump。您能做的最好的事情是从备份重建数据库。


    – 

  • 1
    PS 请使用的输出进行显示psql -c "show archive_mode" -U DB_USERNAME,其中DB_USERNAME是数据库的用户名,如果您不知道用户名,请尝试不使用-U DB_USERNAME,或者尝试-U root


    – 


  • @paladin 我搞清楚了为什么systemd-coredump会出现这个用户,只是主机系统上的 ID 映射与 Docker 容器中的映射不同。没什么大不了的。 “` root@55dfad542295:/# ls -l /var/lib/postgresql/data/pg_wal/ total 16388 -rw——- 1 postgres postgres 16777216 Oct 15 18:20 000000010000000400000004 drwx—— 2 postgres 998 4096 Oct 15 04:50 archive_status “` “` $ file db_backup2/pg_wal/000000010000000400000002 db_backup2/pg_wal/000000010000000400000002:数据 “`


    – 

  • @paladin 我已将 ` psql -c “show archive_mode” -U gitea` 的结果添加到我的帖子中。


    – 

  • I don't know why deleting one of those WAL files allowed /more/ data to show up.那是您的数据。它被提交到数据库之前的文件中。这些是事务日志。它们按顺序应用。它们用于在恢复期间前滚和应用更改。在删除有问题的文件后,它可能能够继续进行或显示不同的已提交数据。


    – 

0