惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

W
WeLiveSecurity
Jina AI
Jina AI
博客园 - 司徒正美
雷峰网
雷峰网
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园_首页
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
I
InfoQ
博客园 - Franky
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
Cyberwarzone
Cyberwarzone
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Schneier on Security
P
Privacy & Cybersecurity Law Blog
T
Threatpost
Cloudbric
Cloudbric
D
Docker
M
MIT News - Artificial intelligence
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Vercel News
Vercel News
Martin Fowler
Martin Fowler
J
Java Code Geeks
AWS News Blog
AWS News Blog
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
L
Lohrmann on Cybersecurity
Hacker News: Ask HN
Hacker News: Ask HN
Last Week in AI
Last Week in AI
S
Security @ Cisco Blogs
Help Net Security
Help Net Security
C
Cisco Blogs
V
V2EX
博客园 - 【当耐特】
I
Intezer
爱范儿
爱范儿
F
Fortinet All Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Privacy International News Feed
IT之家
IT之家
L
LINUX DO - 最新话题
B
Blog RSS Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO

博客园 - 小得盈满

Hudi 时间旅行与增量查询的区别分析 NATS 的基本安装及使用 Doris 和 StarRocks 性能测试对比 ClickHouse 添加磁盘扩容存储 使用 Docker 部署 Elasticsearch 集群 Qdrant 单机和集群的安装方法 ClickHouse UDF 的基本用法 StarRocks 容器镜像构建 Kafka 线上性能调优 StarRocks 集群安装 Kafka 的分片和副本机制 Flink 自定义 ClickHouse Table Connector 的简单实现 Redis 性能优化实战 记一次 Go 调用系统命令出现的问题分析 APISIX proxy-cache 插件用法 APISIX 简单的自定义插件开发步骤 Kafka 集群副本数量调整 使用 Docker Compose 安装 APISIX etcd 集群安装 Consul 集群安装
etcd 历史版本回溯的方法
小得盈满 · 2023-11-18 · via 博客园 - 小得盈满

在使用 etcd 作为配置存储或者其他的场景,如果因为误操作对其中 key 的值进行了修改,如果想要找回原来的值,可以利用 etcd 的版本机制进行回溯找回以前的值。在具体操作之前,我们首先获取一下 key 完整的信息来举个例子:

# 如果开启认证要指定 --user <username>:<password>
etcdctl get --endpoints=$ENDPOINTS <key> -w=json

上面 -w=json 是以 JSON 格式输出指定 key 的数据和元数据,例如下面的内容:

{"header":{"cluster_id":6783024485397960691,"member_id":2129759788194751561,"revision":583,"raft_term":5},"kvs":[{"key":"YWJjZA==","create_revision":370,"mod_revision":375,"version":4,"value":"NA=="}],"count":1}

我们首先看 header 里面的 revision 是当前 etcd 集群全局最新的版本号,注意这个版本号是当前存在数据的版本号,也就是最新写入键值的版本号,不是下一个版本号。然后 kvs 里面就是数据了,其中 key, value 都做了 Base64 编码,其中 create_revision 表示这个 key 创建时所对应的全局版本号,mod_revision 是表示当前版本的修订版本号,version 表示当前这个 key 的局部版本号,创建时的版本是 1,version 是 4 也就是说当前这个 key 在创建后修改了 3 次。

假如我们由于误操作而覆盖了这个 key 的值,那么可以就可以基于版本号来进行历史版本的回溯,前提是不能执行 compact,否则为了压缩空间所有的版本数据都会被清理掉,无法再进行恢复了。

然后如果没有合并过,我们就可以执行回溯,以上面的数据为例,创建版本为 370,修改版本为 375,那么我们要获得上一个版本,首先就需要拿 mod_revision - 1 去查询,然后就可以查询到上一个版本:

etcdctl get --endpoints=$ENDPOINTS <key> -w=json --rev=374

注意对于某个 key 来说,版本不是连续的,因为同时有其他 key 也在操作,这点要明白,现在我们拿到的信息如下:

{"header":{"cluster_id":6783024485397960691,"member_id":2129759788194751561,"revision":583,"raft_term":5},"kvs":[{"key":"YWJjZA==","create_revision":370,"mod_revision":373,"version":3,"value":"Mw=="}],"count":1}

可以看到上一个实际的版本是 373,然后我们再继续使用 372 获取:

etcdctl get --endpoints=$ENDPOINTS <key> -w=json --rev=372

结果如下:

{"header":{"cluster_id":6783024485397960691,"member_id":2129759788194751561,"revision":583,"raft_term":5},"kvs":[{"key":"YWJjZA==","create_revision":370,"mod_revision":371,"version":2,"value":"Mg=="}],"count":1}

可以看到上一个版本实际是 371,那么由于创建版本是 370,两者之间不可能再有其他版本号,所以到这里我们就得到了所有的版本号,当然在实际情况下也可能是不连续的,我们要迭代到两者相等为止,所以我们再继续查询一下:

etcdctl get --endpoints=$ENDPOINTS <key> -w=json --rev=370
{"header":{"cluster_id":6783024485397960691,"member_id":2129759788194751561,"revision":583,"raft_term":5},"kvs":[{"key":"YWJjZA==","create_revision":370,"mod_revision":370,"version":1,"value":"MQ=="}],"count":1}

毫无疑问,现在 create_revisionmod_revision 是相等的,那么也就意味着迭代完毕,拿到的版本号就是 370。

所以根据我们的迭代顺序,将版本号序列反转,结果为:370, 371, 373, 375,这样就得到的全部的版本号序列,然后我们就可以按照版本号依次查看 key 具体版本的值了:

# 只需要去掉 -w 选项,正常输出即可
etcdctl get --endpoints=$ENDPOINTS <key> --rev=<revision>

上面就是指定对于指定 key 整个回溯历史版本的过程,需要注意的是 etcd 只能根据全局的 revision 来回溯,不支持按照每个 key 的 version 来回溯,不过局部这个 version 可以用来直观地查看这个 key 被改了几次。

我们也可以根据上面的过程编写一个程序来实现快速查找 key 历史版本的功能,帮助我们快速拿到指定 key 的所有历史记录。

另外也可以使用 watch 的方法来拿到所有的历史记录,比如我们知道这个 key 对应的 create_revision,那么可以 watch 时指定初始版本,这样就会输出所有的历史:

etcdctl watch --endpoints=$ENDPOINTS <key> --rev=<create_revision>

这样就可以一次性拿到所有的修改记录,不过命令是阻塞的,需要手动结束,写程序时也需要做好控制。上面指定的版本号也不一定是 create_revision,指定个比较小的也可以,比如 1。

以上操作基于 etcd 3.5 版本。

基于 Rust 编写的 etcd 版本回溯工具:https://github.com/monchickey/etcd-version-history 可以参考使用。

Reference:

  1. https://etcd.io/docs/v3.5/learning/data_model/
  2. https://github.com/etcd-io/etcd/issues/7292
  3. https://github.com/etcd-io/etcd/issues/11027