作者 青鸟

在最近的工作中想用go-micro来实现一个简单的微服务,选择了etcd作为服务与注册中心,于是就在服务器上部署了etcd,并使用systemed来作为进程守护,虽然最后还是选择了Consul来作为注册中心,但是在部署Etcd这个过程中遇到了大大小小的问题,于是写一篇博客记录一下,其中着重介绍一下静态部署

Etcd的简介

首先要说明的就是为什么会选etcd来作为服务与注册中心。etcd 是一个分布式键值对存储,设计用来可靠而快速的保存关键数据并提供访问。通过分布式锁,leader选举和写屏障(write barriers)来实现可靠的分布式协作。etcd集群是为高可用,持久性数据存储和检索而准备。从上述的定义中就可以看出Etcd的核心作用就是两个方面:持久化的键值存储系统分布式系统数据一致性服务提供者

所有的分布式系统,都面临的一个问题是多个节点之间的数据共享问题,这个和团队协作的道理是一样的,成员可以分头干活,但总是需要共享一些必须的信息,比如谁是 leader, 都有哪些成员,依赖任务之间的顺序协调等。所以分布式系统要么自己实现一个可靠的共享存储来同步信息(比如 Elasticsearch ),要么依赖一个可靠的共享存储服务,而 etcd 就是可以一个可靠的共享存储服务。

etcd 以一致和容错的方式存储元数据。分布式系统使用 etcd 作为一致性键值存储,用于配置管理,服务发现和协调分布式工作。使用 etcd 的通用分布式模式包括领导选举,[分布式锁][etcd-concurrency]和监控机器活动。

Etcd 主要提供以下能力:

  • 提供存储以及获取数据的接口,它通过协议保证 Etcd 集群中的多个节点数据的强一致性。用于存储元信息以及共享配置。
  • 提供监听机制,客户端可以监听某个key或者某些key的变更(v2和v3的机制不同)。用于监听和推送变更。
  • 提供key的过期以及续约机制,客户端通过定时刷新来实现续约(v2和v3的实现机制也不一样)。用于集群监控以及服务注册发现。
  • 提供原子的CAS(Compare-and-Swap)和 CAD(Compare-and-Delete)支持(v2通过接口参数实现,v3通过批量事务实现)。用于分布式锁以及leader选举。

相比于其他的服务,Etcd的核心优势是使用简洁的方式实现 Raft 协议。使用 Raft 算法充分保证了分布式系统数据的强一致性。 etcd 集群是一个分布式系统,由多个节点相互通信构成整体的对外服务,每个节点都存储了完整的数据,通过 Raft 协议保证了每个节点维护的数据都是一致的。

Etcd在Kubernetes的使用

Etcd在最知名的一个使用案例是Kubernetes(也就是我们俗称的k8s)将配置数据存储到etcd中,用于服务发现和集群管理; etcd 的一致性对于正确安排和运行服务至关重要。Kubernetes API 服务器将群集状态持久化在 etcd 中。它使用etcd的 watch API监视集群,并发布关键的配置更改。下面是具体的作用:

  • 配置存储:etcd被用来存储Kubernetes集群的配置数据,包括集群状态、配置选项、服务发现信息、网络配置等。所有Kubernetes组件和节点都可以从etcd中读取和写入数据,以保持集群的一致性和同步性。
  • Leader选举:etcd利用Raft一致性算法来实现高可用性。当etcd集群中的一个节点成为leader节点时,它负责处理客户端的读写请求,而其他节点则作为follower节点。如果leader节点失效,etcd会自动进行leader选举,选出新的leader节点,确保集群的可用性。
  • 存储Kubernetes对象:etcd存储了Kubernetes中所有的资源对象,如Pod、Service、Deployment等。这些对象的定义和状态都存储在etcd中,并且可以通过API服务器进行访问和管理。
  • 配置变更和触发事件:当Kubernetes的配置发生变化时,如创建、更新或删除资源对象,相关的配置更改会被写入etcd。这些变更会触发相应的事件,通知Kubernetes各组件和节点进行相应的操作和调整。

Etcd的单机部署

关于Etcd的安装,我们按照官方文档的提示即可轻松完成,这里就不再赘述

准备配置文件和数据目录

1
2
mkdir -p /etc/etcd
mkdir -p /data/etcd

然后是准备环境变量的配置文件

1
vim /etc/etcd/etcd.conf

在文件中写入

1
2
3
4
5
6
7
8
9
ETCD_NAME="etcd01"
ETCD_DATA_DIR="/data/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://127.0.0.1:2380"
ETCD_LISTEN_CLIENT_URLS="http://127.0.0.1:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://127.0.0.1:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://127.0.0.1:2379"
ETCD_INITIAL_CLUSTER="etcd01=http://127.0.0.1:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

