ClickHouse 在本地存储或 NAS 上的备份和恢复

概述

本文介绍如何使用本地存储或挂载到目录的 NAS,对 observability 数据库中的 ClickHouse 表进行备份和恢复。此操作步骤适用于使用 ReplicatedMergeTree 表引擎的集群。

对于 ClickHouse,NAS 挂载会被视为本地文件系统路径。因此,本地存储和 NAS 可以使用相同的备份和恢复方法。

本文提供以下指导:

  • 创建全量备份。
  • 基于全量备份创建增量备份。
  • 将备份数据存储在本地目录或 NAS 挂载路径中。
  • 从本地或 NAS 备份中恢复数据。
  • 验证备份和恢复结果。

本文以 observability.audit 表为例。你可以将相同步骤应用于其他表。

常见示例如下:

  • audit:存储审计数据。
  • event:存储事件数据。
  • log_kubernetes:存储 Kubernetes 日志。
  • log_platform:存储平台服务日志。
  • log_system:存储节点级系统日志。
  • log_workload:存储应用和工作负载日志。

存储类型差异如下:

  • LocalVolume:ClickHouse 数据存储在节点本地目录中,例如 /cpaas/data/clickhouse/BACKUP ... TO File(...) 会将备份文件写入该本地数据目录下的 ClickHouse 备份目录,例如 /cpaas/data/clickhouse/backups
  • StorageClass,例如 TopoLVM、NFS 或 Ceph:ClickHouse 数据存储在对应的 StorageClass 卷上。BACKUP ... TO File(...) 会将备份文件写入该卷上的 ClickHouse 备份目录。
  • NAS 归档路径:可以将备份文件从 ClickHouse 备份目录复制到挂载的 NAS 路径或其他自定义备份目录,以便长期保留。

对于 LocalVolume 和 StorageClass 部署,在归档备份文件时,请使用实际的 ClickHouse 备份目录作为源路径;在将备份文件复制回去以进行恢复时,请使用该目录作为目标路径。

前提条件

在开始之前,请确保满足以下条件。

环境要求

项目要求
ClickHouse25.3 或更高版本
表引擎ReplicatedMergeTree
备份目标挂载到主机的本地目录或 NAS 目录

访问要求

本文中的所有 SQL 语句均使用内置的 ClickHouse 管理员账户 default

你可以在任意健康的 ClickHouse 实例上运行这些 SQL 语句。为保持一致性,本文以单个 ClickHouse Pod 为例。

在运行 SQL 语句之前,请连接到目标 Pod:

kubectl exec -ti -n cpaas-system chi-cpaas-clickhouse-replicated-0-0-0 -- bash

然后连接到容器中的 ClickHouse:

clickhouse-client

default 用户已具备执行此操作步骤所需的权限,包括 BACKUPRESTORESELECTALTER

目录要求

请确保满足以下条件:

  • ClickHouse 进程对目标目录具有读写权限。
  • 目标目录有足够的可用容量。
  • 如果使用 NAS,则在 Pod 或主机重启后挂载点可以自动恢复。
  • 在 Kubernetes 中,通过 PVC、hostPath 或 CSI 挂载目录。

备份策略

请采用以下备份策略:

  • 仅在任意健康从节点上执行一次备份操作。
  • 使用 BACKUP TABLE 创建一致性快照,无需停止服务。
  • 增量备份使用 base_backup 进行文件级去重。
  • 在恢复增量备份时,保留可访问的基础全量备份。
  • 每周保留一个全量备份、每天保留一个增量备份,以平衡恢复复杂度和存储成本。

操作步骤

以下示例使用初始全量备份和每日增量备份。

创建全量备份

全量备份会为后续增量备份创建基线。

在任意健康的 ClickHouse 实例上运行以下命令:

BACKUP TABLE observability.audit
TO File('audit_full_20260423')
SETTINGS compression_method = 'zstd';

注意:

  • File(...) 会将备份写入 ClickHouse 数据卷上的 ClickHouse 备份目录。
  • 对于 LocalVolume 部署,备份目录通常位于主机节点上的 /cpaas/data/clickhouse/backups
  • 对于 TopoLVM、NFS 或 Ceph 等 StorageClass 部署,备份目录位于对应 StorageClass 卷上。
  • compression_method = 'zstd' 使用 zstd 压缩备份内容,以减少存储占用。
  • 如果需要将备份归档到 NAS 或其他自定义备份目录,请在备份任务完成后复制该备份。

验证备份是否成功

