背景说明
前置知识
什么是ETCD
ETCD 是 Core OS 基于 Raft 协议开放的分布式 key-value 存储,可用于服务发现,共享配置以及一致性保障(数据库选主,分布式锁等)。
主要功能
- key-value存储
- 监听机制
- key 的过期及续约机制,用于监控和服务发现
- 原子 Compare And Swap 和Compare And Delete,用于分布式锁和 leader 选举。(对key设置或者删除需要满足一定的条件才能执行,条件如下)
- prevExist: key当前赋值前是否存在
- prevvalue: key 当前赋值前的值
- prevlndex: key Index
使用场景
服务注册与发现(使用较多,与 zookeeper 类似)
强一致性、高可用的服务存储目录。
可以对注册的 key 设置 ttl ,到期后 key 会自动删除,定时保持服务的心跳以达到健康检查的效果。
- 基于监听机制的分布式异步系统
- 在分布式系统中,最常用的一种组件间通信方式就是消息发布与订阅。
- 创建一个消息中心,生产者在这个消息中心发布消息,消费者订阅他们关心的主题,一旦主题有消息发布,就会实时通知订阅者。
- 应用在启动的时候主动从 etcd 获取一次配置信息,同时,在 etcd 节点上注册一个Watcher 并等待,以后每次配置有更新的时候,etcd 都会实时通知订阅者,以此达到获取最新配置信息的目的。
- key-value存储,应用程序可以读取和写入 etcd 中的数据
- 采用 KV 型数据存储,一般情况下比关系型数据库快。
- 支持动态存储(内存)以及静态存储(磁盘)。
- 分布式存储,可集成为多节点集群。
- 存储方式,采用类似目录结构(B+tree)。 只有叶子节点才能真正存储数据,相当于文件。 叶子节点的父节点一定是目录,目录不能存储数据。
SSL证书
集群证书与工具说明
ETCD集群均在 TLS 环境下运行,所以需要签发私有证书。
- CA证书
- ca-config.json: CA证书签发配置,主要包含CA过期时间
- ca-csr.json: CA证书签发主体信息与加密方法
- ca.pem: CA证书文件
- ca-key.pem: CA证书私钥
- 对等证书
- etcd-csr.json: ETCD成员配置、证书签发地区
- etcd.csr: 证书公钥文件
- etcd-key.pem: 对等证书
- etcd.pem: 对等证书文件
- cfssl
- 是 CloudFlare 的 PKI/TLS 利器。
- 它既是命令行工具,又可以用于签名,验证和捆绑 TLS 证书的 HTTP API 服务器,环境构建方面需要 Go 1.12+。
- cfssljson
- 是 cfssl 的配套工具,可以从 cfssl 获取 JSON 输出的二进制程序,并将证书、密钥、CSR和 bundle 写入指定位置。
附加说明
数字证书中主题(Subject)中字段的含义:
- 一般的数字证书产品的主题通常含有如下字段:
- 公用名称 (Common Name) 简称:CN 字段,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名;
- 组织名称,公司名称(Organization Name) 简称:O 字段,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端单位证书则为证书申请者所在单位名称;
- 组织单位名称,公司部门(Organization Unit Name) 简称:OU字段
- 证书申请单位所在地
- 所在城市 (Locality) 简称:L 字段
- 所在省份 (State/Provice) 简称:S 字段,State:州,省
- 所在国家 (Country) 简称:C 字段,只能是国家字母缩写,如中国:CN
版本选型
因为当前线上 K8S 的 ETCD 版本为 3.3.10 , 而业务集群需要使用 Learner 角色实现边缘/跨区域从节点信息同步,需要 3.4.X 版本以上才可支持;
同时在 3.5.X 版本中,默认情况下新增一个 member 其状态为 learner ,在当前新 member 未变成 “voting-member” 前,是不会改变 quorum size ,同样 Misconfiguration 能够撤销保证 quorum 不会 lose 。
所以本次部署直接采用当前最新 release 版本 3.5.4 进行部署运行。
部署流程
节点信息
节点IP | 节点名称 | Peer通信端口 | 客户端交互端口 | 节点域名 |
---|---|---|---|---|
192.168.7.91 | etcd1-cn | 2501 | 2500 | etcd1-cn.nestealin.com |
192.168.7.92 | etcd2-cn | 2501 | 2500 | etcd2-cn.nestealin.com |
192.168.7.93 | etcd3-cn | 2501 | 2500 | etcd3-cn.nestealin.com |
环境准备
内网解析
根据上述表格对每台ETCD新增内网解析,便于后续Api操作。
工具下载
mkdir /opt/etcd_tools && cd /opt/etcd_tools
# 证书工具
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_linux_amd64
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_linux_amd64
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl-certinfo_1.6.1_linux_amd64
mv cfssl_1.6.1_linux_amd64 cfssl
mv cfssl-certinfo_1.6.1_linux_amd64 cfssl-certinfo
mv cfssljson_1.6.1_linux_amd64 cfssljson
chmod 755 cfssl*
mv cfssl* /usr/local/bin/
# ETCD下载
wget https://github.com/etcd-io/etcd/releases/download/v3.5.4/etcd-v3.5.4-linux-amd64.tar.gz
tar zxvf etcd-v3.5.4-linux-amd64.tar.gz
cd etcd-v3.5.4-linux-amd64/
mv etcd* /usr/local/bin/
chmod 755 /usr/local/bin/etcd*
创建etcd数据目录
包含启动后自动创建的 etcd 数据文件目录 /data/etcd_2500/member 与集群证书目录 /data/etcd_2500/ssl .
每台节点都需要创建
mkdir -p /data/etcd_2500/ssl
cd /data/etcd_2500/ssl
自签证书
CA证书
创建 ca-config.json
签发配置
{
"signing": {
"default": {
"expiry": "438000h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "438000h"
}
}
}
}
创建 CA 主体配置 ca-csr.json
{
"CN": "CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Guangdong",
"L": "Guangzhou",
"O": "etcd",
"OU": "System"
}
]
}
生成 CA 证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
2022/05/26 00:51:01 [INFO] generating a new CA key and certificate from CSR
2022/05/26 00:51:01 [INFO] generate received request
2022/05/26 00:51:01 [INFO] received CSR
2022/05/26 00:51:01 [INFO] generating key: rsa-2048
2022/05/26 00:51:01 [INFO] encoded CSR
2022/05/26 00:51:01 [INFO] signed certificate with serial number 706048808790509849515892382867387858686891109238
此时生成证书文件
ca.csr
ca.pem
ca-key.pem
集群证书
创建集群证书配置 etcd-csr.json
{
"CN": "etcd",
"hosts": [
"192.168.7.91",
"192.168.7.92",
"192.168.7.93",
"*.nestealin.com",
"127.0.0.1"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Guangdong",
"L": "Guangzhou",
"O": "etcd",
"OU": "System"
}
]
}
根据CA证书签发集群证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd etcd-csr.json | cfssljson -bare etcd
2022/05/26 00:53:35 [INFO] generate received request
2022/05/26 00:53:35 [INFO] received CSR
2022/05/26 00:53:35 [INFO] generating key: rsa-2048
2022/05/26 00:53:36 [INFO] encoded CSR
2022/05/26 00:53:36 [INFO] signed certificate with serial number 252487637386301147036590697689340402714034710017
2022/05/26 00:53:36 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
将 /data/etcd_2500/ssl
目录拷贝至其他节点
Node1部署
创建 service 管理文件
注意关键参数:
–initial-cluster-state: 集群首次运行,需要设置为 new .
另外注意,因为有配置
--initial-cluster
默认集群信息,不急于单台配置完成后立刻启动,会导致连不上其他节点而启动失败。
vim /etc/systemd/system/etcd_2500.service
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/etcd-io/etcd
[Service]
Type=notify
WorkingDirectory=/usr/local/bin
ExecStart=/usr/local/bin/etcd \
--name=etcd1-cn \
--cert-file=/data/etcd_2500/ssl/etcd.pem \
--key-file=/data/etcd_2500/ssl/etcd-key.pem \
--peer-cert-file=/data/etcd_2500/ssl/etcd.pem \
--peer-key-file=/data/etcd_2500/ssl/etcd-key.pem \
--trusted-ca-file=/data/etcd_2500/ssl/ca.pem \
--peer-trusted-ca-file=/data/etcd_2500/ssl/ca.pem \
--initial-advertise-peer-urls=https://etcd1-cn.nestealin.com:2501 \
--listen-peer-urls=https://0.0.0.0:2501 \
--listen-client-urls=https://0.0.0.0:2500 \
--advertise-client-urls=https://etcd1-cn.nestealin.com:2500 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=etcd1-cn=https://etcd1-cn.nestealin.com:2501,etcd2-cn=https://etcd2-cn.nestealin.com:2501,etcd3-cn=https://etcd3-cn.nestealin.com:2501 \
--initial-cluster-state=new \
--data-dir=/data/etcd_2500 \
--snapshot-count=50000 \
--auto-compaction-retention=1 \
--max-request-bytes=10485760 \
--quota-backend-bytes=8589934592
Restart=always
RestartSec=15
LimitNOFILE=65536
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
CLI管理工具: /usr/local/bin/etcdcli
简化每次操作带上endpoint等信息。
通用工具,集群内节点同样配置使用。
#!/bin/bash
# -----
# ETCD-Cli-Ops-Tools.
# -----
# Input Var
USER_COMMAND=$@
# Env Info
ETCDCTL_API=3
ETCD_CERT_PATH=/data/etcd_2500/ssl
ETCD_DOMAINS=https://etcd1-cn.nestealin.com:2500,https://etcd2-cn.nestealin.com:2500,https://etcd3-cn.nestealin.com:2500
USAGE="Usage: `basename $0` (member list|endpoint health)"
# Common Command
COMMON_COMMAND="etcdctl --endpoints=$ETCD_DOMAINS --cacert=$ETCD_CERT_PATH/ca.pem --cert=$ETCD_CERT_PATH/etcd.pem --key=$ETCD_CERT_PATH/etcd-key.pem"
# Functions
function help() {
echo "$USAGE"
echo "-------------"
echo "Usage_Sample:"
echo "Member List: `basename $0` member list"
echo "Endpoint Health: `basename $0` endpoint health"
echo "Put Data For Test: `basename $0` put /test/ok 11"
echo "Get Data For Test: `basename $0` get /test/ok"
echo "Delete Data For Test: `basename $0` del /test/ok"
}
# Entrance
case $USER_COMMAND in
(-h|--help|help)
help
;;
(*)
$COMMON_COMMAND $USER_COMMAND
;;
esac
Node2部署
- 下载etcd二进制、证书工具及证书文件至相应目录。
- 运行配置与Node1大同小异,将节点信息换成本机即可。
vim /etc/systemd/system/etcd_2500.service
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/etcd-io/etcd
[Service]
Type=notify
WorkingDirectory=/usr/local/bin
ExecStart=/usr/local/bin/etcd \
--name=etcd2-cn \
--cert-file=/data/etcd_2500/ssl/etcd.pem \
--key-file=/data/etcd_2500/ssl/etcd-key.pem \
--peer-cert-file=/data/etcd_2500/ssl/etcd.pem \
--peer-key-file=/data/etcd_2500/ssl/etcd-key.pem \
--trusted-ca-file=/data/etcd_2500/ssl/ca.pem \
--peer-trusted-ca-file=/data/etcd_2500/ssl/ca.pem \
--initial-advertise-peer-urls=https://etcd2-cn.nestealin.com:2501 \
--listen-peer-urls=https://0.0.0.0:2501 \
--listen-client-urls=https://0.0.0.0:2500 \
--advertise-client-urls=https://etcd2-cn.nestealin.com:2500 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=etcd1-cn=https://etcd1-cn.nestealin.com:2501,etcd2-cn=https://etcd2-cn.nestealin.com:2501,etcd3-cn=https://etcd3-cn.nestealin.com:2501 \
--initial-cluster-state=new \
--data-dir=/data/etcd_2500 \
--snapshot-count=50000 \
--auto-compaction-retention=1 \
--max-request-bytes=10485760 \
--quota-backend-bytes=8589934592
Restart=always
RestartSec=15
LimitNOFILE=65536
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
Node3部署
与Node2做法一致
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/etcd-io/etcd
[Service]
Type=notify
WorkingDirectory=/usr/local/bin
ExecStart=/usr/local/bin/etcd \
--name=etcd3-cn \
--cert-file=/data/etcd_2500/ssl/etcd.pem \
--key-file=/data/etcd_2500/ssl/etcd-key.pem \
--peer-cert-file=/data/etcd_2500/ssl/etcd.pem \
--peer-key-file=/data/etcd_2500/ssl/etcd-key.pem \
--trusted-ca-file=/data/etcd_2500/ssl/ca.pem \
--peer-trusted-ca-file=/data/etcd_2500/ssl/ca.pem \
--initial-advertise-peer-urls=https://etcd3-cn.nestealin.com:2501 \
--listen-peer-urls=https://0.0.0.0:2501 \
--listen-client-urls=https://0.0.0.0:2500 \
--advertise-client-urls=https://etcd3-cn.nestealin.com:2500 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=etcd1-cn=https://etcd1-cn.nestealin.com:2501,etcd2-cn=https://etcd2-cn.nestealin.com:2501,etcd3-cn=https://etcd3-cn.nestealin.com:2501 \
--initial-cluster-state=new \
--data-dir=/data/etcd_2500 \
--snapshot-count=50000 \
--auto-compaction-retention=1 \
--max-request-bytes=10485760 \
--quota-backend-bytes=8589934592
Restart=always
RestartSec=15
LimitNOFILE=65536
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
集群启动
分别在每台节点执行如下命令
systemctl daemon-reload
service etcd_2500 start
service etcd_2500 status
集群验证
ETCDCTL方式
# 环境声明
ETCD_CERT_PATH=/data/etcd_2500/ssl
PORT=2500
ETCD1=etcd1-cn.nestealin.com
ETCD2=etcd2-cn.nestealin.com
ETCD3=etcd3-cn.nestealin.com
# 成员信息,增加 --write-out=table / -w table 参数可以表格形式输出
ETCDCTL_API=3 etcdctl \
--endpoints=https://$ETCD1:$PORT,https://$ETCD2:$PORT,https://$ETCD3:$PORT \
--cacert="$ETCD_CERT_PATH/ca.pem" \
--cert="$ETCD_CERT_PATH/etcd.pem" \
--key="$ETCD_CERT_PATH/etcd-key.pem" \
member list --write-out=table
# 集群健康检查
ETCDCTL_API=3 etcdctl \
--endpoints=https://$ETCD1:$PORT,https://$ETCD2:$PORT,https://$ETCD3:$PORT \
--cacert="$ETCD_CERT_PATH/ca.pem" \
--cert="$ETCD_CERT_PATH/etcd.pem" \
--key="$ETCD_CERT_PATH/etcd-key.pem" \
endpoint health
# 写入数据
ETCDCTL_API=3 etcdctl \
--endpoints=https://$ETCD1:$PORT,https://$ETCD2:$PORT,https://$ETCD3:$PORT \
--cacert="$ETCD_CERT_PATH/ca.pem" \
--cert="$ETCD_CERT_PATH/etcd.pem" \
--key="$ETCD_CERT_PATH/etcd-key.pem" \
put /test/ok 11
# 查询数据示例
ETCDCTL_API=3 etcdctl \
--endpoints=https://$ETCD1:$PORT,https://$ETCD2:$PORT,https://$ETCD3:$PORT \
--cacert="$ETCD_CERT_PATH/ca.pem" \
--cert="$ETCD_CERT_PATH/etcd.pem" \
--key="$ETCD_CERT_PATH/etcd-key.pem" \
get /test/ok
# 删除数据示例
ETCDCTL_API=3 etcdctl \
--endpoints=https://$ETCD1:$PORT,https://$ETCD2:$PORT,https://$ETCD3:$PORT \
--cacert="$ETCD_CERT_PATH/ca.pem" \
--cert="$ETCD_CERT_PATH/etcd.pem" \
--key="$ETCD_CERT_PATH/etcd-key.pem" \
del /test/ok
CURL验证
# 检查集群版本
curl --cacert /data/etcd_2500/ssl/ca.pem --cert /data/etcd_2500/ssl/etcd.pem --key /data/etcd_2500/ssl/etcd-key.pem -sL https://etcd1-cn.nestealin.com:2501/version
# 查看member信息
curl --cacert /data/etcd_2500/ssl/ca.pem --cert /data/etcd_2500/ssl/etcd.pem --key /data/etcd_2500/ssl/etcd-key.pem -sL https://etcd1-cn.nestealin.com:2501/members
ETCDCLI验证
# 省略声明证书、endpoint地址,简化操作
etcdcli member list --write-out=table
etcdcli endpoint status -w=table
常用操作
以下操作以 ETCDCLI 方式操作为例。
增加节点
以下事例为新增一台 Learner 节点;
- 添加内网解析;
- 参照Node2部署,拷贝证书、ETCD相关二进制程序、证书工具、service文件等至相关目录。
新签证书
由于集群通信还是使用IP,所以每新增一台节点都要修改一次集群证书配置,并重新签发集群证书。
cd /data/etcd_2500/ssl
vim /data/etcd_2500/ssl/etcd-csr.json
{
"CN": "etcd",
"hosts": [
"192.168.7.91",
"192.168.7.92",
"192.168.7.93",
"192.168.7.35",
"*.nestealin.com",
"127.0.0.1"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Guangdong",
"L": "Guangzhou",
"O": "etcd",
"OU": "System"
}
]
}
重新签发集群证书
cfssl gencert -ca=/data/etcd_2500/ssl/ca.pem -ca-key=/data/etcd_2500/ssl/ca-key.pem -config=/data/etcd_2500/ssl/ca-config.json -profile=kubernetes /data/etcd_2500/ssl/etcd-csr.json | cfssljson -bare etcd
向集群注册新节点,在当前集群任意节点操做即可
如果是新增 member 节点,则去掉
--learner
参数即可。
# 语法格式: peer-urls声明新节点peer连接地址,member add $节点名称
etcdcli --peer-urls=https://etcd4-cn.nestealin.com:2501 member add etcd4-cn --learner
- 操作输出
允许加入集群,此时状态为待加入,并提示启动参数需要带上
ETCD_INITIAL_CLUSTER_STATE="existing"
而非作为new
集群方式启动。
Member 7f42839e765cb6e7 added to cluster f0d6d791690a1a29
ETCD_NAME="etcd4-cn"
ETCD_INITIAL_CLUSTER="etcd1-cn=https://etcd1-cn.nestealin.com:2501,etcd3-cn=https://etcd3-cn.nestealin.com:2501,etcd4-cn=https://etcd4-cn.nestealin.com:2501,etcd2-cn=https://etcd2-cn.nestealin.com:2501"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcd4-cn.nestealin.com:2501"
ETCD_INITIAL_CLUSTER_STATE="existing"
- 检查节点加入状态
此时因为新节点尚未启动,所以状态仍为unstarted,
etcdcli member list
1bd2d3991de8cd18, started, etcd1-cn, https://etcd1-cn.nestealin.com:2501, https://etcd1-cn.nestealin.com:2500, false
2e1a4370a35a09fd, started, etcd3-cn, https://etcd3-cn.nestealin.com:2501, https://etcd3-cn.nestealin.com:2500, false
7f42839e765cb6e7, unstarted, , https://etcd4-cn.nestealin.com:2501, , true
a3358166c4af2269, started, etcd2-cn, https://etcd2-cn.nestealin.com:2501, https://etcd2-cn.nestealin.com:2500, false
启动新节点
systemctl daemon-reload
service etcd_2500 start
service etcd_2500 status
检查集群状态
节点上线正常,且为Leaner角色
etcdcli member list
1bd2d3991de8cd18, started, etcd1-cn, https://etcd1-cn.nestealin.com:2501, https://etcd1-cn.nestealin.com:2500, false
2e1a4370a35a09fd, started, etcd3-cn, https://etcd3-cn.nestealin.com:2501, https://etcd3-cn.nestealin.com:2500, false
7f42839e765cb6e7, started, etcd4-cn, https://etcd4-cn.nestealin.com:2501, https://etcd4-cn.nestealin.com:2500, true
a3358166c4af2269, started, etcd2-cn, https://etcd2-cn.nestealin.com:2501, https://etcd2-cn.nestealin.com:2500, false
修改Cli文件,增加endpoint
vim /usr/local/bin/etcdcli
# 忽略其他相同内容,追加Node4客户端连接信息
ETCD_DOMAINS=https://etcd1-cn.nestealin.com:2500,https://etcd2-cn.nestealin.com:2500,https://etcd3-cn.nestealin.com:2500,https://etcd4-cn.nestealin.com:2500
检查集群同步状态
etcdcli endpoint status
https://etcd1-cn.nestealin.com:2500, 1bd2d3991de8cd18, 3.5.4, 25 kB, true, false, 2, 13, 13,
https://etcd2-cn.nestealin.com:2500, a3358166c4af2269, 3.5.4, 20 kB, false, false, 2, 13, 13,
https://etcd3-cn.nestealin.com:2500, 2e1a4370a35a09fd, 3.5.4, 20 kB, false, false, 2, 13, 13,
https://etcd4-cn.nestealin.com:2500, 7f42839e765cb6e7, 3.5.4, 20 kB, false, true, 2, 13, 13,
删除节点
# 语法 member remove $节点ID
etcdcli member remove 12d20db544264be2
关于监控
集群自带指标暴露,只需要在Prometheus添加对应Job即可
- job_name: etcd
static_configs:
- targets: ['192.168.7.91:2500','192.168.7.92:2500','192.168.7.93:2500']
监控指标访问测试
curl --cacert /data/etcd_2500/ssl/ca.pem --cert /data/etcd_2500/ssl/etcd.pem --key /data/etcd_2500/ssl/etcd-key.pem -sL https://etcd1-cn.nestealin.com:2500/metrics
可能遇到的问题
如有集群数据问题启动失败,请在关闭 ETCD 后直接删除 /data/etcd_2500/member/*
下内容,待调整完后重新启动即可。
相关文档
https://wghdr.top/archives/285
https://wiki.shileizcc.com/confluence/pages/viewpage.action?pageId=60227790