ETCD生产环境一文搞定

314 阅读5分钟

前言: 本教程专注于 Etcd v3 协议。Etcd 的历史上曾使用过两种协议:v2 和 v3。然而,v2 已被视为过时且不推荐用于生产环境。此外,Etcd v2 和 v3 具有不同的数据存储结构。因此,不能使用 Etcd v2 读取 Etcd v3 的数据,也不能使用 Etcd v2 的快照来恢复 Etcd v3 的数据。本文档专为包含 v3 数据的 Etcd 实例而编写。

如何部署 Etcd 集群?

Etcd 是一种广泛使用的分布式键值存储,在 Kubernetes 中起着至关重要的作用,并被全球许多互联网公司使用。对于后端工程师和运维人员来说,掌握这一中间件并学会处理错误是非常重要的。在本节中,我们将探讨如何使用 "Docker" 和 "Docker Compose" 等容器工具来部署 Etcd 集群。让我们开始吧!

我选择了一台 Ubuntu 虚拟机作为工作平台,具体版本是 20.04.2 LTS。我假设你已经在机器上安装了 Docker 和 Docker Compose。如果没有,可以参考 Docker 和 Docker Compose 的官方文档进行安装。

docs.docker.com/desktop/ins…

1. Etcd 目录结构

$ tree
.
├── docker-compose.yaml
├── etcd
├── etcd.conf
└── etcd-ssl
    ├── ca.crt
    ├── ca.key
    ├── ca.srl
    ├── client.crt
    ├── client.csr
    ├── client.key
    ├── server.cnf
    ├── server.crt
    ├── server.csr
    └── server.key

如你所知,我们使用 Docker Compose 来加载 Etcd 集群。因此,我们需要创建一个 docker-compose.yaml 文件来描述我们的 Etcd 集群。此外,我们还需要一个自定义的 etcd.conf 文件,因为接下来的部分将涉及配置我们的 Etcd 集群。“etcd”和“etcd-ssl”目录用于存储 Etcd 数据和客户端认证的证书。

2. 完成 docker-compose.yaml 文件

version: "3"
services:
  etcd:
    image: quay.io/coreos/etcd:v3.5.9
    network_mode: "host"
    container_name: etcd_container
    command: /usr/local/bin/etcd --config-file /etcd.conf
    volumes:
      - ./etcd:/var/lib/etcd
      - ./etcd.conf:/etcd.conf:ro
      - ./etcd-ssl:/etcd/etcd-ssl/:ro
    restart: always
    ulimits:
      nofile:
        soft: 1048576
        hard: 1048576
  1. 我们使用的是来自 quay.io 的官方 Etcd 镜像,具体版本为 v3.5.9。
  2. network_mode: "host" 配置允许 Etcd 集群相互通信,能够使用主机的 IP 地址连接到 Etcd 集群。
  3. restart: always 设置确保 Etcd 集群保持运行,充分利用 Etcd 的高可用机制。

3. 自定义 etcd.conf 文件

#---基本运行配置---#
#[basic]
name: k8s-etcd01
data-dir: /var/lib/etcd
enable-v2: true
listen-client-urls: https://0.0.0.0:2379
listen-peer-urls: http://0.0.0.0:2380

#[cluster]
initial-advertise-peer-urls: http://10.0.24.14:2380
advertise-client-urls: https://10.0.24.14:2379
# 多集群设置: k8s-etcd01=http://10.0.24.14:2380,k8s-etcd02=http://10.146.80.49:2380,k8s-etcd03=http://10.146.80.52:2380
initial-cluster: k8s-etcd01=http://10.0.24.14:2380
initial-cluster-state: existing
initial-cluster-token: etcd-cluster

#---集群选举配置---#
initial-election-tick-advance: true
heartbeat-interval: 500
election-timeout: 3000

#---性能调优配置---#
quota-backend-bytes: 8589934592
auto-compaction-mode: 'periodic'
auto-compaction-retention: '1'
max-request-bytes: 10485760
snapshot-count: 50000
max-snapshots: 5
max-wals: 5

