云社区 博客 博客详情
云社区 博客 博客详情

【华为云专家原创首发】10 分钟快速入门云原生存储组件 Etcd (下)

aoho 发表于 2020-10-21 16:46:29 10-21 16:46
aoho 发表于 2020-10-21 16:46:29 2020-10-21
0
1

【摘要】 10 分钟快速入门云原生存储组件 Etcd ,掌握常见的概念及操作。

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
    # 删除了一个键
     

    删除从 foo to foo9 范围的键的命令:

    $ etcdctl del foo foo9
    # 删除了两个键
     

    删除键 zoo 并返回被删除的键值对的命令:

    $ etcdctl del --prev-kv zoo
    1   # 一个键被删除
    zoo # 被删除的键
    val # 被删除的键的值
     

    删除前缀为 zoo 的键的命令:

    $ etcdctl del --prefix zoo
    # 删除了两个键
     

    删除大于等于键 b 的 byte 值的键的命令:

    $ etcdctl del --from-key b
    # 删除了两个键
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 之后,希望大家能够参照本文的介绍,进行相应的实践,期待你的讨论交流。


登录后可下载附件,请登录或者注册

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:huaweicloud.bbs@huawei.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
评论文章 //点赞 收藏 1
点赞
分享文章到微博
分享文章到朋友圈

评论 (0)


0/1000
评论

登录后可评论,请 登录注册

评论

您没有权限执行当前操作

温馨提示

您确认删除评论吗?

确定
取消
温馨提示

您确认删除评论吗?

删除操作无法恢复,请谨慎操作。

确定
取消
温馨提示

您确认删除博客吗?

确定
取消

确认删除

您确认删除博客吗?

确认删除

您确认删除评论吗?

温馨提示

登录超时或用户已下线,请重新登录!!!

确定
取消