Kratos 容器时区错误99%是系统配置问题,需同时设置TZ环境变量、挂载/etc/localtime和/etc/timezone,并在Alpine镜像中预装tzdata并复制时区文件。

直接结论:Kratos 应用在 Docker 容器内时区不对,99% 是容器系统时区未配置,而非 Kratos 本身的问题。它不干预系统时区,完全依赖 time.Now() 返回的本地时间——而这个时间由容器的 /etc/localtime 和 TZ 环境变量共同决定。
确认 Kratos 容器当前时区是否生效
别猜,先验证。进入正在运行的 Kratos 容器(比如服务名是 kratos-api):
docker exec -it kratos-api sh
然后执行:
-
date—— 看输出是否带CST或Asia/Shanghai;如果显示UTC,说明时区没生效 -
cat /etc/timezone—— 正常应输出Asia/Shanghai;空或Etc/UTC就是错的 -
ls -l /etc/localtime—— 应指向/usr/share/zoneinfo/Asia/Shanghai;若指向UTC或是 dangling link,就失效 -
go run -e 'package main; import "time"; func main() { print(time.Now().Format("2006-01-02 15:04:05 MST")) }'—— 这行 Go 代码会直接调用系统时钟,输出结果应与date一致
docker run 启动时必须传 TZ 且挂载 /etc/localtime
Kratos 基于 Go,Go 运行时对 TZ 环境变量有原生支持,但部分基础镜像(尤其是 Alpine)默认不装 tzdata,仅设 TZ 不够。必须双管齐下:
- 加
-e TZ=Asia/Shanghai:让 Go stdlib、log 包、time.Now()解析正确 - 加
-v /etc/localtime:/etc/localtime:ro:确保 C 库级调用(如某些 syscall、第三方 CGO 组件)也走对时区 - 加
-v /etc/timezone:/etc/timezone:ro:部分 Go 第三方日志库(如zerolog的某些封装)会读这个文件
完整命令示例:
docker run -d \ -e TZ=Asia/Shanghai \ -v /etc/localtime:/etc/localtime:ro \ -v /etc/timezone:/etc/timezone:ro \ --name kratos-api \ your-kratos-image
Kratos Dockerfile 构建阶段要预装 tzdata(尤其 Alpine)
如果你用的是 alpine:latest 或 golang:alpine 作为 base 镜像,TZ 环境变量不会自动生效——因为 Alpine 默认不带 tzdata 包。构建时必须显式安装并复制时区文件:
Docker Hub (hub.docker.com)。此技能适用于所有 Docker Hub 操作,包括读取、创建、更新和删除数据。只要任务涉及 Docker Hub,就使用此技能。
FROM alpine:3.20 RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone COPY --from=builder /app/kratos /usr/local/bin/kratos CMD ["kratos", "start"]
注意:apk add tzdata 不能省;只设 ENV TZ 而不 cp /usr/share/zoneinfo/...,date 命令仍会显示 UTC。
Kratos 日志和定时任务出问题?先看 time.Now() 输出是否对
Kratos 自身没有“时区配置项”,所有时间逻辑都来自标准库。排查典型现象:
- 日志里时间比宿主机慢 8 小时 → 容器
date输出是 UTC,不是时区配置问题,是根本没配 -
kratos cron或自定义time.Ticker执行时间偏移 → 检查是否用了time.Now().In(loc)强制转时区,而loc初始化失败(比如time.LoadLocation("Asia/Shanghai")在没tzdata的 Alpine 里会 panic 或 fallback 到 UTC) - API 返回的
created_at字段是 UTC 时间 → 这很可能是你代码里显式用了time.UTC,和容器时区无关;检查业务代码是否误写time.Now().UTC()
最简单验证方式:在 Kratos 的 main.go 开头加一行:
log.Printf("boot time: %s", time.Now().Format("2006-01-02 15:04:05 MST"))
启动容器后看这行日志输出的时区缩写(MST 字段)是不是 CST。不是,说明容器环境没配好,别往下查 Kratos 配置。
真正容易被忽略的是:Alpine 镜像里 time.LoadLocation 会静默失败,返回 UTC location 而不报错。哪怕你写了 loc, _ := time.LoadLocation("Asia/Shanghai"),也可能根本没加载成功——所以构建阶段复制 /etc/localtime 比运行时靠 LoadLocation 更可靠。










