dockerfile——镜像构建工具详解及案例

Dockerfile

Dockerfile是⼀个创建镜像所有命令的⽂本⽂件, 包含了⼀条条指令和说明, 每条指令构建⼀层, 通过docker build命令,根据Dockerfile的内容构建镜像,因此每⼀条指令的内容, 就是描述该层如何构建.有了Dockefile, 就可以制定⾃⼰docker镜像规则,只需要在Dockerfile上添加或者修改指令, 就可⽣成docker 镜像.

docker build 流程简介

docker build命令会读取Dockerfile的内容,并将Dockerfile的内容发送给 Docker 引擎,最终Docker 引擎会解析Dockerfile中的每⼀条指令,构建出需要的镜像。

  • 第⼀步,docker build会将 context 中的⽂件打包传给 Docker daemon。如果 context 中有.dockerignore⽂件,则会从上传列表中删除满⾜.dockerignore规则的⽂件。注意:如果上下⽂中有相当多的⽂件,可以明显感受到整个⽂件发送过程
  • 第二步,docker build命令向 Docker server 发送 HTTP 请求,请求 Docker server 构建镜像,请求中包含了需要的 context 信息。
  • 第三步,Docker server 接收到构建请求之后,会执⾏以下流程来构建镜像:
    • 创建⼀个临时⽬录,并将 context 中的⽂件解压到该⽬录下。
    • 读取并解析 Dockerfile,遍历其中的指令,根据命令类型分发到不同的模块去执⾏。
    • Docker 构建引擎为每⼀条指令创建⼀个临时容器,在临时容器中执⾏指令,然后 commit 容器,⽣成⼀个新的镜像层。
    • 最后,将所有指令构建出的镜像层合并,形成 build 的最后结果。最后⼀次 commit ⽣成的镜像 ID就是最终的镜像 ID。

为了提⾼构建效率,docker build默认会缓存已有的镜像层。如果构建镜像时发现某个镜像层已经被缓存,就会直接使⽤该缓存镜像,⽽不⽤重新构建。如果不希望使⽤缓存的镜像,可以在执⾏docker build命令时,指定--no-cache=true参数。

Docker 匹配缓存镜像的规则
遍历缓存中的基础镜像及其⼦镜像,检查这些镜像的构建指令是否和当前指令完全⼀致,如果不⼀样,则说明缓存不匹配。对于ADD、COPY指令,还会根据⽂件的校验和(checksum)来判断添加到镜像中的⽂件是否相同,如果不相同,则说明缓存不匹配。缓存匹配检查不会检查容器中的⽂件。⽐如,当使⽤RUN apt-get -y update命令更新了容器中的⽂件时,缓存策略并不会检查这些⽂件,来判断缓存是否匹配。最后,可以通过docker history命令来查看镜像的构建历史

Dockerfile关键字

FROM 设置镜像使⽤的基础镜像
MAINTAINER 设置镜像的作者
RUN 编译竟像时运⾏的脚步
CMD 设置容器的启动命令
LABEL 设置镜像标签
EXPOSE 设置镜像暴露的端⼝
ENV 设置容器的环境变量
ADD 编译镜像时复制上下⽂中⽂件到镜像中
COPY 编译镜像时复制上下⽂中⽂件到镜像中
ENTRYPOINT 设置容器的⼊⼝程序
VOLUME 设置容器的挂载卷
USER 设置运⾏ RUN CMD ENTRYPOINT的⽤户名
WORKDIR 设置 RUN CMD ENTRYPOINT COPY ADD 指令的⼯作⽬录
ARG 设置编译镜像时加⼊的参数
ONBUILD 设置镜像的ONBUILD 指令
STOPSIGNAL 设置容器的退出信号量

案例

素材

一个简单的http服务器,打印启动参数和一些环境变量

hello目录下

printEnv.go

package helloimport ("fmt""net/http""os"
)type EnvParam struct {
}func (*EnvParam) ServeHTTP(w http.ResponseWriter, r *http.Request) {env1 := os.Getenv("env1")env2 := os.Getenv("env2")fmt.Printf("env list : env1 = %s and env2 = %s", env1, env2)fmt.Println()fmt.Fprintf(w, "env list : env1 = %s and env2 = %s", env1, env2)
}

printStartParam.go

