个人学习docker整理的一些资料和demo,后续会根据新用到的功能不定期更新,有问题欢迎留言

背景

docker这个东西是什么就不介绍了,百度/google有很多,写这篇文章的初衷是记录一下自己在学习过程中的坑,避免后续可能会用到的情况下忘掉,其次给团队小伙伴入坑避避雷

docker安装

强调一下学一个东西,一定要先进官网,一般比较优秀的项目文档都写得很不错的,比如docker官网
image.png
首先还是要安装一下docker,点击download and install
image.png
安装完成后,打开终端工具,显示下图即安装成功
image.png

启动第一个容器

还记得刚才下载的docker desktop么,除了提供docker环境外,本身也提供一个对新手非常友好的图形化界面,打开docker desktop(默认会安装到“应用程序“),配置之类的可以暂时忽略,点击”dashboard”
image.png

会弹出一个图形化界面,此时默认在containers一栏,当前是没有任何容器的状态,可以看到中间有一段命令,复制执行一下
image.png

执行之后发现多了一个运行中的容器,端口号是80,此时打开浏览器输入127.0.0.1:80,
image.png

打开了一个docker的帮助文档,此时已经成功通过docker启动了第一个服务
image.png

基础概念

学习docker一定要搞明白 “容器” 和 “镜像” 的概念,这个也是前端工程师很少接触的东西

Docker架构图

docker架构简介: https://docs.docker.com/get-started/overview/
image.png

镜像(image)

这个便于理解的话,可以想象成有点类似于提供所需运行环境的一个模板,给后续容器运行时使用,镜像本身是无状态的,或者说预期是无状态的(只读),镜像内容会保持在build时的状态

官方话说是:Docker images由多个只读层组成,这些层是堆叠的,每个层都是上一层的变化的增量

容器(container)

依托于镜像提供的配置信息所创建的运行环境,各个容器之间相互独立,白话一点可以理解为启动了一台服务器

官方话来说是: Docker将一个读写文件系统分配给容器,作为其最后一层。这允许运行中的容器在其本地文件系统中创建或修改文件和目录

镜像/容器/仓库三者关系

image.png

大概的一个流程,获取镜像源,启动镜像生成容器,运行容器并绑定宿主机端口号,通过宿主机端口号即可访docker的服务,如”启动第一个容器”里所输入的命令

  1. docker run -d -p 80:80 docker/getting-started

-p 80:80,即将docekr的80端口绑到宿主机上

拥有一个自己的镜像

了解镜像/容器/仓库的概念之后,会发现如果想要通过docker启动一个自己的应用,首先需要构建自己的镜像,
这时候可以通过Dockerfile实现

关于DockerFile说明

创建一个hello world

新建一个文件夹,创建一个start.sh脚本

  1. mkdir helloWorld && cd helloWorld
  2. vim start.sh
  3. # start.sh 文件内容
  4. echo hello world
  5. vim Dockerfile

Dockerfile 文件内容

  1. # 基础镜像linux alpine版
  2. FROM alpine
  3. # 添加当前文件夹为根目录到容器的app文件夹
  4. ADD . /app/
  5. # 设置根目录为/app
  6. WORKDIR /app
  7. # 启动命令sh ./start.sh
  8. CMD [ "sh", "./start.sh"]

通过命令docker build -t xuan/test .生成镜像, 此时通过docker image ls即可查看镜像是生成

  1. # 依次为镜像名,标签名,镜像id,创建时间, 镜像大小
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. xuan/test latest af126024fa51 8 minutes ago 5.61MB

后续删除镜像等操作比较依赖image id,可以用该命令查询,在dashboard界面上也有相应显示
image.png

执行镜像

image.png
通过docker run xuan/test,即可运行镜像,可以看到echo 出了hello wold,说明镜像已经生成成功且可以运行

容器自动停止? why

但是这时候发现了一个问题,当使用docker ps -l查看容器状态时,发现已经自动停止服务了,这个是怎么回事呢?

  1. # 命令
  2. docker ps -l
  3. # 状态
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. 81b880ce6b37 xuan/test "sh ./start.sh" 2 minutes ago Exited (0) 2 minutes ago gracious_dewdney

image.png
docker run本身是执行一个进程,CMD命令为echo, echo执行结束后进程自然而然终止,如果想让docker一直保持运行状态,必须在前台挂起一个程序
(这个知识点后续会用到)

练习:封装一个node服务镜像

通过上例,已经认识到如何构建一个镜像,但是例子比较简单无法应用于实战中,接下来封装一个nodejs服务器,
这里用到的框架是koa

