K&

搭建 etcd 集群

 

概述

启动 etcd 集群要求每个成员知道集群中的其他成员。在一些场景中,集群成员的 IP 地址可能无法提前知道。在这种情况下,etcd 集群可以在发现服务的帮助下启动。

一旦 etcd 集群启动并运行,可以通过 运行时重配置 来添加或者移除成员。

这份指南将覆盖下列用于启动 etcd 集群的机制:

  • 静态
  • etcd 发现
  • DNS 发现

启动机制的每一种都将用于启动三台机器的 etcd 集群,详情如下:

 

名字地址主机
infra0 10.0.1.10 infra0.example.com
infra1 10.0.1.11 infra1.example.com
infra2 10.0.1.12 infra2.example.com

 

静态

如果在启动前我们知道集群成员,他们的地址和集群的大小,我们可以使用通过设置 initial-cluster 标记来离线启动配置。每个机器将得到下列环境变量或者命令行:

 

ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE=new
--initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \ --initial-cluster-state new

 

注意: 在 initial-cluster 中指定的 URL 是 advertised peer URLs ,例如,他们将匹配对应节点的 initial-advertise-peer-urls 的值。

如果使用同样的配置启动多个集群(或者创建并部署单个集群)用于测试目的,强烈推荐每个集群给予一个唯一的 initial-cluster-token。这样做之后,etcd 可以为集群生成唯一的集群 ID 和成员 ID,甚至他们有完全一样的配置。这可以将 etcd 从可能让集群孵化的跨集群交互中保护起来。

etcd 在 listen-client-urls 上接收客户端访问。etcd 成员将 advertise-client-urls 指定的 URl 上通告给其他成员,代理和客户端。常见的错误是设置 advertise-client-urls 为 localhost 或者留空为默认值,如果远程客户端可以达到 etcd。

在每台机器上,使用这些标记启动 etcd:

 

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new


$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new


$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state new

 

--initial-cluster 开头的命令行参数将在 etcd 随后的运行中被忽略。可以在初始化启动进程之后随意的删除环境变量或者命令行标记。如果配置需要稍后修改(例如,添加成员到集群或者从集群中移除成员),查看 运行时配置 指南。

 

TLS


etcd 支持通过 TLS 协议的加密通讯。TLS 通道可以用于加密伙伴间的内部集群通讯,也可以用于加密客户端请求。这个章节提供例子来搭建使用伙伴和客户端 TLS 的集群。详细描述 etcd 的 TLS 支持的额外信息可以在 加密指南

 

自签名证书


使用自签名证书证书(self-signed certificates)的集群同时加密请求并认证它的连接。要启动使用自签名证书的集群,每个集群成员应该有一个唯一的通过共享的集群CA证书来签名的键对(key pair) (member.crt, member.key),用于伙伴连接和客户端连接。证书可以通过仿照 etcd 搭建TLS 的例子来生成。

在每台机器上,etcd可以使用这些标记来启动:

 

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --listen-client-urls https://10.0.1.10: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://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra0-client.crt --key-file=/path/to/infra0-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra0-peer.crt --peer-key-file=/path/to/infra0-peer.key


$ etcd --name infra1 --initial-advertise-peer-urls https://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls https://10.0.1.11:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra1-client.crt --key-file=/path/to/infra1-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra1-peer.crt --peer-key-file=/path/to/infra1-peer.key


$ etcd --name infra2 --initial-advertise-peer-urls https://10.0.1.12:2380 \
  --listen-peer-urls https://10.0.1.12:2380 \
  --listen-client-urls https://10.0.1.12:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --client-cert-auth --trusted-ca-file=/path/to/ca-client.crt \
  --cert-file=/path/to/infra2-client.crt --key-file=/path/to/infra2-client.key \
  --peer-client-cert-auth --peer-trusted-ca-file=ca-peer.crt \
  --peer-cert-file=/path/to/infra2-peer.crt --peer-key-file=/path/to/infra2-peer.key

 