备份完成后,请验证结果。

检查备份任务状态

在执行备份命令的 ClickHouse 实例上运行以下查询:

SELECT
    id,
    name,
    status,
    error,
    start_time,
    end_time,
    num_files,
    total_size
FROM system.backups
ORDER BY start_time DESC
LIMIT 10
FORMAT Vertical;

预期结果:

通过 name 字段定位 observability.audit 的备份记录。如果满足以下条件,则备份成功:

  • status = 'BACKUP_CREATED'
  • error 为空
  • end_time 有值
  • num_files > 0
  • total_size > 0

检查备份目录

在可以访问 ClickHouse 备份目录的节点或容器中运行以下命令。

对于 LocalVolume 部署,备份目录通常为 /cpaas/data/clickhouse/backups

ls -lah /cpaas/data/clickhouse/backups

对于 StorageClass 部署,请检查对应 StorageClass 卷上的备份目录。

预期结果:

  • 备份目录存在。
  • 目录中包含由此次备份生成的文件或文件夹。
  • 文件数量和总大小大于 0。

创建增量备份

增量备份使用 base_backup,仅写入新增或变更的数据文件。

在任意健康的 ClickHouse 实例上运行以下命令:

BACKUP TABLE observability.audit
TO File('audit_incr_20260424')
SETTINGS
  base_backup = File('audit_full_20260423'),
  compression_method = 'zstd';

注意:

  • 增量备份依赖于 base_backup 指定的备份。
  • 本文建议每天的增量备份都使用最新的全量备份作为 base_backup,而不是使用上一个增量备份作为基础。这样可以简化恢复依赖:恢复每日增量备份时,只需要增量备份及其对应的全量备份。
  • 在恢复增量备份时,请保留其依赖的全量备份。
  • 请定期创建新的全量备份,避免长期依赖同一个基线。

归档备份文件

BACKUP ... TO File(...) 会将备份文件写入执行备份命令的 ClickHouse 数据卷上的 ClickHouse 备份目录。

备份目录取决于存储类型:

  • 对于 LocalVolume 部署,ClickHouse 数据目录位于主机节点上,通常为 /cpaas/data/clickhouse/,备份文件会生成在 /cpaas/data/clickhouse/backups 下。
  • 对于 TopoLVM、NFS 或 Ceph 等 StorageClass 部署,ClickHouse 数据目录位于对应的 StorageClass 卷上,备份文件会生成在该卷上的 ClickHouse 备份目录中。

备份成功后,请将备份文件从实际的 ClickHouse 备份目录复制到自定义备份目录或 NAS 挂载路径,以便保留。

对于 LocalVolume 部署,源路径通常为 /cpaas/data/clickhouse/backups

cp -r /cpaas/data/clickhouse/backups/audit_full_20260423 /my_dir/audit_full_20260423
cp -r /cpaas/data/clickhouse/backups/audit_incr_20260424 /my_dir/audit_incr_20260424

对于 StorageClass 部署,请将 /cpaas/data/clickhouse/backups 替换为 StorageClass 卷上的实际 ClickHouse 备份目录。

注意:

  • /my_dir 可以是指定的本地归档目录或 NAS 挂载路径。
  • 请使用位于 ClickHouse 数据目录之外的归档路径。否则,在灾难恢复期间清理 ClickHouse 数据目录时,备份文件可能会被删除。
  • 每次全量或增量备份完成后,都应将相应的备份目录复制到归档位置。
  • 成功复制增量备份后,如果不再需要,可以删除 ClickHouse 备份目录下的本地增量备份。
  • 只有在下一次全量备份成功且保留策略允许清理后,才能删除 ClickHouse 备份目录下的本地全量备份。
  • 如果后续使用 RESTORE ... ON CLUSTER ... FROM File(...) 进行恢复,请确保在执行恢复命令之前,所需的全量和增量备份目录已复制回每个 ClickHouse 主机节点上的 ClickHouse 备份目录。

恢复

当表数据损坏、数据目录被删除或表状态异常时,请使用此操作步骤。

恢复前提条件

在开始恢复操作步骤之前,请确保满足以下条件:

  • 你有有效的全量备份或增量备份。
  • 如果从增量备份恢复,则对应的全量备份在恢复路径中可用。
  • 你知道存放最新增量备份及其对应全量备份的归档目录。
  • 你可以访问 Kubernetes 集群以及部署 ClickHouse 实例的主机节点。

恢复操作步骤

