专栏首页网管叨bi叨容器化Go应用--基础镜像的未知时区问题

容器化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

本文分享自微信公众号 - 网管叨bi叨(kevin_tech),作者:KevinYan11

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 老有人问你什么是Docker?把这篇干货文章甩给他!

    假设你们公司正在秘密研发下一个“今日头条”APP,我们姑且称为明日头条,程序员自己从头到尾搭建了一套环境开始写代码,写完代码后程序员要把代码交给测试同学测试,这...

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

    上期的文章:Kubernetes入门实践--部署运行Go项目发布后,有网友留言说我文章里演示的镜像是把项目文件和Go都打包到了镜像里,这样镜像的占用空间会比较大...

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

    Docker这几年的迅猛发展让容器重新流行起来,不过但很多资料里介绍Docker时都说是 "新瓶装旧酒"。除了容器外虚拟机也是我们或多或少会接触到的虚拟化技术。...

    KevinYan
  • 0475-如何统一Hue和Oozie的时区

    在前面的文章中,Fayson介绍过《如何修改Cloudera Manager的时区》,《如何修改Hue的时区》和《如何修改CDSW会话的时区》。在使用Hue创建...

    Fayson
  • Lync 2013 站点池之间迁移中央管理存储(CMS)[有视频]

    Lync 2013站点之间迁移中央管理存储,与Lync 2010升级Lync 2013迁移CMS类似,步骤如下:

    杨强生
  • 【正经说】如何签订合法有效的对赌协议的分析和方法(含案例)

    对赌协议(Valuation Adjustment Mechanism,VAM),最初被翻译为“对赌协议”,或因符合国有文化很形象,一直沿用至今。但其直译意思是...

    辉哥
  • 【非技术面试】接到面试通知需要必做的准备

    接到面试通知需要必做的准备 要得到任何一个职位,必须经过面试这一关,短短几十分钟的面试也许就决定着你的职业生涯,当你接到企业的面试通知电话后,应该做什么呢?...

    Java帮帮
  • 你知道哪个世代的宝可梦最强吗?

    时光荏苒,岁月如梭,宝可梦宝陪伴了我们大多数90后的童年,小编也是比较喜欢宝可梦的,一直到现在出到了第八世代,各种各样的宝可梦让我们大饱眼福。

    用户6825444
  • UCSCXenaTools介绍

    UCSCXenaTools 提供了下载 UCSC Xena 平台数据的 R 客户端,为官方文档 https://ucscxena.gitbook.io/proj...

    生信技能树
  • python高级-动态特性(20)

    动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。现在比较热门的动态语言有:Python、PHP、Java...

    Se7eN_HOU

扫码关注云+社区

领取腾讯云代金券