前言
多阶段构建指在Dockerfile中使用多个FROM语句,每个指令都可使用不同的基础镜像且是一个独立的子构建阶段。使用多阶段构建打包应用具有构建安全、构建速度快、镜像文件体积小等优点。
文章图片
目录
1??引入
2??示例
3??解决方案
4??多阶段构建
1??引入 Docker 的?号是 Build,Ship,and Run Any App,Anywhere(一次封装,到处运行),在我们使? Docker 的?部分时候,的确能感觉到其优越性,但是往往在我们 Build ?个应?的时候,是将我们的源代码也构建进去的,这对于类似于 golang 这样的编译型语?肯定是不?的,因为实际运?的时候我只需要把最终构建的?进制包 给你就?,把源码也?起打包在镜像中,需要承担很多?险,即使是脚本语?,在构建的时候也可能 需要使?到?些上线的?具,这样?疑也增?了我们的镜像体积。
文章图片
【云原生|【云原生丨Docker系列13】Docker 的多阶段构建详解】构建镜像时最具挑战性的事情之一就是缩小镜像大小。Dockerfile 中的每一条指令都会在镜像中添加一个层,在进入下一层之前,您需要记住清除所有不需要的工件。要编写一个真正高效的 Dockerfile,传统上需要使用 shell 技巧和其他逻辑来保持层尽可能小,并确保每一层都有它需要的来自前一层的工件,而没有其他东西。
实际上,有一个 Dockerfile 用于开发环境(包含构建应用程序所需的所有内容),同时有一个精简的 Dockerfile 用于生产环境(仅包含应用程序和运行应用程序所需的内容)是非常常见的。这被称为“建造者模式”。
2??示例 ?如我们现在有?个最简单的 golang 服务,需要构建?个最?的 Docker 镜像,源码如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "PONG")
})
router.Run(":8080")
}
3??解决方案 我们最终的?的都是将最终的可执??件放到?个最?的镜像(?如 alpine )中去执?,怎样得到最终 的编译好的?件呢?基于 Docker 的指导思想,我们需要在?个标准的容器中编译,?如在?个 Ubuntu 镜像中先安装编译的环境,然后编译,最后也在该容器中执?即可。
但是如果我们想把编译后的?件放置到 alpine 镜像中执?呢?我们就得通过上?的 Ubuntu 镜像将 编译完成的?件通过 volume 挂载到我们的主机上,然后我们再将这个?件挂载到 alpine 镜像中 去。
这种解决?案理论上肯定是可?的,但是这样的话在构建镜像的时候我们就得定义两步了,第?步是先??个通?的镜像编译镜像,第?步是将编译后的?件复制到 alpine 镜像中执?,?且通?镜像编译后的?件在 alpine 镜像中不?定能执?。
定义编译阶段的 Dockerfile :(保存为Dockerfile.build)
FROM golang
WORKDIR /go/src/app
ADD . /go/src/app
RUN go get -u -v github.com/kardianos/govendor
RUN govendor sync
RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/app-server
定义 alpine 镜像:(保存为Dockerfile.old)
FROM alpine:latest
RUN apk add -U tzdata
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
WORKDIR /root/
COPY app-server .
CMD ["./app-server"]
根据我们的执?步骤,我们还可以简单定义成?个脚本:(保存为build.sh)
#!/bin/sh
echo Building cnych/docker-multi-stage-demo:build
docker build -t cnych/docker-multi-stage-demo:build . -f Dockerfile.build
docker create --name extract cnych/docker-multi-stage-demo:build
docker cp extract:/go/src/app/app-server ./app-server
docker rm -f extract
echo Building cnych/docker-multi-stage-demo:old
docker build --no-cache -t cnych/docker-multi-stage-demo:old . -f Dockerfile.old
rm ./app-server
当我们执?完上?的构建脚本后,就实现了我们的?标。
4??多阶段构建
文章图片
有没有?种更加简单的?式来实现上?的镜像构建过程呢?
Docker 17.05版本以后,官?就提供了? 个新的特性: Multi-stage builds (多阶段构建)。 使?多阶段构建,你可以在?个 Dockerfile 中使?多个 FROM 语句。每个 FROM 指令都可以使?不同的基础镜像,并表示开始?个新的构建 段。你可以很?便的将?个阶段的?件复制到另外?个阶段,在最终的镜像中保留下你需要的内容即可。
我们可以调整前??节的 Dockerfile 来使?多阶段构建:(保存为Dockerfile)
FROM golang AS build-env
ADD . /go/src/app
WORKDIR /go/src/app
RUN go get -u -v github.com/kardianos/govendor
RUN govendor sync
RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/app-server
FROM alpine
RUN apk add -U tzdata
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=build-env /go/src/app/app-server /usr/local/bin/app-server
EXPOSE 8080
CMD [ "app-server" ]
现在我们只需要?个 Dockerfile ?件即可,也不需要拆分构建脚本了,只需要执? build 命令即可:
$ docker build -t cnych/docker-multi-stage-demo:latest
默认情况下,构建阶段是没有命令的,我们可以通过它们的索引来引?它们,第?个 FROM 指令 从 0 开始,我们也可以? AS 指令为阶段命令,?如我们这?的将第?阶段命名为 build-env ,然后 在其他阶段需要引?的时候使? --from=build-env 参数即可。
最后我们简单的运?下该容器测试:
$ docker run --rm -p 8080:8080 cnych/docker-multi-stage-demo:latest
运?成功后,我们可以在浏览器中打开 http://127.0.0.1:8080/ping 地址,可以看到PONG返回。 现在我们就把两个镜像的?件最终合并到?个镜像??了。
文章图片
多阶段构建是一个新特性,需要 Docker 17.05 或更高版本的守护进程和客户端。对于那些努力优化 Dockerfiles 并使其易于阅读和维护的人来说,多阶段构建非常有用。
文章图片
推荐阅读
- 技术交流|【云原生】Docker 使用详解
- mysql|mysql优化方法有哪些((sql优化面试))
- java|宝塔防火墙内容处理的讲解
- 后端冲鸭|MybatisPlus代码生成器
- Java小知识难点解析|MybatisPlus之代码生成器模板
- mybtais-plus|MyBatisPlus中的代码生成器
- Java基础|mybatisPlus代码生成器
- Java开发|Mybatisplus的代码生成器
- mybatisplus|MybatisPlus之代码生成器(有此一篇即可)