#---客户端 TLS 加密配置---#
client-transport-security:
  # 客户端服务器 TLS 证书文件路径。
  cert-file: /etcd/etcd-ssl/server.crt
  # 客户端服务器 TLS 密钥文件路径。
  key-file: /etcd/etcd-ssl/server.key
  # 启用客户端证书认证。
  # client-cert-auth: false
  # 客户端服务器 TLS 受信任的 CA 证书文件路径。
  trusted-ca-file: /etcd/etcd-ssl/ca.crt
  # 使用生成的证书进行客户端 TLS 加密
  # auto-tls: false

下表提供了 etcd.conf 文件中各个参数的说明:

参数描述
data-dir指定 Etcd 存储数据的目录,包括数据库和事务日志。
enable-v2启用或禁用 Etcd 版本 2 API 的支持。通常设置为 "false",以鼓励使用功能更丰富且更高效的 v3 API。
listen-client-urls定义 Etcd 监听客户端请求的 URL,允许客户端使用这些 URL 连接到 Etcd。
listen-peer-urls指定 Etcd 监听与集群中其他 Etcd 节点通信的 URL。
initial-advertise-peer-urls指定 Etcd 在形成或加入集群时用于向其他成员广告自己的初始 URL。
advertise-client-urlsinitial-advertise-peer-urls 类似,此参数指定客户端应使用的连接到此 Etcd 成员的 URL。
initial-cluster提供初始 Etcd 集群成员及其相关的 initial-advertise-peer-urls 列表,用于集群引导。
initial-cluster-state指示集群应最初设置为 "new" 还是 "existing"。对于新集群设置为 "new",对于向现有集群添加新成员时设置为 "existing"。
initial-cluster-token用于标识集群的任意字符串。集群中的所有成员应具有相同的令牌。
initial-election-tick-advance确定是否在启动时快速推进初始选举计数,以加快选举。当设置为 true 时,本地成员将快速推进选举计数以加速“初始”领导选举。
heartbeat-intervalEtcd 领导者发送的心跳信号之间的时间间隔,以保持领导地位。以毫秒为单位表示。建议设置为成员之间的平均往返时间 (RTT) 的最大值。
election-timeoutEtcd 追随者在启动选举之前可以在没有收到领导者通信的情况下等待的最长时间间隔。以毫秒为单位表示。根据心跳间隔和成员之间的平均往返时间设置。建议参考此链接: etcd.io/docs/v3.5.9…
quota-backend-bytes指定用于存储数据的最大字节数。当超过时,Etcd 会执行自动压缩或发出警报。
auto-compaction-mode决定何时执行自动压缩:"periodic"(定期)或 "revision"(基于修订次数)。
auto-compaction-retentionMVCC 键值存储的自动压缩保留时间(小时)。0 表示禁用自动压缩。
max-request-bytes设置 Etcd 请求的最大字节数。超过此大小的请求将被拒绝。
snapshot-count触发快照的提交事务数量,用于数据备份和恢复。
max-snapshots要保留的最大快照数;当达到此限制时,可能会删除较旧的快照。
max-wals要保留的最大写前日志 (WAL) 数量;当达到此限制时,可能会删除较旧的 WAL。
client-transport-security客户端通信的安全设置,包括客户端证书和密钥文件。

4. 生成客户端认证的证书

为 Etcd 集群启用客户端认证是防止未经授权访问的关键。因此,我们需要生成客户端认证的证书。出于效率考虑,在私有环境中,不需要对等认证,因为它可能会影响性能。

使用 OpenSSL 生成客户端证书的命令如下:

# 生成 ca.crt
$ openssl genrsa -out ca.key 4096
$ openssl req -new -x509 -key ca.key -out ca.crt -subj "/CN=etcd-ca"