创建koa服务

创建一个文件夹,并新建package.json

  1. # 创建文件夹
  2. mkdir koa2Docker && cd koa2Docker
  3. # 初始化package.json
  4. npm init -y
  5. # 安装koa
  6. npm install koa --save
  7. # 创建app.js文件
  8. vim app.js

app.js内容如下:

  1. const koa = require('koa');
  2. const app = new koa();
  3. app.use((ctx, next) => {
  4. ctx.body = 'hello world xuan';
  5. })
  6. app.listen(3001, () => {
  7. console.log('已启动3001');
  8. });

koa的最小服务已经搭建完成,启动看一下是否有问题

  1. # 启动服务
  2. node app.js

浏览器打开地址localhost:3001,发现正常显示,说明服务正常,接下来看下怎么把这个服务封装成一个镜像
image.png

生成镜像

首先创建一个dockerfile

  1. # 基础镜像
  2. FROM node:15.6.0-alpine3.10
  3. # 添加当前文件到容器中的app
  4. ADD . /app/
  5. # 设置工作目录为/app
  6. WORKDIR /app
  7. # 安装包
  8. RUN npm i
  9. # 开放当前3001接口
  10. EXPOSE 3001
  11. # 默认启动命令
  12. CMD ["node", "app.js"]

创建完成后build一下 docker build -t xuan/hello .

  1. # ...以上胜率
  2. Step 5/6 : EXPOSE 3001
  3. ---> Running in 86dd3555883f
  4. Removing intermediate container 86dd3555883f
  5. ---> a9455b8dbf12
  6. Step 6/6 : CMD ["node", "app.js"]
  7. ---> Running in 41fd4f405d70
  8. Removing intermediate container 41fd4f405d70
  9. ---> c4f8645cf369
  10. Successfully built c4f8645cf369
  11. Successfully tagged xuan/hello:latest

此时镜像已经打包成功,可以通过docker images查看,或者通过desktop查看
image.png

运行测试

镜像生成成功后,来测试下是否生成成功,输入docker run xuan/hello ,此时控制台显示

  1. koa2Docker docker run xuan/hello
  2. 已启动3001

使用浏览器打开localhost:3001

image.png

并没有预期打开成功,而是显示无法访问,使用docker ps -a命令查看容器状况,发现容器本身是正常的

  1. hello docker ps -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 66eb8ba8d1d1 xuan/hello "docker-entrypoint.s…" 14 seconds ago Up 13 seconds 3001/tcp vibrant_moser

这是什么原因呢?
其实容器和本地机绑定需要依赖接口绑定才能进行访问的,
目前只开放了容器的3001接口,但是本地机是无法直接访问的,此时可以通过-p参数来指定端口映射,修改一下启动命令为:

  1. docker run -p 3001:3001 xuan/hello

重新打开浏览器, 可以正常访问服务
image.png
(注: 如果需要后台运行的话,可以加个 -d 命令)

进阶:持久化存储

为什么?

因为docker的镜像本身是无状态的不会记录后续再容器中的任何操作,例如在xuan/hello容器中修改中间件返回值,改为”edit”

  1. app.use((ctx, next) => {
  2. ctx.body = 'edit';
  3. })

重新进行docker run,新的容器没有任何变化,也就是无法保留任何不存在build镜像后的任何数据,这里就遇到了一个问题?
使用mysql容器的的话,怎么保存docker环境下的mysql数据表更新,毕竟reload容器会丢失全部data,这明显不是一个可以接受的事情

这里就引出docker的volume功能

Volume的类型

volume分为2种模式,Named Volumes, Bind Mounts,下面是区别:

Named Volumes Bind Mounts
Host Location Docker chooses You control
Mount Example (using -v
)
my-volume:/usr/local/data /path/to/data:/usr/local/data
Populates new volume with container contents Yes No
Supports Volume Drivers Yes No

Named Volumes

Named Volumes 是docker自己管理的volume,通过docker volumes create 创建

Bind Mounts

将宿主机与容器进行文件绑定映射,即不论在宿主机还是容器内进行文件修改,都会同步生效

怎么用?

这里分两种场景,一个是开发应用的时候需要宿主机和容器内的及时同步,另一个是mysql的数据同步,这里为了方便讲解我用的是bind mounts方式

app应用的更新

在上文更新中间件的情况下,发现如果在本地代码中进行修改,不会对容器有任何的影响,那么当有测试和开发的任务时,想要更新容器就只能够重复 打包镜像 -> 运行容器这个动作,对开发有着比较大的阻碍

