etcd配置中心备份必须用etcdctl snapshot save生成二进制.db文件,因其包含租约、ACL、版本号等完整元信息;仅用get导出会丢失关键数据导致服务掉线。

Go本身不提供容灾备份能力,所有可靠方案都依赖架构设计 + 工具链集成 + 显式控制流。硬编码自动切换、依赖全局配置识别地域、在主线程跑备份任务——这三类做法在生产环境几乎必然出问题。
etcd配置中心的备份必须用etcdctl snapshot save,不能只导出key-value
go-zero等框架虽能监听etcd变更,但配置数据的持久化一致性必须靠底层快照保障。仅用etcdctl get --prefix /导出文本,会丢失租约(lease)、权限(ACL)、版本号(mod_revision)等关键元信息,恢复后服务可能因租约过期反复掉线。
- 必须使用
etcdctl snapshot save生成二进制.db文件,它包含完整状态机快照 - 备份命令需显式指定
--endpoints和认证参数(--user/--password),不能复用应用连接池配置 - 快照文件应配合
etcdctl check snapshot校验,避免磁盘写入中断导致损坏 - 建议将快照与最近一次
gofr_migrations表记录的时间戳打标关联,便于故障时对齐配置与数据库状态
数据库备份要区分数据源类型,MySQL和Redis不能共用同一套逻辑
GoFr迁移系统虽统一了UP/DOWN接口,但不同数据源的备份约束差异极大:MySQL需事务一致性,Redis依赖RDB/AOF混合策略,Cassandra则必须走nodetool snapshot。混用会导致恢复失败或数据错乱。
- MySQL备份必须加
--single-transaction参数,否则高并发下mysqldump可能读到部分更新的数据 - Redis备份优先用
SAVE生成RDB,禁用BGSAVE——后者在fork子进程时若内存不足会失败,且无明确错误码返回 - Cassandra备份不能直接打包
/var/lib/cassandra/data,必须调用nodetool snapshot -t backup_$(date +%s)并归档snapshots/子目录 - 所有备份操作应在独立goroutine中执行,并用
context.WithTimeout设上限(如MySQL全量建议≤300s),超时即终止并告警
文件级备份必须跳过/proc、/sys、容器挂载点,且校验要在写入后立即计算
用filepath.Walk递归备份目录时,若未过滤特殊路径,会导致open /proc/1/fd: permission denied类错误,更严重的是把/var/lib/kubelet/pods这类只读挂载点强行打包,造成备份体积虚高且不可恢复。
- 遍历前先检查
info.Sys().(*syscall.Stat_t).Dev,跳过dev == 0(proc/sys)和与根路径Stat_t.Dev不同的挂载点 - 校验哈希(如
sha256)必须在os.WriteFile之后立刻hash.Write(data),不能等整个目录遍历完再统一算——内存溢出或中途panic会导致校验失效 - 避免用
os.ReadFile加载大文件,改用io.Copy配合hash.Hash流式计算,防止OOM - 备份目标路径权限设为
0600,防止未授权读取敏感配置或密钥文件
灾备切换不能只改DNS或Ingress,必须在gRPC Resolver层动态路由
很多团队用Kubernetes Service的ClusterIP配合DNS轮询实现“多活”,但DNS TTL缓存、客户端长连接复用会让流量持续打向已宕机集群,真实RTO远超预期。Go微服务的正确切流点在gRPC的resolver.Builder。
- 自定义
Build()方法中,从target.Endpoint解析region=参数,再根据context中的metadata匹配可用实例列表 - 禁止在
UnaryInterceptor里修改ctx并重试——gRPC连接复用机制会让后续请求继续走旧连接,路由完全失效 - Fallback逻辑必须带降级标记(如
fallback_region=sh),避免雪崩式跨地域重试 - 每次路由决策需记录
region、latency、error_rate到本地指标,供熔断器(如gobreaker)实时判断
真正难的不是写备份代码,而是让每个环节都可验证:快照能否通过etcdctl check,RDB能否被redis-cli --rdb解析,校验和是否与解压后文件一致。没有验证的备份等于没做。


















