2019-07 from–https://blog.csdn.net/wade3015/article/details/95729492
关于构建镜像最具挑战性的事情之一是保持镜像体积小巧。 Dockerfile 中的每条指令都会在镜像中增加一层,并且在移动到下一层之前,需要记住清除不需要的构件。要编写一个非常高效的 Dockerfile,你通常需要使用 shell 技巧和其它方式来尽可能地减少层数,并确保每一层都具有上一层所需的构件,而其它任何东西都不需要。
Docker是轻量化的,当我们在制作镜像的时候就应该尽量使得应用镜像轻量些,减少镜像的体积。例如一个简单的golang服务,如何构建一个最小的镜像:
- package main
- import (
- “fmt”
- “net/http”
- “strings”
- )
- func getNameLen(name string) int {
- return len(name)
- }
- func sayHello(w http.ResponseWriter, r *http.Request) {
- message := r.URL.Path
- message = strings.TrimPrefix(message, “/”)
- message = “Hello “ + message + ” : “ + fmt.Sprintf(“%d”, getNameLen(message))
- w.Write([]byte(message))
- }
- func main() {
- http.HandleFunc(“/”, sayHello)
- if err := http.ListenAndServe(“:8088”, nil); err != nil {
- panic(err)
- }
- }
初学者一般会这样构建:
- FROM golang:latest
- MAINTAINER TigerwolfC@***.com.cn
- WORKDIR /go/src
- COPY . .
- RUN go build –o myapp
- EXPOSE 8088
- CMD [“./myapp”]
这样的构建是可行的,但是golang的基础镜像比较大,所以制作后的镜像体积也大(700M+),我们知道golang的跨平台性,编译好的可执行文件只要在对应的系统中就能运行,那么我们应该将这个编译后得到的可执行文件放到一个更小的镜像中,这样得到的最终镜像就非常的轻量了。
多阶段构建:
在多阶段构建中,您需要在 Dockerfile 中多次使用 FROM
声明。每次 FROM
指令可以使用不同的基础镜像,并且每次 FROM
指令都会开始新阶段的构建。您可以选择将构件从一个阶段复制到另一个阶段,在最终镜像中,不会留下您不需要的所有内容。为了演示这是如何工作的,让我们调整前一节中的 Dockerfile 以使用多阶段构建。
- #编译镜像
- FROM golang:latest as builder
- MAINTAINER TigerwolfC@***.com.cn
- WORKDIR /go/src
- COPY . .
- RUN go build -o myapp
- #产物运行镜像
- FROM alpine:latest
- WORKDIR /root
- COPY –from=builder /go/src/myapp .
- EXPOSE 8088
- CMD [“./myapp”]
简单说下,该 dockerfile 包含了两部分:编译镜像和产物运行镜像,编译镜像跟上边演示的 dockerfile 一样负责生成编译后产物,产物运行镜像通过 COPY –from 语句即可将编译后的产物复制到该镜像内部,最后运行该产物即可。这里提一下,产物运行镜像我采用了 alpine 作为基础镜像,alpine 是一个空镜像,只能用于构建其他镜像,因为它体积很小,提供较小的运行坏境,能够最大限度的减小我们的产物运行镜像大小(当然也可以使用 busybox、scratch等小体积镜像)。
这个Dockerfile构建会生成一个none的中间镜像和一个最终镜像,最终镜像基于alpine镜像,大小只有几十M左右,而生成的中间镜像可以通过docker system prune进行回收。