暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

手把手教你用 ETCD:一篇短文却包含众多知识

polarisxu 2021-07-26
1176

一句话概括的话:ETCD[1] 是一个基于 RAFT[2] 的分布式 KV 存储系统。一个 ETCD 集群通常是由 3、5、7 之类奇数个节点组成的,为什么不选择偶数个节点?在集群系统中为了选出 LEADER 节点,至少要有半数以上的节点达成共识,举例说明:

  • 当集群有 3 个节点的时候,至少要有 2 个节点达成共识,最多容灾 1 个节点。
  • 当集群有 4 个节点的时候,至少要有 3 个节点达成共识,最多容灾 1 个节点。
  • 当集群有 5 个节点的时候,至少要有 3 个节点达成共识,最多容灾 2 个节点。

如上可见当节点数是偶数个的时候,系统的容灾能力并没有得到提升,所以节点数一般选择 3、5、7 之类的奇数,至于具体选择多少的话,一般推荐 3 或者 5 比较合适,太少的话系统没有容灾能力,太多的话 LEADER 节点的通信任务会变得繁重,影响性能。

如何搭建一个 ETCD 集群呢?比如在本地部署一个 3 个节点的集群,参考文档[3]操作:

CLUSTER="etcd-0=http://127.0.0.1:2380,etcd-1=http://127.0.0.1:3380,etcd-2=http://127.0.0.1:4380"

/usr/local/bin/etcd \
    --name etcd-0 \
    --data-dir etcd-0.etcd
    --listen-client-urls http://127.0.0.1:2379 \
    --listen-peer-urls http://127.0.0.1:2380 \
    --advertise-client-urls http://127.0.0.1:2379 \
    --initial-advertise-peer-urls http://127.0.0.1:2380 \
    --initial-cluster ${CLUSTER} &

/usr/local/bin/etcd \
    --name etcd-1 \
    --data-dir etcd-1.etcd
    --listen-client-urls http://127.0.0.1:3379 \
    --listen-peer-urls http://127.0.0.1:3380 \
    --advertise-client-urls http://127.0.0.1:3379 \
    --initial-advertise-peer-urls http://127.0.0.1:3380 \
    --initial-cluster ${CLUSTER} &

/usr/local/bin/etcd \
    --name etcd-2 \
    --data-dir etcd-2.etcd
    --listen-client-urls http://127.0.0.1:4379 \
    --listen-peer-urls http://127.0.0.1:4380 \
    --advertise-client-urls http://127.0.0.1:4379 \
    --initial-advertise-peer-urls http://127.0.0.1:4380 \
    --initial-cluster ${CLUSTER} &

集群运行起来后,可以通过如下命令来确认状态是否正常:

shell> etcdctl member list -w table
+------------------+---------+--------+-----------------------+-----------------------+------------+
|        ID        | STATUS  |  NAME  |      PEER ADDRS       |     CLIENT ADDRS      | IS LEARNER |
+------------------+---------+--------+-----------------------+-----------------------+------------+
| b71f75320dc06a6c | started | etcd-0 | http://127.0.0.1:2380 | http://127.0.0.1:2379 |      false |
| d07d5325fff892c1 | started | etcd-1 | http://127.0.0.1:3380 | http://127.0.0.1:3379 |      false |
| b7bacd4212cc9323 | started | etcd-2 | http://127.0.0.1:4380 | http://127.0.0.1:4379 |      false |
+------------------+---------+--------+-----------------------+-----------------------+------------+

shell> etcdctl endpoint status --cluster -w table
+-----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|       ENDPOINT        |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| http://127.0.0.1:2379 | b71f75320dc06a6c |  3.4.10 |   25 kB |     false |      false |         2 |          8 |                  8 |        |
| http://127.0.0.1:3379 | d07d5325fff892c1 |  3.4.10 |   25 kB |      true |      false |         2 |          8 |                  8 |        |
| http://127.0.0.1:4379 | b7bacd4212cc9323 |  3.4.10 |   25 kB |     false |      false |         2 |          8 |                  8 |        |
+-----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

