5.2 数据库操作
etcd 在键的组织上采用了如同类似文件目录的结构,即层次化的空间结构,我们可以为键指定单独的名字。etcd 数据库提供的操作,则主要围绕对键值和目录的增删改查。
5.2.1 键操作
-
set 指定某个键的值。例如:
$ etcdctl put /testdir/testkey "Hello world"
$ etcdctl put /testdir/testkey2 "Hello world2"
$ etcdctl put /testdir/testkey3 "Hello world3"成功写入三对键值,/testdir/testkey、/testdir/testkey2 和 /testdir/testkey3。
-
get 获取指定键的值。例如:
$ etcdctl get /testdir/testkey
Hello world -
get 十六进制读指定的值:
$ etcdctl get /testdir/testkey --hex
\x2f\x74\x65\x73\x74\x64\x69\x72\x2f\x74\x65\x73\x74\x6b\x65\x79 #键
\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64 #值加上
--print-value-only
可以读取对应的值。 -
get 范围内的值
$ etcdctl get /testdir/testkey /testdir/testkey3
/testdir/testkey
Hello world
/testdir/testkey2
Hello world2可以看到,获取了大于等于 /testdir/testkey,且小于 /testdir/testkey3 的键值对。testkey3 不在范围之内,因为范围是半开区间 [testkey, testkey3), 不包含 testkey3。
-
获取某个前缀的所有键值对,通过 --prefix 可以指定前缀:
$ etcdctl get --prefix /testdir/testkey
/testdir/testkey
Hello world
/testdir/testkey2
Hello world2
/testdir/testkey3
Hello world3这样既可获取所有以 /testdir/testkey 开头的键值对。当前缀获取的结果过多时,还可以通过 --limit=2 限制获取的数量:
etcdctl get --prefix --limit=2 /testdir/testkey
-
读取键过往版本的值 应用可能想读取键的被替代的值。例如,应用可能想通过访问键的过往版本来回滚到旧的配置。或者,应用可能想通过多个请求来得到一个覆盖多个键的统一视图,而这些请求可以通过访问键历史记录而来。因为 etcd 集群上键值存储的每个修改都会增加 etcd 集群的全局修订版本,应用可以通过提供旧有的 etcd 修改版本来读取被替代的键。现有如下这些键值对:
foo = bar # revision = 2
foo1 = bar2 # revision = 3
foo = bar_new # revision = 4
foo1 = bar1_new # revision = 5以下是访问以前版本 key 的示例:
$ etcdctl get --prefix foo # 访问最新版本的 key
foo
bar_new
foo1
bar1_new
$ etcdctl get --prefix --rev=4 foo # 访问第 4 个版本的 key
foo
bar_new
foo1
bar1
$ etcdctl get --prefix --rev=3 foo # 访问第 3 个版本的 key
foo
bar
foo1
bar1
$ etcdctl get --prefix --rev=2 foo # 访问第 3 个版本的 key
foo
bar
$ etcdctl get --prefix --rev=1 foo # 访问第 1 个版本的 key -
读取大于等于指定键的 byte 值的键 应用可能想读取大于等于指定键 的 byte 值的键。假设 etcd 集群已经有下列键:
a = 123
b = 456
z = 789读取大于等于键 b 的 byte 值的键的命令:
$ etcdctl get --from-key b
b
456
z
789 -
删除键。客户端应用可以从 etcd 数据库中删除指定的键。假设 etcd 集群已经有下列键:
foo = bar
foo1 = bar1
foo3 = bar3
zoo = val
zoo1 = val1
zoo2 = val2
a = 123
b = 456
z = 789删除键 foo 的命令:
$ etcdctl del foo
1 # 删除了一个键删除从 foo to foo9 范围的键的命令:
$ etcdctl del foo foo9
2 # 删除了两个键删除键 zoo 并返回被删除的键值对的命令:
$ etcdctl del --prev-kv zoo
1 # 一个键被删除
zoo # 被删除的键
val # 被删除的键的值删除前缀为 zoo 的键的命令:
$ etcdctl del --prefix zoo
2 # 删除了两个键删除大于等于键 b 的 byte 值的键的命令:
$ etcdctl del --from-key b
2 # 删除了两个键
5.2.2 watch 历史改动
-
watch 可以用来监测一个键值的变化,当该键值更新,控制台就会输出最新的值。例如:用户更新 watchkey 键值为 newwatchvalue。
$ etcdctl watch watchkey
# 在另外一个终端: etcdctl put watchkey newwatchvalue
watchkey
newwatchvalue从 foo to foo9 范围内键的命令:
$ etcdctl watch foo foo9
# 在另外一个终端: etcdctl put foo bar
PUT
foo
bar
# 在另外一个终端: etcdctl put foo1 bar1
PUT
foo1
bar1以 16 进制格式在键 foo 上进行观察的命令:
$ etcdctl watch foo --hex
# 在另外一个终端: etcdctl put foo bar
PUT
\x66\x6f\x6f # 键
\x62\x61\x72 # 值观察多个键 foo 和 zoo 的命令:
$ etcdctl watch -i
$ watch foo
$ watch zoo
# 在另外一个终端: etcdctl put foo bar
PUT
foo
bar
# 在另外一个终端: etcdctl put zoo val
PUT
zoo
val -
查看 key 的历史修订版本。客户端应用需要获取某个键的所有修改。那么客户端应用连接到 etcd,watch 对应的 key 即可。如果 Watch 的过程中,etcd 或者客户端应用出错,又恰好发生了改动,这种情况下客户端应用可以在 Watch 时指定历史修订版本。
假设我们完成了下列操作序列:$ etcdctl put foo bar # revision = 2
OK
$ etcdctl put foo1 bar1 # revision = 3
OK
$ etcdctl put foo bar_new # revision = 4
OK
$ etcdctl put foo1 bar1_new # revision = 5
OK观察历史改动:
# 从修订版本 2 开始观察键 `foo` 的改动
$ etcdctl watch --rev=2 foo
PUT
foo
bar
PUT
foo
bar_new从上一次历史修改开始观察:
# 在键 `foo` 上观察变更并返回被修改的值和上个修订版本的值
$ etcdctl watch --prev-kv foo
# 在另外一个终端: etcdctl put foo bar_latest
PUT
foo # 键
bar_new # 在修改前键 foo 的上一个值
foo # 键
bar_latest # 修改后键 foo 的值 -
压缩修订版本。etcd 保存了历史修订版本,客户端应用可以读取键的历史版本。大量的历史版本数据,会占据很多存储,因此需要压缩历史修订版本。经过压缩,etcd 会删除历史修订版本,释放出资源。压缩修订版本之前的版本数据不可访问。压缩修订版本的命令如下所示:
$ etcdctl compact 5
compacted revision 5 $ etcdctl get --rev=4 foo
{"level":"warn","ts":"2020-05-04T16:37:38.020+0800","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-c0d35565-0584-4c07-bfeb-034773278656/127.0.0.1:2379","attempt":0,"error":"rpc error: code = OutOfRange desc = etcdserver: mvcc: required revision has been compacted"}
Error: etcdserver: mvcc: required revision has been compacted
5.2.3 租约
-
授予租约 客户端应用可以为 etcd 数据库存储内的键授予租约。当 etcd 中的键被授予租约时,该键的存活时间与租约的时间绑定,而租约的存活时间相应的被 time-to-live (TTL)管理。在租约授予时每个租约的最小 TTL 值由客户端应用指定。当租约的 TTL 到期,即代表租约就过期,此时该租约绑定的键都将被删除。
# 授予租约,TTL 为 100 秒
$ etcdctl lease grant 100
lease 694d71ddacfda227 granted with TTL(10s)
# 附加键 foo 到租约 694d71ddacfda227
$ etcdctl put --lease=694d71ddacfda227 foo10 bar
OK建议时间设置久一点,否则来不及操作会出现如下的错误:
-
撤销租约 应用通过租约 id 可以撤销租约。撤销租约将删除所有它附带的 key。 假设我们完成了下列的操作:
$ etcdctl lease revoke 694d71ddacfda227
lease 694d71ddacfda227 revoked
$ etcdctl get foo10 -
刷新租期
应用程序可以通过刷新其 TTL 来保持租约活着,因此不会过期。$ etcdctl lease keep-alive 694d71ddacfda227
lease 694d71ddacfda227 keepalived with TTL(100)
lease 694d71ddacfda227 keepalived with TTL(100)
... -
查询租期 应用程序可能想要了解租赁信息,以便它们可以续订或检查租赁是否仍然存在或已过期。应用程序也可能想知道特定租约所附的 key。
假设我们完成了以下一系列操作:
$ etcdctl lease grant 300
lease 694d71ddacfda22c granted with TTL(300s)
$ etcdctl put --lease=694d71ddacfda22c foo10 bar
OK获取有关租赁信息以及哪些 key 使用了租赁信息:
$ etcdctl lease timetolive 694d71ddacfda22c
lease 694d71ddacfda22c granted with TTL(300s), remaining(282s)
$ etcdctl lease timetolive --keys 694d71ddacfda22c
lease 694d71ddacfda22c granted with TTL(300s), remaining(220s), attached keys([foo10]
6 etcd 安全运维
etcd 支持通过 TLS 协议进行的加密通信。TLS 通道可用于对等体之间的加密内部群集通信以及加密的客户端流量。
6.1 TLS 与 SSL
互联网信息明文传播,带来了窃听风险,即第三方可以窃取通信内容;篡改风险,通信内容被篡改;冒充风险,身份被冒充,从而参与通信。
通过 SSL/TLS 协议实现互联网的通信安全。SSL/TLS 协议可以解决上述提到的三个问题,通信加密,使得第三方无法窃听;校验机制防止篡改,一旦被篡改,通信双方会立刻发现;安全身份证书防止身份被冒充。
TLS 是安全传输层协议,如果需要实现 HTTPS 加密访问,保障通信数据的安全,就需要 SSL 证书,下面我们开始实践如何实现 etcd 集群成员之间的通信。
6.2 进行 TLS 加密实践
为了进行实践,我们将会安装一些实用的命令行工具,这包括 cfssl、cfssljson。
CFSSL 是 CloudFlare 的 PKI/TLS 工具,既是一个命令行工具,同时又可以用于签名,作为 HTTP API 服务器,验证和绑定 TLS 证书。 它需要 Go 1.12+ 才能构建。环境配置如下:
HostName | ip | 客户端交互端口 | peer 通信端口 |
---|---|---|---|
etcd1 | 192.168.202.128 | 2379 | 2380 |
etcd2 | 192.168.202.129 | 2379 | 2380 |
etcd3 | 192.168.202.130 | 2379 | 2380 |
6.2.1 安装 cfssl
$ ls ~/Downloads/cfssl
cfssl-certinfo_1.4.1_linux_amd64 cfssl_1.4.1_linux_amd64 cfssljson_1.4.1_linux_amd64
chmod +x cfssl_1.4.1_linux_amd64 cfssljson_1.4.1_linux_amd64 cfssl-certinfo_1.4.1_linux_amd64
mv cfssl_1.4.1_linux_amd64 /usr/local/bin/cfssl
mv cfssljson_1.4.1_linux_amd64 /usr/local/bin/cfssljson
mv cfssl-certinfo_1.4.1_linux_amd64 /usr/bin/cfssl-certinfo
安装完成之后,查看版本信息的结果:
$ cfssl version
Version: 1.4.1
Runtime: go1.12.12
6.2.2 配置 CA 并创建 TLS 证书
我们将使用 CloudFlare's PKI 工具 cfssl 来配置 PKI Infrastructure,然后使用它去创建 Certificate Authority(CA), 并为 etcd 创建 TLS 证书。
首先创建 ssl 配置目录:
mkdir /data/etcd/{bin,cfg,ssl} -p
cd /data/etcd/ssl/
etcd ca 配置:
cat << EOF | tee ca-config.json
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"etcd": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
etcd ca 证书:
cat << EOF | tee ca-csr.json
{
"CN": "etcd CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Shanghai",
"ST": "Shanghai"
}
]
}
EOF
生成 CA 凭证和私钥:
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2020/04/30 20:36:58 [INFO] generating a new CA key and certificate from CSR
2020/04/30 20:36:58 [INFO] generate received request
2020/04/30 20:36:58 [INFO] received CSR
2020/04/30 20:36:58 [INFO] generating key: rsa-2048
2020/04/30 20:36:58 [INFO] encoded CSR
2020/04/30 20:36:58 [INFO] signed certificate with serial number 252821789025044258332210471232130931231440888312
$ ls
ca-config.json ca-csr.json ca-key.pem ca.csr ca.pem
etcd server 证书:
cat << EOF | tee server-csr.json
{
"CN": "etcd",
"hosts": [
"192.168.202.128",
"192.168.202.129",
"192.168.202.130"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing"
}
]
}
EOF
生成 server 证书:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd server-csr.json | cfssljson -bare server
2020/04/30 20:44:37 [INFO] generate received request
2020/04/30 20:44:37 [INFO] received CSR
2020/04/30 20:44:37 [INFO] generating key: rsa-2048
2020/04/30 20:44:37 [INFO] encoded CSR
2020/04/30 20:44:37 [INFO] signed certificate with serial number 73061688633166283265484923779818839258466531108
ls
ca-config.json ca-csr.json ca-key.pem ca.csr ca.pem server-csr.json server-key.pem server.csr server.pem
启动 etcd 集群,命令的配置如下:
#etcd1 启动
$ /data/etcd/bin/etcd --name etcd1 --initial-advertise-peer-urls https://192.168.202.128:2380 \
--listen-peer-urls https://192.168.202.128:2380 \
--listen-client-urls https://192.168.202.128:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://192.168.202.128:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=https://192.168.202.128:2380, etcd2=https://192.168.202.129:2380, etcd3=https://192.168.202.130:2380 \
--initial-cluster-state new \
--client-cert-auth --trusted-ca-file=/data/etcd/ssl/ca.pem \
--cert-file=/opt/etcd/ssl/server.pem --key-file=/data/etcd/ssl/server-key.pem \
--peer-client-cert-auth --peer-trusted-ca-file=/data/etcd/ssl/ca.pem \
--peer-cert-file=/opt/etcd/ssl/server.pem --peer-key-file= /data/etcd/ssl/server-key.pem
#etcd2、etcd3 类似,此处省略
通过三台服务器的控制台可以知道,集群已经成功建立,我们进行验证:
查看三个节点的健康状况,endpoint health
;其次,查看集群的成员列表,是否输出三个成员;最后,经过 TLS 加密的 etcd 集群,在进行操作时,需要加上认证相关的信息,尝试先写再读的操作是否能够正常。
按照上述的步骤进行操作,这个验证的实践交给读者自行尝试。
6.2.3 自动证书
如果集群需要加密的通信但不需要经过身份验证的连接,则可以将 etcd 配置为自动生成其密钥。 在初始化时,每个成员都基于其通告的 IP 地址和主机创建自己的密钥集。
在每台机器上,etcd 将使用以下标志启动:
$ etcd --name etcd1 --initial-advertise-peer-urls https://192.168.202.128:2380 \
--listen-peer-urls https://192.168.202.128:2380 \
--listen-client-urls https://192.168.202.128:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://10.0.1.10:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster infra0=https://192.168.202.128:2380,infra1=https://192.168.202.129:2380,infra2=https://192.168.202.130:2380 \
--initial-cluster-state new \
--auto-tls \
--peer-auto-tls
由于自动签发证书并不认证身份,因此直接 curl 会返回错误。需要使用 curl 的 -k
命令屏蔽对证书链的校验。
7 小结
etcd 具有极佳的稳定性、可靠性、可伸缩性,为云原生分布式系统提供了必要的协调机制。
本次 chat 主要介绍了 etcd 的入门知识以及相关的实践。etcd 被设计为大型分布式系统的存储基石,etcd 以一致且容错的方式存储元数据。etcd 集群旨在提供具有稳定性、可靠性、可伸缩性和性能的键值存储。使用 etcd 的常见分布式场景包括领导者选举、键值对存储、分布式锁和消息订阅与发布等。
实践是最好的学习方式,在看完本篇 chat 之后,希望大家能够参照本文的介绍,进行相应的实践,期待你的讨论交流。
评论 (0)
登录后可评论,请 登录 或 注册
评论