此操作步骤按表逐个恢复 ClickHouse 数据。请根据故障范围选择准备步骤,然后对每个需要恢复的表执行相同的表恢复操作步骤。

停止写入

首先停止 razor,以防止恢复期间写入新数据。

登录到集群主节点并创建 ResourcePatch 以停止 razor

cat <<EOF > /tmp/rp-stop-razor.yaml
apiVersion: operator.alauda.io/v1alpha1
kind: ResourcePatch
metadata:
  generateName: rp-
  name: rp-stop-razor
spec:
  jsonPatch:
  - op: replace
    path: /spec/replicas
    value: 0
  release: cpaas-system/logclickhouse
  target:
    apiVersion: apps/v1
    kind: StatefulSet
    name: razor
    namespace: cpaas-system
EOF

kubectl apply -f /tmp/rp-stop-razor.yaml

根据故障范围准备 ClickHouse

请根据故障范围选择以下一种准备路径。准备完成后,继续执行相同的按表恢复操作步骤。

情况 A:表数据损坏,但数据目录健康

当一个或多个表损坏、被误删或数据异常,但 ClickHouse 数据目录和 Keeper 状态仍然健康时,使用此情况。

在这种情况下,不要停止 ClickHouse,也不要清理 ClickHouse 数据目录。直接继续执行表恢复操作步骤。

情况 B:数据目录或 Keeper 元数据损坏

仅当 ClickHouse 数据目录损坏、节点被重建或 Keeper 元数据不可用时,才使用此情况。

在此部署中,ClickHouse Keeper 与 ClickHouse 集成,其数据也存储在 ClickHouse 数据目录下。因此,清理 ClickHouse 数据目录也会删除本地 ClickHouse Keeper 数据。

警告: 清理数据目录会删除目标节点上的本地 ClickHouse 数据。继续之前,请确认备份可用。

请根据存储类型清理每个 ClickHouse 实例的实际 ClickHouse 数据目录。对于 LocalVolume 部署,目录通常为主机节点上的 /cpaas/data/clickhouse/。对于 StorageClass 部署,请清理对应 StorageClass 卷或 PV 上的 ClickHouse 数据目录。除非已确认 /cpaas/data/clickhouse/ 就是实际挂载的数据路径,否则不要在 StorageClass 部署中使用该路径。

登录到集群主节点并创建 ResourcePatch 以停止 ClickHouse:

cat <<EOF > /tmp/rp-stop-ck.yaml
apiVersion: operator.alauda.io/v1alpha1
kind: ResourcePatch
metadata:
  generateName: rp-
  name: rp-stop-ck
spec:
  jsonPatch:
  - op: add
    path: /spec/stop
    value: "true"
  release: cpaas-system/logclickhouse
  target:
    apiVersion: clickhouse.altinity.com/v1
    kind: ClickHouseInstallation
    name: cpaas-clickhouse
    namespace: cpaas-system
EOF

kubectl apply -f /tmp/rp-stop-ck.yaml

确认所有 ClickHouse Pod 已停止:

kubectl get pod -n cpaas-system | grep -i "chi-cpaas-clickhouse-replicated"

根据存储类型清理每个 ClickHouse 实例的 ClickHouse 数据目录。

对于 LocalVolume 部署,在部署了 ClickHouse 实例的每个主机节点上运行以下命令:

