技术流ken

运维拯救世界

Docker: Etcd高可用的键值数据库(十三)–技术流ken

Etcd 简介

Etcd CoreOS 团队于 2013 年6月发起的开源项目,它的目标是构建一个高可用的分布式键值( key-value )仓库,遵循 Apache v2许可,基于 Go 语言实现,接触过分布式系统的读者应该知道,分布式系统中最基本的问题之一就是实现信息的共识,在此基础上才能实现对服务配置信息的管理、服务的发现、更新、同步,等等 而要解决这些问题,往往需要利用一套能保证一致性的分布式数据库系统,比如经典的 Apache ZooKeeper 项目 ,采用了 Paxos 算法来实现数据的强一致性。

Etcd 专门为集群环境设计,采用了更为简洁的 Raft 共识算法,Raft 是一套通过选举主节点来实现分布式系统一致性的算法,同样可以实现数据强一致性,并支持集群节点状态管理和服务自动发现等

 

Etcd 在进行设计的时候重点考虑了下面四个要素:

口简单:支持 RESTfulAPI和gRPCAPI;

口安全: 基于 TLS 式实 安全连接访

口快速: 支持每 一万次 并发写操作,超时控制在毫秒量级

口可靠: 支持分布式结构 基于 Raft 算法实现一致性

通常情况下,用户使用 Etcd 可以在多个节点上启动多个实例,并将它们添加为 个集群。同一个集群中的 Etcd 实例将会自动保持彼此信息的一致性,这意味着分布在各个节点上的应用也将获取到一致的信息。

 

 

安装和使用 Etcd

 

第一步:下载

https://github.com/etcd-io/etcd/releases

 

第二步:解压

[root@ken2 ~]# tar xf etcd-v3.3.1-linux-amd64.tar.gz 
[root@ken2 ~]# cp etcd-v3.3.1-linux-amd64/etcd* /usr/local/bin

 


其中 etcd 是服务主文件, etcdctl 是提供给用户的命令客户端,其他都是文档文件

 

使用etcd

 

下面将先以单节点 式为例讲解 Etcd 支持的功能和操作

1.可通过如下命令查看 etcd 的版本信息:

[root@ken2 ~]# etcdctl --version
etcdctl version: 3.3.1
API version: 2

 


