docker的multi stage build
docker
的multi-stage build
把「build」的「过程」和「结果」拆分开。
我们在build容器的时候,会在容器里面加入一些编译工具和依赖包,目的是为了得到编译后的结果。但是实际在后续的容器运行周期里面,并不再需要这些build过程中所依赖的工具,但是因为相关的软件包已经装入了容器,所以这些中间过程用到的工具极大地增加了build出来的image的尺寸。
因此,docker
引入了multi-stage build
的概念,把「build过程」和「build结果」拆分开。下面是配置文件的例子:
# First Stage
FROM golang:1.6-alpine
RUN mkdir /app
ADD . /app/
WORKDIR /app
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# Second Stage
FROM alpine
EXPOSE 80
CMD ["/app"]
# Copy from first stage
COPY --from=0 /app/main /app
可以看到,上面的Dockerfile
里面包含两个FROM
,因此会build两个images。其中第一个FROM
描述的容器,是用来使用golang
的开发环境来build一个go
语言写的程序。而第二个FROM
所描述的容器,是使用第一个容器里面所build出来的程序,并运行这个程序用的。第一个容器的build过程如下:
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
上面这个命令会编译go
写的程序。第二个容器使用第一个容器的build结果的方法如下:
COPY --from=0 /app/main /app
其中,/app/main
是来自于第一个容器的build结果。--from=0
,就是告知第二个容器,从第一个容器里面拷贝。其中0
是第一个容器的索引编号。
这样,第二个用来运行程序的容器,就不需要安装go
的开发环境了。我们可以在katacoda
(Creating optimised Docker Images using Multi-Stage Builds)上面在线实验上面的例子,截图如下:
可以看到,用来build的容器为293mb
,而运行用的容器只有12mb
,因为它不需要保留编译过程中所需的go
环境。从上面的结果可以看到,把build容器和runtime容器拆分开,可以大大精简最终的runtime容器的尺寸。docker
提供的这个multi-stage build
功能对于实际的生产和部署至关重要。