自动证书

如果集群需要加密通讯,但是不需要认证连接,etcd 可以配置为自动生成 key。在初始化时,每个集群基于它的通告IP(advertised IP) 地址和主机名创建它自己的 key 集合。

在每台机器上,etcd 使用这些标记启动:

 

$ etcd --name infra0 --initial-advertise-peer-urls https://10.0.1.10:2380 \
  --listen-peer-urls https://10.0.1.10:2380 \
  --listen-client-urls https://10.0.1.10: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://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls


$ etcd --name infra1 --initial-advertise-peer-urls https://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls https://10.0.1.11:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.11:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls


$ etcd --name infra2 --initial-advertise-peer-urls https://10.0.1.12:2380 \
  --listen-peer-urls https://10.0.1.12:2380 \
  --listen-client-urls https://10.0.1.12:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.1.12:2379 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.1.11:2380,infra2=https://10.0.1.12:2380 \
  --initial-cluster-state new \
  --auto-tls \
  --peer-auto-tls

 

错误案例

 

案例1

在下面的例子中,我们没有在列举的节点列表中包含我们新的主机地址。如果这是一个新的集群,节点 必须 添加到初始化集群成员的列表中。

 

$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls https://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380 \
  --initial-cluster-state new
etcd: infra1 not listed in the initial cluster config
exit 1

 

案例2

在这个例子中,我们试图映射节点(infra0)在不同地址(127.0.0.1:2380)而不是它在集群列表(10.0.1.10:2380)中列举的地址。如果这个节点是监听多个地址,所有地址 必须 在 “initial-cluster” 配置指令中反映出来。

 

$ etcd --name infra0 --initial-advertise-peer-urls http://127.0.0.1:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
  --initial-cluster-state=new
etcd: error setting up initial cluster: infra0 has different advertised URLs in the cluster and advertised peer URLs list
exit 1

 

案例3

如果伙伴被用配置参数的不同集合配置并试图加入这个集群,etcd 将报告集群 ID 不匹配并退出。

 

$ etcd --name infra3 --initial-advertise-peer-urls http://10.0.1.13:2380 \
  --listen-peer-urls http://10.0.1.13:2380 \
  --listen-client-urls http://10.0.1.13:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.13:2379 \
  --initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra3=http://10.0.1.13:2380 \
  --initial-cluster-state=new
etcd: conflicting cluster ID to the target cluster (c6ab534d07e8fcc4 != bc25ea2a74fb18b0). Exiting.
exit 1

 

 

发现

 

在一些案例中,集群伙伴的 IP 可能无法提前知道。当使用云提供商或者网络使用 DHCP 时比较常见。在这些情况下,相比指定静态配置,可以使用已经存在的 etcd 集群来启动一个新的。我们称这个过程为”发现”。

有两个方法可以用来做发现:

  • etcd 发现服务
  • DNS SRV 记录

 

etcd发现

 

为了更好的理解发现服务协议的设计,建议阅读发现服务项目 文档。

 

discovery URL 的存活时间

 

discovery URL 标识唯一的 etcd 集群。对于新的集群,总是创建 discovery URL 而不是重用 discovery URL。

此外,discovery URL 应该仅仅用于集群的初始化启动。在集群已经运行之后修改集群成员,阅读 运行时重配置 指南。

 

定制 etcd 发现服务

 

发现使用已有集群来启动自身。如果使用私有的 etcd 集群,可以创建像这样的 URL:

 

curl -X PUT https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3

 

通过设置 URL 的 size,创建了带有期待集群大小为3的 discovery URL。

用于这个场景的 URL 将是 https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 而 etcd 成员将使用 https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83 目录来注册,当他们启动时。

 

 

https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83

 

 

 

 

每个成员必须有指定不同的名字标记。 Hostname 或者 machine-id 是个好选择。. 否则发现会因为重复名字而失败

 

现在我们用这些用于每个成员的相关标记启动 etcd :

 

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83


