跳转至

ETCD 部署与维护

什么是 ETCD

etcd 是一个分布式、可靠的键值存储系统,专门用于存储分布式系统中的关键数据。它通过 Raft 算法来实现分布式一致性和高可用性。

etcd 的主要特点

  • 分布式一致性: etcd 使用 Raft 算法在集群中实现一致性。Raft 算法会选举一个领导者(Leader),由领导者负责处理所有的写请求并将数据同步到其他节点(Follower)。

  • 高可用性: 当领导者节点(Leader)出现故障时,系统会自动选举新的领导者,继续处理数据的同步和分发。 etcd 的高可用性基于 Quorum 机制:只有当集群中超过半数的节点可用时(可用节点数量 > 总节点数量 / 2),集群才能继续提供服务。

使用场景

  • 服务发现: 存储服务注册信息和状态信息,帮助服务实例之间进行通信。

  • 配置管理: 存储分布式系统的配置数据,确保配置变更时所有相关服务能够立即感知并更新。

  • 分布式锁: 提供基于租约和事务的分布式锁功能,确保在分布式环境中的独占访问控制。

  • 元数据存储: 在容器编排系统(如 Kubernetes)中,存储集群的元数据,包括节点状态、Pod 信息、服务定义等。

etcd 的工作机制

  • Raft 算法:确保数据在集群中的一致性,通过领导者节点处理写请求并同步数据到其他节点。
  • Quorum 机制:确保集群在超过半数节点可用时能够正常提供服务。
  • 自动故障恢复:当领导者节点故障时,自动选举新的领导者并恢复数据同步。

etcd 数据存储格式

etcd 中的存储路径格式可以表示为:

prefix + "/" + 资源类型 + "/" + namespace + "/" + 资源名

具体示例

  • Service Account: 存储路径:/registry/serviceaccounts/kube-system/coredns 解释:在 kube-system 命名空间下的名为 coredns 的 Service Account。

  • Pod: 存储路径:/registry/pods/default/nginx 解释:在 default 命名空间下的名为 nginx 的 Pod。

  • Service: 存储路径:/registry/services/specs/default/my-service 解释:在 default 命名空间下的名为 my-service 的 Service 定义信息。

  • 存储路径:/registry/services/endpoints/default/my-service 解释:在 default 命名空间下的名为 my-service 的 Service 端点信息。

  • ConfigMap: 存储路径:/registry/configmaps/default/app-config 解释:在 default 命名空间下的名为 app-config 的 ConfigMap。

  • Secret: 存储路径:/registry/secrets/default/db-secret 解释:在 default 命名空间下的名为 db-secret 的 Secret。

ETCD 证书获取

1. cfssl 工具下载

cfssl 是使用 go 编写,由 CloudFlare 开源的一款 PKI/TLS 工具。主要程序有:

  • cfssl 是 CFSSL 的命令行工具

  • cfssljson 用来从 cfssl 程序获取 JSON 输出,并将证书,密钥,CSR 和 bundle 写入文件中

1. 创建工作目录
mkdir -p /data/k8s-worker/ca
2. 下载 cfssl_1.6.4_linux_amd64
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64
chmod +x cfssl_1.6.4_linux_amd64    
mv cfssl_1.6.4_linux_amd64 /usr/local/bin/cfssl
3. 下载 cfssljson_1.6.4_linux_amd64
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64
chmod +x cfssljson_1.6.4_linux_amd64
mv cfssljson_1.6.4_linux_amd64 /usr/local/bin/cfssljson
4. 下载 cfssl-certinfo_1.6.4_linux_amd64
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl-certinfo_1.6.4_linux_amd64
chmod +x cfssl-certinfo_1.6.4_linux_amd64
mv cfssl-certinfo_1.6.4_linux_amd64 /usr/local/bin/cfssl-certinfo

2. 创建 CA 证书