package helloimport ("flag""fmt""net/http"
)const (defaultStartUpParam = "default"
)var (param1 = flag.String("param1", defaultStartUpParam, "param1 to hello world")param2 = flag.String("param2", defaultStartUpParam, "param2 to hello world")
)func init() {flag.Parse()
}type PrintStartParam struct {
}func (*PrintStartParam) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Printf("start up params:     param1 = %s and param2 = %s ", *param1, *param2)fmt.Println()fmt.Fprintf(w, "start up params:     param1 = %s and param2 = %s ", *param1, *param2)
}

main.go

package mainimport ("fmt""httpServer/hello""net/http"
)func main() {fmt.Println("into main")http.Handle("/print/env", new(hello.EnvParam))http.Handle("/print/startup", new(hello.PrintStartParam))http.ListenAndServe(":80", nil)//	http.ListenAndServeTLS()
}
docker build构建镜像
FROM golang:1.18
ENV env1=env1value
ENV env2=env2value
MAINTAINER dongya
LABEL hello 1.0.0
#也可以用git拉取代码 RUN git clone https://gitee.com/dongyademo/helloworld.git
#这里从上下文中拷贝文件
COPY ./httpServer /go/src/httpServer
WORKDIR /go/src/httpServer
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .
EXPOSE 80
CMD ["./app","--param1=p1","--param2=p2"]
docker build -t httphello:1.0.0 -f Dockerfile .
运行测试
docker run -p 8081:80 -d --name testServer httphello:1.0.0[root@localhost example3]# curl http://localhost:8081/print/env
env list : env1 = env1value and env2 = env2value

查看镜像

[root@localhost example3]# docker images
REPOSITORY                        TAG       IMAGE ID       CREATED         SIZE
httphello                         1.0.0     8ae621256ce2   23 hours ago    988MB
alpine                            latest    f8c20f8bbcb6   2 weeks ago     7.38MB

这破镜像居然有988MB。最终运行的时候就运行一个二进制文件,所以编译时候的环境我们最终运行的时候是基本不需要的。如何缩小打出来的镜像呢呢,可以考虑用多阶段构建

多阶段构建

Docker 17.05版本以后,新增了Dockerfile多阶段构建。所谓多阶段构建,实际上是允许⼀个Dockerfile 中出现多个 FROM 指令。这样做有什么意义呢?

多个 FROM 指令的意义

多个 FROM 指令并不是为了⽣成多根的层关系,最后⽣成的镜像,仍以最后⼀条 FROM 为准,之前的 FROM 会被抛弃,那么之前的FROM ⼜有什么意义呢?

每⼀条 FROM 指令都是⼀个构建阶段,多条 FROM 就是多阶段构建,虽然最后⽣成的镜像只能是最后⼀个阶段的结果,但是,能够将前置阶段中的⽂件拷⻉到后边的阶段中,这就是多阶段构建的最⼤意义。

最⼤的使⽤场景是将编译环境和运⾏环境分离,⽐如,之前我们需要构建⼀个Go语⾔程序,那么就需要⽤到go命令等编译环境

Dockerfile

FROM golang:1.18
ADD ./httpServer /go/src/httpServer/
WORKDIR /go/src/httpServer
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .FROM alpine:latest
ENV env1=env1value
ENV env2=env2value
MAINTAINER dongya
LABEL hello 1.0.0 # 这个label 不是打包出来的镜像tag,只是config里的字段
WORKDIR /app/
#--from=0代表从阶段0进行操作
COPY --from=0 /go/src/httpServer/app ./
EXPOSE 80
CMD ["./app","--param1=p1","--param2=p2"]

除了用--from=0指定阶段,还可以通过as关键词,为构建阶段指定别名,也提⾼了可读性

FROM golang:1.18 as stage0
ADD ./httpServer /go/src/httpServer/
WORKDIR /go/src/httpServer
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .FROM alpine:latest
ENV env1=env1value
ENV env2=env2value
MAINTAINER dongya
LABEL hello 1.0.0
WORKDIR /app/
#--from=stage0代表从阶段stage0进行操作
COPY --from=stage0 /go/src/httpServer/app ./
EXPOSE 80
CMD ["./app","--param1=p1","--param2=p2"]

构建镜像

docker build -t httphello:1.0.0 -f Dockerfile .

查看镜像,这个时候我们发现打出来的镜像就小很多了,因为是基于一个轻量级linux alpine打包出来的。多阶段构建基于最后一个from来构建

[root@localhost example3]# docker images
REPOSITORY                        TAG       IMAGE ID       CREATED         SIZE
helloserver                       1.0.2     3eaa5b73a5b4   4 hours ago     13.9MB
httphello                         1.0.0     8ae621256ce2   23 hours ago    988MB
alpine                            latest    f8c20f8bbcb6   2 weeks ago     7.38MB
运行测试
docker run -p 8081:80 -d --name testServer helloserver:1.0.2[root@localhost example3]# curl http://localhost:8081/print/env
env list : env1 = env1value and env2 = env2value

