1. 概述
对于Golang程序而言,源码需要编译,且在编译过程中还会下载一些依赖的packages,最终生成一个可执行的binary文件。如果使用docker部署,只需要将binary文件放到docker image里面即可,不需要golang源码和依赖的packages。
因此,容易想到的方法:
- 首先,在本地编译或通过docker编译,将binary文件保存在本地
- 然后,将binary文件放到新的docker image中,即可生成一个体积较小的docker镜像
- 最后,部署新的docker镜像到测试环境或生产环境
这种方法比较麻烦,只拷贝一个binary文件,还好。如果来回copy多个文件或文件夹,就不make sense了,例如
- 前端代码混淆的场景,只需要将混淆后的文件copy到镜像中即可
- 通过cython编译python代码的场景,将生成一堆so文件(通常用于加密python代码)
上面两个scenario还有一个共性,源码不能出现在最终的docker image的layer
中,否则就失去了初心。
这个时候,Multi-stage builds就非常有必要了。
2. 多阶段构建Multi-stage builds
看一个来自docker官方文档的例子:
FROM golang:1.16 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go ./
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]
语法还是比较简单的,基本一看就懂,只需要注意两个点:
- From,会开启一个新的build阶段,如果有
as
,则为当前stage的name
,否则默认从0开始
- Copy,默认从本地目录复制文件或文件夹到docker image,如果有
--from
,则会从指定的docker stage
或其他docker image
中复制,例如--from=builder
、--from==0
、--from==nginx:latest
3. 其他用法
关于多阶段构建镜像,docker官方还提供了一些其他玩法,可以根据需要灵活选择。
3.1. 只构建特定stage的docker image
通过--target
指明stage name,build该stage后将停止构建。
A few scenarios where this might be very powerful are:
- Debugging a specific build stage
- Using a debug stage with all debugging symbols or tools enabled, and a lean production stage
- Using a testing stage in which your app gets populated with test data, but building for production using a different stage which uses real data
docker build --target builder -t alexellis2/href-counter:latest .
3.2. 引用已定义的stage作为from源
# syntax=docker/dockerfile:1
FROM alpine:latest AS builder
RUN apk --no-cache add build-base
FROM builder AS build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp
FROM builder AS build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp