容器化Go应用--基础镜像的未知时区问题

Go开发的应用程序的一个优势在于,可以从"零"开始构建应用的Docker镜像,镜像中仅需要包含Go应用程序编译后的二进制文件,不需要额外安装其他执行环境。这样一来Go应用镜像占用的空间确实很小(通常是几MB),而且也会更安全些。常用的alpine镜像(alpine是专门为容器设计的小型Linux发行版)中存在一个安全漏洞,该漏洞为大量生产容器留下了空的root用户密码,所以如果你的的Go应用程序在没有alpine(或任何其他操作系统)的容器中运行,黑客就不能利用操作系统的漏洞去攻击容器里的应用。

使用Docker的多阶段构建,从头开始构建映像非常简单,上一期的文章《线上Go项目的Docker镜像应该怎么构建?》已经介绍了怎么从"scratch"基础镜像,使用多阶段构建制作Go应用程序的镜像。今天接着上期的话题继续说一个从零构建的应用镜像的容器时区设置的问题。

如果你的应用程序在初始化函数init里有设置时区的操作,那么在启动应用容器时会遇到下面这个运行时panic

unknown time zone Asia/Shanghai

如果你在应用程序里不显示地设置时区,应用容器确实是能正常启动的,只不过这样time包里的函数统一用的是UTC时区,等你发现问题时再在程序里去显示设置时区仍然会遇到上面的运行时错误。

下面我们来做个试验,看看上面说的问题的现象。

首先写一个简单的Go应用程序

package main

import (
   "fmt"
   "time"
)

func main() {
   // 输出当前的时区
   fmt.Print("Local time zone ")
   fmt.Println(time.Now().Zone())
   fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
}

然后写一个用来构建应用镜像的Dockerfile,使用的就是之前介绍的多阶段构建。

FROM golang:alpine as build
RUN apk --no-cache add tzdata
WORKDIR /app
ADD . /app
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp

FROM scratch as final

COPY --from=build /app/myapp .
ENV TZ=Asia/Shanghai

CMD ["/myapp"]

Dockerfile里,我们用ENV指令设置了TZ这个环境变量。Go运行时会查找TZ这个环境变量来设置自己的时区,上面我们把TZ设置成了Asia/Shanghai,接下来我们看看在容器里应用是不是能如期运行,输出正确的时区和时间。

➜  docker build -t go_timezone .


➜  docker run --rm go_timezone
Local time zone UTC 0
2020-07-17 04:47:37

根据运行结果发现时区的设置并没生效。

Linux系统下Go运行时会从多个来源读取时区信息,在$GOROOT/src/time/zoneinfo.unix文件里能够找到Go运行时是从哪些地方读取时区信息的。

// Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ.
var zoneSources = []string{
   "/usr/share/zoneinfo/",
   "/usr/share/lib/zoneinfo/",
   "/usr/lib/locale/TZ/",
   runtime.GOROOT() + "/lib/time/zoneinfo.zip",
}

于是我就进到刚才镜像的容器里看了看,上面列的几个目录都没有找到。到这里算是定位到问题了,scratch镜像里并不包含这些时区文件。那么解决办法就是从build阶段的镜像里拷贝时区文件到最终的应用镜像。

FROM golang:alpine as build
RUN apk --no-cache add tzdata
WORKDIR /app
ADD . /app
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp

FROM scratch as final

COPY --from=build /app/myapp .
### 下面这行是新加的
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
ENV TZ=Asia/Shanghai

CMD ["/myapp"]

重新构建镜像、运行容器后就能发现时区设置已经正常了,Go运行时按照环境变量TZ里指定的时区打印了当前时间。

➜  docker image rm go_timezone
➜  docker run --rm go_timezone
Local time zone CST 28800
2020-07-17 13:12:18.206 CST

线上Go项目的Docker镜像应该怎么构建?

容器和虚拟机到底有啥区别?

Go原子操作和互斥锁的区别

❤️爱心三连

1.看到这里了就点个在看支持下吧,你的「在看」是我创作的动力。

2.关注公众号网管叨bi叨,「每周为您分享原创技术文章」!

3.特殊阶段,带好口罩,做好个人防护。

“在看转发”是最大的支持

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值