, ,

在数据库损坏中幸存

我尚未遇到过完美的软件。错误、故障和缺陷是软件开发人员的现实。它们通常也有好处,无论是学习大型应用程序中代码的新领域,还是提出防止问题的想法。

我们最近遇到一个有趣的问题,Podman 数据库损坏了。数据库的主要工作是跟踪容器、它们的状态和它们的配置。在这种情况下,报告者正在测试 Podman,模拟特殊设备上的断电。虽然 Podman 团队和我们的用户有意或无意地进行了有限的断电测试,但我们很少看到数据库损坏,因为要使数据库损坏,它必须正在写入(至少理论上是这样)。而且写入我们的数据库通常是一个非常快速的操作,因此窗口很小。

虽然我们无法立即查明根本原因,但我们被问及如何从此类故障中恢复。如果您的数据库损坏,大多数 Podman 命令将无法工作。在这种情况下,他们的容器通常在每次启动时都会重新创建,并且是通过 RESTFul API 以编程方式完成的。

Podman 数据库损坏

如果您的 Podman 数据库损坏,在几乎所有情况下,您将无法恢复任何现有容器。

在这种情况下,容器是由特权用户(root)运行的。因此,我将以特权用户的身份演示恢复过程,并为无根用户添加任何注意事项。过程大致相同。首先是错误信息:

$ sudo podman ps
panic: invalid freelist page: 66, page type is leaf

goroutine 1 [running]:
go.etcd.io/bbolt.(*freelist).read(0x50c95d?, 0x7f96a7e42000)
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/freelist.go:266 +0x22e
go.etcd.io/bbolt.(*DB).loadFreelist.func1()
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/db.go:323 +0xb8
sync.(*Once).doSlow(0xc00011e1c8?, 0x10?)
	/usr/lib/golang/src/sync/once.go:74 +0xc2
sync.(*Once).Do(...)
	/usr/lib/golang/src/sync/once.go:65
go.etcd.io/bbolt.(*DB).loadFreelist(0xc00011e000?)
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/db.go:316 +0x47
go.etcd.io/bbolt.Open({0x7ffd1855f26a, 0x23}, 0x1b6?, 0x0)
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/db.go:293 +0x48b
main.(*CheckCommand).Run(0xc00005fe58, {0xc0000141a0, 0x1, 0x1})
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/cmd/bbolt/main.go:202 +0x1a5
main.(*Main).Run(0xc000104f40, {0xc000014190, 0x2, 0x2})
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/cmd/bbolt/main.go:112 +0x979
main.main()
	/home/baude/go/pkg/mod/go.etcd.io/bbolt@v1.3.6/cmd/bbolt/main.go:70 +0xae

恢复的第一步是删除现有数据库。

$ sudo rm /var/lib/containers/storage/libpod/bolt_state.db

无根数据库路径

特权用户的数据库默认存储在/var/lib/containers/storage/libpod/bolt_state.db。无根用户的数据库默认存储在~/.local/share/containers/storage/libpod/bolt_state.db

当 Podman 无法找到其数据库时,它将创建一个新的空数据库。

$ sudo podman ps -a
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES

现在我们可以重新创建我们的容器,它们分别叫做 container1、container2 和 container3。

$ sudo podman create --name container1 alpine top
Error: creating container storage: the container name "container1" is already in use by e77786c096e083b258bad2e196255f7dc1a2859cfb9dd35436648e1541bdce23. You have to remove that container to be able to reuse that name: that name is already in use

容器如何可能已经存在却未在所有容器列表中显示?错误消息可以提供更多帮助。有一个鲜为人知的选项叫做 `–external`,用于 `podman ps` 以查看处于这种外部存储状态的容器。

$ sudo podman ps -a --external
CONTAINER ID  IMAGE                            COMMAND     CREATED             STATUS                    PORTS       NAMES
69f78dfaa0a6  docker.io/library/alpine:latest  storage     About a minute ago  Storage                               container1
e5db3ad9125e  docker.io/library/alpine:latest  storage     About a minute ago  Storage                               container2
52591b8b7676  docker.io/library/alpine:latest  storage     About a minute ago  Storage                               container3

请注意,所有三个容器的 STATUS 列都显示 STORAGE。再次强调,这是因为这些容器仍然存在于文件系统中,这也解释了之前的错误。与其尝试删除它们并重新创建容器(这也是完全有效的),我们可以简单地使用 --replace 选项,该选项适用于 podman 的 runcreate 命令。

$ sudo podman create --replace --name container1 alpine top
container1
128876a5b3828350c7bfbc268dee99f69fb2374b5c8ce94cff879b4f83e78c6d

--replace 选项将执行从文件系统中移除先前容器的操作,并使用相同的名称运行或创建新容器。此选项也适用于数据库中和常规存储中的容器。

发表评论

订阅

输入您的电子邮件地址以接收来自本网站的电子邮件更新。

返回

您的消息已发送

警告
警告
警告。

分类


搜索