Docker下如何实现镜像多阶级构建?
Docker下如何实现镜像多阶级构建?
在Docker早期版本中,对于编译型语言(例如C、Java、Go)的镜像构建,我们只能将应用的编译和运行环境的准备,全部放在一个Dockerfile
里面,这就导致我们构建出来的镜像体积很大,从而增加了镜像的存储和分发成本。
1、借助额外脚本构建
为了减小镜像体积,我们需要借助一个额外的脚本,将镜像的编译过程和运行过程分开。
- 编译阶段:负责将我们的代码编译成可执行的二进制文件。
- 运行时构建节点:准备应用程序运行的依赖环境,然后将编译好的可执行对象拷贝到镜像中。
以Go开发的一个HTTP服务为例,代码如下:
package main
import (
"fmt"
"io"
"net/http"
)
func getInfoHandler(w http.ResponseWriter, r *http.Request){
user := r.URL.Query().Get("user")
if user != "" {
io.WriteString(w,fmt.Sprintf("hello [%s]\n",user))
}else{
io.WriteString(w,"hello [Stranger]\n")
}
for k, v := range r.Header{
io.WriteString(w,fmt.Sprintf("%s=%s",k,v))
}
}
func health(w http.ResponseWriter, r *http.Request){
fmt.Fprintln(w,http.StatusOK)
}
func main() {
http.HandleFunc("/",getInfoHandler)
http.HandleFunc("/health",health)
http.ListenAndServe(":8080",nil)
}
将这个 Go 服务构建成镜像分为两个阶段:代码的编译阶段和镜像构建阶段。
我们构建镜像时,镜像中需要包含 Go 语言编译环境,应用的编译阶段我们可以使用 Dockerfile.build
文件来构建镜像。Dockerfile.build
的内容如下:
FROM golang:1.15
WORKDIR /go/src/httpServer/
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o http-server .
Dockefile.Build
可以帮助我们把代码编译成可执行的二进制文件,我们使用以下的Dockerfile
构建一个运行环境:
FROM alpine:latest
WORKDIR /root/
COPY http-server .
CMD ["./http-server"]
我们将应用的编译和运行环境的准备步骤,都放到一个 build.sh
脚本文件中,内容如下:
#!/bin/sh
echo Building http-server:build # 声明shell文件,然后输出开始构建信息
# 使用 Dockerfile.build 文件来构建一个临时镜像 http-server:build
docker build -t http-server:build . -f Dockerfile.build
# 用 http-server:build 镜像创建一个名称为 builder 的容器
# 该容器包含编译后的 http-server 二进制文件。
docker create --name builder http-server:build
# 使用docker cp命令从 builder 容器中拷贝 http-server 文件到当前构建目录
# 并且删除名称为 builder 的临时容器。
docker cp builder:/go/src/httpServer/http-server ./http-server
docker rm -f builder
# 输出开始构建镜像信息。
echo Building http-server:latest
# 构建运行时镜像,然后删除临时文件 http-server
docker build -t http-server:latest .
rm ./http-server
上面我们使用 Dockerfile.build
文件来编译应用程序,使用 Dockerfile
文件来构建应用的运行环境。然后我们通过创建一个临时容器,把编译后的 http-server
文件拷贝到当前构建目录中,然后再把这个文件拷贝到运行环境的镜像中,最后指定容器的启动命令为 http-server。
使用这种方式虽然可以实现分离镜像的编译和运行环境,但是我们需要额外引入一个 build.sh 脚本文件,而且构建过程中,还需要创建临时容器 builder 拷贝编译后的 http-server 文件,这使得整个构建过程比较复杂,并且整个构建过程也不够透明。
为了解决这种问题, Docker 在 17.05 推出了多阶段构建(multistage-build)的解决方案。
2、使用多阶段构建
Docker 允许我们在 Dockerfile
中使用多个FROM
语句,而每个 FROM
语句都可以使用不同基础镜像。最终生成的镜像,是以最后一条 FROM
为准,所以我们可以在一个 Dockerfile
中声明多个 FROM
,然后选择性地将一个阶段生成的文件拷贝到另外一个阶段中,从而实现最终的镜像只保留我们需要的环境和文件。多阶段构建的主要使用场景是分离编译环境和运行环境。
接下来我们使用多阶段构建将上述未使用多阶段构建的过程精简:
# 编译,生成http-server
FROM golang:1.15
WORKDIR /go/src/httpServer/
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o http-server .
# 构建运行时镜像
FROM alpine:latest
WORKDIR /root/
# --from=0 表示从第一阶段构建结果中拷贝文件到当前构建阶段
COPY --from=0 /go/src/httpServer/http-server .
CMD ["./http-server"]
构建镜像:
docker build -t http-server:latest .
3、多阶段构建的其他使用方式
3.1、为构建阶段命名
默认情况下,每一个构建阶段都没有被命名,可以通过 FROM
指令出现的顺序来引用这些构建阶段,构建阶段的序号是从 0 开始的。然而,为了提高 Dockerfile
的可读性,我们需要为某些构建阶段起一个名称,这样即便后面我们对Dockerfile
中的内容进程重新排序或者添加了新的构建阶段,其他构建过程中的 COPY
指令也不需要修改。
对上面的Dockerfile
进行优化:
# 编译,生成http-server
FROM golang:1.15 AS builder
WORKDIR /go/src/httpServer/
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o http-server .
# 构建运行时镜像
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /go/src/httpServer/http-server .
CMD ["./http-server"]
我们在第一个构建阶段,使用 AS 指令将这个阶段命名为 builder。然后在第二个构建阶段使用 --from=builder
指令,即可从第一个构建阶段中拷贝文件,使得 Dockerfile
更加清晰可读。
3.2、停止在特定的构建阶段
有时候,我们的构建阶段非常复杂,我们想在代码编译阶段进行调试,但是多阶段构建默认构建 Dockerfile
的所有阶段,为了减少每次调试的构建时间,我们可以使用 target 参数来指定构建停止的阶段。
例如,我只想在编译阶段调试 Dockerfile
文件,可以使用如下命令:
# docker build -t http-server:latest . --target builder
Sending build context to Docker daemon 6.656kB
Step 1/4 : FROM golang:1.15 AS builder
---> 40349a2425ef
Step 2/4 : WORKDIR /go/src/httpServer/
---> Using cache
---> 90a929dd37de
Step 3/4 : COPY main.go .
---> Using cache
---> fee18351e532
Step 4/4 : RUN CGO_ENABLED=0 GOOS=linux go build -o http-server .
---> Using cache
---> e7356e46c9c4
Successfully built e7356e46c9c4
Successfully tagged http-server:latest
3.3、使用现有镜像作为构建阶段
使用多阶段构建时,不仅可以从 Dockerfile
中已经定义的阶段中拷贝文件,还可以使用COPY --from指令从一个指定的镜像中拷贝文件,指定的镜像可以是本地已经存在的镜像,也可以是远程镜像仓库上的镜像。
例如,我们从上面编译环境镜像中拷贝出编译好的二进制文件
# 构建运行时镜像
FROM alpine:latest
WORKDIR /root/
COPY --from=http-server:latest /go/src/httpServer/http-server .
CMD ["./http-server"]
使用下面命令进行构建:
# docker build -t http-server:v2.0 .
Sending build context to Docker daemon 7.68kB
Step 1/4 : FROM alpine:latest
---> c059bfaa849c
Step 2/4 : WORKDIR /root/
---> Running in bc1e94677d94
Removing intermediate container bc1e94677d94
---> ab8143431ed0
Step 3/4 : COPY --from=http-server:latest /go/src/httpServer/http-server .
---> 133f25c0d3f0
Step 4/4 : CMD ["./http-server"]
---> Running in d625e202db78
Removing intermediate container d625e202db78
---> 00a4c8370bc9
Successfully built 00a4c8370bc9
Successfully tagged http-server:v2.0
又例如,当我们想要拷贝 nginx 官方镜像的配置文件到我们自己的镜像中时,可以在 Dockerfile 中使用以下指令:
COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/local/nginx.conf
Docker下如何实现镜像多阶级构建?的更多相关文章
- Docker下使用daocloud镜像加速(基于Centos6)
Docker加速器使用时不需要任何额外操作.就像这样下载官方Ubuntu镜像 实际操作(添加镜像源):在 /etc/sysconfig/docker下添加两条命令 other_args="- ...
- Docker下打包FastDFS镜像以及上传遇到的问题
官方地址:https://github.com/happyfish100/fastdfs 一.先下载个包,然后解压(自己找个目录下载即可) [root@localhost soft]# wget ht ...
- Docker下搭建Jenkins构建环境
首先需要搭建好docker环境的linux系统,这个教程多如牛毛,在此不再赘述. 然后编写一个dockerfile来生成一个镜像,dockerfile其实就是一系列命令的集合,有点像windows的批 ...
- Docker系列(三)Dockerfile 离线构建镜像
一.Dockfile介绍: Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docker程序将这些Dockerfil ...
- Docker基础内容之镜像构建
前言 Docker可以通过读取Dockerfile中的指令来自动构建图像.Dockerfile是一个文本文档,包含用户可以在命令行上调用的所有命令来组装一个图像.使用docker构建用户可以创建一个自 ...
- Docker的数据管理(下)——docke镜像的创建
Docker的数据管理(下)--docke镜像的创建 1.基于现有镜像创建 2.基于本地模板创建 3.基于 dockerfile 创建 4.Dockerfile 镜像操作常用命令 5.dockerfi ...
- docker在centos7系统镜像下遇到的一些问题
一.成功安装服务后发现无法启动 报错为:Failed to get D-Bus connection: Operation not permitted 系统为centos7官方版镜像,源和依赖之类的都 ...
- (一)构建基于ubuntu docker MySQL 5.6 镜像并推送到Docker Hub
一,创建目录二,文件准备三,构建四,使用五,在宿主机上连接docker 中的mysql六,推送镜像到Docker hub 一,创建目录 mkdir -p mysql/5.6 二,文件准备 注意执行脚本 ...
- 在sun jdk 8镜像基础上构建maven 3的docker镜像
2019独角兽企业重金招聘Python工程师标准>>> 在https://my.oschina.net/ytqvip/blog/1595054文章的sun jdk 8镜像基础上构建m ...
- Docker Buildx使用教程:使用Buildx构建多平台镜像
写在前边 记录一下前阵子在X86_64平台使用Docker Buildx构建多平台镜像的办法,包含但不限于构建ARM镜像. 构建环境 软件名 版本 Ubuntu 18.04.2 LTS Docker ...
随机推荐
- es6 形参的陷阱
先看代码: var x = 1; function s (a,y = function (){ x = 2 }){ var x = 1; y(); console.log(x) ...
- js 时间转时间戳
前言 有时候我们用时间插件,选择好时间后,需要把日期格式转化为时间戳,再传到后台 时间转时间戳 let time = Math.floor(new Date("2014-04-23 18:5 ...
- Git工作流介绍
前言 工作流其实不是一个初级主题,背后的本质问题其实是有效的项目流程管理和高效的开发协同约定,不仅是Git或SVN等SCM工具的使用. 集中式工作流 如果你的开发团队成员已经很熟悉Subversion ...
- 从 PostgreSQL 升级至 IvorySQL 4.0
本文作者:严少安,IvorySQL 贡献者. 本文为授权转载. 2024 年 8 月,我在<PG 12 即将退役,建议升级到 16.4>一文中提到,PostgreSQL 12 版本即将&q ...
- K8S组件详解
K8S的控制平面.和工作节点是集群正常运行的核心,通过这两部分的协同工作,K8S才能够实现高效的容器编排.管理.和自动化运维. K8S Kubernetes(简称K8s),是一个开源的容器编排平台,用 ...
- 记一次Linux虚拟机分配内存不足的处理方案
记一次Linux虚拟机硬盘空间不足的处理方案 **起因:**公司的服务器是windows的,而我需要一个基于Linux的dev环境,于是用vmvare创建了一个centos7的系统实例,里面安装mys ...
- 基于C#的学生社团管理系统(简单基础版)
前言 该系统为个人独立编写测试,也算自己的孩子吧,虽然基础功能简单但是也为了大家能有个可以借鉴,可以改写的模版使用,我就写个博客让大家参考,但是拒绝搬运售卖. * 正式介绍 该系统基于C#开发,使用V ...
- study Rust-4【所有权】这个太重要了!
由于Rust内存垃圾自动回收,那就得搞清楚这个所有权玩意.这个太重要了.因为关系到贯穿于你以后的程序编写. 几个概念: 一.移动 1.咱们一般语言,自己申请内存,自己管理和释放.就是new和free. ...
- springAPI对事物支持之XML式配置
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerData ...
- Spring解决创建单例bean,而存在线程不安全问题,的解决方案
一.线程安全问题都是由全局变量.静态变量和类的成员变量引起的.若每个线程中对全局变量.静态变量和类的成员变量只有读操作,而无写 操作,一般来说,这个全局变量是线程安全的,反之线程存在问题 二.因为Sp ...