# 生成 server.crt
$ vim server.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
[ v3_req ]
subjectAltName = @alt_names
[alt_names]
DNS.1 = xxx.xxx.xxx.xxx
IP.1 = xxx.xxx.xxx.xxx
IP.2 = xxx.xxx.xxx.xxx
IP.3 = xxx.xxx.xxx.xxx
$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr -subj "/CN=etcd-server" -config server.cnf
$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -extensions v3_req -extfile server.cnf

# 生成 client.crt
$ openssl genrsa -out client.key 2048
$ openssl req -new -key client.key -out client.csr -subj "/CN=etcd-client"
$ openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365

5. 优化 Etcd 环境

为了使 Etcd 机器达到最佳性能,必须优化 Etcd 配置和主机的硬件环境。以下是一些在启动 Etcd 集群之前建议调整的参数:

  1. 文件描述符:
    • Linux 将一切视为文件,包括普通文件、目录、套接字、管道等。
    • 文件描述符是用于访问打开的文件或其他资源的唯一整数标识符。它作为句柄,供进程使用,以读取、写入或操作资源。
    • 有三个标准的文件描述符:0 (stdin)、1 (stdout) 和 2 (stderr)。
    • 你可以使用 lsof -p <pid> | wc -l 查看进程当前打开的文件数量。
    • 如何查看进程的打开文件限制? cat /proc/<pid>/limits | grep "Max open files"
    • 如何查看用户的当前文件描述符限制(每个用户)? ulimit -n
    • 要更改用户的文件描述符限制,请修改 /etc/security/limits.conf 或在 /etc/security/limits.d/ 下创建一个新文件,内容如下:
      * soft nofile 1048576
      * hard nofile 1048576
      
    • 你也可以在 systemd 服务文件或 Docker Compose YAML 文件中调整文件描述符限制,例如,在 [Service] 部分添加以下行:
      LimitNOFILE=new_limit
      LimitNPROC=new_limit
      
  2. 最大打开文件数:
    • “最大打开文件数”或“文件描述符限制”是系统范围内的限制,限制一个进程同时可以打开的文件描述符数量。
    • 这个限制的目的是防止单个进程通过打开过多的文件或网络连接来消耗过多的系统资源。
    • 这对于需要处理大量并发客户端的服务器应用程序、系统守护进程和后台进程尤其重要。
    • 你可以使用 ulimit -Hn 命令查看进程的当前文件描述符限制。
    • 要更改系统范围的文件描述符限制,请修改 /etc/sysctl.conf,添加以下行:
      fs.file-max = new_limit
      
    请记住,增加文件描述符限制时应谨慎操作,并考虑系统资源。设置过高的限制可能导致资源耗尽,因此必须在满足应用需求和保持系统稳定性之间找到平衡。
  3. TCP 保持连接:
    • 启用 TCP 保持连接以防止空闲连接超时。修改 net.ipv4.tcp_keepalive_timenet.ipv4.tcp_keepalive_probesnet.ipv4.tcp_keepalive_intvl,例如:
      net.ipv4.tcp_keepalive_time = 60
      net.ipv4.tcp_keepalive_probes = 3
      net.ipv4.tcp_keepalive_intvl = 10
      
  4. I/O 调度器: 选择最适合工作负载的 I/O 调度器。例如,对于 SSD,可以使用 deadlinemq-deadline 调度器,对于 HDD,可以使用 cfq 调度器。要查看当前的调度器,使用 cat /sys/block/<device>/queue/scheduler,要更改它,使用 echo <scheduler> > /sys/block/<device>/queue/scheduler
  5. CPU: Etcd 性能受益于快速 CPU,因此选择具有多个核心和高主频的机器。
  6. 内存: Etcd 的性能直接受到可用内存的影响。确保你有足够的 RAM 来适应你的 etcd 工作负载。
  7. 存储: Etcd 性能可能受 I/O 影响,因此考虑使用更快的存储解决方案,如 SSD,以减少延迟并提高整体性能。
  8. 带宽: 确保你的网络带宽足以处理预期的 etcd 流量。
  9. 延迟: 尽量减少 Etcd 节点之间的网络延迟,以提高通信速度。

