深入理解 etcd

深入理解 etcd:一个分布式键值存储系统

好的,根据您提供的内容,我整理了一篇关于 Kubernetes 控制平面组件 etcd 的技术博客,内容如下:

深入理解 Kubernetes 基石:etcd 详解

作者: 孟凡杰(前 eBay 资深架构师)

引言

在 Kubernetes(K8s)集群中,etcd 扮演着至关重要的角色。它是整个集群的“大脑”,负责存储集群状态、配置信息以及进行服务发现。本文将深入探讨 etcd 的核心概念、功能、使用场景、Raft 协议、存储机制、高可用方案以及在 Kubernetes 中的应用,帮助您全面理解这一关键组件。

什么是 etcd?

etcd 是由 CoreOS(现已被 Red Hat 收购)开发的一个开源、分布式、高可用的键值存储系统。它基于 Raft 一致性算法,确保数据在多个节点之间保持一致性,即使在部分节点故障的情况下也能正常运行。

etcd 的主要特点:

  • 键值对存储: 数据以键值对的形式存储在分层目录结构中,类似于文件系统。
  • 变更监测: 可以监测特定键或目录的变化,并在发生变化时做出反应。
  • 简单易用: 提供 HTTP+JSON 的 API,易于使用 curl 等工具进行访问。
  • 安全可靠: 支持 SSL 客户端证书认证,使用 Raft 算法保证数据一致性。
  • 高性能: 单实例每秒可处理数千次读写操作。

etcd 的核心功能

etcd 主要提供以下功能:

  1. 基本的键值存储: 存储和检索数据。
  2. 监听机制: 监听键或目录的变化,实现实时通知。
  3. 键的过期及续约: 用于监控和服务发现,确保服务的可用性。
  4. 原子 Compare And Swap (CAS) 和 Compare And Delete (CAD): 用于实现分布式锁和领导者选举。

etcd 的典型应用场景

etcd 在分布式系统中有着广泛的应用,常见的场景包括:

  1. 服务注册与发现: 服务提供者将自己的信息注册到 etcd,服务消费者通过 etcd 发现可用的服务实例。
  2. 消息发布与订阅: 构建配置共享中心,实现配置的集中管理和动态更新。
  3. 分布式锁: 利用 CAS 或 CAD 操作实现分布式锁,协调多个进程对共享资源的访问。
  4. 领导者选举: 在分布式系统中选举出一个领导者,负责协调和管理其他节点。
  5. 键值对存储: 作为数据库存储集群数据。

服务注册与发现示例(Mermaid 流程图):

graph LR
    subgraph 服务注册
        A[服务提供者] --> B(向 etcd 注册服务)
    end

    subgraph 服务发现
        C[服务消费者] --> D(从 etcd 查询服务)
    end

    B --> E[etcd]
    D --> E

    subgraph 服务绑定
        F[服务消费者] --> G[服务提供者]
    end

消息发布与订阅示例(Mermaid 流程图):

graph LR
    subgraph 消息发布
        A[生产者] --> B(向 etcd 发布消息)
    end

    subgraph 消息订阅
        C[服务消费者] --> D(从 etcd 订阅消息)
    end

    B --> E[etcd]
    D --> E

etcd 的安装与使用

etcd 的安装非常简单,可以从 GitHub Releases 页面下载预编译的二进制文件,解压后即可运行。

安装步骤(示例):

1
2
3
4
5
6
7
ETCD_VER=v3.4.17
DOWNLOAD_URL=https://github.com/etcd-io/etcd/releases/download
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test
curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz

常用客户端工具:

  • etcdctl: 命令行客户端工具,用于与 etcd 集群交互。
  • go-etcd: Go 语言客户端库。
  • jetcd: Java 客户端库。
  • python-etcd: Python 客户端库。

基本数据读写操作(etcdctl 示例):

1
2
3
4
5
6
7
8
9
10
11
# 写入数据
etcdctl --endpoints=localhost:12379 put /a b