1. 配置 CA 证书请求文件 ca-csr.json
cat > ca/ca-csr.json <<"EOF"
{
"CN": "kubernetes",
"key": {
    "algo": "rsa",
    "size": 2048
},
"names": [
    {
    "C": "CN",
    "ST": "Beijing",
    "L": "Beijing",
    "O": "deployment",
    "OU": "CN"
    }
],
"ca": {
        "expiry": "87600h"
}
}
EOF
2. 创建 CA 证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca/ca
3. 创建 CA 证书策略
cat > ca/ca-config.json <<"EOF"
{
"signing": {
    "default": {
        "expiry": "87600h"
        },
    "profiles": {
        "kubernetes": {
            "usages": [
                "signing",
                "key encipherment",
                "server auth",
                "client auth"
            ],
            "expiry": "87600h"
        }
    }
}
}
EOF

注意

  1. server auth 表示client可以对使用该ca对server提供的证书进行验证

  2. client auth 表示server可以使用该ca对client提供的证书进行验证

3. 创建 etcd 证书

mkdir etcd
1. 配置 etcd 请求文件
cat > etcd/etcd-csr.json <<"EOF"
{
"CN": "etcd",
"hosts": [
    "127.0.0.1",
    "192.168.3.40",
    "192.168.3.41",
    "192.168.3.42",
    "192.168.3.43",
    "192.168.3.44",
    "192.168.3.45",
    "192.168.3.46",
    "192.168.3.47",
    "192.168.3.48",
    "192.168.3.49",
    "192.168.3.50",
    "192.168.3.51",
    "192.168.3.52",
    "192.168.3.53",
    "192.168.3.54",
    "192.168.3.55",
    "192.168.3.56",
    "192.168.3.57",
    "192.168.3.58",
    "192.168.3.59",
    "192.168.3.60",
    "192.168.3.61",
    "192.168.3.62",
    "192.168.3.63",
    "192.168.3.64",
    "192.168.3.65",
    "192.168.3.66",
    "192.168.3.67",
    "192.168.3.68",
    "192.168.3.69",
    "192.168.3.70",
    "192.168.3.200"
],
"key": {
    "algo": "rsa",
    "size": 2048
},
"names": [{
    "C": "CN",
    "ST": "Beijing",
    "L": "Beijing",
    "O": "deployment",
    "OU": "CN"
}]
}
EOF
2. 生成 etcd 证书
cfssl gencert -ca=ca/ca.pem -ca-key=ca/ca-key.pem -config=ca/ca-config.json -profile=kubernetes etcd/etcd-csr.json | cfssljson  -bare etcd/etcd

ETCD 部署

下载 etcd 软件包

所有 etcd 节点上都需要。

1. 填写 ETCD_VER=v3.5.13
cat > etcd-install.sh <<"EOF"
ETCD_VER=v3.5.13

# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}

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

mkdir /var/lib/etcd/

/tmp/etcd-download-test/etcd --version
/tmp/etcd-download-test/etcdctl version
/tmp/etcd-download-test/etcdutl version

# 将 etcd, etcdctl 和 etcdutl 复制到 /usr/local/bin
cp /tmp/etcd-download-test/etcd /usr/local/bin/
cp /tmp/etcd-download-test/etcdctl /usr/local/bin/
cp /tmp/etcd-download-test/etcdutl /usr/local/bin/
EOF

创建 etcd 配置文件