ADD和COPY

  • ADD 与 COPY 不能拷⻉上下⽂以外的⽂件
  • COPY 命令语法格式

COPY语法

COPY <src> <dest> //将上下⽂中源⽂件,拷⻉到⽬标⽂件
COPY prefix* /destDir/ //将所有prefix 开头的⽂件拷⻉到 destDir ⽬录下
COPY prefix?.log /destDir/ //⽀持单个占位符,例如 : prefix1.log、
prefix2.log 等
  • 对于⽬录⽽⾔,COPY 和 ADD 命令具有相同的特点:只复制⽬录中的内容⽽不包含⽬录⾃身
COPY srcDir /destDir/ //只会将源⽂件夹srcDir下的⽂件拷⻉到 destDir ⽬录下
  • COPY 区别于ADD在于Dockerfile中使⽤multi-stage。可以拷贝不同阶段的文件
FROM golang:1.18 as stage0
ADD ./httpServer /go/src/httpServer/
WORKDIR /go/src/httpServer
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .FROM alpine:latest
ENV env1=env1value
ENV env2=env2value
MAINTAINER dongya
LABEL hello 1.0.0
WORKDIR /app/
#--from=stage0代表从阶段stage0进行操作
COPY --from=stage0 /go/src/httpServer/app ./
EXPOSE 80
CMD ["./app","--param1=p1","--param2=p2"]

ADD语法

ADD <src> <dest>

ADD 命令除了不能⽤在 multistage 的场景下,ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类的功能:

  • 解压压缩⽂件并把它们添加到镜像中,对于宿主机本地压缩⽂件,ADD命令会⾃动解压并添加到镜像
  • 从 url 拷⻉⽂件到镜像中,需要注意:url 所在⽂件如果是压缩包,ADD 命令不会⾃动解压缩

例如把nginx打包进镜像里

FROM golang:1.18 as stage0
ADD ./httpServer /go/src/httpServer/
WORKDIR /go/src/httpServer
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .FROM alpine:latest
ENV env1=env1value
ENV env2=env2value
MAINTAINER dongya
LABEL hello 1.0.0
ADD https://nginx.org/download/nginx-1.21.6.tar.gz /soft/
COPY nginx-1.21.6.tar.gz /soft/copy/
ADD nginx-1.21.6.tar.gz /soft/add/
WORKDIR /app/
#--from=stage0代表从阶段stage0进行操作
COPY --from=stage0 /go/src/httpServer/app ./
EXPOSE 80
CMD ["./app","--param1=p1","--param2=p2"]

启动进容器里查看对应目录即可

docker build --no-cache -t helloserver:1.0.3 -f Dockerfile .
docker run -p 8081:80 -d --name helloserver helloserver:1.0.3
docker exec -it d873894c0493 /bin/sh

注意:ADD⽬标⽂件位置要注意路径后⾯是否带 “/” ,带斜杠表示⽬录,不带斜杠表示⽂件名⽂件名⾥带有空格,需要再 ADD(或COPY)指令⾥⽤双引号的形式标明:

ADD "space file.txt" "/tmp/space file.txt"

CMD 和 ENTRYPOINT

CMD
# shell 格式
CMD <command>
# exec格式,推荐格式
CMD ["executable","param1","param2"]
# 为ENTRYPOINT 指令提供参数
CMD ["param1","param2"]

CMD 指令提供容器运⾏时的默认值,这些默认值可以是⼀条指令,也可以是⼀些参数。⼀个dockerfile中可以有多条CMD指令,但只有最后⼀条CMD指令有效。CMD参数格式是在CMD指令与ENTRYPOINT指令配合时使⽤,CMD指令中的参数会添加到ENTRYPOINT指令中。使⽤shell 和exec 格式时,命令在容器中的运⾏⽅式与RUN 指令相同。不同在于,RUN指令在构建镜像时执⾏命令,并⽣成新的镜像。CMD指令在构建镜像时并不执⾏任何命令,⽽是在容器启动时默认将CMD指令作为第⼀条执⾏的命令。如果在命令⾏界⾯运⾏docker run 命令时指定命令参数,则会覆盖CMD指令中的命令。

例子,前文也提到