# 读取数据
etcdctl --endpoints=localhost:12379 get /a

# 按 key 的前缀查询数据
etcdctl --endpoints=localhost:12379 get --prefix /

# 只显示键值
etcdctl --endpoints=localhost:12379 get --prefix / --keys-only --debug

深入理解 Raft 一致性协议

etcd 的数据一致性是基于 Raft 协议实现的。Raft 是一种易于理解的分布式一致性算法,它将一致性问题分解为几个子问题:

  1. 领导者选举(Leader Election): 初始启动或领导者失效时,集群中的节点会通过投票选举出一个新的领导者。
  2. 日志复制(Log Replication): 领导者接收客户端的请求,并将请求作为日志条目复制到其他节点。
  3. 安全性(Safety): 确保每个节点都执行相同序列的命令,保证数据的一致性。

Raft 协议角色:

  • Leader(领导者): 负责接收客户端请求、日志复制和向 Follower 发送心跳。
  • Follower(跟随者): 接收 Leader 的日志并复制到本地,参与投票选举 Leader。
  • Candidate(候选者): 在 Leader 选举过程中,Follower 会转变为 Candidate,发起投票。
  • Learner: 新加入的节点,只接收数据而不参与投票,因此增加
    learner节点时,集群的quorum不变。

Raft 协议流程(Mermaid 流程图):

graph LR
    subgraph 客户端请求
        A[客户端] --> B[Leader]
    end

    subgraph Leader 处理
        B --> C[将请求追加到本地日志]
        B --> D[通过心跳将日志同步给 Follower]
    end

    subgraph Follower 处理
        D --> E[Follower 接收日志并记录]
        E --> F[向 Leader 发送 ACK]
    end

    subgraph Leader 提交
        B -- 收到多数 Follower ACK --> G[将日志设置为已提交]
        G --> H[通知客户端]
        B -- 下次心跳 --> I[通知 Follower 提交日志]
    end

Raft 协议失效处理:

  1. Leader 失效: Follower 在超时时间内未收到 Leader 心跳,会发起选举。
  2. Follower 失效: 重新加入集群后,从 Leader 复制日志。
  3. 多个 Candidate: 随机等待一段时间后再次发起投票。

WAL 日志:

etcd 使用预写式日志(WAL)来记录所有的数据变更。WAL 日志是二进制格式的,包含以下字段:

  • type: 0 表示 Normal,1 表示 ConfChange(配置变更)。
  • term: 主节点任期,每次主节点变更时递增。
  • index: 变更序号,严格有序递增。
  • data: Raft 请求对象的 pb 结构。

etcd 的存储机制

etcd v3 的存储分为两部分:

  1. 内存索引(kvindex): 基于 Google 开源的 btree 实现,用于加速查询。
  2. 后端存储(backend): 目前使用 boltdb,一个单机的支持事务的 KV 存储。

etcd 在 boltdb 中存储的 key 是 reversion,value 是 etcd 自己的 key-value 组合,实现了多版本机制。

reversion: 由两部分组成:

  1. main rev: 每次事务进行加一。
  2. sub rev: 同一个事务中的每次操作加一。

存储机制示意图(Mermaid 流程图):

graph LR
    subgraph etcd v3 存储
        A[客户端请求] --> B[Leader]
        B --> C["一致性模块 (Raft)"]
        C --> D["预检查 (鉴权, 包大小等)"]
        C --> E["配额, 限速, 选主, 日志复制"]
        E --> F["写 WAL 日志"]
        F --> G["写 raftLog (内存)"]
        G --> H["收到半数确认, 更新 MatchIndex, Apply"]
        H --> I["MVCC 模块"]
        I --> J["treeIndex (内存索引)"]
        I --> K["BoltDB (后端存储)"]
    end

etcd 的 Watch 机制

etcd v3 的 Watch 机制支持监听特定 key 或范围(模拟目录结构)。每个 WatchableStore 包含两种 watcherGroup:

  1. synced: watcher 数据已同步完毕,等待新的变更。
  2. unsynced: watcher 数据同步落后于当前最新变更,还在追赶。

