etcd是近些年比较流行的分布式键值对存储系统,他采用Go语言编写而成,常被用于微服务中的服务注册与发现。

K8s高可用架构图

K8s系统默认就采用etcd存储数据,看看下面比较常见的高可用部署方案:

image-20210419180603314

image-20210421170705957

以上两种方案的区别是:一种用docker方式部署etcd,一种是独立部署etcd(官方更推荐用这种独立部署的方式)。

etcd的工作原理

etcd是Kubernetes集群的大脑。使用etcd的“Watch”功能监控更改序列。通过这个功能,Kubernetes可以订阅集群内的更改并执行来自API服务器的任何状态请求。etcd与分布式集群中的不同组件协作。etcd对组件状态的更改作出反应,其他组件可能会对更改作出反应。

可能存在这样一种情况:在维护集群中一组etcd组件的所有状态的相同副本时,需要将相同的数据存储在两个etcd实例中。但是,etcd不应该在不同的实例中更新相同的记录。

在这种情况下,etcd不会处理每个集群节点上的写操作。相反,只有一个实例负责在内部处理写操作。那个节点叫做leader。集群内其他节点采用RAFT算法选择一个leader。一旦leader选定,其他节点就成为follower节点。

现在,当写请求到达leader节点时,leader处理写入。leader etcd节点向其他节点广播数据的副本。如果一个follower节点在那一刻处于不活动或脱机状态,那么基于大多数可用节点,写请求将得到一个完整的标志。通常,如果leader得到集群中其他成员的同意,写操作将获得完整标志。

这是它们选择leader,以及如何确保在所有实例中传播写操作的方式。利用raft协议在etcd中实现了这种分布式共识。

etcd集群部署

在生产环境中,为了整个集群的高可用,etcd 正常都会集群部署,避免单点故障。本节将会介绍如何进行 etcd 集群部署。引导 etcd 集群的启动有以下三种机制:

  • 静态
  • etcd 动态发现
  • DNS 发现

静态启动 etcd 集群要求每个成员都知道集群中的另一个成员。下面我们着重介绍静态独立部署etcd集群。

环境准备

我们用4台centos8.3虚拟机来做配置试验,每台服务都做下面的配置:(我刚好有4台新服务器,但绝不意味着4台集群是好主意,最好采用奇数集群,推荐采用5台服务器部署,这样能容忍2台服务器同时出现故障,集群依然能够稳定运行)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 每台服务器 /etc/hosts 中加入解析
10.10.200.146 DXHJZW146
10.10.200.147 DXHJZW147
10.10.200.148 DXHJZW148
10.10.200.149 DXHJZW149

# 1. 下载最新版本的etcd
https://github.com/etcd-io/etcd/releases
# 下载相应平台最新版解压之后,将 etcd、etcdctl 放入 /usr/bin
# 可执行权限 chmod +x /usr/bin/etcd*

# 2. 下载证书生成工具
https://github.com/cloudflare/cfssl/releases
mv cfssl_1.5.0_linux_amd64 cfssl
mv cfssljson_1.5.0_linux_amd64 cfssljson
mv cfssl-certinfo_1.5.0_linux_amd64 cfssl-certinfo
# 下载相应平台最新版解压之后,将 cfssl、cfssljson、cfssl-certinfo 放入 /usr/bin
# 可执行权限 chmod +x /usr/bin/cfssl*

# 3. 新建立目录
mkdir /opt/etcd/ssl
mkdir /opt/etcd/cfg
mkdir /opt/etcd/data
mkdir /opt/etcd/check

配置安装

下面配置etcd互相访问的加密证书(我们以安全方式访问)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 4. 找一台200.146开始配置
# 在 /opt/etcd/ssl 下面新建下面3个配置文件

# vi ca-config.json
{
  "signing": {
    "default": {
      "expiry": "876000h"
    },
    "profiles": {
      "k8setcd": {
        "usages": [
          "signing",
          "key encipherment",
          "server auth",
          "client auth"
        ],
        "expiry": "876000h"
      }
    }
  }
}

# vi ca-csr.json
{
  "CN": "etcd-CA",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BEIJING",
      "L": "BEIJING",
      "O": "DXHJ",
      "OU": "WEB"
    }
  ]
}

# vi server-csr.json
# 注意:hosts 中加入所有参加集群配置的服务器IP地址和hostname
{
  "CN": "etcd-CA",
  "hosts": [
    "127.0.0.1",
    "10.10.200.146",
    "10.10.200.147",
    "10.10.200.148",
    "10.10.200.149",
    "DXHJZW146",
    "DXHJZW147",
    "DXHJZW148",
    "DXHJZW149",
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BEIJING",
      "L": "BEIJING",
      "O": "DXHJ",
      "OU": "WEB"
    }
  ]
}

# 有了上面三个配置,执行下面两个证书生成命令:
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
	-profile=k8setcd server-csr.json | cfssljson -bare server
# 结果如下:
├── ca-config.json
├── ca.csr
├── ca-csr.json
├── ca-key.pem
├── ca.pem
├── server.csr
├── server-csr.json
├── server-key.pem
└── server.pem

# 将146上这些文件(主要是*.pem 四个)scp到其它三台服务器的/opt/etcd/ssl目录中,大家公用证书