FROM golang:1.18 as stage0
ADD ./httpServer /go/src/httpServer/
WORKDIR /go/src/httpServer
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .FROM alpine:latest
ENV env1=env1value
ENV env2=env2value
MAINTAINER dongya
LABEL hello 1.0.0
WORKDIR /app/
#--from=stage0代表从阶段stage0进行操作
COPY --from=stage0 /go/src/httpServer/app ./
EXPOSE 80
CMD ["./app","--param1=p1","--param2=p2"]

ENTRYPOINT

ENTRYPOINT指令有两种格式

# shell 格式
ENTRYPOINT <command>
# exec 格式,推荐格式
ENTRYPOINT ["executable","param1","param2"]

ENTRYPOINT指令和CMD指令类似,都可以让容器每次启动时执⾏相同的命令,但它们之间⼜有不同。⼀个Dockerfile中可以有多条ENTRYPOINT指令,但只有最后⼀条ENTRYPOINT指令有效。当使⽤shell格式时,ENTRYPOINT指令会忽略任何CMD指令和docker run 命令的参数,并且会运⾏在bin/sh -c。推荐使⽤exec格式,使⽤此格式,docker run 传⼊的命令参数将会覆盖CMD指令的内容并且附加到ENTRYPOINT指令的参数中。

CMD可以是参数,也可以是指令,ENTRYPOINT只能是命令;docker run 命令提供的运⾏命令参数可以覆盖CMD,但不能覆盖ENTRYPOINT。

# syntax=docker/dockerfile:1
FROM golang:1.18
ENV env1=env1value
ENV env2=env2value
MAINTAINER nick
LABEL hello 1.0.0
RUN git clone https://gitee.com/nickdemo/helloworld.git
WORKDIR helloworld
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .
EXPOSE 80
#ENTRYPOINT ["./app"]
#ENTRYPOINT ./app --param1=p11.0.3 --param2=p2
ENTRYPOINT ["./app","--param1=p1","--param2=p2"]
#CMD ["./app","--param1=p1","--param2=p2"]
#CMD ./app --param1=p1 --param2=p2
CMD ["--param1=p1","--param2=p2"]
替换CMD和ENTRYPOINT参数
#shell形式
docker run <image-name> --cmd "param1 param2 param3"
docker run --entrypoint "command" <image-name> "param1" "param2" "param3"
#exec命令形式
docker run --entrypoint '["command", "param1", "param2", "param3"]' <image-name>
docker run <image-name> --cmd '["command", "param1", "param2", "param3"]'

CMD和ENTRYPOINT一起使用

FROM alpine:latest
ENTRYPOINT ["echo"]
CMD ["Hello, World!"]

参数替换

docker run --entrypoint "echo" <image-name> "Hello, Docker!"

--entrypoint 参数必须在最前面,紧随其后的是镜像名称,然后是要传递的命令及参数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/581530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QString的处理及中文乱码问题

QString 是 Qt 框架中用于表示字符串的一个类。它提供了丰富的功能来处理 Unicode 字符串&#xff0c;使得国际化和本地化的应用程序开发更加简单。QString 与标准 C 的 std::string 类似&#xff0c;但提供了更多与 Unicode 和国际化相关的功能。 常用功能 判空 代码演示 is…

计算机网络复习1

概论 文章目录 概论计算机网络的组成功能分类性能指标&#xff08;搞清楚每个时延的具体定义&#xff09;分层结构协议、接口和服务服务的分类ISO/OSITCP/IP两者的不同 计算机网络的组成 组成部分&#xff1a;硬件&#xff0c;软件和协议&#xff08;协议&#xff1a;传输数据…

HPCC:高精度拥塞控制

HPCC&#xff1a;高精度拥塞控制 文章目录 HPCC&#xff1a;高精度拥塞控制摘要1 引言1.1 背景1.2 现有CC的局限性1.3 HPCC的提出 2 研究动机2.1 大型RDMA部署2.2 RDMA目标2.3 当前RDMA CC中的权衡DCQCNTIMELY 2.4 下一代高速CC 3 技术方案3.1 INT3.2 HPCC设计3.3 HPPC的参数 4…

【力扣题解】P404-左叶子之和-Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【力扣题解】 文章目录 【力扣题解】P404-左叶子之和-Java题解&#x1f30f;题目描述&#x1f4a1;题解&#x1f30f;总结…

计算机毕业设计-----ssm流浪猫狗救助管理系统

项目介绍 流浪猫狗救助管理系统。该项目分为前后台&#xff1b; 前台主要功能包括&#xff1a;会员的注册登陆,流浪猫狗知识&#xff0c;领养中心&#xff0c;团队活动&#xff0c;流浪宠物详情&#xff0c;申请领养等&#xff1b; 后台主要功能包括&#xff1a;管理员的用户…

