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

推荐订阅源

The Register - Security
The Register - Security
IT之家
IT之家
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
The Cloudflare Blog
T
The Blog of Author Tim Ferriss
Apple Machine Learning Research
Apple Machine Learning Research
C
Check Point Blog
Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
量子位
F
Fortinet All Blogs
Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
Last Week in AI
Last Week in AI
博客园 - 聂微东
美团技术团队
Stack Overflow Blog
Stack Overflow Blog
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享
M
MIT News - Artificial intelligence
U
Unit 42
罗磊的独立博客
MyScale Blog
MyScale Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
V
Visual Studio Blog
Jina AI
Jina AI
Recorded Future
Recorded Future
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MongoDB | Blog
MongoDB | Blog
N
Netflix TechBlog - Medium
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
B
Blog
J
Java Code Geeks
爱范儿
爱范儿
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
aimingoo的专栏
aimingoo的专栏
B
Blog RSS Feed
月光博客
月光博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy International News Feed
Know Your Adversary
Know Your Adversary
雷峰网
雷峰网

博客园 - 小得盈满

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