添加配置文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 5. 在每台服务器 /opt/etcd/cfg 中加入

# vi /opt/etcd/cfg/etcd.yml
# 每台机器上需要修改对应的IP地址,比如分别将下面 146 替换出 147、148、149

# +++++++++++++++++++++++++++++++++++
# [Member]
name: "etcd146"
data-dir: "/opt/etcd/data/"
wal-dir: "/opt/etcd/data/"
listen-peer-urls: "https://10.10.200.146:2380"
listen-client-urls: "https://10.10.200.146:2379,https://127.0.0.1:2379"
logger: "zap"

# [Clustering]
initial-advertise-peer-urls: "https://10.10.200.146:2380"
advertise-client-urls: "https://10.10.200.146:2379"
# 备注:这里4项是一样,而且4台服务器都一样
initial-cluster: "
etcd146=https://10.10.200.146:2380,etcd147=https://10.10.200.147:2380,
etcd148=https://10.10.200.148:2380,etcd149=https://10.10.200.149:2380"
initial-cluster-token: "etcd-cluster"
initial-cluster-state: "new"

# [Security]
client-transport-security:
  cert-file: "/opt/etcd/ssl/server.pem"
  key-file: "/opt/etcd/ssl/server-key.pem"
  client-cert-auth: true
  trusted-ca-file: "/opt/etcd/ssl/ca.pem"
  auto-tls: true

peer-transport-security:
  key-file: "/opt/etcd/ssl/server-key.pem"
  cert-file: "/opt/etcd/ssl/server.pem"
  client-cert-auth: true
  trusted-ca-file: "/opt/etcd/ssl/ca.pem"
  auto-tls: true
# +++++++++++++++++++++++++++++++++++

配置开机自启动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 6. 在每台服务器中都做同样的配置
# vi /etc/systemd/system/etcd.service
# 在上面文件中放入以下内容:
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
User=root
Type=notify
WorkingDirectory=/opt/etcd/data/
Restart=always
#Restart=on-failure
RestartSec=10s
LimitNOFILE=65536
ExecStart=/usr/bin/etcd --config-file=/opt/etcd/cfg/etcd.yaml

[Install]
WantedBy=multi-user.target

# 执行生效
systemctl reload-daemon
systemctl enable etcd.service
systemctl start etcd.service
# 到此4台服务器etcd都会启动,数据放在 /opt/etcd/data 目录中

检查集群状态

因为我们部署的是加入了证书的etcd集群,查询集群状态命令稍显复杂了些,我们可以把每个命令做成一个sh文件,放入/opt/etcd/check目录中,方便以后直接执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 比如这里的一个 status_etcd.sh 文件,最后一句是不同的命令

# ++++++++++++++++++++++
#!/bin/bash

etcdctl \
-w table \
--cacert=/opt/etcd/ssl/ca.pem \
--cert=/opt/etcd/ssl/server.pem \
--key=/opt/etcd/ssl/server-key.pem \
--endpoints=" \
https://10.10.200.146:2379,https://10.10.200.147:2379, \
https://10.10.200.148:2379,https://10.10.200.149:2379" \
endpoint status
# ++++++++++++++++++++++

endpoint status 	# 集群状态
member list     	# 成员列表
endpoint health 	# 健康状态
get --from-key '' 	# 显示所有key-value
del / --prefix  	# 删除所有key
defrag  			# 压缩空间

顺利的话,恭喜你,etcd独立集群部署成功了,后面你可以用他来做k8s的部署。

常见问题

某个节点损坏

这种情况下就需要先将损坏的节点排除出集群,然后再重新加入即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
0. 假设顺坏的是200.54
先停止服务:systemctl stop etcd.service

1. 找到一台正常的节点服务器,比如https://10.10.200.51:2379
etcdctl --cacert=ca.pem --cert=server.pem --key=server-key.pem \
--endpoints="https://10.10.200.51:2379" member list

2. 从集群中删除损坏的节点(假设是200.54)
etcdctl --cacert=ca.pem --cert=server.pem --key=server-key.pem \
--endpoints="https://10.10.200.51:2379" member remove 78c05f4178265b0e

3. 重新加入这个节点
etcdctl --cacert=ca.pem --cert=server.pem --key=server-key.pem \
--endpoints="https://10.10.200.51:2379" member add etcd54 \
--peer-urls=https://10.10.200.54:2380

4. 修改配置并启动损坏的节点(假设是200.54上操作)
删除所有数据rm -rf data/*
修改配置:
initial-cluster-state: "new" # 改成下面这句
initial-cluster-state: "existing"

systemctl start etcd.service
systemctl status etcd.service 

参考博客

记住标准的配置文件参考:https://github.com/etcd-io/etcd/blob/master/etcd.conf.yml.sample#L96

配置文件详解:https://www.cnblogs.com/linuxws/p/11194403.html

ETCD集群配置大全:

https://www.cnblogs.com/yuezhimi/p/12931002.html

https://www.cnblogs.com/LouisZJ/articles/11178153.html

https://www.cnblogs.com/winstom/p/11811373.html

https://www.cnblogs.com/winstom/p/11988329.html

https://zhuanlan.zhihu.com/p/96499288

https://www.cnblogs.com/winstom/p/11992124.html

https://www.jianshu.com/p/85803026a9a1

(完)