入门篇:从 etcd 名字的由来,到安装部署和使用姿势

2022-07-14 08:36:45 浏览数 (1)

大家好,我是三十一[0],本次分享分布式神器etcd[1]的 5 种部署方式和 10 种使用姿势,阅读全文预计花费 15 分钟。

在学习 etcd 之前,我们先来聊一聊 etcd 名字的由来[2]。etcd 中的 etc 取自 unix 系统的/etc目录,再加上一个d代表distributed system就组成了 etcd。在 unix 系统中 /etc 目录用于存储系统的配置数据,单从名字看 etcd 可用于存储分布式系统的配置数据,有时候也把 etcd 简单理解为分布式 /etc 配置目录。

etcd 简介

etcd 是一个可靠的分布式 key-value 存储系统,主要用于配置共享服务注册和发现,具有以下特性:

  • 简单:基于 gRPC 定义了清晰、面向用户的 API。
  • 安全:支持可选的客户端 TLS 证书自动认证特性。
  • 快速:支持每秒 10000 次的写入。
  • 可靠:基于 Raft 算法协议保证一致性。

etcd 使用 Go 语言开发,底层基于 Raft 共识算法管理高可用的复制日志。当前已经被许多公司用于关键生产项目,比如:Kubernetes、locksmith、vulcand、Doorman 等。

当然,也有其他组件可以提供配置共享和服务注册和发现的功能,比如最为广泛和大家最为熟知的 Zookeeper,也被很多 Java 系的知名开源项目认可和使用,比如:Hadoop、HBase、Kafka 等。

但 etcd 是唯一一个可以媲美甚至超越 Zookeeper 的组件。

相较之下,Zookeeper 有如下缺点[3]:

  • 复杂:Zookeeper 基于 ZAB 协议,属于类 Paxos 协议,而 Paxos 算法素以复杂难懂闻名;Zookeeper 的使用也比较复杂,需要安装客户端,而官方目前只提供了 Java 和 C 两种语言接口。
  • 发展慢:由于基金会庞大的结构以及松散的管理,导致项目发展缓慢。

而 etcd 作为后起之秀,其优点也很明显:

  • 简单:使用 Go 语言编写部署简单;使用 gRPC 定义接口,支持跨语言、跨平台特性;使用了易于用户理解的 Raft 算法保证一致性,优于 Paxos 算法。
  • 发展快:etcd 正处于高速迭代开发中。
  • 性能优越:官方提供的基准测试数据中,etcd 集群可以支持每秒 10000 次的写入,性能优于 Zookeeper。
  • 安全性:etcd 支持 TLS 访问,而 ZooKeeper 在权限控制方面做得略显粗糙。

环境搭建

一、本地安装包部署

「下载」:下载最新的安装包(当前最新:v3.5.4),下载地址:https://github.com/etcd-io/etcd/releases/

「安装」:在解压后的文件目录下etcdetcdctl分别为安装包和客户端的编译后的执行文件,可使用三种方法进行运行配置。

  • 方法一:解压目录下直接运行
  • 方法二:把etcdetcdctl文件复制到GOBIN目录下。
  • 方法三:在环境变量里添加etcdetcdctl文件所在的目录。

注:运行过程中,可能会涉及权限问题,授权即可。

「验证」

代码语言:javascript复制
# 验证 etcd 版本
$ etcd --version

etcd Version: 3.5.4
Git SHA: 08407ff76
Go Version: go1.16.15
Go OS/Arch: darwin/amd64

# 验证 etcdctl 版本
$ etcdctl version

etcdctl version: 3.5.4
API version: 3.5

二、本地编译部署

「下载」:使用以下命令克隆代码

代码语言:javascript复制
# 下载最新版
$ git clone https://github.com/etcd-io/etcd.git
# 指定版本下载
$ git clone -b v3.5.4 https://github.com/etcd-io/etcd.git

「编译安装」

代码语言:javascript复制
# 编译
$ cd etcd
$ make build

# 安装
$ export PATH="$PATH:`pwd`/bin"

「验证」

代码语言:javascript复制
# 验证 etcd 版本
$ etcd --version

# 验证 etcdctl 版本
$ etcdctl version

# 验证 etcdutl 版本
$ etcdutl version

三、本地集群部署

首先需要安装goreman组件,它基于 Procfile 配置文件管理 etcd 应用进程。

代码语言:javascript复制
$ go install github.com/mattn/goreman@latest

「启动集群」:源码目录下Procfile脚本已经构建好了本地演示集群,直接运行启动即可

代码语言:javascript复制
$ goreman start

「验证」

代码语言:javascript复制
$ etcdctl member list

8211f1d0f64f3269, started, infra1, http://127.0.0.1:12380, http://127.0.0.1:2379, false
91bc3c398fb3c146, started, infra2, http://127.0.0.1:22380, http://127.0.0.1:22379, false
fd422379fda50e48, started, infra3, http://127.0.0.1:32380, http://127.0.0.1:32379, false