IP多播多播多播

一、简述 1、IP地址 ABCDE类地址 类别网络号第一字节固定值范围A1字节0xxx0~127B2字节10xx128~191C3字节110x192~223D4字节1110224~239E1111 计算机网络——组播地址&#xff08;多播地址、D类地址&#xff09;详解 二、多播 1、参数设置 -----IP_ADD_MEMBERSHIP加入多播…

MySQL 核心模块揭秘 |《发刊词》

1. 为什么要写专栏&#xff1f; 我还在做业务系统研发的时候&#xff0c;有一段时间&#xff0c;系统不稳定&#xff0c;慢 SQL 很多。我们团队花了很长时间持续优化 SQL。 我们有一个表格&#xff0c;从慢查询日志里整理出了很多慢 SQL。其中一些 SQL&#xff0c;按照我们的…

React面试题

1. 什么是 React&#xff1f; React 是一个用于构建用户界面的 JavaScript 库。它由 Facebook 开发并开源&#xff0c;广泛应用于现代 Web 应用程序的开发中。 2. React 中的组件是什么&#xff1f; 组件是 React 中构建用户界面的基本单位。它们是可重用且自包含的代码块&a…

详细讲解Java使用EasyExcel函数来操作Excel表(附实战)

目录 前言1. EasyExcel类2. 原理分析3. demo4. 实战 前言 前阵时间好奇下载Excel&#xff0c;特意学习实战了该功能&#xff1a;详细讲解Java使用HSSFWorkbook函数导出Excel表&#xff08;附实战&#xff09; 现在发觉还有个EasyExcel也可专门用来读写Excel表 1. EasyExcel类…

flutter 使用高德地图

网址 引入高德地图组件 #地图插件amap_flutter_map: ^3.0.0# 定位插件amap_flutter_location: ^3.0.0 并执行命令 flutter pub get由于高德地图Flutter插件内不包含基础SDK包&#xff0c;所以需要单独引入地图基础SDK&#xff0c;在android文件加下的build.gradle文件中添加…

Spring Boot整合RocketMQ

pom.xml导入RocketMQ依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.2</version> </dependency>application.yml中添加配置 rocketmq:name-ser…

MySQL8 一键部署

#!/bin/bash ### 定义变量 mysql_download_urlhttps://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz mysql_package_namemysql-8.0.33-linux-glibc2.12-x86_64.tar.xz mysql_dec_namemysql-8.0.33-linux-glibc2.12-x86_64 mysql_download_…

uni-app uni.scss内置全局样式变量

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

python3 函数

Python 定义函数使用 def 关键字&#xff0c;一般格式如下&#xff1a; def 函数名&#xff08;参数列表&#xff09;&#xff1a;函数体 让我们使用函数来输出"Hello World&#xff01;"&#xff1a; >>> def hello() :print("Hello World!") &…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第三节 栈与堆,值类型与引用类型

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第三节 栈与堆&#xff0c;值类型与引用类型 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工…

EasyExcel 通过模板 导入、导出、下载模板

EasyExcel 通过模板 导入、导出、下载模板 import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; import java.io.…

Linux 系统管理和监控命令---- auditctl 命令

auditctl 是 Linux 审计系统&#xff08;audit system&#xff09;的一部分&#xff0c;它允许管理员配置审计规则&#xff0c;以跟踪和记录系统活动。这些规则可以帮助你监控对特定文件的访问、追踪特定用户的活动或记录系统调用。以下是 auditctl 的一些常用用法及其示例&…

django调用矩阵分解推荐算法模型做推荐系统

在Django中调用推荐算法模型来构建推荐系统&#xff0c;通常需要几个步骤&#xff1a;训练模型、保存模型、在Django中加载模型以及使用模型进行推荐。以下是这个过程的一个简化示例&#xff1a; 步骤 1: 训练推荐算法模型 首先&#xff0c;你需要使用Python的机器学习库&…

【项目】玩具租赁博客测试报告

目录 一、项目背景 二、项目功能 三、功能测试 一、项目背景 玩具租赁系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&#xff0c;同时将其部署到云服务器上。前端主要有十五个页面构成&#xff1a;用户注册、管理员注册、登录页、用户和管理…

Qt 中使用 MySQL 数据库保姆级教程(下)

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 上篇中我们安装好了 MySQL 数据库和 Navicat 软件&#xff0c;下面在 Qt 中尝试使用数据库 1. 在 Qt 中连接 MySQL 数据库&#…