那么怎么利用volume进行本地代码和docker容器的联动呢?

文件映射

上面讲解过bind mounts是将本地文件和docker文件进行绑定,那么就来试一下,重新运行命令

  1. docker run -p 3001:3001 -v /Users/xuan/Desktop/hello/koa2Docker:/app xuan/hello

修改中间件ctx.body = ‘edit’

  1. const koa = require('koa');
  2. const app = new koa();
  3. app.use((ctx, next) => {
  4. ctx.body = 'edit';
  5. })
  6. app.listen(3001, () => {
  7. console.log('.........3001');
  8. });

进入容器看下具体情况docker exec -it dafe1b24c301 /bin/sh,这里dafe1b24c301为容器id,可以通过docker ps -l查看,进入容器后默认在/app文件夹下,查看app.js检查下状况

  1. # 打开app.js
  2. vi app.js

app.js 可以看到app.js内部器是修改成功的
image.png

服务更新

刷新浏览器,发现没什么变化,这个其实是因为虽然修改了文件,但实际上服务没有重启,实际上平时写nodejs也是每次更新要手动重启下服务
image.png
重启下容器 docker restart dafe1b24c301,刷新浏览器
image.png
页面更新成功

pm2自动刷新

平时在开发的时候为了避免每次修改都要进行服务器重启操作,通常会使用pm2帮监听文件变化,一旦文件内容变动自动重启服务,帮助节约时间,这里同理

在dockerfile里添加pm2的全局安装,修改cmd命令,通过pm2-runtime app.js --watch进行启动

  1. FROM node:15.6.0-alpine3.10
  2. ADD . /app/
  3. WORKDIR /app
  4. RUN npm install pm2 -g
  5. RUN npm i
  6. EXPOSE 3001
  7. CMD ["pm2-runtime", "app.js"]

重新打包一个镜像并运行,重新打开浏览器,修改app.js文件内ctx.body = ‘pm2 start’

  1. docker build -t xuan/hello .
  2. docker run -p 3001:3001 -v /Users/xuan/Desktop/hello/koa2Docker:/app xuan/hello
  3. # log
  4. 2021-01-29T12:17:52: PM2 log: Launching in no daemon mode
  5. 2021-01-29T12:17:52: PM2 log: [Watch] Start watching app
  6. 2021-01-29T12:17:52: PM2 log: App [app:0] starting in -fork mode-
  7. 2021-01-29T12:17:52: PM2 log: App [app:0] online
  8. 已启动3001

command + R 刷新浏览器,发现内容已经修改
image.png

额外关注

突发奇想一下,从创建app应用到最终通过端口号访问服务,其实是没有任何依赖于宿主机的开发环境的,也就是说,在本机上,只要有docker环境,就可以进行正常业务的开发

这也是docker带来的便利之处

mysql持久存储案例

docker一般情况下推荐一个container只做一件事情,这样即使一个容器挂了也很容易回复和查找原因,所以通常会用单独的容器运行mysql

这里也就会遇到前文所说的问题:
镜像是无状态的,重新启动image的时候数据库保存的信息不会保留,不符合持久存储的预期

所以需要将数据库的data在宿主机同时保存一份,这样重新启动的时候会将本地的库同步到新的容器之中

文件映射

这里为了讲解方便直接用了docker-compose配置文件说明,这里不用太抠细节,后续会将docker-compose,可以找到volumes一行 ./mysql-data:/var/lib/mysql
将当前目录下的mysql-data和容器里的/var/lib/mysql进行关联

  1. version: "3.7"
  2. services:
  3. app:
  4. image: xuanlazy/koa:0.0.6
  5. container_name: app
  6. ports:
  7. - 3001:3001
  8. working_dir: /app
  9. volumes:
  10. - ./:/app
  11. environment:
  12. MYSQL_HOST: mysql
  13. MYSQL_USER: root
  14. MYSQL_PASSWORD: 123456
  15. MYSQL_DB: test
  16. mysql:
  17. image: mysql:latest
  18. container_name: sql
  19. ports:
  20. - 3307:3306
  21. volumes:
  22. - ./db:/var/lib/mysql
  23. environment:
  24. MYSQL_ROOT_PASSWORD: 123456
  25. MYSQL_DATABASE: test

启动服务后,发现当前目录下多了一个db文件夹,可以看到已经把当前mysql整库拖出来
image.png

测试