该脚本创建包含 3 个 etcd 成员节点的集群,每个集群成员都接收键值的读取和写入。也可以按照Procfile.learner脚本指导,学习集群新增节点的操作。

四、Docker 单机部署

此处通过docker-compose进行实验配置。

「镜像拉取」

代码语言:javascript复制
$ docker pull bitnami/etcd:3.5.2

「编辑docker-compose.yml」

代码语言:javascript复制
version: '3.5'

services:
  etcd:
    container_name: builder-etcd
    image: bitnami/etcd:3.5.2
    ports:
      - 2379:2379
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1002
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
    volumes:
      - ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data:/bitnami/etcd

networks:
  default:
    name: builder_dev

「启动服务」

代码语言:javascript复制
$ docker-compose -f docker-compose.yml up

「验证」:验证集群节点的版本

代码语言:javascript复制
$ docker exec -it builder-etcd /bin/bash -c "etcd --version"

etcd Version: 3.5.2
Git SHA: 99018a77b
Go Version: go1.16.3
Go OS/Arch: linux/amd64

五、Docker 集群部署

「编辑docker-compose.yml」

代码语言:javascript复制
version: '3.5'

services:
  etcd1:
    container_name: builder-etcd1
    image: bitnami/etcd:3.5.2
    ports:
      - 12379:2379
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1002
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_NAME=etcd1
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd1:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd1:2379
      - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
    volumes:
      - ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data1:/bitnami/etcd

  etcd2:
    container_name: builder-etcd2
    image: bitnami/etcd:3.5.2
    ports:
      - 22379:2379
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1002
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_NAME=etcd2
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd2:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd2:2379
      - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
    volumes:
      - ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data2:/bitnami/etcd

  etcd3:
    container_name: builder-etcd3
    image: bitnami/etcd:3.5.2
    ports:
      - 32379:2379
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1002
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_NAME=etcd3
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd3:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd3:2379
      - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
    volumes:
      - ${DOCKER_ROOT_DIR:-.}/volumes/etcd/data3:/bitnami/etcd

networks:
  default:
    name: builder_dev

部署配置文件 docker-compose.yml 细节,详见etcd docker-compose yml[4]

「启动服务」

代码语言:javascript复制
$ docker-compose -f docker-compose.yml up

「验证」:验证集群节点的版本

代码语言:javascript复制
$ docker exec -it builder-etcd1 /bin/bash -c "etcd --version"
$ docker exec -it builder-etcd2 /bin/bash -c "etcd --version"
$ docker exec -it builder-etcd3 /bin/bash -c "etcd --version"

# 输出
etcd Version: 3.5.2
Git SHA: 99018a77b
Go Version: go1.16.3
Go OS/Arch: linux/amd64

API 学习

此处,通过使用etcdctl[5]进行 API 学习验证,etcdctl 是一个用于与 etcd 服务器交互的命令行工具。

「1.查看版本」

代码语言:javascript复制
$ etcdctl version

etcdctl version: 3.6.0-alpha.0
API version: 3.6

「2.写入 key」

代码语言:javascript复制
$ etcdctl put foo bar
OK

「3.读取 key」

代码语言:javascript复制
$ etcdctl get foo
foo
bar
# 只是获取值
$ etcdctl get foo --print-value-only
bar

「4.批量取值」

代码语言:javascript复制
$ etcdctl put foo1 bar1
$ etcdctl put foo3 bar2
$ etcdctl put foo3 bar3

# 获取从 foo 到 foo3 的值,不包括 foo3
$ etcdctl get foo foo3 --print-value-only 
bar
bar1
bar2

# 获取前缀为 foo 的值
$ etcdctl get --prefix foo --print-value-only
bar
bar1
bar2
bar3

# 获取符合前缀的前两个值
$ etcdctl get --prefix --limit=2 foo --print-value-only
bar
bar1

「5.删除 key」

代码语言:javascript复制
# 删除 foo 的值
$ etcdctl del foo
1

# 删除 foo 到 foo2 且不包括 foo2 的值
$ etcdctl del foo foo2
1
# 删除前缀为 foo 的所有值
$ etcdctl del --prefix foo
2

「6.监听」

代码语言:javascript复制
# 监听 foo 单个 key
$ etcdctl watch foo
# 另一个控制台执行:etcdctl put foo bar
PUT
foo
bar

# 同时监视多个值
$ etcdctl watch -i
$ watch foo
$ watch zoo
# 另一个控制台执行: etcdctl put foo bar
PUT
foo
bar

# 另一个控制台执行: etcdctl put zoo val
PUT
zoo
val

# 监视 foo 前缀命中的 key
$ etcdctl watch --prefix foo
# 另一个控制台执行:etcdctl put foo1 bar1
PUT
foo1
bar1
# 另一个控制台执行:etcdctl put fooz1 barz1
PUT
fooz1
barz1

「7.设置租约」当一个 key 被绑定到一个租约上时,它的生命周期与租约的生命周期即绑定。