1. etcd 启动文件
cat >  etcd/k8s-master01-etcd.conf << "EOF"
#[Member]
ETCD_NAME="etcd-1"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.3.40:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.3.40:2379,http://192.168.3.40:2390"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.3.40:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.3.40:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.3.40:2380,etcd-2=https://192.168.3.41:2380,etcd-3=https://192.168.3.42:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF
1. etcd 启动文件
cat >  etcd/k8s-master02-etcd.conf << "EOF"
#[Member]
ETCD_NAME="etcd-2"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.3.41:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.3.41:2379,http://192.168.3.41:2390"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.3.41:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.3.41:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.3.40:2380,etcd-2=https://192.168.3.41:2380,etcd-3=https://192.168.3.42:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF
1. etcd 启动文件
cat >  etcd/k8s-master03-etcd.conf << "EOF"
#[Member]
ETCD_NAME="etcd-3"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.3.42:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.3.42:2379,http://192.168.3.42:2390"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.3.42:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.3.42:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.3.40:2380,etcd-2=https://192.168.3.41:2380,etcd-3=https://192.168.3.42:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF
2. 准备 etcd 服务配置文件
cat > etcd/etcd.service <<"EOF"
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=-/etc/etcd/etcd.conf
WorkingDirectory=/var/lib/etcd/
ExecStart=/usr/local/bin/etcd \
--cert-file=/etc/etcd/ssl/etcd.pem \
--key-file=/etc/etcd/ssl/etcd-key.pem \
--trusted-ca-file=/etc/etcd/ssl/ca.pem \
--peer-cert-file=/etc/etcd/ssl/etcd.pem \
--peer-key-file=/etc/etcd/ssl/etcd-key.pem \
--peer-trusted-ca-file=/etc/etcd/ssl/ca.pem \
--peer-client-cert-auth \
--client-cert-auth
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

分发 etcd ssl 与 配置文件

1. 复制 etcd 的 相关配置文件
#!/bin/bash

TARGET_HOSTS=("etcd-1" "etcd-2" "etcd-3" "k8s-master01" "k8s-master02" "k8s-master03")
ETCD_HOSTS=("etcd-1" "etcd-2" "etcd-3")
DIRECTORY=etcd/
CA_DIRECTORY=ca/

# 通过循环执行 scp 命令
for host in "${TARGET_HOSTS[@]}"; do
    echo "正在复制文件到 $host..."

    # 所有 etcd 节点创建工作目录
    ssh -t $host 'mkdir -p /etc/etcd/ssl'

    # 复制 ca 到 /etc/kubernetes/ssl 目录
    scp ${CA_DIRECTORY}ca*.pem  $host:/etc/etcd/ssl/

    # 复制 ca 到 /etc/kubernetes/ssl 目录
    scp ${DIRECTORY}etcd*.pem $host:/etc/etcd/ssl/

    # 复制 systemd 服务文件到 /usr/lib/systemd/system/ 目录
    scp ${DIRECTORY}etcd.service $host:/usr/lib/systemd/system/

    # 复制对应主机的配置文件到 /etc/kubernetes/ 目录
    case $host in
        "etcd-1")
            # 所有 etcd 节点创建工作目录
            scp ${DIRECTORY}k8s-master01-etcd.conf $host:/etc/etcd/etcd.conf
            ;;
        "etcd-2")
            # 所有 etcd 节点创建工作目录
            scp ${DIRECTORY}k8s-master02-etcd.conf $host:/etc/etcd/etcd.conf
            ;;
        "etcd-3")
            # 所有 etcd 节点创建工作目录
            scp ${DIRECTORY}k8s-master03-etcd.conf $host:/etc/etcd/etcd.conf
            ;;
        *)
            echo "未识别的主机名: $host"
            ;;
    esac
done

# 在 etcd 节点上执行 systemctl 命令
for host in "${ETCD_HOSTS[@]}"; do
    echo "在 $host 上执行 systemctl 命令..."
    ssh -t $host 'sudo systemctl daemon-reload'
    ssh -t $host 'sudo systemctl restart etcd'
    ssh -t $host 'sudo systemctl status etcd'
done

验证 etcd 集群状态

1. 以表格形式列出 etcd 集群中的成员
[root@k8s-master01 ~]# ETCDCTL_API=3 /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.3.40:2379,https://192.168.3.41:2379,https://192.168.3.42:2379 endpoint status --write-out=table