在mysql里创建一个表,并且插入数据,下面是现成的的sql:

  1. # 创建表
  2. CREATE TABLE IF NOT EXISTS runoob_tbl (
  3. runoob_id INT UNSIGNED AUTO_INCREMENT,
  4. runoob_title VARCHAR(100) NOT NULL,
  5. runoob_author VARCHAR(40) NOT NULL,
  6. submission_date DATE,
  7. PRIMARY KEY ( runoob_id )
  8. )ENGINE=InnoDB DEFAULT CHARSET=utf8;
  9. # 插入数据
  10. INSERT INTO runoob_tbl
  11. (runoob_title, runoob_author, submission_date)
  12. VALUES
  13. ("学习Dcoker", "test", NOW())
  14. # 查询sql
  15. select * from runoob_tbl;

存储成功后,停掉服务,重启的时候可以试下注释掉volumes,看下区别(这里操作比较复杂信息量也少就不具挂例子了)
image.png

其实答案应该也猜得到,有挂volumes的情况下之前存的数据可以再次查询到,注释掉的情况下相当于启用的是一个新数据库

进阶:多容器组合联动

为什么要多容器联动?

官方文档有写:

  • 很有可能需要以与数据库不同的方式扩展API和前端
  • 单独的容器可让您隔离版本和更新版本
  • 虽然您可以在本地使用数据库的容器,但可能要在生产环境中使用数据库的托管服务。然后,您不想随应用程序一起提供数据库引擎
  • 运行多个进程将需要一个进程管理器(容器仅启动一个进程),这增加了容器启动/关闭的复杂性

所以通常针对一个服务拆成多个容器,例如:koa + mysql + nginx服务,通常会分成koa,mysql,nginx三个容器,那么docker作为容器是完全使用沙箱机制,相互之间不会有任何接口,那么怎么进行联动呢?

network

其实docker提供了network能力,当两个容器在同一个网络的情况下就可以进行通讯,
使用 docker network ls 查看当前网络状况,默认情况下:

  1. NETWORK ID NAME DRIVER SCOPE
  2. e6a4baa5f217 bridge bridge local
  3. 66a3ddcbaa06 host host local
  4. 5d3adcd8922e none null local

具体含义可以查询 network文档
对于实际应用来说可以通过docker network create test-app新建一个新的网络

验证一下,分别用以下两条命令分别启动两台容器:

  1. # 一会进入的主机
  2. docker run -d
  3. --network test-app # 使用test-app 网络
  4. --network-alias start-1 # 网络名start-1
  5. --name test-host # 容器名
  6. docker/getting-started
  7. # 子服务
  8. docker run -d
  9. --network test-app
  10. --network-alias start-2
  11. --name test-child
  12. docker/getting-started

此时进入容器看一下, docker exec -it test-host /bin/sh ,找到开启的子服务网络别名start-2,直接用ping就好了ping start-2 ,可以看到已经链接成功
image.png

通过启动容器时指定 --network 可以让很方便的建立容器和容器之间的链接,例如mysql和应用的通讯

但是另一面每次都需要启动容器时手动指定network,公开port,挂载volume实在是有点累,且不方便规模化(可复制),这时候就需要docker compose,帮进行组装

docker compose

什么是docker compose?

官方给的定义: Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

翻译下其实就是,用于定义和运行多容器Docker应用程序的工具,通过YAML文件来配置应用程序的服务,使用一个命令读取配置并创建启动所有服务

安装

关于安装如果是最新的docker desktop是自带的, docker-compose version 就可以查看版本号

解读配置文件

想了想,还是通过配置文件反推比较好理解一些,下面便是一个简单的node + mysql的配置文件,来看看它到底做了哪些事?

docker-compose.yml

  1. version: "3.7"
  2. services:
  3. app:
  4. image: test
  5. container_name: app
  6. ports:
  7. - 3001:3001
  8. working_dir: /app
  9. volumes:
  10. - ./:/app
  11. environment:
  12. MYSQL_HOST: sql
  13. MYSQL_USER: root
  14. MYSQL_PASSWORD: test123456
  15. MYSQL_DB: test
  16. mysql:
  17. image: mysql:latest
  18. container_name: sql
  19. ports:
  20. - 3307:3306
  21. volumes:
  22. - /usr/local/var/mysql:/var/lib/mysql
  23. environment:
  24. MYSQL_ROOT_PASSWORD: test123456
  25. MYSQL_DATABASE: test