$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83


$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83

 

这将导致每个成员使用定制的 etcd 发现服务注册自身并开始集群,一旦所有的机器都已经注册。

 

公共 etcd 发现服务

 

如果没有现成的集群可用,可以使用托管在 discovery.etcd.io 的公共发现服务。为了使用”new” endpoint来创建私有发现URL,使用命令:

 

$ curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

 

这将创建带有初始化预期大小为3个成员的集群。如果没有指定大小,将使用默认值3。

 

ETCD_DISCOVERY=https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
--discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

 

每个成员必须有指定不同的名字标记。 Hostname 或者 machine-id 是个好选择。. 否则发现会因为重复名字而失败

 

现在我们用这些用于每个成员的相关标记启动 etcd :

 

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de


$ etcd --name infra1 --initial-advertise-peer-urls http://10.0.1.11:2380 \
  --listen-peer-urls http://10.0.1.11:2380 \
  --listen-client-urls http://10.0.1.11:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.11:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de


$ etcd --name infra2 --initial-advertise-peer-urls http://10.0.1.12:2380 \
  --listen-peer-urls http://10.0.1.12:2380 \
  --listen-client-urls http://10.0.1.12:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.12:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de

 

这将导致每个成员使用定制的 etcd 发现服务注册自身并开始集群,一旦所有的机器都已经注册。

使用环境变量 ETCD_DISCOVERY_PROXY 来让 etcd 使用 HTTP 代理来连接到发现服务。

 

错误和警告案例

发现服务错误

 

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcd: error: the cluster doesn’t have a size configuration value in https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de/_config
exit 1

 

警告

这是一个无害的警告,表明发现 URL 将在这台机器上被忽略。

 

$ etcd --name infra0 --initial-advertise-peer-urls http://10.0.1.10:2380 \
  --listen-peer-urls http://10.0.1.10:2380 \
  --listen-client-urls http://10.0.1.10:2379,http://127.0.0.1:2379 \
  --advertise-client-urls http://10.0.1.10:2379 \
  --discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcdserver: discovery token ignored since a cluster has already been initialized. Valid log found at /var/lib/etcd

 

 

DNS发现

DNS SRV记录可以用作发现机制。该--discovery-srv标志可用于设置可在其中找到发现SRV记录的DNS域名。设置--discovery-srv example.com导致按列出的顺序查找DNS SRV记录:

  • _etcd-server-ssl._tcp.example.com
  • _etcd-server._tcp.example.com

如果_etcd-server-ssl._tcp.example.com找到,则etcd将尝试通过TLS进行引导过程。

为了帮助客户端发现etcd群集,按照列出的顺序查找以下DNS SRV记录:

  • _etcd-client._tcp.example.com
  • _etcd-client-ssl._tcp.example.com

如果_etcd-client-ssl._tcp.example.com找到,则客户端将尝试通过SSL / TLS与etcd群集进行通信。

如果etcd使用TLS,则发现SRV记录(例如example.com)必须与主机名一起包含在SSL证书DNS SAN中,否则群集将失败,并显示如下日志消息:

 

[...] rejected connection from "10.0.1.11:53162" (error "remote error: tls: bad certificate", ServerName "example.com")

 

如果etcd使用的是没有自定义证书颁发机构的TLS,则发现域(例如example.com)必须与SRV记录域(例如infra1.example.com)匹配。这是为了减轻伪造SRV记录指向不同域的攻击;该域将在PKI下拥有有效的证书,但由未知的第三方控制。

-discovery-srv-name标志还为发现期间查询的SRV名称配置了后缀。使用此标志可以区分同一域下的多个etcd群集。例如,如果设置了discovery-srv=example.com-discovery-srv-name=foo则进行以下DNS SRV查询:

  • _etcd-server-ssl-foo._tcp.example.com
  • _etcd-server-foo._tcp.example.com

 

创建 DNS SRV 记录

 

