现在已经毫无疑问,Docker使一个应用和多个应用部署变得非常容易。无论是同一工具的不同版本,具有不同版本依赖性的不同应用程序-Docker都涵盖了。但是,这种灵活性会带来一些问题-例如高磁盘使用率和大的映像。使用Docker时,必须谨慎有效地编写Dockerfile,以减小映像大小并缩短构建时间。
Docker提供了一组遵循的标准实践,以使镜像尺寸较小-这就用到了我们介绍的多阶段构建功能。
对于构建二进制文件或可执行文件的用例,多阶段构建特别有用。通常,构建二进制文件需要很多依赖项,例如,GCC,Maven,构建必需项等,但是一旦有了可执行文件,就不需要那些依赖项来运行可执行文件。它们可以在单独的环境中构建可执行文件,然后仅使用可执行文件和运行该可执行文件所需的最小依赖关系来构建最终映像。
例如,这是一个用Go语言编写的简单应用程序。它所做的就是打印“ Hello World !!”。作为输出。让我们开始而不使用多阶段构建。
1 2 3 4 5 6 |
Dockerfile FROM golang ADD . /app WORKDIR /app RUN go build # This will create a binary file named app ENTRYPOINT /app/app |
build然后运行:
1 2 3 |
docker build -t goapp . ~/g/helloworld ❯❯❯ docker run -it --rm goapp Hello World!! |
现在让我们检查镜像大小
1 2 |
~/g/helloworld ❯❯❯ docker images | grep goapp goapp latest b4221e45dfa0 18 seconds ago 805MB |
下面让我们修改dockerfile,使用多阶段构建的方式构建:
1 2 3 4 5 6 7 8 9 10 11 12 |
# Build executable stage FROM golang ADD . /app WORKDIR /app RUN go build ENTRYPOINT /app/app # Build final image FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=0 /app/app . CMD ["./app"] |
同样构建和运行:
1 2 3 |
docker build -t goapp . ~/g/helloworld ❯❯❯ docker run -it --rm goapp Hello World!! |
再看镜像大小:
1 2 3 |
~/g/helloworld ❯❯❯ docker images | grep goapp goapp latest 100f92d756da 8 seconds ago 8.15MB ~/g/helloworld ❯❯❯ |
我们可以看到镜像大小大大减小->从805 MB减小到8.15MB。 这主要是因为Golang镜像具有很多依赖关系,我们的最终可执行文件甚至不需要运行这些依赖关系。
接下来让我们看下具体步骤。
我们正在分两个阶段建立镜像。 首先,我们使用Golang基本镜像,将我们的代码复制到其中,并构建我们的可执行文件App。 现在,在下一步中,我们将使用新的Alpine基本映像并将之前构建的二进制文件复制到新阶段。 这里要注意的重要一点是,在每个阶段构建的映像都是完全独立的。
步骤-0
1 2 3 4 5 6 |
# Build executable stage FROM golang ADD . /app WORKDIR /app RUN go build ENTRYPOINT /app/app |
步骤-1
1 2 3 4 5 6 |
# Build final image FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=0 /app/app . CMD ["./app”] |
请注意COPY -from = 0 / app / app->这行,可以从上一个镜像内部访问数据。
多阶段构建如何工作的呢?
如果仔细看一下过程,那么多阶段构建与实际的Docker构建没有太大区别。 唯一的主要区别是,可以构建多个独立的映像(每个阶段1个),并且能够轻松地将工件/文件从一个映像复制到另一个映像。 多阶段构建现在提供的功能是通过脚本较早实现的。 人们通常用来创建构建映像-手动复制构建-然后将其复制到新映像,而没有其他依赖项。 在上面的示例中,我们在阶段0中构建一个映像,然后在阶段1中构建另一个映像,我们从较早的映像中复制文件,没有什么复杂的。
注意:我们正在将/ app从一个映像复制到另一个映像-而不是从一个容器复制到另一个。
这样可以通过多种方式加快部署并节省成本
我们可以构建有效的轻量级映像-因此在部署期间可以传送较少的数据,从而节省了成本和时间。
可以在任何阶段停止多阶段构建-因此可以使用它来避免使用多阶段构建的构建器模式。 对于开发,登台和部署都只有一个Dockerfile。
上面只是一个小例子,也可以使用多阶段构建来改进用其他语言编写的应用程序的Docker映像。 同样,多阶段构建可以帮助避免编写多个Dockerfile(构建器模式)-而是可以对具有多个阶段的单个Dockerfile进行调整,以简化开发过程。