缘由
原本我们 kuberntes集群所使用的配置中心是 configmap,但是鉴于服务没有做热更新,由于Configmap中data对应 json文件的修改不太友好,增加删除一个服务,增加一个文件,常常出现少打一个逗号,多打一个逗号不容易发现,应用时频频出现错误的情况,所以下一个版本决定将配置中心移出来,并采用 ETCD 作为我们的配置中心。
ETCD
官网介绍:A distributed, reliable key-value store for the most critical data of a distributed system.
一种分布式的、可靠的键值存储,用于存储分布式系统中最关键的数据
etcd 是一个高度一致的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器集群访问的数据。
特征:
- 简单的界面:使用标准的HTTP工具(例如curl)读取和写入值;
- 键值存储:将数据存储在分层组织的目录中,例如在标准文件系统中;
- 注意变化:监视特定的键或目录以进行更改,并对值的更改做出反应;
我们采用 ETCD 主要出于 “注意变化” 与 “键值存储” ,由于原先所采用的 Kubernetes Configmap,中使用的配置也主要是 json 类型的文件,所以“键值存储“很便于配置,而“注意变化” 则可以根据 ETCD中配置的改变,实时的更新到 Pod 中,使得配置数据为最新实时的,虽然 Configmap 同样具有这样一个功能效率会稍慢,但是原先版本的服务并没有做再次重载配置的过程,所以这次开发那边也多做一了一步,监听kv变化,根据配置更新重载服务。
ETCD 概念词:
- Raft:etcd所采用的保证分布式系统强一致性的算法。
- Node:一个Raft状态机实例。
- Member: 一个etcd实例。它管理着一个Node,并且可以为客户端请求提供服务。
- Cluster:由多个Member构成可以协同工作的etcd集群。
- Peer:对同一个etcd集群中另外一个Member的称呼。
- Client: 向etcd集群发送HTTP请求的客户端。
- WAL:预写式日志,etcd用于持久化存储的日志格式。
- snapshot:etcd防止WAL文件过多而设置的快照,存储etcd数据状态。
- Proxy:etcd的一种模式,为etcd集群提供反向代理服务。
- Leader:Raft算法中通过竞选而产生的处理所有数据提交的节点。
- Follower:竞选失败的节点作为Raft中的从属节点,为算法提供强一致性保证。
- Candidate:当Follower超过一定时间接收不到Leader的心跳时转变为Candidate开始竞选。
- Term:某个节点成为Leader到下一次竞选时间,称为一个Term。
- Index:数据项编号。Raft中通过Term和Index来定位数据。
Docker 启动 ETCD集群
目前采用单机伪集群,一台主机上跑了3个docker etcd服务,通过监听不同的端口。
$ docker run -itd \
--net=host \
--name etcd01 \
--restart=always \
-v /data/etcd1:/etcd-data \
quay.io/coreos/etcd:v3.3.10 /usr/local/bin/etcd \
--name etcd01 \
--data-dir=/etcd-data \
--initial-advertise-peer-urls http://192.168.92.100:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--advertise-client-urls http://192.168.92.100:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--initial-cluster etcd01=http://192.168.92.100:2380,etcd02=http://192.168.92.100:2382,etcd03=http://192.168.92.100:2384 \
--initial-cluster-state new \
--initial-cluster-token abc123dd
$ docker run -itd \
--net=host \
--name etcd02 \
--restart=always \
-v /data/etcd2:/etcd-data \
quay.io/coreos/etcd:v3.3.10 /usr/local/bin/etcd \
--name etcd02 \
--data-dir=/etcd-data \
--initial-advertise-peer-urls http://192.168.92.100:2382 \
--listen-peer-urls http://0.0.0.0:2382 \
--advertise-client-urls http://192.168.92.100:2381 \
--listen-client-urls http://0.0.0.0:2381 \
--initial-cluster etcd01=http://192.168.92.100:2380,etcd02=http://192.168.92.100:2382,etcd03=http://192.168.92.100:2384 \
--initial-cluster-state new \
--initial-cluster-token abc123dd
$ docker run -itd \
--net=host \
--name etcd03 \
--restart=always \
-v /data/etcd3:/etcd-data \
quay.io/coreos/etcd:v3.3.10 /usr/local/bin/etcd \
--name etcd03 \
--data-dir=/etcd-data \
--initial-advertise-peer-urls http://192.168.92.100:2384 \
--listen-peer-urls http://0.0.0.0:2384 \
--advertise-client-urls http://192.168.92.100:2383 \
--listen-client-urls http://0.0.0.0:2383 \
--initial-cluster etcd01=http://192.168.92.100:2380,etcd02=http://192.168.92.100:2382,etcd03=http://192.168.92.100:2384 \
--initial-cluster-state existing \
--initial-cluster-token abc123dd
进入容器查看集群是否健康
$ docekr exec -it 978e sh
/ # etcdctl cluster-health
member 3673481cdc30c1b8 is healthy: got healthy result from http://192.168.92.100:2379
member a1e728ddaa50b2b2 is healthy: got healthy result from http://192.168.92.100:2383
member e20b169a814fa5b0 is healthy: got healthy result from http://192.168.92.100:2381
cluster is healthy
/ # etcdctl member list
3673481cdc30c1b8: name=etcd01 peerURLs=http://192.168.92.100:2380 clientURLs=http://192.168.92.100:2379 isLeader=true
a1e728ddaa50b2b2: name=etcd03 peerURLs=http://192.168.92.100:2384 clientURLs=http://192.168.92.100:2383 isLeader=false
e20b169a814fa5b0: name=etcd02 peerURLs=http://192.168.92.100:2382 clientURLs=http://192.168.92.100:2381 isLeader=false
Docker 启动参数含义:
参数 | 使用说明 |
---|---|
--name etcd01 | 本member的名字 |
--initial-advertise-peer-urls http://192.168.92.100:2380 | 其他member使用,其他member通过该地址与本member交互信息。一定要保证从其他member能可访问该地址。静态配置方式下,该参数的value一定 要同时在 --initial-cluster参数中存在。memberID的生成受 --initial-cluster-token和--initial-advertise-peer-urls影响。 |
--listen-peer-urls http://0.0.0.0:2380 | 本member侧使用,用于监听其他member发送信息的地址。ip为全0代表监听本member侧所有接口。 |
--listen-client-urls http://0.0.0.0:2379 | 本member侧使用,用于监听etcd客户发送信息的地址。ip为全0代表监听本member侧所有接口。 |
--advertise-client-urls http://192.168.92.100:2379 | etcd 客户使用,客户通过该地址与本member交互信息。一定要保证从客户侧能访问该地址。 |
--initial-cluster-token etcd-cluster | 同一个集群下的token应相同,用于区分不同集群。本地如有多个集群要设为不同。 |
--initial-cluster etcd01=http://192.168.92.100:2380,etcd02=http://192.168.92.100:2382,etcd03=http://192.168.92.100:2384 | 本member侧使用。描述集群中所有节点的信息,本member根据此信息去联系其他member。memberID的生成受--initial-cluster-token和--initial-advertise-peer-url影响。 |
--initial-cluster-state new | 用于指示本次是否为新建集群。有两个取值 new 或 existing。如果填existing,则该member启动时会尝试与其他member交互。集群初次简历时,要填为new,经尝试最后一个节点填existing也正常,其他节点不能填为existing。 |
-data-dir | 指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定-wal-dir,还会存储WAL文件;如果不指定会用缺省目录。 |
etcdkeeper
- 轻量级etcd Web客户端。
- 支持etcd 2.x和etcd3.x。
- 服务器使用etcd go客户端界面,并且服务器使用etcd客户端软件包进行编译。
- 基于easyui框架实现(easyui许可easyui网站)。
特征
- 客户端视图,添加,更新或删除节点。
- 内容编辑使用ace编辑器(Ace编辑器)。支持toml,ini,yaml,json,xml等高亮显示视图。
- 内容格式。(当前仅支持json,其他类型可以在以后扩展)
示例图:
Docker 部署
$ docker run -itd \
--name etcdkeeper \
--restart=always \
--net=host \
deltaprojects/etcdkeeper
默认etcdkeeper docker启动是连接本地的2379端口,所以直接使用的 host 模式启动即可,然后就可以通过 8080 端口直接创建文件配置 etcd 中的数据了。
**默认登入为 ETCD v3版本,在etcdv2中的数据是一个纯内存
的实现,在etcdv3中的key放在内存中,利用btree进行索引,而将真正的value存放在磁盘中。**
备份
环境配置好了,用起来了,毕竟是数据又需要考虑到备份这个话题上了,etcd 本身支持以快照的方式备份,但是由于主要是一些配置文件,都是通过 可视化etcdkeeper配置的,文件也都不大,所以并未采用快照的形式,考虑直接将文件取出来备份,修改错了需恢复时,直接查看一下该文件没问题直接重新写入即可。
那么问题来了:
1、由于前面清理 Harbor,以及 Elasticsearch 用习惯了使用 HTTP 请求直接处理,然后就百度了一会儿 etcd的HTTP API;
https://blog.csdn.net/meifannao789456/article/details/103480842
参考了这篇博客,但始终拿不到数据,仔细一看才发现用的 3.3.10 版本,得用 v3beta,但是却仍然获取不到数据!!!
然后搜索了一圈才找到一个答案:
v3和http接口的数据不在同一个存储区,用v2的api才会和http同一个存储区。
https://ask.csdn.net/questions/663853
2、然后决定发现python的etcd3库,确实通过这个可以得出所有的value,但是问题又来了!!!
通过他的 watch_prefix ,通过key的前缀来匹配匹配不出来!!! 啊啊啊啊
3、有点心灰意冷了,决定看一下命令etcdctl 直接写个 Shell 吧,结果没想到 etcdctl 做这样的操作实在太简单了,然后就写了个这样的备份脚本;
etcd-backup.sh
#!/bin/bash
set -u
day=`date +%Y%m%d`
day7=`expr $day - 7`
dir=/data/backup/etcd/${day}
name=`ETCDCTL_API=3 /usr/local/bin/etcdctl get / --prefix --keys-only`
num=0
for i in $name;
do
# 后缀
postfix=`echo ${i##*.}`
# 前缀
prefix=`echo ${i%/*}`
if [ ! -d $dir$prefix ]; then
mkdir -p $dir$prefix
fi
ETCDCTL_API=3 /usr/local/bin/etcdctl get $i | sed '1d' > $dir$i
let num+=1
done
echo "共完成备份文件${num}个。"
rm -rf /data/backup/etcd/${day7}
echo "7天前备份删除完成!"
etcd-rollback.sh
#!/bin/bash
set -u
read -p "请输入ETCD需要恢复的年月日(例:20200330):" day
data=`find /data/backup/etcd/${day} -name *.json`
for i in $data;
do
#echo $i
key=`echo $i | awk -F"$day" '{print $2}'`
#echo $key
#echo /usr/local/bin/etcdctl put $key "`cat $i`"
ETCDCTL_API=3 /usr/local/bin/etcdctl put $key "`cat $i`"
done
注意:
*本机上的 etcdctl 命令 是我通过github 下载tar包,自行移动到 /usr/local/bin 路径下去的,如用docker 启动宿主机是默认没有的。
由于使用的 v3 版本,etcdctl 命令我们需声明一下写入环境变量,或每次执行命令前加上 ETCDCTL_API=3
:*
$ vim /etc/profile
......
export ETCDCTL_API=3
$ source /etc/profile
操作etcdkeeper 创建文件的时候不用建目录,直接 /a/b/c.json 就可以,建目录会出现一个空的文件,毫无意义,而且我那个备份就会拉闸,因为不能在一个目录下出现两个名字相同的 文件 和目录。