请注意,这些参数的具体值取决于你的工作负载、硬件和网络环境。在进行更改后进行负载测试和基准测试,以评估它们对 Etcd 性能和稳定性的影响。此外,在应用到生产系统之前,请确保做好备份,并在受控的测试环境中进行更改。

6. 启动 Etcd 集群

$ docker-compose up
$ docker-compose ps
     Name                   Command               State   Ports
---------------------------------------------------------------
etcd_container   /usr/local/bin/etcd --conf ...   Up
# 检查日志
$ docker-compose logs -f
...

# 检查 Etcd 集群状态
ETCDCTL_API=3 etcdctl --endpoints=https://10.0.24.14:2379 --cacert=./etcd-ssl/ca.crt --cert=./etcd-ssl/client.crt --key=./etcd-ssl/client.key endpoint status -w table
+-------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|        ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://10.0.24.14:2379 | c8e267dee39d90f6 |   3.5.9 |   20 kB |      true |      false |         6 |         38 |                 38 |        |
+-------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

Etcd 基本命令

以下是一些你可以使用的 Etcd 基本命令:

描述命令
设置键值对etcdctl put key value
获取键的值etcdctl get key
删除一个键etcdctl delete key
监控键的变化etcdctl watch key
设置带 TTL(生存时间)的键etcdctl put key value --ttl seconds
列出目录中的所有键(带前缀)etcdctl get dir --prefix
删除目录中的所有键(带前缀)etcdctl delete dir --prefix
创建新用户(用于认证)etcdctl user add username:password
创建角色并授予权限(RBAC)etcdctl role add rolename
etcdctl role grant-permission rolename
添加新 Etcd 集群成员etcdctl member add --peer-urls=https://new-node:2380
列出 Etcd 集群成员etcdctl member list
删除 Etcd 集群成员etcdctl member remove member-id
对 Etcd 数据进行快照etcdctl snapshot save snapshot.db
从快照中恢复 Etcd 数据etcdctl snapshot restore snapshot.db
显示集群的健康状态etcdctl endpoint status -w table

Etcd 常见问题及解决方法

1. 灾难恢复

对 Etcd 数据进行快照

你可以使用以下命令在 Etcd 运行时对数据进行快照备份:

$ ETCDCTL_API=3 etcdctl --endpoints=https://10.0.24.14:2379 --cacert=./etcd-ssl/ca.crt --cert=./etcd-ssl/client.crt --key=./etcd-ssl/client.key snapshot save ./snapshot.db
{"level":"info","ts":"2023-09-06T00:20:31.982857+0800","caller":"snapshot/v3_snapshot.go:65","msg":"created temporary db file","path":"./snapshot.db.part"}
{"level":"info","ts":"2023-09-06T00:20:31.992423+0800","logger":"client","caller":"v3@v3.5.9/maintenance.go:212","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":"2023-09-06T00:20:31.992476+0800","caller":"snapshot/v3_snapshot.go:73","msg":"fetching snapshot","endpoint":"https://10.0.24.14:2379"}
{"level":"info","ts":"2023-09-06T00:20:32.003568+0800","logger":"client","caller":"v3@v3.5.9/maintenance.go:220","msg":"completed snapshot read; closing"}
{"level":"info","ts":"2023-09-06T00:20:32.008874+0800","caller":"snapshot/v3_snapshot.go:88","msg":"fetched snapshot","endpoint":"https://10.0.24.14:2379","size":"20 kB","took":"now"}
{"level":"info","ts":"2023-09-06T00:20:32.008952+0800","caller":"snapshot/v3_snapshot.go:97","msg":"saved","path":"./snapshot.db"}
Snapshot saved at ./snapshot.db

命令执行后将生成一个输出,显示快照已成功保存。

从快照中恢复 Etcd 数据

要从快照中恢复 Etcd 数据,请确保恢复命令中的参数与 etcd.conf 文件中的参数(如 initial-cluster-token)一致:

