在Docker 17.05之后,Docker在构建中支持了多阶段构建,简单来说,Dockerfile中可以有多个FROM,这篇文章通过一个简单的示例来说明多阶段构建的使用场景和方式。


目录

  • 基本语法
  • 场景说明
    • 代码示例
    • 构建go语言应用
    • 构建go应用镜像
    • 运行go应用容器
  • 上述场景的其他解法
    • 解法示例Dockerfile
    • 分阶段方式的构建
    • 运行go应用容器
  • 总结

基本语法

FROM的三种语法格式如下所示:

语法格式1: FROM [--platform=<platform>] <image> [AS <name>]

语法格式2: FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

语法格式3: FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]


场景说明

比如有如下go语言的类似spring boot的web应用,提供在8080端口提供信息显示服务。此应用构建时需要go语言的编译环境,而一旦运行的时候则只需要一个Alpine容器即可,一般来说可以分两步进行,先编译,编译完生成的二进制文件再进行构建生成可执行的容器。

代码示例

liumiaocn:go liumiao$ cat basic-web-hello.go
package mainimport "fmt"
import "net/http"func Hello(response http.ResponseWriter, request *http.Request) {fmt.Fprintf(response, "Hello, LiuMiao, Welcome to go web programming...\n")
}func main() {http.HandleFunc("/", Hello)http.ListenAndServe(":8080", nil)
}
liumiaocn:go liumiao$

构建go语言应用

执行命令:docker pull golang:1.15.3-alpine3.12

可以看到此编译环境即使是alpine方式也是不小的,如果运行时使用此镜像作为Base镜像大小比较浪费,如果用在边缘设备上基本上不太可行。

liumiaocn:go liumiao$ docker images |grep 1.15.3-alpine3.12
golang                                                               1.15.3-alpine3.12               d099254f5fc3        7 days ago          299MB
liumiaocn:go liumiao$

使用此镜像进行构建

liumiaocn:go liumiao$ docker run --rm -it -v `pwd`:/build golang:1.15.3-alpine3.12 sh
/go # cd /build
/build # ls
basic-web-hello.go
/build # go build -o http-server basic-web-hello.go
/build # ls
basic-web-hello.go  http-server
/build #

可以看到已经成功构建出来所需要的二进制文件http-server了

构建go应用镜像

此处使用一个Alpine镜像即可运行此go语言应用了,我们这里使用如下简单的Dockerfile来实现

liumiaocn:go liumiao$ cat Dockerfile
FROM alpine:3.12.1 WORKDIR /target
COPY    http-server /target/EXPOSE  8080
ENTRYPOINT ["/target/http-server"]
liumiaocn:go liumiao$

然后即可进行镜像的构建了

liumiaocn:go liumiao$ docker build -t test-go:latest .
Sending build context to Docker daemon  6.453MB
Step 1/5 : FROM alpine:3.12.1---> d6e46aa2470d
Step 2/5 : WORKDIR /target---> Running in da7d4cae785c
Removing intermediate container da7d4cae785c---> ae974a6c826c
Step 3/5 : COPY    http-server /target/---> 3fce2c8c9c1c
Step 4/5 : EXPOSE  8080---> Running in dc98c1a16510
Removing intermediate container dc98c1a16510---> 395a0c569fb1
Step 5/5 : ENTRYPOINT ["/target/http-server"]---> Running in e30b500061ea
Removing intermediate container e30b500061ea---> da9281ce2a5f
Successfully built da9281ce2a5f
Successfully tagged test-go:latest
liumiaocn:go liumiao$

可以看到构建的镜像大小也比较适中

liumiaocn:go liumiao$ docker images |grep test-go
test-go                                                              latest                          da9281ce2a5f        2 minutes ago       12MB
liumiaocn:go liumiao$

运行go应用容器

这里使用docker run命令运行此容器,也可以使用其他各种方式,比如docker-compose或者在kubernetes中

liumiaocn:go liumiao$ docker run --rm -p 8080:8080 test-go

在另外的终端或者浏览器中进行确认此8080端口启动的go的web应用

liumiaocn:go liumiao$ curl http://localhost:8080
Hello, LiuMiao, Welcome to go web programming...
liumiaocn:go liumiao$

可以看到已经可以正常运行了

上述场景的其他解法

上述场景是一个非常常见的开发过程的编译、构建和运行阶段的操作,比较典型的就是构建阶段所用到的很多依赖和环境在最终的结果中可能只是部分需要,所以导致的问题就是要分开来做,构建产生二进制文件,然后将此二进制文件拿出来然后再进行构建,而在Docker 17.03之后的这个版本,现在有了新的解法了。

解法示例Dockerfile

首先来看一个示例解法,使用如下Dockerfile可以直接解决此问题