$ dig +noall +answer SRV _etcd-server._tcp.example.com
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra0.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra1.example.com.
_etcd-server._tcp.example.com. 300 IN  SRV  0 0 2380 infra2.example.com.


$ dig +noall +answer SRV _etcd-client._tcp.example.com
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra0.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra1.example.com.
_etcd-client._tcp.example.com. 300 IN SRV 0 0 2379 infra2.example.com.


$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com.  300  IN  A  10.0.1.10
infra1.example.com.  300  IN  A  10.0.1.11
infra2.example.com.  300  IN  A  10.0.1.12

 

使用DNS引导etcd集群

etcd群集成员可以公告域名或IP地址,引导过程将解析DNS A记录。从3.2开始(3.1将显示警告),--listen-peer-urls并且--listen-client-urls将拒绝域名,进行网络接口绑定。

在 --initial-advertise-peer-urls 中解析出来的地址 必须匹配 在 SRV 目录中解析出来的地址中的一个。 etcd成员读取解析的地址,以查找其是否属于SRV记录中定义的群集。

 

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra0.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra0.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380


$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra1.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra1.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380


$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://infra2.example.com:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://infra2.example.com:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380

 

 

集群也可以使用 IP 地址而不是域名来启动:

 

$ etcd --name infra0 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.10:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.10:2379 \
--listen-client-urls http://10.0.1.10:2379 \
--listen-peer-urls http://10.0.1.10:2380


$ etcd --name infra1 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.11:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.11:2379 \
--listen-client-urls http://10.0.1.11:2379 \
--listen-peer-urls http://10.0.1.11:2380


$ etcd --name infra2 \
--discovery-srv example.com \
--initial-advertise-peer-urls http://10.0.1.12:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster-state new \
--advertise-client-urls http://10.0.1.12:2379 \
--listen-client-urls http://10.0.1.12:2379 \
--listen-peer-urls http://10.0.1.12:2380

 

 

从v3.1.0(v3.2.9除外)开始,etcd --discovery-srv=example.com使用TLS配置时,服务器仅在提供的证书具有根域example.com作为“使用者备用名称”(SAN)字段中的条目时,身份验证对等方/客户端(就是证书中要有这个域名)。请参阅“ DNS SRV注释”。

 

 

网关

etcd 网关是一个简单的 TCP 代理,转发网络数据到 etcd 集群。请阅读 网关指南来获取更多信息。

代理

当 --proxy 标记被设置时,etcd 运行于 代理模式。这个代理模式仅仅支持 etcd v2 API; 没有支持 v3 API 的计划。取而代之的是,对于 v3 API 支持,在 etcd 3.0 发布之后将会有一个新的有增强特性的代理。

为了搭建带有v2 API的代理的 etcd 集群,请阅读 clustering doc in etcd 2.3 release.

 

 

 

运行时重配置

 

 

etcd 自带对渐进的运行时重配置的支持,这容许用户在运行时更新集群成员。

 

重配置请求仅能在集群成员的大多数正常工作时可以处理。强烈推荐 在产品中集群大小总是大于2.从一个两成员集群中移除一个成员是不安全的。在移除过程中如果有失败,集群可能无法前进而需要从重大失败中重启.

 

为了更好的理解运行时重配置后面的设计,建议阅读 运行时重配置文档.

 

 

重配置使用案例

 

 

让我们过一下一些重配置集群的常见理由。他们中的大多数仅仅涉及添加或者移除成员的组合,这些在后面的 集群重配置操作 下解释。

 

循环或升级多台机器

 

 

如果多个集群成员因为计划的维护(硬件升级,网络停工)而需要移动,推荐一次一个的修改多个成员。

 

移除 leader 是安全的,但是在选举过程发生的期间有短暂的停机时间。如果集群保存超过50MB,推荐 迁移成员的数据目录.

 

 

修改集群大小

 

 

增加集群大小可以改善 失败容忍度 并提供更好的读取性能。因为客户端可以从任意成员读取,增加成员的数量可以提高整体的读取吞吐量。

 