代码语言:javascript复制
# 设置10秒后过期时间
$ etcdctl lease grant 10
lease 32698142c52a170a granted with TTL(10s)

# 把 foo 和租约绑定,设置成 10 秒后过期
$ etcdctl put --lease=32698142c52a170a foo bar
OK
$ etcdctl get foo
foo
bar

# 10 秒后,获取不到 foo
$ etcdctl get foo
# 返回空

「8.撤销租约」通过租约 ID 撤销租约,撤销租约将删除其所有绑定的 key。

代码语言:javascript复制
$ etcdctl lease grant 10
lease 32698142c52a170c granted with TTL(10s)
$ etcdctl put --lease=32698142c52a170c foo bar
OK

# 撤销租约
$ etcdctl lease revoke 32698142c52a170c
lease 32698142c52a170c revoked

$ etcdctl get foo
# 返回空

「9.续约」通过刷新 TTL 值来保持租约的有效,使其不会过期。

代码语言:javascript复制
# 设置 10 秒后过期租约
$ etcdctl lease grant 10
lease 32698142c52a170f granted with TTL(10s)

# 把 foo 和租约绑定,设置成 10 秒后过期
$ etcdctl put foo bar --lease=32698142c52a170f

# 自动定时执行续约,续约成功后每次租约为 10 秒
$ etcdctl lease keep-alive 32698142c52a170f
lease 32695410dcc0ca06 keepalived with TTL(10)
lease 32695410dcc0ca06 keepalived with TTL(10)
lease 32695410dcc0ca06 keepalived with TTL(10)
...

「10.查看租约」查看租约信息,以便续租或查看租约是否仍然存在或已过期

代码语言:javascript复制
# 设置 50 秒 TTL
$ etcdctl lease grant 50
lease 32698142c52a1711 granted with TTL(50s)

# zoo1 绑定 32698142c52a1711 租约
$ etcdctl put --lease=32698142c52a1711 zoo1 val1
OK

# 查看租约,remaining(32s) 剩余有效时间32秒;--keys 获取租约绑定的 key
$ etcdctl lease timetolive --keys 32698142c52a1711
lease 32698142c52a1711 granted with TTL(50s), remaining(32s), attached keys([zoo1])

注:一个租约支持绑定多个 key

代码语言:javascript复制
$ etcdctl lease grant 50
lease 32698142c52a1713 granted with TTL(50s)

$ etcdctl put --lease=32698142c52a1713 zoo1 val1
OK

$ etcdctl put --lease=32698142c52a1713 zoo2 val2
OK

$ etcdctl put --lease=32698142c52a1713 zoo3 val3
OK

租约过期后,所有 key 值都会被删除,因此:

  • 当租约只绑定了一个 key 时,想删除这个 key,最好的办法是撤销它的租约,而不是直接删除这个 key。
  • 当租约没有绑定key时,应主动把它撤销掉,单纯删除 key 后,续约操作持续进行,会造成内存泄露。
代码语言:javascript复制
# 方法一:直接删除`key`
# 设置租约并绑定 zoo1
$ etcdctl lease grant 50
lease 32698142c52a1715 granted with TTL(50s)
$ etcdctl --lease=32698142c52a1715 put zoo1 val1
OK

# 续约
$ etcdctl lease keep-alive 32698142c52a1715
lease 32698142c52a1715 keepalived with TTL(50)

# 另一个控制台执行:etcdctl del zoo1
# 单纯删除 key 后,续约操作持续进行,会造成内存泄露
lease 32698142c52a1715 keepalived with TTL(50)
lease 32698142c52a1715 keepalived with TTL(50)
lease 32698142c52a1715 keepalived with TTL(50)
...

# 方法二:撤销`key`的租约
# 设置租约并绑定 zoo1
$ etcdctl lease grant 50
lease 32698142c52a1717 granted with TTL(50s)
$ etcdctl --lease=32698142c52a1717 put zoo1 val1
OK

# 续约
$ etcdctl lease keep-alive 32698142c52a1717
lease 32698142c52a1717 keepalived with TTL(50)
lease 32698142c52a1717 keepalived with TTL(50)

# 另一个控制台执行:etcdctl lease revoke 32698142c52a1717

# 续约撤销并退出
lease 32698142c52a1717 expired or revoked.
$ etcdctl get zoo1
# 返回空

以上,就是今天的全部内容,几乎包含了 etcd 安装和使用的各种姿势,欢迎各位号友敬请尝试。

References

  • [0] 三十一: http://www.lee31.cn/assets/image/ThirtyOneLee.jpeg
  • [1] etcd: https://github.com/etcd-io/etcd
  • [2] etcd 名字的由来: https://etcd.io/docs/v3.5/faq/
  • [3] https://www.infoq.cn/article/etcd-interpretation-application-scenario-implement-principle/
  • [4] etcd docker-compose yml: https://github.com/liyaodev/docker-compose
  • [5] etcdctl: https://etcd.io/docs/v3.5/dev-guide/interacting_v3/

0 人点赞