镜像多阶段构建-YAML-Compose
一、镜像 Cache 机制
1.镜像Cache机制简述
- Docker Daemnon 通过 Dockerfile 构建镜像时,当发现即将新构建出的镜像与已有的某镜像重复时,可以选择放弃构建新的镜像,而是选用已有的镜像作为构建结果,也就是采取本地已经 cache 的镜像作为结果
# 在构建镜像的时候,会优先使用已缓存的镜像缩短构建时间
[root@localhost dockerfile-arg-env]# docker build -t nginx:v1 .=> CACHED [2/5] WORKDIR /data 0.0s=> CACHED [3/5] COPY nginx.repo /etc/yum.repos.d/nginx.repo 0.0s=> CACHED [4/5] RUN yum -y install nginx-1:1.26.1-1.el9.ngx.x86_64 && yum clean all 0.0s=> CACHED [5/5] RUN echo "daemon off;" >> /etc/nginx/nginx.conf
注:所以在构建镜像的时候要尽量使用缓存
2.查看镜像缓存
[root@localhost ~]# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLEBuild Cache 96 0 448.7MB 448.7MB
3.缓存注意事项
- ADD 命令、COPY 命令和RUN 命令都会产生缓存
- ADD 命令或者 COPY 命令后紧接的文件发生变化,则原则上不再使用缓存
- RUN 命令存在外部依赖,如安装命令变更或更新,用户可以使用参数 --no-cache 确保获取最新的外部依赖,命令为
docker build --no-cache -t="my_new_image"
注:只要一层不走缓存, 接下去的所有层都不走缓存(所以优先把不会变更的放前面,如静态的安装、配置命令)
二、Docker 多阶段构建
1.Go语言编译
1.编写Go文件
[root@localhost gin-demo]# pwd
/root/09-multistage-build/gin-demo
[root@localhost gin-demo]# cat main.go
package mainimport ("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")
}2.编写配置文件
[root@localhost gin-demo]# cat Dockerfile-build
FROM golang:1.20 as buildENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn # 使用代理WORKDIR /go/src/app
COPY . .RUN go mod download # 下载依赖
RUN CGO_ENABLED=0 go build -o /go/bin/app # 编译生成二进制文件3.制作镜像
[root@localhost gin-demo]# docker build -t go-app:v1 -f Dockerfile-build .4.从容器中提取二进制文件
[root@localhost gin-demo]# docker run -it go-app:v1 bash
root@a6f10e054cb9:/go/src/app#
# 另开终端
[root@localhost gin-demo]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a6f10e054cb9 go-app:v1 "bash" 22 seconds ago Up 22 seconds reverent_wilson[root@localhost gin-demo]# docker cp a6f10e054cb9:/go/bin/app .
Successfully copied 11.2MB to /root/09-multistage-build/gin-demo/.
[root@localhost gin-demo]# ls
app Dockerfile Dockerfile-build Dockerfile-distroless Dockerfile-multistage go.mod go.sum main.go5.测试
[root@localhost gin-demo]# ./app
# 另开终端
[root@localhost gin-demo]# curl 127.0.0.1:8080/ping
PONG[root@localhost gin-demo]#
2.Go语言制作镜像
配置文件:
[root@localhost gin-demo]# cat Dockerfile
FROM alpine:3.18
COPY app /
CMD ["/app"]
制作镜像:
[root@localhost gin-demo]# docker build -t go-app:v2 .
[root@localhost gin-demo]# docker images go-app:v2
REPOSITORY TAG IMAGE ID CREATED SIZE
go-app v2 eefe620ec200 57 seconds ago 18.6MB# 可以看到当前镜像的大小只有18.6MB,可以直接运行
[root@localhost gin-demo]# docker run -d go-app:v2
3a292993c95cd7c8eb46a964c636ba66efa73f153006568120fe4abb5e2f53d0
3.多阶段构建
注:一次性构建镜像,将之前的两步结合在一起
配置文件:
[root@localhost gin-demo]# cat Dockerfile-multistage
# Start by building the application.
FROM golang:1.20 as buildENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cnWORKDIR /go/src/app
COPY . .RUN go mod download
RUN CGO_ENABLED=0 go build -o /go/bin/app# Now copy it into our base image.
FROM alpine:3.18
COPY --from=build /go/bin/app /
CMD ["/app"]
制作镜像:
[root@localhost gin-demo]# docker build -t go-app:v3 -f Dockerfile-multistage .
[root@localhost gin-demo]# docker run -d go-app:v3
440f8219b75fd2c2ae26e1b8b3079345683093bf1ac3ef9e071edc7a9b05ce0c
[root@localhost gin-demo]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
440f8219b75f go-app:v3 "/app" 3 seconds ago Up 2 seconds practical_heisenberg
问题:多阶段构建主要在什么场景下使用?
- 适合编译型语言制作镜像时使用
4. Dockerfile 最佳实践 BP
-
不要安装无效软件包
-
应简化镜像中同时运行的进程数,理想状况下,每个镜像应该只有一个进程
-
如果无法避免同一镜像运行多进程时,应选择合理的初始化进程(init process)
-
最小化层级数
-
最新的Docker只有 RUN、COPY、ADD 创建新层,其他指令创建临时层,不会增加镜像大小。比如 EXPOSE 指令就不会生成新层。
-
多条 RUN 命令可通过连接符连接成一条指令集,以减少层数 &&
-
通过多阶段构建减少镜像层数
-
-
编写 Dockerfile 的时候,应该把变更频率低的编译指令优先构建,放在镜像底层,有效利用 build cache
-
复制文件时,每个文件应独立复制,这确保某个文件变更时,只影响该文件对应的缓存
-
选择可以满足业务需求最优的 base image
三、YAML语言
1.三种常见的数据格式
-
XML:可扩展标记语言, 可用于数据交换和配置
-
JSON:对象标记法, 主要用于数据交换和配置,不支持注释
-
YAML:不是一种标记语言, 主要用于应用启动时提供配置文件,大小写敏感

2.YAML 简介
YAML 语言实质上是一种通用的数据串行化格式,基本语法规则如下:
-
使用空白与缩进表示层次(有点类似 Python),可以不使用花括号和方括号。
-
可以使用 # 书写注释,比起 JSON 是很大的改进。
-
对象(字典)的格式与 JSON 基本相同,但 Key 不需要使用双引号。
-
数组(列表)是使用 - 开头的清单形式(有点类似 MarkDown)。
-
表示对象的 : 和表示数组的 - 后面都必须要有 空格。
-
可以使用 --- 在一个文件里分隔多个 YAML 对象
3.YAML 数据结构
-
对象:键值对的集合,又称为 映射(mapping)/ 哈希(hashes) / 字典(dictionary)
-
数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
-
纯量(scalars):单个的、不可再分的值
(1)数组
OS:- linux- macOS- Windows
(2)对象
Kubernetes:master: 1worker: 3
(3)复合结构
languages:- Ruby- Perl- Python
websites:YAML: yaml.org Ruby: ruby-lang.org Python: python.org Perl: use.perl.org
(4)纯量 标量
# 数值直接以字面量的形式表示:
number: 12.30# 布尔值用true和false表示
isSet: true# 时间采用 ISO8601 格式
iso8601: 2001-12-14t21:59:43.10-05:00
(5)字符串
# 字符串默认不使用引号表示
str: 这是一行字符串# 如果字符串之中包含 空格或特殊字符,需要放在引号之中
str: '内容: 字符串'# 单引号和双引号都可以使用,双引号不会对特殊字符转义
s1: '内容\n字符串'
s2: "内容\n字符串"# 字符串可以写成多行,从第二行开始,必须有一个单空格缩进
str: 这是一段多行字符串# 多行字符串可以使用|保留换行符,也可以使用>折叠换行
this: |FooBar
that: >FooBar# +表示保留文字块末尾的换行,-表示删除字符串末尾的换行
s1: |FooBarSecond s2: |+Foos3: |-Foo
补充:可以用工具互相转换,参考网站
https://www.json2yaml.com/
http://www.bejson.com/json/json2yaml/


注:多行文本转换,注意缩进和管道符;多个yaml写到一个文件里面,注意用---隔开
四、Docker Compose
1.简介
Compose负责实现对 Docker 容器集群的快速编排,一台服务器,多个容器编排- 定义和运行多个 Docker 容器的应用
- 允许用户通过一个单独的
docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project) - 服务 (service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
- 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
-
默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理
2.安装
补充说明:目前 Docker 官方用 GO 语言重写了 Docker Compose,并将其作为了 docker cli 的子命令,称为 Compose V2。你可以参照官方文档安装,然后将 v1 版的 docker-compose 命令替换为 docker compose,即可使用 Docker Compose
- 查看当前版本
[root@localhost ~]# docker compose version
Docker Compose version v2.40.3
3.compose应用模型
一个项目由以下几部分组成:
-
services
-
networks
-
volumes
-
configs
-
secrets

如何启动?
docker run --name=webapp \-v /data/app/conf:/etc/nginx/conf.d:ro \ -v /data/app/certs:/etc/nginx/certs:ro \-p 443:443 \--network frontend \--network backend \--restart=always \webapp:v1 # 站在容器的角度考虑,有很多参数我们是不清楚的,所以最好用compose.yml
Compose 声明文件:
services: # 描述 容器frontend:image: example/webappports:- "443:8043"- "8080:80"networks:- front-tier- back-tierconfigs:- httpd-config # 名字 secrets:- server-certificatebackend:image: example/databasevolumes:- db-data: /data/databasenetworks:- back-tiervolumes:db-data:driver: localdriver_opts:size: "10GiB"configs:httpd-config:file: ./httpd-config.confsecrets:server-certificate:file: ./xxx.crtnetworks:# The presence of these objects is sufficient to define themfront-tier: {}back-tier: {}
4.常用的顶级指令top-level
- - services 必须 -描述 容器-启动方式
- - volumes
- - networks
- - configs
- - secrets
五、Compose 项目
1.配置安装相关文件
注:关于这些文件已经上传到资源中了,可以自行下载
[root@localhost 01-compose-python-demo]# pwd
/root/08-docker-compose/dockercompose-example/01-compose-python-demo
[root@localhost 01-compose-python-demo]# ll
总用量 16
-rw-r--r-- 1 root root 565 2月 21 2025 app.py
-rw-r--r-- 1 root root 162 11月 12 15:05 compose.yml
-rw-r--r-- 1 root root 241 11月 12 15:01 Dockerfile
-rw-r--r-- 1 root root 12 2月 21 2025 requirements.txt
2.安装python环境
# 安装pip命令
[root@localhost 01-compose-python-demo]# dnf -y install python3-pip
# 安装依赖
[root@localhost 01-compose-python-demo]# pip install -r requirements.txt
3.修改配置文件
# 镜像配置文件
[root@localhost 01-compose-python-demo]# cat Dockerfile
FROM registry.cn-beijing.aliyuncs.com/xxhf/python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]# 定义compose声明文件
[root@localhost 01-compose-python-demo]# cat compose.yml
services:web:image: python-app:v1 #build: .ports:- "8000:5000"redis:image: registry.cn-beijing.aliyuncs.com/xxhf/redis:alpine
4.制作python镜像
注:在当前目录下使用docker compose命令
[root@localhost 01-compose-python-demo]# docker build -t python-app:v1 .
# 启动容器
[root@localhost 01-compose-python-demo]# docker compose up -d
[+] Running 2/2✔ Container 01-compose-python-demo-redis-1 Started 0.7s ✔ Container 01-compose-python-demo-web-1 Started 0.8s # 显示当前项目的容器
[root@localhost 01-compose-python-demo]# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
01-compose-python-demo-redis-1 registry.cn-beijing.aliyuncs.com/xxhf/redis:alpine "docker-entrypoint.s…" redis 2 minutes ago Up 31 seconds 6379/tcp
01-compose-python-demo-web-1 python-app:v1 "flask run" web 2 minutes ago Up 31 seconds 0.0.0.0:8000->5000/tcp, [::]:8000->5000/tcp# 测试
[root@localhost 01-compose-python-demo]# curl 127.0.1:8000/
Hello World! I have been seen 1 times.
[root@localhost 01-compose-python-demo]# curl 127.0.1:8000/
Hello World! I have been seen 2 times.
[root@localhost 01-compose-python-demo]# curl 127.0.1:8000/
Hello World! I have been seen 3 times.
[root@localhost 01-compose-python-demo]# curl 127.0.1:8000/ping
pong# 查看日志
[root@localhost 01-compose-python-demo]# docker compose logs -f# 停止运行
[root@localhost 01-compose-python-demo]# docker compose down
[+] Running 3/3✔ Container 01-compose-python-demo-web-1 Removed 10.2s ✔ Container 01-compose-python-demo-redis-1 Removed 0.3s ✔ Network 01-compose-python-demo_default Removed 0.2s
注:不管是docker compose up 还是docker compose ps,都省略了指定路径的-f选项,默认选择了当前目录下的compose.yml文件