减少集群大小可以改善集群的写入性能,作为交换是降低弹性。写入到集群是要复制到集群成员的大多数才能被认定已经提交。减少集群大小消减了大多数的数量,从而每次写入可以更快提交。

 

 

替换失败的机器

 

 

如果机器因为硬件故障,数据目录损坏,或者一些其他致命情况而失败,它应该尽快被替代。已经失败但是还没有移除的机器对法定人数有不利影响并减低对额外失败的容忍性。

 

为了替换机器,遵循从集群中 移除成员 的建议, 然后再 添加新成员 替代它的未知。 如果集群保存超过50MB, 推荐 迁移失败成员的数据目录, 如果它还可以访问。

 

 

从多数失败中重启集群

 

 

如果集群的多数已经丢失或者所有的节点已经修改了IP地址,则需要手工动作来安全恢复。

 

恢复过程中的基本步骤包括 使用旧有数据创建新的集群, 强制单个成员成,并最终使用运行时配置来一次一个 添加新的成员 到这个新的集群.

 

 

集群重配置操作

 

 

现在我们心里有使用案例了,让我们展示每个案例中涉及到的操作。

 

在任何变动前,etcd 成员的简单多数(quorum) 必须可用。

 

对于任何其他到 etcd 的写入,这也是根本性的同样要求。

 

所有集群的改动一次一个的完成:

 

  • 要更新单个成员peerURLs,做一个更新操作
  • 要替代单个成员,做一个添加然后一个删除操作
  • 要将成员从3增加到5,做两次添加操作
  • 要将成员从5减少到3,做两次删除操作

 

所有这些案例将使用etcd自带的 etcdctl 命令行工具。

 

如果不用 etcdctl 修改成员,可以使用 v2 HTTP members API 或者 v3 gRPC members API.

 

 

更新成员

 

 

更新 advertise client URLs

 

 

为了更新成员的 advertise client URLs,简单用更新后的 client URL 标记(--advertise-client-urls)或者环境变量来重启这个成员(ETCD_ADVERTISE_CLIENT_URLS)。重新后的成员将自行发布更新后的URL。错误更新的client URL 将不会影响 etcd 集群的健康。

 

 

更新 advertise peer URLs

 

 

要更新成员的 advertise peer URLs, 首先通过成员命令更新它然后再重启成员。需要额外的行为是因为更新 peer URL 修改了集群范围配置并能影响 etcd 集群的健康。

 

要更新 peer URL,首先,我们需要找到目标成员的ID。使用 etcdctl 列出所有成员:

 

$ etcdctl member list
6e3bd23ae5f1eae0: name=node2 peerURLs=http://localhost:23802 clientURLs=http://127.0.0.1:23792
924e2e83e93f2560: name=node3 peerURLs=http://localhost:23803 clientURLs=http://127.0.0.1:23793
a8266ecf031671f3: name=node1 peerURLs=http://localhost:23801 clientURLs=http://127.0.0.1:23791

 

在这个例子中,让我们 更新 a8266ecf031671f3 成员ID并修改它的 peerURLs 值为 http://10.0.1.10:2380。

 

$ etcdctl member update a8266ecf031671f3 http://10.0.1.10:2380
Updated member with ID a8266ecf031671f3 in cluster

 

删除成员

 

假设我们要删除的成员ID是 a8266ecf031671f3.

我们随后用 remove 命令来执行删除:

 

$ etcdctl member remove a8266ecf031671f3
Removed member a8266ecf031671f3 from cluster

 

此时目标成员将停止自身并在日志中打印出移除信息:

 

etcd: this member has been permanently removed from the cluster.Exiting.

 

 

以安全的移除 leader,当然在新 leader 被选举时集群将不活动(inactive)。这个持续时间通常是选举超时时间加投票过程。

添加新成员

添加成员的过程有两个步骤:

  • 通过 HTTP members API 添加新成员到集群, gRPC members API, 或者 etcdctl member add 命令.
  • 使用新的层原配置启动新成员,包括更新后的成员列表(以后成员加新成员)

使用 etcdctl 指定 name 和 advertised peer URLs 来添加新的成员到集群:

 

$ etcdctl member add infra3 http://10.0.1.13:2380
added member 9bf1b35fc7761a23 to cluster
ETCD_NAME="infra3"
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
ETCD_INITIAL_CLUSTER_STATE=existing

 

etcdctl 已经给出关于新成员的集群信息并打印出成功启动它需要的环境变量。现在用关联的标记为新的成员启动新 etcd 进程:

 

$ export ETCD_NAME="infra3"
$ export ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra3=http://10.0.1.13:2380"
$ export ETCD_INITIAL_CLUSTER_STATE=existing
$ etcd --listen-client-urls http://10.0.1.13:2379--advertise-client-urls http://10.0.1.13:2379--listen-peer-urls http://10.0.1.13:2380--initial-advertise-peer-urls http://10.0.1.13:2380--data-dir %data_dir%

 

新成员将作为集群的一部分运行并立即开始赶上集群的其他成员。

如果添加多个成员,最佳实践是一次配置单个成员并在添加更多新成员前验证它正确启动。

如果添加新成员到一个节点的集群,在新成员启动前集群无法继续工作,因为它需要两个成员作为galosh才能在一致性上达成一致。这个行为仅仅发生在 etcdctl member add 影响集群和新成员成功建立连接到已有成员的时间内。

添加成员时的错误案例

在下面的案例中,我们没有在列举节点的列表中包含新的host。如果这是一个新的集群,节点必须添加到初始化集群成员列表中。

 

$ etcd --name infra3 \
--initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
--initial-cluster-state existing
etcdserver: assign ids error: the member count is unequal
exit 1

 

 

在这个案例中,我们给出一个和我们用来加入集群的(10.0.1.13:2380)不同的地址(10.0.1.14:2380)

 

$ etcd --name infra4 \
--initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380,infra4=http://10.0.1.14:2380 \
--initial-cluster-state existing
etcdserver: assign ids error: unmatched member while checking PeerURLs
exit 1

 

当我们使用一个被移除成员的数据目录来启动 etcd 时,etcd将立即退出,如果它连接到任何集群中的活动成员:

 

$ etcd
etcd: this member has been permanently removed from the cluster.Exiting.
exit 1

 

严格重配置检查模式 (-strict-reconfig-check)

如上所述,添加新成员的最佳实践是一次配置单个成员并在添加更多新成员前验证它正确启动。这个逐步的方式非常重要,因为如果最新添加的成员没有正确配置(例如 peer URL不正确),集群会丢失法定人数。发生法定人数丢失是因为最新加入的成员被法定人数计数,即使这个成员对其他已经存在的成员是无法访问的。同样法定人数丢失可能发生在有连接问题或者操作问题时。

为了避免这个问题,etcd 提供选项 -strict-reconfig-check. 如果这个选项被传递给 etcd, etcd 拒绝重配置请求, 如果启动的成员的数量将少于被重配置的集群的法定人数。

推荐开启这个选项。当然,为了保持兼容它被默认关闭。

 

 

运行时重配置的设计

在分布式系统中,运行时重配置是最困难和最有错误倾向的特性,尤其是基于一致性的系统如 etcd。

继续阅读来学习关于 etcd 的运行时重配置命令的设计和我们如何解决这些问题。

 

两阶段配置修改保持集群安全

 

在etcd中,为了安全每个运行时重配置必须通过 两阶段. 例如,为了添加成员,首先通知集群新配置然后再启动新成员。

 

阶段 1 - 通知集群新配置

 

为了添加成员到 etcd 集群,发起一个 API 调用来请求要添加一个新成员到集群。这是添加新成员到现有集群的唯一方法。当集群同意配置修改时 API 调用返回。

 

阶段 2 - 启动新成员

 

