最佳实践之 - Golang's Dockerfile

常用做法

基于 golang 官方基础镜像打包,Dockerfile如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 基于 golang 官方基础镜像打包
FROM golang:1.12

NV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct

WORKDIR /app

COPY . .

RUN go build .

EXPOSE 10010

ENTRYPOINT ["./app"]

但是最终 docker build 出来的镜像高达300多M, 这是由于 golang 这个基础镜像中的工具链及其依赖项(git,mercurial 等)重达几百MB,而这一部分我们在运行的时候是不需要的。

因此,我们需要对基础镜像进行精简。

精简镜像

使用 scratch

该镜像是一个空的镜像,可以用于构建 busybox 等超小镜像,可以说是真正的从零开始构建属于自己的镜像。在此基础镜像上运行的应用程序只能访问内核,尽管Go宣称自己只需要Linux内核,但是我们在真实的项目中或多或少会有其他依赖项,比如 我们项目会用到 CGO 等等。所以在构建 Golang 项目是,一般不建议直接采用 scratch 做基础镜像,否则你需要在 Dockerfile 中手动加载很多依赖项。

1
2
# 基于 scratch 官方基础镜像打包
FROM scratch
使用 alpine

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。它不同于通常 Linux 发行版,alpine 采用了 musl libc 和 busybox 以减小系统的体积和运行时资源消耗,但功能上比 busybox 又完善的多,因此得到开源社区越来越多的青睐。

Alpine Docker 镜像也继承了 Alpine Linux 发行版的这些优势。相比于其他 Docker 镜像,它的容量非常小,仅仅只有 5 MB 左右(对比 Ubuntu 系列镜像接近 200 MB),且拥有非常友好的包管理机制。官方镜像来自 docker-alpine 项目。目前 Docker 官方已开始推荐使用 Alpine 替代之前的 Ubuntu 做为基础镜像环境。

Alpine 和其他通用 Linux 发行版对于 Golang 编译出来的可执行文件要求有所不同,Alpine 要求可执行文件必须是静态链接的可执行文件。所以在编译 Golang 时需要添加 -tags netgo ,来生成静态链接的可执行文件。

1
2
# 基于 alpine 官方基础镜像打包
FROM alpine

但是对 C 的支持不太好。

在 Alpine Docker 镜像上运行 cgo 项目会出现问题,提示一下问题:

1
panic: standard_init_linux.go:175: exec user process caused "no such file or directory"

原因是当 cgo 开启时,默认是按照动态库的方式来链接 so 文件的,但 alpine 只支持静态链接,所以会出错。
解决方案有两种:

通过设置 CGO_ENABLED=0 来解决,此时 cgo 也不可用了

调用 go build –ldflags “-extldflags -static” ,来让gcc使用静态编译可以解决问题

使用 ubuntu

ubuntu 镜像

1
2
# 基于 ubuntu 官方基础镜像打包
FROM ubuntu

最佳实践

多层构建方式

FROM golang:1.12
用对go功能比较齐全镜像编译源程序,生成可执行文件

FROM ubuntu
用精简镜像作为最终的容器的镜像

拷贝可执行文件到容器内

打包生成最终的镜像

Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 打包依赖阶段使用golang作为基础镜像
FROM golang:1.12 as builder

LABEL version="1.0" description="xiaolong.li" by="xiaolonghuster"

# 启用go module
ENV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct

WORKDIR /app-pkg

COPY . /app-pkg

# 指定OS等,并go build
RUN GOOS=linux GOARCH=amd64 && cd src && go build -o app main.go

# 由于不止依赖二进制文件,还依赖conf文件夹下的配置文件,将这些文件放到了publish文件夹
RUN mkdir publish && cp src/app publish && \
cp -r conf publish

# 运行阶段为精简镜像包,指定ubuntu作为基础镜像
FROM ubuntu

WORKDIR /app

# 将上一个阶段publish文件夹下的所有文件复制进来
COPY --from=builder /app-pkg/publish .

EXPOSE 10086

ENTRYPOINT ["./app"]

----------本文结束感谢您的阅读----------
xiaolong wechat
一只程序猿对世界的不完全理解