rm -rf /cpaas/data/clickhouse/*

对于 StorageClass 部署,请清理对应 StorageClass 卷或 PV 上的 ClickHouse 数据目录。具体路径取决于 StorageClass 和 CSI 实现。请使用实际挂载的 ClickHouse 数据路径,而不是 LocalVolume 示例路径。

通过删除 ClickHouse ResourcePatch 仅启动 ClickHouse 组件。此时不要启动 razor

kubectl delete -f /tmp/rp-stop-ck.yaml

确认所有 ClickHouse Pod 正在运行:

kubectl get pod -n cpaas-system | grep -i "chi-cpaas-clickhouse-replicated"

在每个 ClickHouse 节点上准备备份文件

请确保每个 ClickHouse 主机节点上的 ClickHouse 备份目录中都可用备份文件。

如果备份文件已归档到自定义备份目录或 NAS 挂载路径,并且 ClickHouse 备份目录下的本地文件已被删除,请在恢复表之前,将最新增量备份及其对应的全量备份复制回每个 ClickHouse 主机节点上的 ClickHouse 备份目录。

对于 LocalVolume 部署,目标路径通常为 /cpaas/data/clickhouse/backups

mkdir -p /cpaas/data/clickhouse/backups
cp -r /my_dir/audit_full_20260423 /cpaas/data/clickhouse/backups/audit_full_20260423
cp -r /my_dir/audit_incr_20260424 /cpaas/data/clickhouse/backups/audit_incr_20260424

对于 StorageClass 部署,请将备份文件复制回 StorageClass 卷上的实际 ClickHouse 备份目录。

注意:

  • 如果从增量备份恢复,请同时准备其依赖的全量备份。
  • 备份文件必须放置在每个 ClickHouse 主机节点上的 ClickHouse 备份目录下,以便 RESTORE ... ON CLUSTER ... FROM File(...) 可以在每个 ClickHouse 实例上访问这些文件。
  • 请将 /my_dir/cpaas/data/clickhouse/backupsaudit_full_20260423audit_incr_20260424 替换为实际的归档路径、ClickHouse 备份目录和备份目录名称。

检查集群就绪状态

在运行恢复命令之前,请确保 ClickHouse 集群配置和宏可用。

检查 replicated 集群配置:

SELECT *
FROM system.clusters
WHERE cluster = 'replicated';

检查本地 ClickHouse 宏:

SELECT *
FROM system.macros;

确保集群包含预期的 ClickHouse 从节点,并且 shardreplica 等宏可用。

逐个恢复表

对每个需要恢复的表运行以下恢复操作步骤。

在 ClickHouse 集群上删除目标表:

DROP TABLE observability.audit ON CLUSTER 'replicated' SYNC;

从本地或 NAS 备份中恢复表:

RESTORE TABLE observability.audit
ON CLUSTER 'replicated'
FROM File('audit_incr_20260424');

注意:

  • 对于此 3 从节点部署中的 ReplicatedMergeTree 表,请使用 ON CLUSTER 'replicated',以便将恢复操作分发到集群中的所有 ClickHouse 从节点。
  • 如果从增量备份恢复,ClickHouse 会自动读取其依赖的基础备份。
  • 在恢复期间,请保持相应的全量备份目录可访问。
  • 请将 observability.audit 和备份目录名称替换为要恢复的实际表和备份。
  • 对每个需要恢复的表重复相同的操作步骤。表列表由客户确定。

检查恢复任务状态

执行完每个恢复命令后,在启动任何相关组件之前,请检查恢复任务状态:

SELECT
    id,
    name,
    status,
    error,
    start_time,
    end_time,
    num_files,
    total_size
FROM system.backups
ORDER BY start_time DESC
LIMIT 10
FORMAT Vertical;

预期结果:

如果满足以下条件,则恢复成功:

  • status 表示恢复操作已成功完成。
  • error 为空。
  • end_time 有值。

验证恢复成功

恢复完成后,请从数据、分区和从节点三个角度验证结果。

验证总行数

在每个 ClickHouse 实例上运行以下查询:

SELECT count(*) AS total_rows
FROM observability.audit;

预期结果:

所有 ClickHouse 实例上的 total_rows 值都相同。

验证分区级数据

在每个 ClickHouse 实例上运行以下查询:

SELECT
    partition,
    sum(rows) AS rows,
    count() AS active_parts
FROM system.parts
WHERE active
  AND database = 'observability'
  AND table = 'audit'
GROUP BY partition
ORDER BY partition;

预期结果:

  • 所有 ClickHouse 实例上的 partition 列表都相同。
  • 所有 ClickHouse 实例上每个分区的 rows 值都相同。

active_parts 仅用于观察物理 part 布局。part 数量不同并不一定表示恢复失败。

验证从节点状态

在任意 ClickHouse 实例上运行以下查询:

SELECT
    database,
    table,
    total_replicas,
    active_replicas,
    queue_size,
    absolute_delay
FROM system.replicas
WHERE database = 'observability'
  AND table = 'audit';

预期结果:

对于 3 从节点集群,如果满足以下条件,则恢复成功:

  • total_replicas = 3
  • active_replicas = 3
  • queue_size = 0
  • absolute_delay 接近 0

启动相关组件

在恢复成功验证通过后,通过删除 ResourcePatch 重新启动 razor

kubectl delete -f /tmp/rp-stop-razor.yaml

建议

在生产环境中建议采用以下做法:

  • 每周保留一个全量备份、每天保留一个增量备份,以维护备份链。
  • 使用统一的备份目录命名规范,例如 full_YYYYMMDDincr_YYYYMMDD
  • 定期在测试环境中执行恢复演练。
  • 为过期增量备份和历史全量备份制定保留策略。