为了将 etcd 成员加入已有的集群,指定正确的 initial-cluster 并设置 initial-cluster-state 为 existing. 当成员启动时, 它会首先联系已有的集群并验证当前集群配置匹配在 initial-cluster 中期待的配置。当新成员成功启动时,集群就达到了期待的配置。

通过将过程拆分为两个分离的阶段,用户被强制去明确关于集群成员的修改。这实际给了用户更多灵活性并让事情容易推导。例如,如果有尝试使用和集群中现有成员相同的ID添加新成员,这个行为将在阶段1期间立即失败而不影响运行中的集群。提供类似的保存来放置错误添加新成员。如果新 etcd 成员试图在集群接受配置修改前加入集群,它将无法被集群接受。

没有围绕集群成员的明确工作流,etcd 将因意外的集群成员修改而容易受伤。例如,如果 etcd 运行在初始化系统例如 systemd,etcd 在通过成员API移除之后将被重启,并试图在启动时重新加入集群。这个循环在每次成员被通过API删除时继续,而 systemd 在失败之后被设置为重启 etcd,这是出乎意料的。

我们期待运行时重配置是极少进行的操作。我们决定让它保持明确和用户驱动以保证配置安全和保持集群总是在明确控制下平稳运行。

 

法定人数永久丢失需要新集群

 

如果集群永久丢失了它的成员的多数,需要从旧有的数据目录启动新的集群来恢复之前的状态。

从已有集群中强制删除失败成员来恢复是完全可能的。但是,我们决定不支持这个方法,因为它绕开了正常的一致性提交阶段,这是不安全的。如果要删除的成员并没有实际死亡或者是通过在同一个集群中的不同成员强制删除,etcd 将以分离的有同样ID的集群方式结束。这非常危险而之后难于调试/修改。

正确部署时,永久多数丢失的可能性非常低。但是它是一个足够严重的问题,值得特别小心。强烈建议阅读 灾难恢复文档 并在产品中使用 etcd 前为永久多数丢失做好准备。

 

不要为运行时重配置使用公开发现服务

 

公开发现服务(discovery service)仅仅应该用于启动集群。要往已有集群中添加成员,使用运行时重配置API。

发现服务设计用于在云环境下启动 etcd 集群,当所有成员的 IP 地址实现不知道时。在成功启动集群后, 所有成员的 IP 地址都已知。严格说,发现服务应该不再需要。

看上去使用公开发现服务是做运行时重配置的捷径,毕竟发现服务已经有所有集群配置信息。但是,依赖公开发现服务将带来问题:

  1. 它为集群的完整生命周期引入额外依赖,而不仅仅是启动时间. 如果在集群和公开发现服务之间有网络问题,集群将为此受折磨。

  2. 公开发现服务必须考虑在生命周期期间集群的正确运行时配置。它必须提供安全机制来避免恶劣行为,而这是很困难的。

  3. 公开发现服务必须保持数以万计的集群配置。我们的公开发现服务没有为这种负载做好准备。

要有支持运行时重配置的发现服务,最佳选择是搭建一个私有的。

 

在容器内运行etcd集群

下列指南展示如何使用 static bootstrap process 来用 rkt 和 docker 运行 etcd 。

rkt

 

运行单节点 etcd

 

下列 rkt 运行命令将在端口 2379 上暴露 etcd 客户端API,而在端口 2380上暴露伙伴API。

当配置 etcd 时使用 host IP地址。

 

export NODE1=192.168.1.21

 

信任 CoreOS App Signing Key.

 

sudo rkt trust --prefix coreos.com/etcd
# gpg key fingerprint is: 18AD 5014 C99E F7E3 BA5F  6CE9 50BD D3E0 FC8A 365E

 

运行 v3.0.6 版本的 etcd or 或者指定其他发布版本。

 