最后是编写service中的配置文件,我们选择使用systemed来守护进程

1
vim /usr/lib/systemd/system/etcd.service

在文件中写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=/etc/etcd/etcd.conf
ExecStart=etcd
Restart=on-failure
LimitNOFILE=65536
RestartSec=5

[Install]
WantedBy=multi-user.target

这样子我们就做好了多有的配置文件

然后启动即可

1
2
systemctl daemon-reload
systemctl start etcd

最后检查一下Etcd的运行状态

1
etcdctl endpoint status --cluster -w table

Etcd的集群部署

在生产环境或对高可用有要求的环境下,需要使用 etcd 的高可用部署方式进行部署,etcd 的 raft 协议保障各个节点数据的一致性。etcd 静态部署的核心配置就是 initial-cluster 参数,该参数在节点启动时就明确指定了该集群由哪些节点组成。

我们以3个节点的高可用静态方式部署 etcd,3个节点的IP地址分别是 10.100.0.13、10.100.0.14 和 10.100.0.15。由于每个节点的 IP 地址差异,每个节点上配置不完全相同;主要的差异就是当前节点的 IP 地址和命名。

  • 10.100.0.13
1
2
3
4
5
6
7
8
9
ETCD_NAME="etcd01"
ETCD_DATA_DIR="/data/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://10.100.0.13:32380"
ETCD_LISTEN_CLIENT_URLS="http://10.100.0.13:32379,http://127.0.0.1:32379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.100.0.13:32380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.100.0.13:32379"
ETCD_INITIAL_CLUSTER="etcd01=http://10.100.0.13:32380,etcd02=http://10.100.0.14:32380,etcd03=http://10.100.0.15:32380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
  • 10.100.0.14

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ETCD_NAME="etcd02"
    ETCD_DATA_DIR="/data/etcd/default.etcd"
    ETCD_LISTEN_PEER_URLS="http://10.100.0.14:32380"
    ETCD_LISTEN_CLIENT_URLS="http://10.100.0.14:32379,http://127.0.0.1:32379"
    ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.100.0.14:32380"
    ETCD_ADVERTISE_CLIENT_URLS="http://10.100.0.14:32379"
    ETCD_INITIAL_CLUSTER="etcd01=http://10.100.0.13:32380,etcd02=http://10.100.0.14:32380,etcd03=http://10.100.0.15:32380"
    ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
    ETCD_INITIAL_CLUSTER_STATE="new"
  • 10.100.0.15

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ETCD_NAME="etcd03"
    ETCD_DATA_DIR="/data/etcd/default.etcd"
    ETCD_LISTEN_PEER_URLS="http://10.100.0.15:32380"
    ETCD_LISTEN_CLIENT_URLS="http://10.100.0.15:32379,http://127.0.0.1:32379"
    ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.100.0.15:32380"
    ETCD_ADVERTISE_CLIENT_URLS="http://10.100.0.15:32379"
    ETCD_INITIAL_CLUSTER="etcd01=http://10.100.0.13:32380,etcd02=http://10.100.0.14:32380,etcd03=http://10.100.0.15:32380"
    ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
    ETCD_INITIAL_CLUSTER_STATE="new"

    然后分别启动各个节点的 etcd 服务即可

我们检查一下Etcd的运行状态,会发现会有随机一个节点被选为Leader

1
etcdctl endpoint status --cluster -w table

相关产品比较

这里的相关产品主要指的便是Zookeeper,Consul这两个

首先,Etcd 和 Zookeeper 提供的能力非常相似,都是通用的一致性元信息存储,都提供watch机制用于变更通知和分发,也都被分布式系统用来作为共享信息存储,在软件生态中所处的位置也几乎是一样的,可以互相替代的。二者除了实现细节,语言,一致性协议上的区别,最大的区别在周边生态圈。

简单来说Zookeeper在分布式系统使用更广泛(hadoop, solr, kafka, mesos 等),但是接口只提供了C和JAVA的。

Etcd 比较新,其简单好用的rest接口深受喜爱,在新的一些集群中得到使用(比如kubernetes)。虽然v3为了性能也改成二进制rpc接口了,但其易用性上比 Zookeeper 还是好一些。

Consul 的目标则更为具体一些,以服务发现和配置变更为主要目标,同时附带了kv存储。Etcd 和 Zookeeper 提供的是分布式一致性存储能力,具体的业务场景需要用户自己实现,比如服务发现,比如配置变更。

在软件生态中,越抽象的组件适用范围越广,但同时对具体业务场景需求的满足上肯定有不足之处。

参考文章:

  1. Etcd文档中文版