docker入门一文就够了
Published in:2023-06-28 | category: 前端 docker

个人学习 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
2
3
4
5
6
7
mkdir helloWorld && cd helloWorld

vim start.sh
# start.sh 文件内容
echo hello world

vim Dockerfile

Dockerfile 文件内容

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

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

1
2
3
# 依次为镜像名,标签名,镜像id,创建时间, 镜像大小
REPOSITORY TAG IMAGE ID CREATED SIZE
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
3
4
5
# 命令
docker ps -l
# 状态
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
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
3
4
5
6
7
8
# 创建文件夹
mkdir koa2Docker && cd koa2Docker
# 初始化package.json
npm init -y
# 安装koa
npm install koa --save
# 创建app.js文件
vim app.js

app.js 内容如下:

1
2
3
4
5
6
7
8
9
10
const koa = require("koa");
const app = new koa();

app.use((ctx, next) => {
ctx.body = "hello world xuan";
});

app.listen(3001, () => {
console.log("已启动3001");
});

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

1
2
# 启动服务
node app.js

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

生成镜像

首先创建一个 dockerfile

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

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

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

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

运行测试

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

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

使用浏览器打开 localhost:3001

image.png

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

1
2
3
➜  hello docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
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
2
3
app.use((ctx, next) => {
ctx.body = "edit";
});

重新进行 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
2
3
4
5
6
7
8
9
10
const koa = require("koa");
const app = new koa();

app.use((ctx, next) => {
ctx.body = "edit";
});

app.listen(3001, () => {
console.log(".........3001");
});

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

1
2
# 打开app.js
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
2
3
4
5
6
7
FROM node:15.6.0-alpine3.10
ADD . /app/
WORKDIR /app
RUN npm install pm2 -g
RUN npm i
EXPOSE 3001
CMD ["pm2-runtime", "app.js"]

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

1
2
3
4
5
6
7
8
docker build -t xuan/hello .
docker run -p 3001:3001 -v /Users/xuan/Desktop/hello/koa2Docker:/app xuan/hello
# log
2021-01-29T12:17:52: PM2 log: Launching in no daemon mode
2021-01-29T12:17:52: PM2 log: [Watch] Start watching app
2021-01-29T12:17:52: PM2 log: App [app:0] starting in -fork mode-
2021-01-29T12:17:52: PM2 log: App [app:0] online
已启动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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
version: "3.7"

services:
app:
image: xuanlazy/koa:0.0.6
container_name: app
ports:
- 3001:3001
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: 123456
MYSQL_DB: test
mysql:
image: mysql:latest
container_name: sql
ports:
- 3307:3306
volumes:
- ./db:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: test

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

测试

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建表
CREATE TABLE IF NOT EXISTS runoob_tbl (
runoob_id INT UNSIGNED AUTO_INCREMENT,
runoob_title VARCHAR(100) NOT NULL,
runoob_author VARCHAR(40) NOT NULL,
submission_date DATE,
PRIMARY KEY ( runoob_id )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 插入数据
INSERT INTO runoob_tbl
(runoob_title, runoob_author, submission_date)
VALUES
("学习Dcoker", "test", NOW())

# 查询sql
select * from runoob_tbl;

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

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

进阶:多容器组合联动

为什么要多容器联动?

官方文档有写:

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

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

network

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

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

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

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

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

# 子服务
docker run -d
--network test-app
--network-alias start-2
--name test-child
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
version: "3.7"
services:
app:
image: test
container_name: app
ports:
- 3001:3001
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: sql
MYSQL_USER: root
MYSQL_PASSWORD: test123456
MYSQL_DB: test
mysql:
image: mysql:latest
container_name: sql
ports:
- 3307:3306
volumes:
- /usr/local/var/mysql:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: test123456
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
2
3
4
5
6
FROM node:dubnium-buster-slim
ADD . /app/
WORKDIR /app
RUN npm config set registry http://registry.npm.taobao.org && npm install pm2 -g && npm i
EXPOSE 3001
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
3
4
5
# 错误案例
CMD ["npm", "start"] -> npm start 等于"start": "pm2-runtime app.js --watch"

# 正确案例
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/

相关资料

Prev:
sentry - 清理数据
Next:
前端监控 SDK 的一些技术要点原理分析