执行快照命令时才发现数据存储是 SQLite
目标
确认单台 k3s 服务器的存储是 SQLite(kine),了解 etcd 快照被拒绝的原因,然后通过 --cluster-init 重启迁移到内置 etcd。 把快照连同令牌一起备份,并用 --cluster-reset 恢复,记录快照之后的变更消失了,以及之前的数据保留在哪里。
为什么重要
k3s 以单台启动时,使用 SQLite 而不是 etcd。它轻量、快速,但多台服务器无法共享,而且 k3s etcd-snapshot 之类的 etcd 工具无法工作。
因此,直到“该接入备份了”的那天,才会第一次确认存储到底是什么,而此时的选择要么是整体复制文件的 SQLite 备份,要么是迁移到 etcd。
迁移到 etcd 只需重启一次,但恢复是“回到那个时间点”,所以快照之后创建的东西都会消失。而且没有服务器令牌,快照就毫无用处。
本实验让你在真实的 k3s 上把这三件事——确认存储、迁移、恢复所撤回的范围——各经历一遍。与一开始就使用 etcd 的 k0s 实验不同,这里处理的是更换存储的过程本身。
步骤
- 在命名空间
k3s-ds中创建 ConfigMapmarker(stage=sqlite)。然后在/root/k3s-ds/datastore.json中写入datastore(sqlite或etcd)、db_files(/var/lib/rancher/k3s/server/db中文件名排序后的数组)、kine_table(state.db 内的表名中 kine 使用的那一个)、kine_endpoint(k3s 日志中Kine available at之后的地址)、marker_uid(marker 的 UID)。 - 在当前状态下运行
k3s etcd-snapshot save,把标准输出和标准错误一起保存到/root/k3s-ds/snapshot-refused.txt。然后在/root/k3s-ds/sqlite-backup.txt中,每行一个,写出备份 SQLite 存储需要复制的目录和文件的绝对路径。 - 在
/etc/rancher/k3s/config.yaml中写入cluster-init: true并重启 k3s。确认 marker 仍然存在,并在/root/k3s-ds/migrate.json中写入marker_uid(重启后 marker 的 UID)、sqlite_file_now(db 目录中原来的 state.db 被改成的名称)、node_roles(节点的 node-role.kubernetes.io/ 标签名称排序后的数组,例如:["control-plane"])、member_name(db/etcd/name文件的内容)。 - 把 marker 的
stage改为snapshotted,然后用k3s etcd-snapshot save --name lab-before生成快照。在/root/k3s-ds/snapshot.json中写入name(生成的完整快照名称)、path(文件绝对路径)、size(字节,整数)、sha256(文件的 SHA-256)。 - 把第 4 步的快照文件(同名)和服务器令牌文件(名为
token,权限 600)复制到/root/k3s-ds/backup/目录。在/root/k3s-ds/token-backup.json中写入token_source(令牌原件的绝对路径)、token_sha256(令牌文件的 SHA-256)、snapshot_sha256(所备份快照的 SHA-256)。令牌的值本身不要写在任何地方。 - 在快照之后,在命名空间
k3s-ds中创建 ConfigMapafter-snap(x=1),并把 marker 的stage改为changed。在/root/k3s-ds/after.json中写入after_uid(after-snap 的 UID)、after_created(它的 creationTimestamp)、marker_stage(当前 marker 的 stage)。 - 停止 k3s,用
k3s server --cluster-reset --cluster-reset-restore-path=<4단계 스냅숏 경로>(占位符为第 4 步的快照路径)进行恢复(把完整输出保存到/root/k3s-ds/reset.log),然后重新启动 k3s。在/root/k3s-ds/restore.json中写入old_dir(恢复时移动旧 etcd 数据所到的目录名)、restart_hint(reset.log 中 k3s 提示下一步该做什么的句子里,包含restart without的那部分之前的第一句)、after_snap_exists(布尔值)、marker_stage(恢复后 marker 的 stage)、member_name(恢复后db/etcd/name的内容)。 - 在
/root/k3s-ds/report.json中写入datastore_now(sqlite或etcd)、lost_objects(因恢复而消失的 ConfigMap 名称排序后的数组)、marker_stage(当前值)、snapshot_dir(保存快照的目录绝对路径)、restore_needs_token(在其他服务器上恢复时是否需要原来的令牌,布尔值)、member_name_changed(恢复前后 etcd 成员名称是否发生了变化,布尔值)。
参考
- VM 中有一台 k3s v1.35.8+k3s1(已关闭 traefik 和 metrics-server)。重启的命令是
systemctl restart k3s。 - 第 3 步之后,
k3s etcd-snapshot命令会因为不认识 config.yaml 中的 cluster-init 这个键而输出一行警告。这是无害的(实测)。 - 恢复命令按文档步骤,在用
systemctl stop k3s停止服务之后运行。 - 常见错误:只备份快照,而漏掉
/var/lib/rancher/k3s/server/token。 - 常见错误:把
--cluster-reset写进服务参数或 config.yaml。恢复是单独运行一次的命令,k3s 通过 reset-flag 文件防止连续初始化。 - Cluster Datastore · High Availability Embedded etcd · etcd-snapshot · Backup and Restore
这个集群的存储是什么
在命名空间 k3s-ds 中创建 ConfigMap marker(stage=sqlite)。然后在 /root/k3s-ds/datastore.json 中写入 datastore(sqlite 或 etcd)、db_files(/var/lib/rancher/k3s/server/db 中文件名排序后的数组)、kine_table(state.db 内的表名中 kine 使用的那一个)、kine_endpoint(k3s 日志中 Kine available at 之后的地址)、marker_uid(marker 的 UID)。
k3s 不会把 SQLite 直接接到 API 服务器上,而是在中间放了一层模拟 etcd API 的 kine。请在日志中看 API 服务器的 --etcd-servers 指向什么。表名可以用 sqlite3 <파일> .tables(占位符为文件名)查看。
执行了快照命令,存储却是 SQLite
在当前状态下运行 k3s etcd-snapshot save,把标准输出和标准错误一起保存到 /root/k3s-ds/snapshot-refused.txt。然后在 /root/k3s-ds/sqlite-backup.txt 中,每行一个,写出备份 SQLite 存储需要复制的目录和文件的绝对路径。
etcd-snapshot 会向服务器发送请求,由服务器用内置 etcd 生成快照。如果存储是 SQLite,服务器就会拒绝,并把详细原因记录在服务器日志中。SQLite 不需要特殊命令,复制文件就能备份,但用于加密存储中机密数据的那个值也必须一并保管。
重启一次就迁移到 etcd
在 /etc/rancher/k3s/config.yaml 中写入 cluster-init: true 并重启 k3s。确认 marker 仍然存在,并在 /root/k3s-ds/migrate.json 中写入 marker_uid(重启后 marker 的 UID)、sqlite_file_now(db 目录中原来的 state.db 被改成的名称)、node_roles(节点的 node-role.kubernetes.io/ 标签名称排序后的数组,例如:["control-plane"])、member_name(db/etcd/name 文件的内容)。
用 cluster-init 启动原本以 SQLite 运行的服务器时,k3s 会把 SQLite 的内容迁移到 etcd。如果磁盘上已经有 etcd 数据,这个参数会被忽略。请在日志中找一找 Migrating content from sqlite to etcd。
给快照命名后生成
把 marker 的 stage 改为 snapshotted,然后用 k3s etcd-snapshot save --name lab-before 生成快照。在 /root/k3s-ds/snapshot.json 中写入 name(生成的完整快照名称)、path(文件绝对路径)、size(字节,整数)、sha256(文件的 SHA-256)。
--name 只决定名称的前半部分,k3s 会追加节点名称和时刻。保存位置是 --etcd-snapshot-dir 的默认值。k3s etcd-snapshot ls 和 kubectl get etcdsnapshotfile 显示的是同样的快照。
仅凭快照无法恢复
把第 4 步的快照文件(同名)和服务器令牌文件(名为 token,权限 600)复制到 /root/k3s-ds/backup/ 目录。在 /root/k3s-ds/token-backup.json 中写入 token_source(令牌原件的绝对路径)、token_sha256(令牌文件的 SHA-256)、snapshot_sha256(所备份快照的 SHA-256)。令牌的值本身不要写在任何地方。
k3s 用服务器令牌加密存储中的机密引导数据。如果用别的令牌恢复,快照就无法使用。复制时不要让权限变宽。
快照之后产生的东西
在快照之后,在命名空间 k3s-ds 中创建 ConfigMap after-snap(x=1),并把 marker 的 stage 改为 changed。在 /root/k3s-ds/after.json 中写入 after_uid(after-snap 的 UID)、after_created(它的 creationTimestamp)、marker_stage(当前 marker 的 stage)。
目的是观察下一步的恢复会如何处置这两者。为了在恢复之后还能确认这份记录是真实的,请准确写下 UID 和时刻。
恢复之后,快照之后的变更消失了
停止 k3s,用 k3s server --cluster-reset --cluster-reset-restore-path=<4단계 스냅숏 경로>(占位符为第 4 步的快照路径)进行恢复(把完整输出保存到 /root/k3s-ds/reset.log),然后重新启动 k3s。在 /root/k3s-ds/restore.json 中写入 old_dir(恢复时移动旧 etcd 数据所到的目录名)、restart_hint(reset.log 中 k3s 提示下一步该做什么的句子里,包含 restart without 的那部分之前的第一句)、after_snap_exists(布尔值)、marker_stage(恢复后 marker 的 stage)、member_name(恢复后 db/etcd/name 的内容)。
恢复是在服务停止的状态下,单独运行一次同一个二进制文件。结束后它会自行退出,并提示重新启动。旧数据不会被删除,而是被移到旁边。防止连续初始化的标记文件会先生成,待正常启动后再被删除。
什么保留了下来,什么消失了
在 /root/k3s-ds/report.json 中写入 datastore_now(sqlite 或 etcd)、lost_objects(因恢复而消失的 ConfigMap 名称排序后的数组)、marker_stage(当前值)、snapshot_dir(保存快照的目录绝对路径)、restore_needs_token(在其他服务器上恢复时是否需要原来的令牌,布尔值)、member_name_changed(恢复前后 etcd 成员名称是否发生了变化,布尔值)。
依据前面步骤的 json 以及当前的磁盘和集群来填写。评分器会从记录文件、etcd-old 目录和集群中重新计算同样的值。