$ ETCDCTL_API=3 etcdctl --endpoints=https://10.0.24.14:2379 --cacert=./etcd-ssl/ca.crt --cert=./etcd-ssl/client.crt --key=./etcd-ssl/client.key snapshot restore --skip-hash-check ./snapshot.db --initial-cluster=k8s-etcd01=http://10.0.24.14:2380 --initial-cluster-token=etcd-cluster --initial-advertise-peer-urls=http://10.0.24.14:2380 --name k8s-etcd01  --data-dir=./data

这个命令将根据快照文件恢复 Etcd 数据。

仅恢复特定的键或目录

如果你只需要恢复特定的键或目录,也可以使用以下命令:

$ etcdctl snapshot restore <snapshot-file> --data-dir <data-directory> --restore-from-key <key>
# 或者
$ etcdctl snapshot restore /path/to/snapshot.db --data-dir /var/lib/etcd --restore-from-key /mydir/mykey

在恢复数据后,重新启动 Etcd 集群:

$ docker-compose up -d
$ ETCDCTL_API=3 etcdctl --endpoints=https://10.0.24.14:2379 --cacert=./etcd-ssl/ca.crt --cert=./etcd-ssl/client.crt --key=./etcd-ssl/client.key endpoint status -w table

2. 问题:“etcdserver: mvcc: database space exceeded”

相关链接: etcd.io/blog/2023/h…

如果你遇到 "etcdserver: mvcc: database space exceeded" 问题,可以按照以下步骤解决:

$ ETCDCTL_API=3 etcdctl --endpoints localhost:2379 alarm list/disarm
$ ETCDCTL_API=3 etcdctl --endpoints localhost:2379 endpoint status -w table
$ ETCDCTL_API=3 etcdctl --endpoints localhost:2379 compact
$ ETCDCTL_API=3 etcdctl --endpoints localhost:2379 defrag

这些命令将帮助你管理 Etcd 数据库的大小,包括压缩和碎片整理。

如果需要确定具有最多修订版本的键,可以使用以下命令:

$ ETCDCTL_API=3 etcdctl --endpoints localhost:2379 endpoint status -w json
$ ETCDCTL_API=3 etcdctl --endpoints localhost:2379 get "" --prefix --keys-only | grep -v ^$ | awk -F '/'  '{ h[$3]++ } END {for (k in h) print h[k], k}' | sort -nr

3. 添加新的 Etcd 集群成员

要向现有的 Etcd 集群添加新成员,按照以下步骤操作:

  • 配置新节点的 etcd.conf 文件,确保设置了以下参数:

    1. name: 新节点的唯一名称。
    2. data-dir: Etcd 存储数据的目录。
    3. initial-advertise-peer-urls: 新节点向其他成员广告的 URL。
    4. listen-peer-urls: 新节点监听与其他 Etcd 节点通信的 URL。
    5. listen-client-urls: 新节点监听客户端请求的 URL。
    6. advertise-client-urls: 客户端应使用的连接到新节点的 URL。
    7. initial-cluster: 初始 Etcd 集群成员的列表及其相关的 initial-advertise-peer-urls,应与其他节点的 etcd.conf 中的列表匹配。
    8. initial-cluster-state: 如果是向现有集群添加新成员,设置为 "existing"。
  • 使用 etcdctl 添加新成员:

$ ETCDCTL_API=3 etcdctl --endpoints=https://ETCDCLUSTER --cacert=./etcd-ssl/ca.crt --cert=./etcd-ssl/client.crt --key=./etcd-ssl/client.key member add ETCDNAME --peer-urls=https://NEW-NODE:2380
  • 启动新节点并检查其状态:
$ docker-compose up -d
$ ETCDCTL_API=3 etcdctl --endpoints=https://ETCDCLUSTER --cacert=./etcd-ssl/ca.crt --cert=./etcd-ssl/client.crt --key=./etcd-ssl/client.key endpoint status -w table