如何在kubernetes中安全重启postgresql容器?

问题描述 投票:0回答:2

我想在我的服务器上发布一个小型爱好网站。为此,我选择使用 kubernetes,因为我在工作中也部分使用它,所以我想熟悉一下。我购买了hetzner debian服务器并安装了k3s。现在正在按照此教程部署 PostgreSQL 容器(版本 15.2,不是集群)。 (我做了一些从其他教程中看到的小改动,应该不相关)。

运行良好,我对此感到满意。但我尝试重新启动部署,以确保如果服务器因某种原因出现故障,数据不会丢失。重新启动几次后,数据库已损坏。

我一看到:

PANIC:  invalid magic number 0000 in log segment 000000010000000000000000, offset 0

另一次:

invalid contrecord length 1174 (expected 48430224) at 0/195BC90

另一次:

PANIC:  could not locate a valid checkpoint record

当我尝试用谷歌搜索如何从中恢复时,我没有找到任何安全的选项,大多数建议是恢复备份。

所以我的问题是,如何安全地重新启动/关闭 PostgreSQL 容器?我是否缺少 k8s 中 PostgreSQL pod 的一些关闭配置?

更新1:

我使用

r
命令从 k9s 重新启动部署。我认为 UI 让它看起来像是立即旋转的,但这可能需要一些时间。所以我认为我每 10 秒触发多次重新启动,这可能会损坏数据库。无论如何,我添加了
terminationGracePeriodSeconds: 60
并使用了答案中的
preStop
钩子。谢谢

更新2: 我导入了数据库,重新启动并再次出现同样的问题:

找不到有效的检查点记录

更新3: 我将

Deployment
替换为
StatefulSet
,它似乎可以更好地处理重启。尝试了 10 多次重启,没有任何问题。而在第四次重新启动时崩溃之前。

postgresql kubernetes k3s
2个回答
4
投票

当然,最佳实践是使用像 cloudnative-pgpostgres-operator 这样的运算符,但它们相当大,并且对于简单的工作负载可能具有更多功能。这是解决您问题的简单方法。

解决方案

将以下内容添加到您的 pod 规格中

preStop:
  exec:
    command: ["/usr/local/bin/pg_ctl stop -D /var/lib/postgresql/data -w -t 60 -m fast"]

说明

基本上,当您杀死一个 pod 时,Kubernetes 会发出信号

SIGTERM
并为您的 pod 提供 30 秒的时间,在那之后它会发送
SIGKILL
。当 postgres 收到
SIGTERM
时,它不会接受网络连接,但不会终止现有的终止,因此任何客户端都会阻止 db 的终止,并且 30 秒后 pod 将收到
SIGKILL
,这对 postgres doc 非常不利。所以你需要以某种方式安全地关闭 postgres,使用
preStop
钩子就可以。

Kubernetes

这是您的 Pod 的确切时间顺序:

  1. 从 Pod 控制器设置
    state=Terminating
  2. terminationGracePeriodSeconds
    计时器启动(默认为30秒)
  3. preStop
    挂钩:
    pg_cli ...
  4. SIGTERM
    已发送:Postgres 不会接受新连接
  5. k8s 等待直到
    terminationGracePeriods
    (可从 yaml 配置)
  6. 如果应用程序仍然存在
    SIGKILL
    已发送

还需要在部署中设置

.spec.strategy.type==Recreate

Postgres

对于

pg_cli
命令,您可以参考此摘要,对您来说最有用的命令看起来像
-m fast

SIGTERM

  • “智能关机模式”
  • 不允许新连接
  • 让我们继续现有的联系

SIGINT

  • “快速关机模式”
  • 禁止新连接
  • 发送
    SIGTERM
    到现有服务器进程(?)它们会立即退出

SIGQUIT

  • “立即关机模式”
  • 发送
    SIGQUIT
    到所有子进程,如果它们在 5 秒内没有终止,则发送
    SIGKILL

编辑:

显然

Recreate
仅保证更新时的娱乐(旧RS到新RS),但如果Pod随机死亡,它不能保证一次有1个Pod。当新 Pod 正在创建旧 Pod 时,旧 Pod 可能处于终止阶段,并且由于竞争条件,数据可能会损坏。 相关文件

这只能保证 Pod 在创建升级之前终止。如果升级 Deployment,旧版本的所有 Pod 将立即终止。在创建新修订版的任何 Pod 之前,需要等待成功删除。如果你手动删除一个 Pod,生命周期由 ReplicaSet 控制,并且会立即创建替换的 Pod(即使旧 Pod 仍处于 Terminate 状态)。 如果您的 Pod 需要“最多”保证,您应该考虑使用 StatefulSet。


0
投票

作为对 @umut-gerçek 优秀答案的补充,在我使用的 docker 镜像 postgres 中,pg_ctl 的正确路径是:

/usr/lib/postgresql/16/bin/pg_ctl

验证这一点的一个简单方法是在正在运行的 postgres pod 中运行 shell:

> kubectl get pods
> kubectl exec --stdin --tty pod_name -- /bin/bash
root@pod_name:/# find / -name pg_ctl
/usr/lib/postgresql/16/bin/pg_ctl

遵循 @umut-gerçek 的建议,我的 StatefulSet 清单类似于此:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgresql-deployment
  labels:
    app: postgresql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgresql # has to match .spec.template.metadata.labels
  serviceName: "postgresql"
  replicas: 1
  template:
    metadata:
      labels:
        app: postgresql # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 30
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: postgresql-pvc
      containers:
      - name: postgresql
        image: postgres:16
        lifecycle:
          preStop:
            exec:
              command: ["/usr/lib/postgresql/16/bin/pg_ctl stop -D /var/lib/postgresql/data -w -t 15 -m fast"]
        ports:
        - name: postgres
          containerPort: 5432
          protocol: TCP
        volumeMounts:
        - mountPath: /var/lib/postgresql/data
          name: data
        env:
           [...]
© www.soinside.com 2019 - 2024. All rights reserved.