version — 定义当前配置文件所使用的文件格式版本,不同的版本可能会存在兼容问题,具体文档
services — 服务列表
app — 定义单例服务
image —所用镜像
container_name —容器名
ports —端口映射
working_dir —工作目录,等同于Dockerfile中的workdir
volumes — 等同于docker run -v
environment — 设置环境变量

更多配置信息: https://docs.docker.com/compose/compose-file/compose-file-v3/

启动容器

其实在services下的配置服务,大致可以理解为将dockerfile和docker run的很多信息展示在一个配置表中,例如 -v, -p,working_dir,方便统一管理

依旧是使用刚才的配置表,执行docker-compose up即可启动服务,通过desktop查看
image.png

这里需要注意一点docker-compose之所以能够让多个容器互相通信连在一起,并不是有多特殊,而是帮忙做了一些事情,例如创建网络
image.png
可以看到提示信息第一条就是create network “test_node_default”,通过docker network list查看,确实是新加了一条网络
image.png

常用指令整理

  • docker-compose up —部署应用,添加-d参数可以后台运行
  • docker-compose stop —停止所有应用
  • docker-compose rm —删除已停止的应用,但不会清除volumes / 镜像 / 网络
  • docker-compose restart —重启应用
  • docker-compose down —停止并删除运行中的应用
  • docker-compose ps —显所有容器示

更多指令:https://docs.docker.com/compose/reference/overview/

一些案例

链接mongoDB

docker-compose启动容器虽然在同一network下,但是容器间的ip还是不一样的,无法直接用127.0.0.1 / 0.0.0.0链接,需要修改一下url

  1. mongodb://root:mongodb@mongodb:27017/admin

DockerFile 配置详解

官方文档: https://docs.docker.com/engine/reference/builder/
DockerFile最佳实践: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

DockerHub 远程仓库

官方文档: https://www.docker.com/products/docker-hub
Docker Hub是Docker官方提供的托管存储库服务,用于与团队查找和共享容器映像。主要功能包括:

  • 专用存储库:推拉容器图像
  • 自动化构建:自动从GitHub和Bitbucket构建容器映像并将其推送到Docker Hub
  • 团队和组织:管理对私有存储库的访问
  • 官方映像:提取并使用Docker提供的高质量容器映像
  • 发布者图像:拉出并使用外部供应商提供的高质量容器图像。认证映像还包括支持并保证与Docker Enterprise的兼容性
  • Webhooks:成功推送到存储库以将Docker Hub与其他服务集成后触发动作

其实有点类似于github,默认pull的镜像源其实就于dockerhub,也可以push自己的镜像到线上
image.png

配置仓库

阿里云

如果是生产环境的话,还是推荐建一个私有仓库用来进行托管,这里使用的是阿里云的容器镜像服务,应该是免费的,可以自己看下
image.png

docker Registry

官方文档:https://docs.docker.com/registry/

貌似可以通过registry镜像搭建私人仓库,还没试过

docker-slim 镜像压缩

这个是关于docker镜像的压缩工具,可以帮助制作slim版本的镜像,仓库地址:点击这里

介绍

首先看下官方介绍,大致的意思不需要任何额外的操作,就可以获得最小的镜像,并且给出了个比较夸张的数字,优化30倍,看了下github有了9.6k,还是比较可靠的
image.png

原理

官方介绍:docker-slim will optimize and secure your containers by understanding your application and what it needs using various analysis techniques.

按官方文档来解释,docker-slim是通过依赖分析干掉镜像中没有产生使用的文件/内容,从而节约体积

试用测评

将刚才的demo用来做下实验,效果确实比较惊人

  1. FROM node:dubnium-buster-slim
  2. ADD . /app/
  3. WORKDIR /app
  4. RUN npm config set registry http://registry.npm.taobao.org && npm install pm2 -g && npm i
  5. EXPOSE 3001
  6. CMD ["pm2-runtime", "./app.js", "--watch"]

一个简单的dockerfile打包完之后,之前的情况下是588.52MB,打包后缩小了11倍以上,默默收藏
image.png

安装

下载地址: https://github.com/docker-slim/docker-slim/releases
image.png
image.png
将docekr-slim和docker-slim-sensor移动到/usr/local/bin文件夹下,这样可以在全局使用docker-slim命令,命令行输入 docker-slim -v ,显示版本信息表明安装成功
image.png

使用

在使用docker-slim对镜像进行压缩前需要提前打包一次,docker build -t test .
拿到打包生成的image ID,使用docker-slim build —target [image ID],即自动生成[image Name].slim格式的镜像,如下:
image.png

