容器化 Go 应用:基础镜像的未知时区问题
source link: https://mp.weixin.qq.com/s/HAFMqooL-ucOWsnL4NjkaQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
用 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
:heart:爱心三连
1.看到这里了就点个在看支持下吧,你的 「 在看 」 是我创作的动力。
2.关注公众号 网管叨bi叨
, 「每周为您分享原创技术文章」
!
3.特殊阶段,带好口罩,做好个人防护。
“在看转发” 是最大的支持
Recommend
-
50
-
43
Java学习录 There's nothing here. Take me home.
-
39
阿航在开发Springboot项目时, 前端告诉验证码一直无效. 本地测试没有问题, 一看远程服务器的数据库时间, 哇塞–早了8小时. 很明显, 是MySQL的时区问题. 本篇文章就来记录下如何修改Docker 的 MySQL 容器时区. 解决方案
-
27
用 Go 开发的应用程序的一个优势在于,可以从"零"开始构建应用的 Docker 镜像,镜像中仅需要包含 Go 应用程序编译后的二进制文件,不需要额外安装其他执行环境。这样一来 Go 应用镜...
-
9
Lua 夏令时时区问题 我之前的一篇文章介绍了怎样在服务器和客户端之间同步时间和时区. 同步时间相对简单些, 本质就是一个时间差; 而时区相对复杂...
-
6
如何设置容器中的时区 之前以为设置容器时区很简单,跟设置Linux时区一样的操作就行. 但是实际没有那么简单,因为有些基础镜像,比如Ubuntu是没有安装tzdata的, 而当在Dockerfile用apt来安装tzdata时会发现这玩意安装好后居然要手工选择一...
-
5
binaryless 容器基础镜像 ...
-
5
使用以语言为中心的容器基础镜像 distroless2021年10月14日
-
3
Docker 镜像时区设置 原创 青衫解衣 2022-04-29 15:47:03...
-
8
一、docker镜像与容器 docker镜像是一个可执行的静态独立软件包,包含打包程序代码和软件运行环境等文件。如:代码、运行时库、环境变量和配置文件等都包含在其中。容器是镜像的运行时状态(镜像中的软件、程序运行),占用服务器C...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK