k3s 的数据存储从 SQLite 开始
一句话总结
一台 k3s 服务器默认通过 kine 使用 SQLite,所以 etcd 快照无法工作;用 --cluster-init 重启后,数据会被保留并迁移到内置 etcd;恢复 etcd 快照必须有服务器令牌,并且会撤回快照之后的所有变更。
为什么需要它
Kubernetes API 服务器在设计上是把状态保存在 etcd 中的。然而 etcd 是为了法定人数(quorum)而以多台节点为前提设计的分布式存储,对于一台树莓派或在 CI 中临时使用的集群来说太重了。
k3s 用 kine 弥合了这个差距。kine 是一个模拟 etcd API 的薄薄一层,后面可以接 SQLite、MySQL、PostgreSQL 等普通数据库。实测查看日志,API 服务器以 --etcd-servers=unix://kine.sock 启动,而 k3s 会先宣告 Kine available at unix://kine.sock。
官方文档写道,SQLite 是在没有其他存储配置、磁盘上也没有内置 etcd 数据时使用的默认值,并且不能用于多台服务器的集群。
问题在于这个默认值是悄无声息的。集群运行得好好的,直到想要接入备份的那天才会暴露出来。
$ k3s etcd-snapshot save
level=fatal msg="Error: see server log for details: etcd datastore disabled"
这是实测输出。服务器日志中同样的提示以 HTTP 400 响应的形式保留下来。快照命令向 k3s 服务器发出了请求,服务器以“我不使用 etcd”为由拒绝了它。
工作原理
SQLite 时的备份没有专门的命令。按照文档,复制 /var/lib/rancher/k3s/server/db/,恢复时把其内容放回去。此时必须同时保管服务器令牌文件 /var/lib/rancher/k3s/server/token。令牌用于加密存储中的机密数据,所以如果用别的令牌恢复,这份备份就无法使用。
迁移到 etcd 只需要用 --cluster-init 重新启动原本以 SQLite 运行的服务器即可。实测结果如下。
config.yaml 에 cluster-init: true → systemctl restart k3s (API 준비 12초)
로그 Migrating content from sqlite to etcd
디스크 db/state.db → db/state.db.migrated, db/etcd/ 와 db/snapshots/ 생성
노드 node-role.kubernetes.io/etcd=true 가 붙음
데이터 재시작 전에 만든 ConfigMap 의 UID 가 그대로
该代码块中的韩文说明依次为:在 config.yaml 中加入 cluster-init: true 后重启 k3s(API 12 秒后就绪);日志出现从 sqlite 向 etcd 迁移内容的提示;磁盘上 db/state.db 被改名为 db/state.db.migrated,并生成 db/etcd/ 与 db/snapshots/;节点上被加上 node-role.kubernetes.io/etcd=true;重启前创建的 ConfigMap 的 UID 保持不变。
反方向不存在。文档写道,如果在磁盘上发现了 etcd 数据,--cluster-init、--server、--datastore-endpoint 这类存储参数都会被忽略。这意味着一旦变成 etcd,即使去掉参数也不会回到 SQLite。
快照有定期和手动两种。定期快照默认在 0 点和 12 点(0 */12 * * *)生成,保留 5 个,名称为 etcd-snapshot-<노드>-<시각>(占位符依次为节点名和时刻)。手动快照通过 k3s etcd-snapshot save 生成,没有保留数量的限制,需要自己删除,--name 只决定名称的前半部分。两者都保存在 --etcd-snapshot-dir 的默认值,即数据目录下的 db/snapshots 中,实测路径为 /var/lib/rancher/k3s/server/db/snapshots/lab-before-<노드>-<유닉스시각>(占位符依次为节点名和 Unix 时间)。kubectl get etcdsnapshotfile 会把整个集群的快照显示为对象。
恢复需要停止服务,然后单独运行一次同一个二进制文件。
systemctl stop k3s
k3s server --cluster-reset --cluster-reset-restore-path=/var/lib/rancher/k3s/server/db/snapshots/<이름>
# Managed etcd cluster membership has been reset, restart without --cluster-reset flag now.
systemctl start k3s
恢复不会删除当前的 etcd 数据,而是先把它移到 db/etcd-old-<시각>(占位符为时刻),再解开快照,并去掉其他所有成员,使其成为只有自己的集群。为了防止连续初始化,会创建 db/reset-flag,正常启动后再把它删除。如果只给 --cluster-reset 而不给 --cluster-reset-restore-path,则只会重置成员关系,而不会恢复快照。
在现场相遇的样子
实测中,先生成了快照,然后创建一个 ConfigMap 并修改另一个的值,再进行恢复。新创建的那个消失了,值回到了快照时的状态。消失的对象的 UID 仍然以字节的形式保留在 etcd-old-<시각>(占位符为时刻)里的旧数据文件中——恢复不会删除旧数据,就是这样得到确认的。etcd 成员名称(db/etcd/name)也被重新赋了一个与恢复前不同的值。
现场常见的事故有两种。一种是“恢复之后,昨天部署的东西没了”。恢复是回溯,如果不先确认快照之后有谁做了什么,就会变成第二次故障。另一种是只把快照搬到新服务器上想要恢复,结果失败。文档写道,在其他主机上恢复时,必须用 --token 传入原服务器的令牌。如果快照在对象存储中,而令牌只在已经消失的节点的磁盘上,那这份备份就无法使用。
实际工作中真正重要的事
- 接手集群时,先确认存储是什么。只需要看
db/中是有state.db还是有etcd/。 - 如果计划增加服务器,从一开始就用 cluster-init 启动。文档说明,内置 etcd 为了法定人数,必须由奇数台服务器组成。
- 备份是快照加令牌为一组。令牌权限设为 600,并且要保存在与快照不同的其他位置。
- 恢复之前,要记录“快照之后有什么发生了变化”。这会成为恢复之后需要重新应用的清单。
- 恢复会把旧数据保留为
etcd-old-<시각>(占位符为时刻)。它会占用磁盘,所以要在确认恢复成功之后再清理。 - 根据文档,恢复时不要求使用与创建快照时相同的 k3s 版本,更高的次版本也可以接受。
下一项实验要做什么
在 VM 中的 k3s 上,通过 kine 日志和表确认存储是 SQLite,并记录快照被拒绝的情况。通过 cluster-init 重启迁移到 etcd,查看标记 ConfigMap 的 UID 是否保留;把命名的快照连同令牌一起备份,然后制造快照之后的变更并进行恢复,确认什么消失了、又保留在哪里。
参考文档:Cluster Datastore · High Availability Embedded etcd · etcd-snapshot · Backup and Restore