需要注意的坑

如果预期需要使用docker-slim进行压缩,那么不要使用npm scripts这样的命令,在当前版本里npm scripts是无法被依赖分析读取的,也就是说会干掉全局状态下的node_modules,导致应用无法被启动

例如:

  1. # 错误案例
  2. CMD ["npm", "start"] -> npm start 等于"start": "pm2-runtime app.js --watch"
  3. # 正确案例
  4. CMD ["pm2-runtime", "./app.js", "--watch"]

具体的issue: https://github.com/docker-slim/docker-slim/issues/150

配置github CI/CD

官方文档: https://docs.docker.com/ci-cd/github-actions/

更换docker源 —国内cdn

毕竟国外的资源部分情况下还是比较慢(慢的过分..),同理npm一般要换成淘宝源或者使用cnpm,这里的话我用的是阿里云的源

查看当前状态

输入 docker info 查看当前docker基础信息,行数比较多,可以直接 command + F 搜索 ”Registry”,一般情况下会显示:

  1. # 官方镜像源
  2. Registry: https://index.docker.io/v1/

阿里云镜像源

登录阿里云账号:https://www.aliyun.com/,找到容器镜像服务 -> 镜像工具 -> 镜像加速器,
image.png
image.png

设置Preferences

如果是最新的docker desktop可以略过前面的文字,直接找到这段,按流程操作,找到preferences,添加到json中,重启docker服务即可
image.png
发现多了一行Registry Mirrors即添加成功,此时可以重新docker pull [images]体验一下新的速度~
image.png

杂项知识

读懂docker tag

一般镜像名的tag都会带一些后缀,以node为例,如下图:
image.png

可以看到tag名多的眼花缭乱,第一次看大概率会懵逼,这里就大概解释下,其实所有的这些标签名都是对应基础镜像版本,如下:

linux发行版: alpine, debian
debian版本下linux代差:

  • buster Debian10 (目前node默认)
  • stretch Debian9
  • jessie Debian8
  • wheezy Debian7

slim为修饰词,如buster-slim,为buster版本下的删减版

这一下来看的话就比较清晰了,alpine和debian对比来看的话,alpine为linux的最小发行版本,貌似只有5M,debian是完整版本
当然这里还有个坑,docker的官方镜像一般都是用debian作为镜像的,在网上查了下资料,除了包大小区别外,执行机制也有区别,具体没研究过,如果用在生产环境的话还是优先考虑debian
image.png
image.png
关于debian版本代差的问题这个如果没有历史包袱的话默认buster就可以了,node的latest就是用的这个版本,其实可以选择slim版本,从官网上给出的代码包大小来看相差了6倍,对于内存占用敏感的话可以使用
image.png

具体怎么自己做slim 点击这里

dockerfile缓存机制

构建映像时,Docker将逐步Dockerfile执行您的指令, 并按指定的顺序执行每个指令。在检查每条指令时,Docker会在其缓存中查找可重用的现有映像,而不是创建新的(重复的)映像。
如果根本不想使用缓存,则可以使用命令--no-cache=true 上的选项docker build。但是,如果您确实让Docker使用其缓存,那么了解何时可以找到匹配的映像,这一点很重要。Docker遵循的基本规则概述如下:

  • 从已在缓存中的父映像开始,将下一条指令与从该基本映像派生的所有子映像进行比较,以查看是否其中一个是使用完全相同的指令构建的。如果不是,则高速缓存无效。
  • 在大多数情况下,只需将中的指令Dockerfile与子图像之一进行比较就足够了。但是,某些说明需要更多的检查和解释。
  • 对于ADDCOPY指令,将检查图像中文件的内容,并为每个文件计算一个校验和。在这些校验和中不考虑文件的最后修改时间和最后访问时间。在缓存查找期间,将校验和与现有映像中的校验和进行比较。如果文件中的任何内容(例如内容和元数据)发生了更改,则缓存将无效。
  • 除了ADDCOPY命令之外,缓存检查不会查看容器中的文件来确定缓存是否匹配。例如,在处理RUN apt-get -y update命令时,不检查容器中更新的文件以确定是否存在缓存命中。在这种情况下,仅使用命令字符串本身来查找匹配项。

缓存无效后,所有后续Dockerfile命令都会生成新映像,并且不使用缓存

原文地址:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

关于更多命令

docker cli 更多命令

官方文档: https://docs.docker.com/engine/reference/run/

docker-compose cli 更多命令

官方文档:https://docs.docker.com/compose/reference/overview/

相关资料