2.接下来,直接执行 Etcd 命令,将启动 个服务节点,监昕在本地的 2379 (客户端端口)和 2380 (其他节点连 端口

显示类似如下的信息:

[root@ken2 ~]# etcd
[WARNING] Deprecated '--logger=capnslog' flag is set; use '--logger=zap' flag instead
2019-09-10 14:24:00.393171 I | etcdmain: etcd Version: 3.4.0
2019-09-10 14:24:00.393235 I | etcdmain: Git SHA: 898bd1351
2019-09-10 14:24:00.393242 I | etcdmain: Go Version: go1.12.9
2019-09-10 14:24:00.393253 I | etcdmain: Go OS/Arch: linux/amd64
2019-09-10 14:24:00.393262 I | etcdmain: setting maximum number of CPUs to 4, total number of available CPUs is 4
2019-09-10 14:24:00.393273 W | etcdmain: no data-dir provided, using default data-dir ./default.etcd
[WARNING] Deprecated '--logger=capnslog' flag is set; use '--logger=zap' flag instead
2019-09-10 14:24:00.492487 I | embed: name = default
2019-09-10 14:24:00.492507 I | embed: data dir = default.etcd
2019-09-10 14:24:00.492513 I | embed: member dir = default.etcd/member
2019-09-10 14:24:00.492518 I | embed: heartbeat = 100ms
2019-09-10 14:24:00.492522 I | embed: election = 1000ms
2019-09-10 14:24:00.492526 I | embed: snapshot count = 100000
2019-09-10 14:24:00.492540 I | embed: advertise client URLs = http://localhost:2379
2019-09-10 14:24:00.500959 I | etcdserver: starting member 8e9e05c52164694d in cluster cdf818194e3a8c32
raft2019/09/10 14:24:00 INFO: 8e9e05c52164694d switched to configuration voters=()
raft2019/09/10 14:24:00 INFO: 8e9e05c52164694d became follower at term 0
raft2019/09/10 14:24:00 INFO: newRaft 8e9e05c52164694d [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0]
raft2019/09/10 14:24:00 INFO: 8e9e05c52164694d became follower at term 1
raft2019/09/10 14:24:00 INFO: 8e9e05c52164694d switched to configuration voters=(10276657743932975437)
2019-09-10 14:24:00.512116 W | auth: simple token is not cryptographically signed
2019-09-10 14:24:00.514290 I | etcdserver: starting server... [version: 3.4.0, cluster version: to_be_decided]
2019-09-10 14:24:00.516603 I | etcdserver: 8e9e05c52164694d as single-node; fast-forwarding 9 ticks (election ticks 10)
raft2019/09/10 14:24:00 INFO: 8e9e05c52164694d switched to configuration voters=(10276657743932975437)
2019-09-10 14:24:00.524693 I | etcdserver/membership: added member 8e9e05c52164694d [http://localhost:2380] to cluster cdf818194e3a8c32
2019-09-10 14:24:00.529322 I | embed: listening for peers on 127.0.0.1:2380
raft2019/09/10 14:24:01 INFO: 8e9e05c52164694d is starting a new election at term 1
raft2019/09/10 14:24:01 INFO: 8e9e05c52164694d became candidate at term 2
raft2019/09/10 14:24:01 INFO: 8e9e05c52164694d received MsgVoteResp from 8e9e05c52164694d at term 2
raft2019/09/10 14:24:01 INFO: 8e9e05c52164694d became leader at term 2
raft2019/09/10 14:24:01 INFO: raft.node: 8e9e05c52164694d elected leader 8e9e05c52164694d at term 2
2019-09-10 14:24:01.307817 I | etcdserver: setting up the initial cluster version to 3.4
2019-09-10 14:24:01.310805 I | etcdserver: published {Name:default ClientURLs:[http://localhost:2379]} to cluster cdf818194e3a8c32
2019-09-10 14:24:01.333900 I | embed: ready to serve client requests
2019-09-10 14:24:01.334870 N | etcdserver/membership: set the initial cluster version to 3.4
2019-09-10 14:24:01.335022 I | etcdserver/api: enabled capabilities for version 3.4
2019-09-10 14:24:01.353146 N | embed: serving insecure client requests on 127.0.0.1:2379, this is strongly discouraged!

 

 

此时,可以通过 REST API 直接查看集群健康状态:

[root@ken2 ~]# curl -L 127.0.0.1:2379/health
{"health":"true"}

 

当然,也可以使用自带的 etcdctl 命令进行查看(实际上是封装了REST API 调用):


[root@ken1 ~]# etcdctl cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://localhost:2379
cluster is healthy

 

3.通过 etcdctl 设置和获取值也十分方便,例如设置键值对name : ken


[root@ken1 ~]# etcdctl set name ken
ken
[root@ken1 ~]# etcdctl get name
ken

 

说明键值对已经设 成功了

 

数据目录

 

作为数据库,最重要的自 然是数据存放位置 Etcd 认创建的本地数据目录为 ${name}.etcd ,其中${ name }为节点别名 默认情况下本地数据路径为 default.etcd,

[root@ken1 ~]# ls
192.168.64.6  anaconda-ks.cfg  default.etcd  etcd-v3.3.1-linux-amd64  etcd-v3.3.1-linux-amd64.tar.gz  n1.etcd
[root@ken1 ~]# tree default.etcd/
default.etcd/
└── member
    ├── snap
    │   └── db
    └── wal
        ├── 0000000000000000-0000000000000000.wal
        └── 0.tmp

3 directories, 3 files

 

其中, snap 目录下将定期记录节点的状态快照信息, wal 目录下则记录数据库的操作日志信息、(可以通过 wal-dir 参数来指定存放到特定目录)

用户也可以通过一data-dir 选项来指定本地数据存放的位置

 

 

 

 

使用客户端命令

 

etcdctl是Etcd官方提供的命令行客户端, 它支持一些基于 HTTP API 封装好的命令,供用户直接跟Etcd服务打交道, 而无须基千 API 的方式。 当然, 这些命令跟API 实际上是对应的, 最终效果上并无不同之处。某些情况下使用etcdctl十分方便。 例如用户需要对Etcd服务进行简单测试或者手动来修改数据库少量内容;也推荐在刚接触Etcd时通过etcdctl命令来熟悉服务相关功能。Etcd项目二进制发行包中已经包含了etcdctl.

 

etcdctl的命令格式为:

etcdctl  [全局选项]命令[命令选项] [命令参数]

 

全局选项参数如下:

 

支持的命令大体上分为:数据类操作和非数据类操作

 

非数据类操作命令见下, 主要是 Etcd 提供的系统配置、 权限管理等

 

 

下面分别来看各个操作的主要用法和功能

数据类操作

数据类操作围绕对键值和目录的CRUD (符合REST风格的一套操作:Create)完整生命周期的管理。Etcd在键的组织上十分灵活。用户指定的键可以为只有一级的名字,如name, 此时实际上都直接放在根目录/下面,也可以为指定层次化目录结构(类似于ZooKeeper), 如:clusster1/node2/ name, 则将创建相应的目录结构。

CRUD即Create, Read, Update, Delete , 是符合REST风格的一套API操作规范。

 

 

1.设置某个键的值为给定值


[root@ken1 ~]# etcdctl set age 18
18

 

2.获取指定键的值

[root@ken1 ~]# etcdctl get age
18

 

3.更新键值

[root@ken1 ~]# etcdctl update age 20
20
[root@ken1 ~]# etcdctl get age
20

 

4.mk 如果给定的键不存在, 则创建一个新的键值。

[root@ken1 ~]# etcdctl mk address jiangsu
jiangsu
[root@ken1 ~]# etcdctl get address
jiangsu

 

5.rm删除某个键值

[root@ken1 ~]# etcdctl rm address
PrevNode.Value: jiangsu
[root@ken1 ~]# etcdctl get address
Error:  100: Key not found (/address) [8]

 

 

6.watch监测一个键值的变化, 一旦键值发生更新, 就会输出最新的值并退出。

[root@ken1 ~]# etcdctl watch name

ken1

 

7.exec-watch

监测一个键值的变化, 一旦键值发生更新, 就执行给定命令。 这个功能十分强大, 很多时候可以用于实时根据键值更新本地服务的配置信息, 并重新加载服务。 可以实现分布式应用配置的自动分发。

例如, 一旦检测到键值被更新, 则执行ls命令:

[root@ken1 ~]# etcdctl exec-watch name -- sh -c "ls"
anaconda-ks.cfg  default.etcd  etcd-v3.3.1-linux-amd64	etcd-v3.3.1-linux-amd64.tar.gz

 

8.1s

列出目录(默认为根目录)下的键或者子目录, 默认不显示子目录中内容。 例如:

[root@ken1 ~]# etcdctl set key hi
hi
[root@ken1 ~]# etcdctl set ken/key hi1
hi1
[root@ken1 ~]# etcdctl ls
/name
/age
/key
/ken
[root@ken1 ~]# etcdctl ls ken
/ken/key

 

9.mkdir

如果给定的键目录不存在, 则创建一个新的键目录。 例如:

[root@ken1 ~]# etcdctl mkdir test
[root@ken1 ~]# etcdctl ls 
/name
/age
/key
/ken
/test

 

10.rmdir

删除一个空目录, 或者键值对。若目录不空, 会报错, 例如:

[root@ken1 ~]# etcdctl rmdir test
[root@ken1 ~]# etcdctl rmdir ken
Error:  108: Directory not empty (/ken) [14]

 

 

11.setdir

创建一个键目录, 无论存在与否。 实际上, 目前版本当目录已经存在的时候会报错,

例如:

[root@ken1 ~]# etcdctl setdir test
[root@ken1 ~]# etcdctl ls 
/name
/age
/key
/ken
/test

 

12.updatedir

更新一个已经存在的目录的属性(目前只有存活时间), 例如:

[root@ken1 ~]# etcdctl mkdir test1 --ttl 100
[root@ken1 ~]# etcdctl updatedir test1 --ttl 200

支待的选项为—ttlvalue: 超时时间(单位为秒),默认值为0, 意味着永不超时。

 

非数据类操作

 

非数据类操作不直接对数据本身进行管理, 而是负责围绕集群自身的一些配置。

 

1.cluster-health

查看Etcd集群的健康状态。 例如:

[root@ken1 ~]# etcdctl cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://localhost:2379
cluster is healthy

 

2.member

通过list、add、remove等子命令列出、 添加、 删除Etcd实例到Etcd集群中。 例如, 本

地启动一个Etcd服务实例后, 可以用如下命令进行查看 默认的实例成员:

[root@ken1 ~]# etcdctl member list
8e9e05c52164694d: name=default peerURLs=http://localhost:2380 clientURLs=http://localhost:2379 isLeader=true

 

3.user

对用户进行管理, 包括一系列子命令:

add: 添加一个用户;

get:查询用户细节;

list: 列出所有用户;

remove: 删除用户;

grant:添加用户到角色;

revoke: 删除用户的角色;

passwd: 修改用户的密码。

默认情况下, 需要先创建(启用) root用户作为etcd集群的最高权限管理员:

会要求输入密码

[root@ken1 ~]# etcdctl user add root
New password: 
User root created

分配某些已有角色给用户

[root@ken1 ~]# etcdctl user grant root --roles root
auth: Granting duplicate role root for user root

 

 

 

4.role

对用户角色进行管理,包括一系列子命令:

add :添加一个角色;

get :查询角色细节;

list 列出所有用户角色;

remove :删除用户角色;

grant 添加路径到角色控制,可以为 read write 或者 readwrite;

revoke 删除某路径的用户角色信息

默认带有 root,guest 两种角色,前者为全局最高权限,后者为不带验证情况下的用户

例如

[root@ken1 ~]# etcdctl role add testrole
Role testrole created
[root@ken1 ~]# etcdctl role list
root
testrole
[root@ken1 ~]# etcdctl role grant testrole --path='/ken/*' --rw
Role testrole updated

 

5.auth

是否启用访问验证 enable 为启用, disable 禁用。例如,在 root 用户创建后,启用

认证:

[root@ken1 ~]# etcdctl auth enable
Authentication Enabled

 

 

Etcd 集群管理

tcd 集群也采用了典型的“主一从”模型,通过 Raft 协议来保证在 段时间内有一个节点为主节点 ,其 节点为从节点 一旦主节点发生故障其他节点可以自动再重新选举出新的主节点。与其他分布式系统类似,集群中节点个数推荐为奇数个,最少为3个,此时 quorum为2,越多节点个数自然能提供更多的冗余’性 ,但同时会带来写数据性能的下降

在分布式系统中有一个很重要的概念 quorum ,意味着一个集群正常工作需要能参加投票的节点个数的最小值

 

构建集群

构建集群无非是让节点们知道自己加入了哪个集群,其他对等节点的访问信息是啥。

Etcd 支持两种模式来构建集群:静态配 和动态发现

.静态配置集群信息

顾名思义, 静态配置就是提取写好集群中的有关信息。接下来演示使用三个节点创建集群

 

 

第一步:首先在各个 点上将地址和别名信息添加到 etc/hosts

[root@ken1 ~]# cat /etc/hosts
localhost 127.0.0.1
ken1 192.168.64.5
ken2 192.168.64.6
ken3 192.168.64.7

 

第二步:需要在每个节点安装etcd

 

第三步:可以通过如下命令来启动各个节点上 etcd 服务 ,分别命名为 nl n2 n3

在节点1上,执行如下命令:

[root@ken1 ~]# etcd --name n1  --initial-cluster-token cluster1 --initial-cluster-state new  --listen-client-urls http://192.168.64.5:2379,http://127.0.0.1:2379 --listen-peer-urls http://192.168.64.5:2380 --advertise-client-urls http://192.168.64.5:2379  --initial-advertise-peer-urls http://ken1:2380 --initial-cluster n1=http://ken1:2380,n2=http://ken2:2380,n3=http://ken3:2380

 

参数分析
-name 节点名称
-initial-advertise-peer-urls 通知其他 Etcd 实例地址
-listen-peer-urls 监听其他 Etcd 实例的地址
-initial-cluster-token 初始化集群 token
-initial-cluster 初始化集群内节点地址
-initial-cluster-state 初始化集群状态,new 表示新建

 

在节点2上执行如下的命令:

[root@ken2 ~]# etcd --name n2  --initial-cluster-token cluster1 --initial-cluster-state new  --listen-client-urls http://192.168.64.6:2379,http://127.0.0.1:2379 --listen-peer-urls http://192.168.64.6:2380 --advertise-client-urls http://192.168.64.6:2379  --initial-advertise-peer-urls http://ken2:2380 --initial-cluster n1=http://ken1:2380,n2=http://ken2:2380,n3=http://ken3:2380

 

在节点3上执行如下的命令:

[root@ken3 ~]# etcd --name n3  --initial-cluster-token cluster1 --initial-cluster-state new  --listen-client-urls http://192.168.64.7:2379,http://127.0.0.1:2379 --listen-peer-urls http://192.168.64.7:2380 --advertise-client-urls http://192.168.64.7:2379  --initial-advertise-peer-urls http://ken3:2380 --initial-cluster n1=http://ken1:2380,n2=http://ken2:2380,n3=http://ken3:2380

 

成功后,可以在任一节点上通过 et cdctl 来查看当前集群中的成员信息:

[root@ken1 ~]# etcdctl member list
906a78458065a88a: name=n2 peerURLs=http://ken2:2380 clientURLs=http://192.168.64.6:2379 isLeader=false
c41dc5eb2046fd27: name=n3 peerURLs=http://ken3:2380 clientURLs=http://192.168.64.7:2379 isLeader=true
c7ce0761a258afbd: name=n1 peerURLs=http://ken1:2380 clientURLs=http://192.168.64.5:2379 isLeader=false

 

测试:在任意一台主机创建键值

[root@ken1 ~]# etcdctl set name1 ken1
ken1

 

可以在另外节点查看该键值

[root@ken2 ~]# etcdctl get name1
ken1

 

 

[root@ken1 ~]# etcdctl member list
906a78458065a88a: name=n2 peerURLs=http://ken2:2380 clientURLs=http://192.168.64.6:2379 isLeader=true
c41dc5eb2046fd27: name=n3 peerURLs=http://ken3:2380 clientURLs=http://192.168.64.7:2379 isLeader=false
c7ce0761a258afbd: name=n1 peerURLs=http://ken1:2380 clientURLs=http://192.168.64.5:2379 isLeader=false

当前主机点为ken2

把ken2集群退掉

可以发现主机点已经切换为ken3

[root@ken1 ~]# etcdctl member list
906a78458065a88a: name=n2 peerURLs=http://ken2:2380 clientURLs=http://192.168.64.6:2379 isLeader=false
c41dc5eb2046fd27: name=n3 peerURLs=http://ken3:2380 clientURLs=http://192.168.64.7:2379 isLeader=true
c7ce0761a258afbd: name=n1 peerURLs=http://ken1:2380 clientURLs=http://192.168.64.5:2379 isLeader=false

 

发表评论

电子邮件地址不会被公开。