文章简介:为了提高 etcd 在延迟非常高的磁盘上的稳定性,提出了两种解决方法
背景
etcd 官方文档有提到对磁盘写 io latency 比较敏感。为满足要求,已经将 etcd 存储 bind mount 到了系统盘中,尽可能的隔离数据盘的影响。参考 openshift-scale benchmark 磁盘脚本,生产环境已经在安装检查了磁盘的性能满足要求,但是几乎所有的私有云虚拟化平台提供的磁盘在后续业务负载提高、磁盘写入增加的时候,磁盘写 io lantency 异常增高,导致 etcd 异常主从切换,k3s controller manager 异常 leaderelection lost
而重启,跟着其上的 pod 也会因 NodeNotReady 而失联。为了解决这个问题,参考 蚂蚁集团万级规模 K8s 集群 etcd 高可用建设之路 的建议。
解决思路
- 考虑在磁盘性能特别差的环境中使用 -unsafe-no-fsync,但注意,这个参数会导致 etcd 有丢失数据的风险
- 将数据放到 tmpfs 中,为 etcd 提供稳定的存储,并使用定时同步到磁盘做持久化 + 定时 snapshot
方案 1: unsafe-no-fsync
如果 etcd 版本 >= 3.5,可以在 etcd config 中添加 unsafe-no-fsync=true。其实现在这里(https://github.com/etcd-io/etcd/pull/11946/files),sync 不真正持久化,但已经写入 os buf,在异常断电等场景会丢数据的可能,建议 snapshot 定时备份数据周期改小一点。
方案 2: etcd on tmpfs
首先 tmpfs 提供稳定的读写
1
2
3
|
mkdir -p /var/lib/rancher/k3s/server/db/
echo 'tmpfs /var/lib/rancher/k3s/server/db/ tmpfs size=4g 0 0' >> /etc/fstab
mount -a
|
tmpfs 需要定时同步到磁盘
1
|
*/5 * * * * /usr/local/bin/etcd-on-tmpfs backup
|
每次服务启动如果文件不存在需要同步到 tmpfs 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/usr/bin/env bash
set -o errtrace
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
mkdir -p /usr/lib/systemd/system/k3s.service.d/
cat <<EOF > /usr/lib/systemd/system/k3s.service.d/cosmos-restore-etcd-pre-start.conf
[Service]
ExecStartPre=/usr/local/bin/etcd-on-tmpfs restore
EOF
cat <<EOF > /usr/lib/systemd/system/k3s.service.d/cosmos-backup-etcd-post-stop.conf
[Service]
ExecStopPost=/usr/local/bin/etcd-on-tmpfs backup
EOF
systemctl daemon-reload
|
其中 /usr/local/bin/etcd-on-tmpfs
代码见:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
#!/bin/bash
set -o errtrace
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
shopt -s extglob
# etcd-tmpfs 管理脚本
# 功能:
# 1. 将 etcd 的数据目录使用 tmpfs
# 2. 开机和定时同步数据到 /opt/backup 中
# 3. 开机及数据目录无数据时将 /opt/backup 中的数据 copy 到数据目录中
# 4. 备份恢复命令使用 rsync
# 配置变量
ETCD_DATA_DIR="/var/lib/rancher/k3s/server/db"
BACKUP_DIR="/var/lib/rancher/k3s/etcdpersistent"
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 检查是否以 root 权限运行
check_root() {
if [ "$EUID" -ne 0 ]; then
log "请以 root 权限运行此脚本"
exit 1
fi
}
# 备份 etcd 数据到 /opt/backup
backup_etcd() {
log "开始备份 etcd 数据到 $BACKUP_DIR/etcd"
# 创建备份目录
mkdir -p "$BACKUP_DIR/etcd"
# 如果 etcd 数据目录不存在或为空,则不执行备份
if [ ! -d "$ETCD_DATA_DIR" ] || [ ! "$(ls -A $ETCD_DATA_DIR)" ]; then
log "etcd 数据目录不存在或为空,跳过备份"
return 0
fi
CLUSTER_NAME_PATH="$BACKUP_DIR/name"
if [ ! -f "$CLUSTER_NAME_PATH" ];then
info ${CLUSTER_NAME_PATH} 不存在,开始备份
cp "/var/lib/rancher/k3s/server/db/etcd/name" "$CLUSTER_NAME_PATH"
log ${CLUSTER_NAME_PATH} 备份完成
else
log $CLUSTER_NAME_PATH 存在
fi
if ! diff /var/lib/rancher/k3s/server/db/etcd/name ${CLUSTER_NAME_PATH}; then
log "etcd/name 变化,禁止备份,请人工检查一下"
return 1
fi
# 使用 rsync 同步数据
if rsync -av --progress --delete "$ETCD_DATA_DIR/" "$BACKUP_DIR/etcd/"; then
log "成功备份 etcd 数据到 $BACKUP_DIR/etcd"
tail -n 10 $BACKUP_DIR/backup.log > $BACKUP_DIR/.backup.log || true
date "+%Y-%m-%d %H:%M:%S %Z" >> $BACKUP_DIR/.backup.log
mv $BACKUP_DIR/.backup.log $BACKUP_DIR/backup.log
return 0
else
log "备份 etcd 数据失败"
return 1
fi
}
# 从 /opt/backup 恢复 etcd 数据
restore_etcd() {
log "检查是否需要从 $BACKUP_DIR/etcd 恢复 etcd 数据"
# 创建 etcd 数据目录
mkdir -p "$ETCD_DATA_DIR"
# 如果 etcd 数据目录不为空,则不执行恢复
if [ "$(ls -A $ETCD_DATA_DIR)" ]; then
log "etcd 数据目录不为空,跳过恢复"
return 0
fi
# 检查备份目录是否存在且不为空
if [ ! -d "$BACKUP_DIR/etcd" ] || [ ! "$(ls -A $BACKUP_DIR/etcd)" ]; then
log "备份目录不存在或为空,跳过恢复"
return 0
fi
CLUSTER_NAME_PATH="$BACKUP_DIR/name"
if [ ! -f "$CLUSTER_NAME_PATH" ];then
log 文件 ${CLUSTER_NAME_PATH} 不存在,不允许恢复, 不允许启动
return 1
fi
log "从 $BACKUP_DIR/etcd 恢复 etcd 数据"
# 使用 rsync 恢复数据
if rsync -av --progress "$BACKUP_DIR/etcd/" "$ETCD_DATA_DIR/"; then
log "成功从 $BACKUP_DIR/etcd 恢复 etcd 数据"
return 0
else
log "从 $BACKUP_DIR/etcd 恢复 etcd 数据失败"
return 1
fi
}
# 显示帮助信息
show_help() {
echo "用法: $0 [选项]"
echo "选项:"
echo " backup 备份 etcd 数据到 $BACKUP_DIR/etcd"
echo " restore 从 $BACKUP_DIR/etcd 恢复 etcd 数据"
echo " sync 同步 etcd 数据到 $BACKUP_DIR/etcd"
echo " status 显示当前状态"
echo " help 显示此帮助信息"
}
# 显示当前状态
show_status() {
set +o xtrace
echo "=== etcd-tmpfs 状态 ==="
echo "etcd 数据目录: $ETCD_DATA_DIR"
echo "备份目录: $BACKUP_DIR/etcd"
if mount | grep -q "$ETCD_DATA_DIR.*tmpfs"; then
echo "tmpfs 状态: 已挂载"
echo "tmpfs 使用情况:"
df -h "$ETCD_DATA_DIR"
else
echo "tmpfs 状态: 未挂载"
fi
echo "etcd 数据目录内容:"
ls -la "$ETCD_DATA_DIR" 2>/dev/null || echo "目录不存在或为空"
echo "备份目录内容:"
ls -la "$BACKUP_DIR/etcd" 2>/dev/null || echo "目录不存在或为空"
set -o xtrace
}
# 主函数
main() {
# 检查 root 权限
check_root
case "$1" in
backup)
backup_etcd
;;
restore)
restore_etcd
;;
status)
show_status
;;
help|*)
show_help
;;
esac
}
# 执行主函数
main "$@"
|
定时 snapshot 备份
k3s 默认自动备份,无须调整,文档见这里
总结
综上,正常开关机或服务挂掉,有 systemd service 的 ExecStartPre 和 ExecStopPost 备份与还原;异常场景有 etcd 集群其他节点的数据 raft 自动同步及定时 snapshot 可以兜底,基本可以确保 etcd 的数据不丢失了。