+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|         ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.3.40:2379 | acaa7314b910a606 |  3.5.13 |   20 kB |     false |      false |         3 |         15 |                 15 |        |
| https://192.168.3.41:2379 | 2e349b91c4bea476 |  3.5.13 |   20 kB |     false |      false |         3 |         15 |                 15 |        |
| https://192.168.3.42:2379 | a33139d3a34b9bc4 |  3.5.13 |   20 kB |      true |      false |         3 |         15 |                 15 |        |
+---------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
2. 检查 etcd 集群中每个端点的健康状况
[root@k8s-master01 ~]# ETCDCTL_API=3 /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.3.40:2379,https://192.168.3.41:2379,https://192.168.3.42:2379 endpoint health

+---------------------------+--------+-------------+-------+
|         ENDPOINT          | HEALTH |    TOOK     | ERROR |
+---------------------------+--------+-------------+-------+
| https://192.168.3.41:2379 |   true | 11.885376ms |       |
| https://192.168.3.42:2379 |   true | 11.975733ms |       |
| https://192.168.3.40:2379 |   true | 11.960122ms |       |
+---------------------------+--------+-------------+-------+

ETCD 维护

备份 ETCD 集群

1. 选择空闲节点备份、不要选择 LEADER 节点
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem  snapshot save ./`date +%Y-%m-%d-%H:%M`-etcdbackup.db
2. 验证备份数据
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem  --write-out=table snapshot status ./2024-07-12-13\:08-etcdbackup.db
3. 将备份恢复到集群
ETCDCTL_API=3 /usr/local/bin/etcdctl --endpoints=https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem  snapshot restore ./2024-07-12-13\:08-etcdbackup.db
#!/bin/bash

# 定义备份文件路径
BACKUP_DIR="/path/to/backup/dir"
BACKUP_FILE="$BACKUP_DIR/etcd-$(date +%Y%m%d%H%M%S).db"

# 创建备份目录(如果不存在)
mkdir -p $BACKUP_DIR

# 执行数据快照
ETCDCTL_API=3 etcdctl snapshot save $BACKUP_FILE

# 验证快照文件
ETCDCTL_API=3 etcdctl snapshot status $BACKUP_FILE

echo "Backup completed: $BACKUP_FILE"

ETCD 集群节点故障怎么办

