
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 环境中) - 检查服务器时区:
date或timedatectl status,必要时用export TZ=Asia/Shanghai配置
备份前压缩文件,gzip 和 archive/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.Statfs(Linux/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: %v\n", c.Entries()[0].Next)
定时备份这事,最难的不是写逻辑,而是让压缩路径不出错、跨设备移动不静默失败、容器里 cron 不“假死”——这三处一漏,备份就形同虚设。