sudo rkt run --net=default:IP=${NODE1} coreos.com/etcd:v3.0.6 -- -name=node1 -advertise-client-urls=http://${NODE1}:2379 -initial-advertise-peer-urls=http://${NODE1}:2380 -listen-client-urls=http://0.0.0.0:2379 -listen-peer-urls=http://${NODE1}:2380 -initial-cluster=node1=http://${NODE1}:2380

 

列出集群成员。

 

etcdctl --endpoints=http://192.168.1.21:2379 member list

 

运行3节点集群

 

使用 rkt 本地搭建 3 节点的集群, 使用-initial-cluster 标记.

 

export NODE1=172.16.28.21
export NODE2=172.16.28.22
export NODE3=172.16.28.23

# node 1 sudo rkt run --net=default:IP=${NODE1} coreos.com/etcd:v3.0.6 -- -name=node1 -advertise-client-urls=http://${NODE1}:2379 -initial-advertise-peer-urls=http://${NODE1}:2380 -listen-client-urls=http://0.0.0.0:2379 -listen-peer-urls=http://${NODE1}:2380 -initial-cluster=node1=http://${NODE1}:2380,node2=http://${NODE2}:2380,node3=http://${NODE3}:2380 # node 2 sudo rkt run --net=default:IP=${NODE2} coreos.com/etcd:v3.0.6 -- -name=node2 -advertise-client-urls=http://${NODE2}:2379 -initial-advertise-peer-urls=http://${NODE2}:2380 -listen-client-urls=http://0.0.0.0:2379 -listen-peer-urls=http://${NODE2}:2380 -initial-cluster=node1=http://${NODE1}:2380,node2=http://${NODE2}:2380,node3=http://${NODE3}:2380 # node 3 sudo rkt run --net=default:IP=${NODE3} coreos.com/etcd:v3.0.6 -- -name=node3 -advertise-client-urls=http://${NODE3}:2379 -initia

 

 

检验集群健康并可以到达。

 

ETCDCTL_API=3 etcdctl --endpoints=http://172.16.28.21:2379,http://172.16.28.22:2379,http://172.16.28.23:2379 endpoint-health

 

 

DNS

 

通过被本地解析器已知的 DNS 名称指向伙伴的产品集群必须挂载 主机的 DNS 配置.

 

Docker

 

为了暴露 etcd API 到 docker host 之外的客户端, 使用容器的 host IP 地址。请见docker inspect 来获取关于如何得到IP地址的更多细节. 或者, 为 docker run 命令指定 --net=host 标记来跳过放置容器在分隔的网络栈中。

 

 

# For each machine
ETCD_VERSION=v3.0.0
TOKEN=my-etcd-token
CLUSTER_STATE=new
NAME_1=etcd-node-0
NAME_2=etcd-node-1
NAME_3=etcd-node-2
HOST_1=10.20.30.1
HOST_2=10.20.30.2
HOST_3=10.20.30.3
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
# For node 1
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
sudo docker run --net=host --name etcd quay.io/coreos/etcd:${ETCD_VERSION} \
    /usr/local/bin/etcd \
    --data-dir=data.etcd --name ${THIS_NAME} \
    --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
    --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
    --initial-cluster ${CLUSTER} \
    --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
# For node 2
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
sudo docker run --net=host --name etcd quay.io/coreos/etcd:${ETCD_VERSION} \
    /usr/local/bin/etcd \
    --data-dir=data.etcd --name ${THIS_NAME} \
    --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
    --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
    --initial-cluster ${CLUSTER} \
    --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
# For node 3
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
sudo docker run --net=host --name etcd quay.io/coreos/etcd:${ETCD_VERSION} \
    /usr/local/bin/etcd \
    --data-dir=data.etcd --name ${THIS_NAME} \
    --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
    --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
    --initial-cluster ${CLUSTER} \
    --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}

 

 

为了使用 API 版本3 来运行 etcdctl :

 

docker exec etcd /bin/sh -c "export ETCDCTL_API=3 && /usr/local/bin/etcdctl put foo bar"

 

posted on 2021-05-11 17:24  K&  阅读(715)  评论(0编辑  收藏  举报