创建最小的Go docker 镜像

虽然曾有一些文章介绍了如何创建一个最小的Go Docker镜像,我也曾写过一篇文章,但是随着Go的新的版本的发布, 以及docker本身的进化,有些技巧已经发生了变化, 本文介绍了最新的创建超小的Go镜像的方法。

一个简单Go程序的镜像

首先让我们创建一个很简单的Go程序:

package main

import "fmt"

func main() {
	fmt.Println("hello world")
}

运行下面的命令会创建一个超小的镜像, 这是我们的 第一种 方式:

GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -o app app.go && tar c app | docker import - app:latest

下一节介绍其中的编译参数

查看镜像, 生成的镜像只有 1.21MB

# docker images app
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
app                 latest              b716e13758cd        11 seconds ago      1.21MB

这命令将编译、打包、输入镜像集成到一条命令了。

第二种方式是使用一个Dockerfile文件:

FROM scratch
ADD app /
CMD ["/app"]

运行下面的命令创建一个镜像:

docker build -t app2 .

查看生成的镜像, 也是 1.21MB :

# docker images app2
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
app2                latest              4e2af2ffb695        4 seconds ago       1.21MB

第三种方式是利用Docker的 multistage 功能,在镜像中编译,Dockerfile文件:

Dockerfile.multistage

1

docker build -t app3 -f Dockerfile.multistage .

查看生成的镜像, 也是``:

# docker images app3
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
app3                latest              9177859dad64        16 seconds ago      1.21MB

你可以结合你的情况选择一种生成镜像的方式。

编译Go程序

上面的例子中我们使用下面的命令编译Go程序:

GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -o app app.go

你可能在其它一些文章中还看到 installsuffix 参数:

GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w"  -installsuffix cgo -o app app.go

自Go 1.10以后,你不必再使用 installsuffix 参数(或许更早的版本),Go的核心开发人员Ian Lance Taylor已经 确认 了这一点。

你可能有人还使用 -a 参数,它强制重新编译相关的包,一般你不会使用它。

-s 忽略符号表和调试信息, -w 忽略DWARF符号表,通过这两个参数,可以进一步减少编译的程序的尺寸,更多的参数可以参考 go link , 或者 go tool link -help (另一个有用的命令是 go tool compile -help )。

你也可以使用 strip 工具对编译的Go程序进行裁剪。

本身Go是静态编译的, 对于CGO, 如果设置 CGO_ENABLED=0 ,则完全静态编译,不会再依赖动态库。

如果设置 CGO_ENABLED=0 ,并且你的代码中使用了标准库的 net 包的话,有可能编译好的镜像无法运行,报 sh: /app: not found 的错误,尽管 /app 这个文件实际存在,并且如果讲基础镜像换为 centos 或者 ubuntu 的话就能执行。这是一个奇怪的错误,原因在于:

默认情况下 net 包会使用静态链接库, 比如libc

知道了原因,解决办法也很简单,就是完全静态链接或者在基础镜像中加入libc库。

下面是几种解决办法:

  • 设置 CGO_ENABLED=0
  • 编译是使用纯go的net: go build -tags netgo -a -v
  • 使用基础镜像加glibc(或等价库musl、uclibc), 比如 busybox:glibc 、alpine + RUN apk add --no-cache libc6-compatfrolvlad/alpine-glibc

基础镜像

其实前面已经列出了一些常用的基础镜像:

  • scratch: 空的基础镜像,最小的基础镜像
  • busybox: 带一些常用的工具,方便调试, 以及它的一些扩展busybox:glibc
  • alpine: 另一个常用的基础镜像,带包管理功能,方便下载其它依赖的包

显然。 你应该只在编译阶段使用 Go的镜像 ,这样才能将你的镜像减小到最小。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章