其他

Go应用在容器中的时区

容器中的时区问题 #

应用直接运行在服务器上需要设置服务器时区为东八区,现在很多应用都是部署在容器中了,同样也是要设置容器镜像的时区。

许多容器镜像默认时区为 UTC (Coordinated Universal Time 协调世界时),比东八区慢八个小时,当程序涉及数据库写入操作或者日志记录等功能时就会有时间差。

常规解决方案一般两大类

  1. build docker镜像时就把镜像内的时区设置为 Asia/Shanghai
  2. 运行容器时把本地时区正常的主机的时区配置文件挂载到容器。

看一下 Go 是如何读取时区文件并设置 time.Time 的时区的 #

Go 源码 src/time/zoneinfo_unix.go 中代码和注释都很清晰👍

package time

import (
    "syscall"
)

// Many systems use /usr/share/zoneinfo, Solaris 2 has
// /usr/share/lib/zoneinfo, IRIX 6 has /usr/lib/locale/TZ,
// NixOS has /etc/zoneinfo.
var platformZoneSources = []string{
    "/usr/share/zoneinfo/",
    "/usr/share/lib/zoneinfo/",
    "/usr/lib/locale/TZ/",
    "/etc/zoneinfo",
}

func initLocal() {
    // consult $TZ to find the time zone to use.
    // no $TZ means use the system default /etc/localtime.
    // $TZ="" means use UTC.
    // $TZ="foo" or $TZ=":foo" if foo is an absolute path, then the file pointed
    // by foo will be used to initialize timezone; otherwise, file
    // /usr/share/zoneinfo/foo will be used.

    tz, ok := syscall.Getenv("TZ")
    switch {
    case !ok:
        z, err := loadLocation("localtime", []string{"/etc"})
        if err == nil {
            localLoc = *z
            localLoc.name = "Local"
            return
        }
    case tz != "":
        if tz[0] == ':' {
            tz = tz[1:]
        }
        if tz != "" && tz[0] == '/' {
            if z, err := loadLocation(tz, []string{""}); err == nil {
                localLoc = *z
                if tz == "/etc/localtime" {
                    localLoc.name = "Local"
                } else {
                    localLoc.name = tz
                }
                return
            }
        } else if tz != "" && tz != "UTC" {
            if z, err := loadLocation(tz, platformZoneSources); err == nil {
                localLoc = *z
                return
            }
        }
    }

    // Fall back to UTC.
    localLoc.name = "UTC"
}

首先检查是否设置了 TZ 环境变量

...

减小go程序编译后的体积

编译经典程序 #

程序代码 #

package main

import "fmt"

func main() {
    fmt.Println("Hello World.")
}

编译环境 #

$ go version
go version go1.16.7 linux/amd64

0. 直接编译 #

$ go build -o helloword main.go
$ ls -lh helloword 
-rwxrwxr-x 1 gobai gobai 1.9M Nov 23 09:34 helloword

1. 修改编译选项 #

除去编译时带的符号表和调试信息

$ go build -ldflags="-s -w" -o helloword main.go
$ ls -lh helloword 
-rwxrwxr-x 1 gobai gobai 1.3M Nov 23 09:38 helloword

2. 使用 UPX #

对直接编译出的二进制使用 upx 进一步压缩

...