liumiaocn:go liumiao$ cat Dockerfile
ARG  GOLANG_VER=1.15.3
ARG  ALPINE_VER=3.12
FROM golang:${GOLANG_VER}-alpine${ALPINE_VER} as go-builder-1.15.3WORKDIR /buildCOPY basic-web-hello.go /build
RUN  cd /build && go build -o http-server basic-web-hello.goFROM alpine:${ALPINE_VER}WORKDIR /targetCOPY --from=go-builder-1.15.3 /build/http-server /target/EXPOSE  8080
ENTRYPOINT ["/target/http-server"]
liumiaocn:go liumiao$

可以看到这个Dockerfile和普通的Dockerfile略有区别,就是感觉是两个Dockerfile拼接在一起的,后续的Dockerfile中对于前面的关联仅仅在于所生成的二进制文件,它的好处?IAAC算不算一个回答?算。原本需要手工操作才能完成的连接工作,将此二进制文件和其相应的Dockerfile进行关联,但是考虑到比如此二进制文件的版本、支持的操作系统的体系架构、权限设定、传输产生的不完整性风险等,以及相关的Dockerfile的版本变化以及人为的误操作因素等,这么小的一个点也可能产生很多问题。而在一个Dockerfile中就没有这个问题了,因为输入的go语言应用,输出的是最终的应用镜像,没有中间的其他结果,那我们来看一下这种方式下的构建过程。

分阶段方式的构建

liumiaocn:go liumiao$ docker build -t go-app .
Sending build context to Docker daemon  3.072kB
Step 1/11 : ARG  GOLANG_VER=1.15.3
Step 2/11 : ARG  ALPINE_VER=3.12
Step 3/11 : FROM golang:${GOLANG_VER}-alpine${ALPINE_VER} as go-builder-1.15.3---> d099254f5fc3
Step 4/11 : WORKDIR /build---> Running in 64c975a9affe
Removing intermediate container 64c975a9affe---> 11f7f0fed685
Step 5/11 : COPY basic-web-hello.go /build---> e0fae581e05b
Step 6/11 : RUN  cd /build && go build -o http-server basic-web-hello.go---> Running in 025158d79879
Removing intermediate container 025158d79879---> 1cb815aa110f
Step 7/11 : FROM alpine:${ALPINE_VER}---> d6e46aa2470d
Step 8/11 : WORKDIR /target---> Using cache---> ae974a6c826c
Step 9/11 : COPY --from=go-builder-1.15.3 /build/http-server /target/---> 7671328fc17e
Step 10/11 : EXPOSE  8080---> Running in 9db998af6571
Removing intermediate container 9db998af6571---> cf5362c2063a
Step 11/11 : ENTRYPOINT ["/target/http-server"]---> Running in d51adf74a6e2
Removing intermediate container d51adf74a6e2---> 0dfeb1ca46df
Successfully built 0dfeb1ca46df
Successfully tagged go-app:latest
liumiaocn:go liumiao$

可以看到构建的过程包括了编译和最终结果的生成,而我们所关心的是这个镜像是否像我们期待的那样没有包括go的编译环境里面几百兆的内容呢?执行docker images可以看到

liumiaocn:go liumiao$ docker images |grep go-app
go-app                                                               latest                          0dfeb1ca46df        7 seconds ago       12MB
liumiaocn:go liumiao$

正是我们所期待的结果,接下来我们运行一下此go应用,看看是否能够正常动作。

运行go应用容器

同样这里也使用docker run命令运行此容器

liumiaocn:go liumiao$ docker run --rm  -d -p 8080:8080 go-app
3f3b951caab2c91f00157966571aae6c90c08029b65e80dbb8d91f1b49888228
liumiaocn:go liumiao$ docker ps |grep go-app
3f3b951caab2        go-app                          "/target/http-server"   6 seconds ago       Up 4 seconds          0.0.0.0:8080->8080/tcp                                               trusting_shamir
liumiaocn:go liumiao$

在另外的终端或者浏览器中进行确认此8080端口启动的go的web应用

liumiaocn:go liumiao$ curl http://localhost:8080
Hello, LiuMiao, Welcome to go web programming...
liumiaocn:go liumiao$

可以看到已经可以正常运行了

总结

实际上分阶段构建主要应用了Docker 17.03之后的FROM as语法,如果as省略的话from参数可以直接使用0这样的需要进行引用,结合起来还是能够实现很多有用的功能的。从本质上来说Dockerfile是单根模式的,但是在实际使用中,有时会出现可能会需要两个不同的Base镜像的内容的可能,但是在本质上并没有改变其单根的特点,比如本文中最终所生成的go语言应用,它的根是Alpine镜像,它继承了Alpine镜像的所有内容,但是同时又需要另外一个镜像中的某个文件,这实际上是一个非常常见的场景,也可以看出Docker也是一直根据用户需求在不断地进行功能演化。

Dockerfile实践之多阶段构建相关推荐

  1. 2021年 最新 多阶段构建dockerfile实现java源码编译打jar包并做成镜像

    多阶段构建指在Dockerfile中使用多个FROM语句,每个FROM指令都可以使用不同的基础镜像,并且是一个独立的子构建阶段.使用多阶段构建打包Java应用具有构建安全.构建速度快.镜像文件体积小等 ...

  2. Dockerfile多阶段构建

    多阶段构建 之前的做法:在Docker17.05版本之前,构建Docker镜像,通常采用两种方式:1.全部放入一个Dockerfile一种方式是将所有的构建过程全都包含在一个Dockerfile中,包 ...

  3. Dockerfile构建Nginx镜像、镜像优化(多阶段构建,最小化镜像构建)

    Dockerfile创建镜像 Dockerfile 有以下指令选项: FROM MAINTAINER RUN CMD EXPOSE ENV ADD COPY ENTRYPOINT VOLUME USE ...

  4. dockerfile 中的 multi-stage 多阶段构建

    在应用了容器技术的软件开发过程中,控制容器镜像的大小可是一件费时费力的事情.如果我们构建的镜像既是编译软件的环境,又是软件最终的运行环境,这是很难控制镜像大小的.所以常见的配置模式为:分别为软件的编译 ...

  5. 从Docker镜像构建演化史来了解多阶段构建的影响

    现在很多开发者都会慢慢习惯在开发环境通过Docker来构建开发环境,有时候可能会有环境移植的问题,所以需要我们写好一套Dockerfile来构建相关的开发镜像,既然说到镜像,那我想问问大家了解Dock ...

  6. Docker 镜像多阶段构建实战总结

    文章目录 Docker 镜像多阶段构建实战总结 一 背景 二 实践步骤 2.1 只通过一个 Dockerfile 来构建[方案一] 2.2 多个 Dockerfile 实现多阶段构建[方案二] 2.3 ...

  7. 【前端部署第四篇】使用 Docker 构建缓存及多阶段构建优化单页应用

    大家好,我是山月,这是我最近新开的专栏:前端部署系列.包括 Docker.CICD 等内容,大纲图示如下: 示例代码开源,置于 Github 中,演示如何对真实项目进行部署上线. simple-dep ...

  8. Dockerfile实践优化建议

    本文讲的是Dockerfile实践优化建议[编者的话]Dockerfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docke ...

  9. docker 多阶段构建

    多阶段构建 之前的做法 在 Docker 17.05 版本之前,我们构建 Docker 镜像时,通常会采用两种方式: 全部放入一个 Dockerfile 一种方式是将所有的构建过程编包含在一个 Doc ...

最新文章

  1. 由于市场判断失误 希捷降低收入预期
  2. python compile正则_Python 正则表达式:compile,match
  3. python剑指offer 包含min函数的栈
  4. 求 1 到 n 的所有数的约数和
  5. poj 1958 Strange Towers of Hanoi
  6. spring整合dubbo服务消费和发现入门示例
  7. vba动态二维数组_VBA实战技巧05: 动态调整数组以存储所需数据
  8. java打印结果横向排列_Java8排列组合(6行代码实现)
  9. 该终端已停用_宣杭老线停用,勾庄、三墩、仓前、老余杭、瓶窑三千多亩地待开发...
  10. No portmap or rpcbind service is running on this host
  11. bash 不是内部或外部命令_Python安装时提示“Python不是内部或外部命令...”解决方法...
  12. Micropython教程之TPYBoard DIY金属探测仪实例演示(萝卜学科编程教育)
  13. redis系列二: linux下安装redis
  14. 毕设外文文献查找方法
  15. DICOM世界观●开篇
  16. Safe Browsing
  17. 苹果自带高德地图搜索周边功能
  18. AutoCAD料表提取到Excel方法介绍
  19. R语言 关于h2o深度学习的一些心得
  20. cmd窗口执行cnpm报错记录:FullyQualifiedErrorId : UnauthorizedAccess或者因为在此系统上禁止运行脚本。有关详细信息,请参阅https。。

热门文章

  1. 2022年我国出生率预测,恐怕。。。
  2. 5v转12v升压电路
  3. 护卫神php返回404,护卫神IIS设置public目录为根目录的解决办法
  4. 搭建内网穿透实现访问windows远程桌面
  5. k8s 安装nfs_K8S 指定 nfs 挂载
  6. 解决maya2020阿诺德渲染器保存图像存在色差变暗的情况或问题
  7. python12306买票_Python爬虫之12306-买票器小白源码
  8. 数据科学概论Learning Road Map
  9. python去噪笔记
  10. 微信常用设备android22,微信(com.tencent.mm) - 8.0.0 - 应用 - 酷安