etcd 会启动一个后台 goroutine 持续同步 unsynced 的 watcher,然后将其迁移到 synced 组。

etcd 的高可用方案

为了确保 etcd 集群的高可用性,通常采用以下方案:

  1. 多节点部署: 部署 3 个或 5 个 etcd 节点,形成集群。
  2. 故障转移: 当 Leader 节点故障时,其他节点会通过 Raft 协议选举出新的 Leader。
  3. 数据备份: 定期创建快照,并将快照上传到网络存储设备,以防数据丢失。

高可用 etcd 解决方案:

  • etcd-operator: CoreOS 开源的基于 Kubernetes CRD 的 etcd 集群配置工具(已归档)。
  • Etcd statefulset Helm chart: Bitnami 提供的 Helm chart,用于在 Kubernetes 上部署高可用 etcd 集群。

使用 Bitnami Helm chart 安装 etcd 高可用集群(示例):

1
2
3
4
5
6
# 安装 helm
# 参考 https://github.com/helm/helm/releases

# 通过 helm 安装 etcd
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-release bitnami/etcd

etcd 在 Kubernetes 中的应用

etcd 是 Kubernetes 的后端存储,用于存储集群的所有状态信息。

  • 对于每一个 Kubernetes Object,都有对应的 storage.go 文件负责对象的存储操作(例如 pkg/registry/core/pod/storage/storage.go)。
  • API Server 启动脚本中会指定 etcd servers 集群的地址。
  • 早期 API server 对 etcd 做简单的 Ping check,现在已经改为真实的 etcd api call。

Kubernetes 对象在 etcd 中的存储路径示例:

1
2
3
4
5
/registry/namespaces/calico-apiserver
/registry/networkpolicies/calico-apiserver/allow-apiserver
/registry/operator.tigera.io/tigerastatuses/apiserver
/registry/pods/calico-apiserver/calico-apiserver-77dffffcdf-g2tcx
/registry/pods/default/toolbox-68f79dd5f8-4664n

Kubernetes 集群中 etcd 的部署拓扑:

  1. 堆叠式(Stacked): 控制平面和 etcd 成员位于同一节点。

    • 优点:易于建立和管理。
    • 缺点:存在耦合失败的风险。
  2. 外部(External): 控制平面和 etcd 成员解耦。

    • 优点:降低耦合失败的风险。
    • 缺点:需要更多的主机。

最佳实践和优化

  1. 集群规模: 建议使用 3 或 5 个 etcd 节点,根据集群规模进行调整。
  2. 网络延迟: 尽量将 etcd 集群部署在同一地域,减少网络延迟。
  3. 磁盘 I/O: 使用 SSD,并将 etcd 数据存放在单独的磁盘。
  4. 日志文件大小: 定期创建快照,并设置合理的存储配额。
  5. 历史版本压缩: 自动压缩历史版本,释放存储空间。
  6. 碎片整理: 定期消除存储碎片。
  7. 数据备份: 定期创建快照,并上传到网络存储设备。
  1. 参数优化: 根据网络延迟调整心跳周期和选举超时时间。
  2. 安全性: 启用 peer 和 client 之间的 TLS 加密,并考虑数据加密。
  3. 事件分离: 对于大规模集群,可以将事件存储在单独的 etcd 集群中。

etcd 常见问题与解决方案

  1. 频繁的leader election: 检查网络,查看是否有丢包现象
  2. etcd 分裂:检查集群配置。
  3. etcd 不响应: 检查磁盘,网络。
  4. 与apiserver之间的链路阻塞: 检查客户端并发。
  5. 磁盘暴涨: 检查是否有大量数据写入。
  6. 少数etcd 成员Down: 检查 down 掉的 etcd 日志。

深入理解 etcd
https://mfzzf.github.io/2025/03/13/etcd/
作者
Mzzf
发布于
2025年3月13日
许可协议