虽然在本地部署 ETCD 集群很简单,但是在 K8S 上部署 ETCD 集群的话,往往会复杂很多,网上能找到一些别人共享的部署脚本[4],估计大家看了会疯掉。难点主要在于当我们在 K8S 上部署 ETCD 集群的时候,出于可伸缩性的考虑,一般不会采用固定 IP 的做法,也就是说节点的 IP 是无法事先确定的,而启动 ETCD 集群的时候又需要确定各个接入的节点(initial-cluster),于是产生了矛盾。好在作为一个有状态服务(StatefulSet[5]),可以使用 Headless service[6] 为每一个 POD 提供一个稳定的并且唯一的网络标识,也就是内网域名:「$(statefulset)-$(ordinal).$(service).$(namespace).svc.cluster.local
」,当交互双方在同一个 namespace 下的话,甚至可以简写成「$(statefulset)-$(ordinal).$(service)
」:

  • $(statefulset) 是 StatefulSet 的名字。
  • $(ordinal) 是 Pod 的序号,从 0 到 N-1。
  • $(service) 是 Service 的名字。
  • $(namespace) 是服务所在的 namespace。

虽然每次部署的时候,节点 IP 都可能会变,但是节点的内网域名是不会变的,并且它会自动解析到对应的 IP 上,了解了这些,我们就可以编写自己的 K8S 脚本了:

apiVersion: v1
kind: Service
metadata:
  name: etcd
  namespace: default
spec:
  clusterIP: None
  publishNotReadyAddresses: true
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: etcd
  namespace: default
spec:
  serviceName: etcd
  replicas: 3
  template:
    spec:
      dnsPolicy: ClusterFirst
      containers:
      - name: etcd
        command:
        - sh
        - -c
        - |
          CLUSTER=""

          for i in $(seq 0 3); do
            CLUSTER=${CLUSTER}${CLUSTER:+,}etcd-${i}=http://etcd-${i}.etcd:2380
          done

          exec /usr/local/bin/etcd \
            --name ${HOSTNAME} \
            --data-dir /var/run/etcd/default.etcd \
            --listen-client-urls http://0.0.0.0:2379 \
            --listen-peer-urls http://0.0.0.0:2380 \
            --advertise-client-urls http://${HOSTNAME}.etcd:2379 \
            --initial-advertise-peer-urls http://${HOSTNAME}.etcd:2380 \
            --initial-cluster ${CLUSTER}

友情提示:如上脚本非完整代码,此外,其中有几个需要留意的配置,分别是:

  • clusterIP: None[7]:表示是一个 Headless service。
  • publishNotReadyAddresses: true[8]:不管 POD 是否准备就绪,都发布内网域名。
  • dnsPolicy: ClusterFirst[9]:请求会优先在集群所在域查询,如此内网域名才会生效。

掌握了前面说的内容之后,独立部署一个 ETCD 集群应该问题不大了,不过如果是在外网部署的话,安全起见,最好学习 TLS 相关知识,官方文档[10]不容错过,其中有一个关于使用 cfssl[11] 生成自签名证书[12]例子[13],此外,强烈建议多看看「TLS完全指南[14]」,里面有关于安全 etcd 机群[15]的精彩描述。

最后,再安利一下 etcd web ui 方面比较出色的工具:etcdkeeper[16],收工。

参考资料

[1]

ETCD: https://etcd.io/

[2]

RAFT: https://raft.github.io/

[3]

文档: https://etcd.io/docs/v3.4.0/op-guide/clustering/

[4]

部署脚本: https://github.com/wilhelmguo/etcd-statefulset

[5]

StatefulSet: https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/

[6]

Headless service: https://kubernetes.io/zh/docs/concepts/services-networking/service/#headless-services

[7]

clusterIP: None: https://kubernetes.io/zh/docs/concepts/services-networking/service/#headless-services

[8]

publishNotReadyAddresses: true: https://kubernetes.io/zh/docs/concepts/services-networking/dns-pod-service/

[9]

dnsPolicy: ClusterFirst: https://kubernetes.io/zh/docs/concepts/services-networking/dns-pod-service/

[10]

官方文档: https://etcd.io/docs/v3.4.0/op-guide/security/

[11]

cfssl: https://github.com/cloudflare/cfssl

[12]

自签名证书: https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md

[13]

例子: https://github.com/etcd-io/etcd/tree/master/hack/tls-setup

[14]

TLS完全指南: https://github.com/k8sp/tls

[15]

安全 etcd 机群: https://github.com/k8sp/tls/blob/master/etcd.md

[16]

etcdkeeper: https://github.com/evildecay/etcdkeeper



觉得不错,欢迎关注:



点个赞、在看和转发是最大的支持


文章转载自polarisxu,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论