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

推荐订阅源

博客园 - 三生石上(FineUI控件)
O
OpenAI News
WordPress大学
WordPress大学
P
Proofpoint News Feed
J
Java Code Geeks
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Register - Security
The Register - Security
Engineering at Meta
Engineering at Meta
H
Help Net Security
人人都是产品经理
人人都是产品经理
Vercel News
Vercel News
N
Netflix TechBlog - Medium
F
Full Disclosure
U
Unit 42
Latest news
Latest news
N
News and Events Feed by Topic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
InfoQ
L
LINUX DO - 最新话题
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
小众软件
小众软件
I
Intezer
V
V2EX
S
SegmentFault 最新的问题
C
CERT Recently Published Vulnerability Notes
阮一峰的网络日志
阮一峰的网络日志
Security Archives - TechRepublic
Security Archives - TechRepublic
Recent Announcements
Recent Announcements
C
Check Point Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Recorded Future
Recorded Future
博客园 - Franky
Project Zero
Project Zero
S
Securelist
Attack and Defense Labs
Attack and Defense Labs
Spread Privacy
Spread Privacy
The Hacker News
The Hacker News
T
The Blog of Author Tim Ferriss
Google DeepMind News
Google DeepMind News
aimingoo的专栏
aimingoo的专栏
博客园 - 叶小钗
NISL@THU
NISL@THU
云风的 BLOG
云风的 BLOG
S
Secure Thoughts
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed

博客园 - 小得盈满

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