1. 查看集群状态获取 ID
ETCDCTL_API=3 etcdctl --endpoints=https://192.168.3.41:2379,https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem member list
2. 移除故障节点
ETCDCTL_API=3 etcdctl --endpoints=https://192.168.3.41:2379,https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem member remove acaa7314b910a606
3. 停止故障节点的服务
systemctl stop etcd
4. 清理数据目录 (如果需要)
# 如果故障节点的数据目录已损坏,可以选择清理数据目录。请注意,这会删除该节点上的所有数据,因此需要从其他节点重新同步数据:
rm -rf /var/lib/etcd/default.etcd/*
5. 从快照恢复 (如果有快照)
ETCDCTL_API=3 etcdctl --endpoints=https://192.168.3.41:2379,https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem \
snapshot restore /opt/etcd/backup/etcdbackup.db --name etcd-1 --data-dir /var/lib/etcd/--initial-cluster etcd-1=https://192.168.3.40:2379  --initial-advertise-peer-urls https://192.168.3.40:2379


#----------------------------------------------------------------------------------------------------------
etcdctl snapshot restore 命令用于从 etcd 的快照文件中恢复数据。在你给出的命令中,有一些参数需要被替换为具体的值来匹配你的 etcd 集群配置。以下是每个参数的解释和应该替换为什么:

ETCDCTL_API=3
#这个环境变量告诉 etcdctl 使用 etcd API 的第 3 版本。通常,你可以直接在命令行前设置这个环境变量,或者在你的 shell 配置文件中设置它。

etcdctl snapshot restore /opt/etcd/backup/etcdbackup.db
#/opt/etcd/backup/etcdbackup.db 是你要从中恢复数据的 etcd 快照文件的路径。确保这个路径是正确的,并且文件是可读的。

--name etcd-master01
#--name 参数定义了 etcd 实例的名称。在你的例子中,它被设置为 etcd-master01。这通常与集群中的特定节点相关联。

--data-dir /var/lib/etcd/
#--data-dir 参数指定了 etcd 存储其数据的目录。在恢复过程中,这个目录将被用于存储恢复的数据。确保这个目录是可写的,并且没有重要的数据,因为恢复过程可能会覆盖它。

--initial-cluster etcd-1=https://192.168.3.40:2379  
#--initial-cluster 参数定义了 etcd 集群的初始成员列表。在恢复过程中,你需要指定集群中所有节点的名称和它们的客户端 URL。

--initial-cluster-token etcd-cluster-token
#--initial-cluster-token 参数用于 etcd 集群中的节点在初次启动时相互发现。它应该是一个唯一的字符串,用于你的 etcd 集群。确保所有节点在启动时都使用相同的集群令牌。

--initial-advertise-peer-urls https://192.168.3.40:2379
#--initial-advertise-peer-urls 参数指定了 etcd 节点在集群中用于通信的 URL。这通常是节点的对等体(peer)URL。
6. 更新配置文件
existing
7. 将节点重新加入集群
ETCDCTL_API=3 etcdctl --endpoints=https://192.168.3.41:2379,https://192.168.3.42:2379 --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem member add etcd-1 --peer-urls=https://192.168.3.40:2380

etcd 集群如何防止 oom

配置优化

设置 --quota-backend-bytes

限制 etcd 后端数据库的大小,以防止内存过度使用。

etcd --quota-backend-bytes=8589934592 # 8GB

--snapshot-count 调整快照间隔

控制在写操作之后触发快照的间隔。

etcd --snapshot-count=50000

调整压缩策略

通过设置自动压缩策略,定期压缩数据库以释放磁盘空间和内存资源。

etcd --auto-compaction-retention=1 # 每小时进行一次自动压缩

调整缓存大小

适度调整缓存大小,以控制内存使用。

etcd --cache-size=1000

资源管理

限制系统级别资源

在系统级别限制 etcd 进程的内存使用。可以使用 ulimit 命令来限制进程的资源。

ulimit -m 2097152 # 设置最大虚拟内存为 2GB
ulimit -v 2097152 # 设置最大物理内存为 2GB

使用 cgroups

在 Linux 系统中,可以使用 cgroups 来限制 etcd 进程的资源使用。

cgcreate -g memory:/etcd
echo 2G > /sys/fs/cgroup/memory/etcd/memory.limit_in_bytes
cgexec -g memory:etcd etcd

数据管理

定期压缩

定期进行 etcd 数据库的压缩操作,以减少数据文件的大小。

ETCDCTL_API=3 etcdctl compact <revision>

清理过期数据

定期清理不再需要的键值对,减少 etcd 数据库的大小。

ETCDCTL_API=3 etcdctl del <key>

监控和报警

使用 Prometheus、Grafana 等监控工具来监控 etcd 的内存使用情况,并设置报警规则。

配置监控指标

etcd 提供了多种监控指标,可以通过这些指标来检测潜在的内存问题。确保你监控以下关键指标:

  • etcd_debugging_mvcc_db_total_size_in_bytes
  • etcd_debugging_mvcc_keys_total
  • etcd_debugging_mvcc_writes_total

运维需要考虑的问题点

  1. etcd 部署方式 考虑不同部署方式的优缺点
  2. 监控 etcd 数据不一致及节点状态 监控集群分裂、数据不一致等问题
  3. 监控及告警 etcd 的潜在隐患 监控 db 大小、配额等潜在隐患并及时告警
  4. 定时及跨区域备份 etcd 数据 实现定时备份和跨区域备份策略
  5. 模拟磁盘 IO 等异常 复现 Bug 和故障,测试系统的健壮性和恢复能力