Golang初级实战:编写一个定时备份脚本 Go语言cron与文件压缩

2次阅读

Golang初级实战:编写一个定时备份脚本 Go语言cron与文件压缩

go 里用 cron 启动定时任务,为什么时间没到就执行了?

cron 包(比如 github.com/robfig/cron/v3)默认使用本地时区,但很多服务器时区是 UTC。如果你按北京时间写 "0 0 2 <em> </em> *" (凌晨2点),而服务器在 UTC 时区,实际会在北京时间上午10点运行。

  • 确保初始化 cron 实例时显式设置时区:cron.New(cron.Withlocation(time.Local))
  • 不要用 time.Now().Location() 动态取,它可能返回 UTC(尤其在容器或 CI 环境中)
  • 检查服务器时区:datetimedatectl status,必要时用 export TZ=Asia/Shanghai 配置

备份前压缩文件,gziparchive/tar 怎么配合才不丢路径?

直接用 os.Open 读单个文件再 gzip.Writer 压缩,只能得到一个扁平的压缩包;真正备份需要保留目录结构,得用 archive/tar 打包后再套一层 gzip

  • 先创建 tar.Writer,再用 gzip.NewWriter 包裹它,顺序不能反
  • 遍历源目录时,tar.Header.Name 必须是相对路径(去掉根目录前缀),否则解压会写到系统根下
  • 示例关键片段:
    tarWriter := tar.NewWriter(gzipWriter) filepath.WalkDir(srcDir, func(path string, d fs.DirEntry, err error) error {   if err != nil { return err }   relPath, _ := filepath.Rel(srcDir, path)   header, _ := tar.FileInfoHeader(d.Info(), "")   header.Name = relPath // 关键:必须设为相对路径   tarWriter.WriteHeader(header)   if !d.IsDir() {       f, _ := os.Open(path)       io.copy(tarWriter, f)       f.Close()   }   return nil })

os.Rename 移动备份文件失败:permission denied 或 invalid cross-device link

常见于把备份从 /tmp(内存盘)移到 /backup(挂载的另一块磁盘),os.Rename 在跨设备时会报 invalid cross-device link;而 windows 下还可能因目标文件正被占用报 permission denied

  • 判断是否跨设备:用 unix.Statfslinux/macos)或 syscall.GetVolumeInformation(Windows)比对 Dev 字段
  • 更稳妥的做法是统一用 io.Copy + os.Remove 替代 os.Rename
  • 备份文件生成时加临时后缀(如 .tmp),写完再重命名,避免程序崩溃留下半成品

脚本跑在 docker 里,cron 定时器不触发?

Docker 默认启动的容器是 pid 1 进程,但很多基础镜像(如 alpine:latest)没有完整信号处理链路,导致 cron.Start() 后收不到系统信号,内部 ticker 停摆。

立即学习go语言免费学习笔记(深入)”;

  • 必须在 Dockerfile 中用 exec 启动 Go 程序:ENTRYPOINT ["./backup"],而不是 sh -c "./backup"
  • Alpine 镜像需额外安装 libc6-compat(glibc 兼容层),否则 cron 的 time.Ticker 可能异常
  • 加日志确认 cron 是否真正 running:fmt.printf("cron started, next run: %vn", c.Entries()[0].Next)

定时备份这事,最难的不是写逻辑,而是让压缩路径不出错、跨设备移动不静默失败、容器里 cron 不“假死”——这三处一漏,备份就形同虚设。

text=ZqhQzanResources