68

创建最小的Go docker 镜像

 5 years ago
source link: http://colobu.com/2018/08/13/create-minimal-docker-image-for-go-applications/?amp%3Butm_medium=referral
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的新的版本的发布, 以及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的镜像 ,这样才能将你的镜像减小到最小。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK