Docker 镜像包含了基础操作系统,以及应用程序运行所需的代码和依赖包
Docker 镜像可理解为 VM 中的模板,或 OOP 中的 Class
安装 Docker
使用 docker version
命令来检测客户端和服务端是否都已经成功运行
用户可以通过引用镜像的 ID 或名称来使用镜像。
一个镜像可以根据用户需要设置多个标签。
查找镜像
使用 docker search keyword
命令搜索Docker Hub 中的相关仓库
--filter
is-official=true
:只显示官方镜像is-automated=true
:只显示自动创建的仓库
--limit
:默认只显示25行结果,最多100行
拉取镜像
使用 docker image pull <repository>:<tag>
命令来拉取镜像(默认拉取标签为 latest 的镜像,但 latest 是一个非强制标签,不保证指向仓库中最新的镜像!)
-a
:拉取仓库中的全部镜像,此时无需指定 tag
查看本地镜像
使用 docker image ls
命令来查看拉取的镜像
--digests
:用于在本地查看镜像的 SHA256 摘要--filter
:用于过滤显示内容dangling=false
:指定仅返回非悬虚镜像(false)那些没有标签的镜像被称为悬虚镜像,在列表中展示为
<none>:<none>
。通常出现这种情况,是因为构建了一个新镜像,然后为该镜像打了一个已经存在的标签。当此情况出现,Docker会构建新的镜像,然后发现已经有镜像包含相同的标签,接着Docker会移除旧镜像上面的标签,将该标签标在新的镜像之上。before=python:3.7-slim
:参数为镜像名称或者ID,返回在其之前被创建的全部镜像since
:返回在指定镜像之后创建的全部镜reference="*:latest"
:仅显示标签为latest的镜像
--format
:通过Go模板对输出内容格式化"{{.Size}}"
:只返回各镜像的大小属性"{{.Repository}}: {{.Tag}}: {{.Size}}"
:只显示仓库、标签和大小信息
-q
:返回本地拉取的全部镜像的ID列表
删除镜像
使用 docker image prune
命令移除全部的悬虚镜像
-a
:Docker 会额外移除没有被任何容器使用的镜像
使用 docker image rm ID
命令删除指定镜像
快速删除镜像的方法:docker image rm $(docker image ls -q) -f
若被删除的镜像上存在运行状态的容器,那么删除操作不会被允许
若某个镜像层被多个镜像共享,那只有当全部依赖该镜像层的镜像都被删除后,该镜像层才会被删除。
一致性检验
从 Docker 1.10 版本开始,镜像就是一系列松耦合的独立层的集合。
镜像本身是一个配置对象,其中包含了镜像层的列表以及一些元数据信息。
镜像层才是实际数据存储的地方(比如文件等,镜像层之间是完全独立的,并没有从属于某个镜像集合的概念)。
镜像的唯一标识是一个加密ID,其值为配置对象本身的散列值。
每个镜像层也由一个加密ID区分,其值为镜像层本身内容的散列值。
镜像配置对象的散列值和各镜像层内容的散列值作为内容散列(Content Hash),被用于辨别内容修改。
而为节省网络带宽和存储空间,镜像传输(推送/拉取)时会被压缩,而压缩会导致前后内容散列可能不一致,所以还需要每个镜像层同时包含一个分发散列(Distribution Hash),用于标记压缩版镜像的一致性
查看镜像分层和配置
使用 docker image inspect <repository>:<tag>
命令查看镜像分层
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合
多个镜像之间可以并且确实会共享镜像层,这样可以有效节省空间并提升性能
使用 systemctl is-active docker
或 service docker status
检查 Docker daemon 的状态
启动容器
使用 docker container run <option> <image>:<tag> <app>
命令从镜像来启动容器
--name
:给容器命名-it
:选项使容器具备交互性并与终端进行连接-d
:表示后台模式,告知容器在后台运行-p host-port:container-port
:将 Docker 主机端口映射到容器内--restart
:重启策略,容器的一种自我修复能力,可以在指定事件或者错误后重启来完成自我修复always
:除非容器被明确停止,比如通过docker container stop
命令,否则该策略会一直尝试重启处于停止状态的容器。当 daemon 重启的时候,被明确停止的容器也会被重启unless-stopped
:与always
的最大区别,就是那些指定了--restart unless-stopped
并处于 Stopped (Exited) 状态的容器,不会在 Docker daemon 重启的时候被重启on-failure
:会在退出容器并且返回值不是0的时候,重启容器。就算容器处于 stopped 状态,在 Docker daemon 重启的时候,容器也会被重启
Docker客户端通过相应 API 调用 Docker daemon,Docker daemon 接收命令后检索 Docker 本地缓存是否有命令所请求的镜像。若本地缓存中无该镜像,则查询在 Docker Hub 中是否存在对应镜像。找到该镜像后,Docker 将镜像拉取到本地,存储在本地缓存当中
Docker daemon 通过位于/var/run/docker.sock
的本地 IPC/Unix socket 来实现 Docker 远程 API。
Docker 默认非 TLS 网络端口为2375,TLS 默认端口为2376
容器会随着其中运行应用的退出而终止
容器如果不运行任何进程则无法存在
在Linux中启动容器的命令如下:
$ docker container run -it ubuntu:latest /bin/bash
root@6dc20d508db0:/#
其中 -it
参数告诉 Docker 开启容器的交互模式并将当前 Shell 连接到容器终端,基于 ubuntu:latest
镜像启动容器,并在容器内部运行 Bash Shell 进程
保持后台运行
Ctrl-PQ 组合键会断开 Shell 和容器终端之间的链接,并在退出后保持容器在后台处于运行(UP)状态。
查看容器
使用 docker container ls
命令列出所有在运行(UP)状态的容器
- 选项
-a
:让 Docker 列出所有容器,甚至包括那些处于停止(Exited)状态的容器
启动容器中的进程
使用 docker container exec <options><container-name or container-id> <command/app>
命令在运行状态的容器中启动一个新进程
- 选项
-it
:使容器具备交互性
停止容器
使用 docker container stop <container-id or container-name>
命令停止运行中的容器,并将状态置为 Exited(0)
通过发送 SIGTERM 信号给容器内 PID 为 1 的进程达到目的。如果进程没有在 10s 之内得到清理并停止运行,那么会接着发送 SIGKILL 信号来强制停止该容器
重启容器
使用 docker container start <container-id or container-name>
命令重启处于停止(Exited)状态的容器
删除容器
使用 docker container rm <container-id or container-name>
命令删除容器
删除容器的最佳方式还是分两步,先停止容器然后删除。这样可以给容器中运行的应用/进程一个停止运行并清理残留数据的机会
快速删除容器的方法:docker container rm $(docker container ls -aq) -f
查看容器配置
使用 docker container inspect <container-id or container-name>
命令显示容器的配置细节和运行时信息
容器的生命周期
创建,运行,根据需要多次停止、启动、暂停以及重启,销毁
直至明确删除容器前,容器都不会丢弃其中的数据。就算容器被删除了,如果将容器数据存储在卷中,数据也会被保存下来。
应用的容器化
将应用整合到容器中并且运行起来的这个过程,称为“容器化”(Containerizing),有时也叫作“Docker化”(Dockerizing)。
完整的应用容器化过程主要分为以下几个步骤:
(1)编写应用代码。
(2)创建一个 Dockerfile,其中包括当前应用的描述、依赖以及该如何运行这个应用。
(3)对该 Dockerfile 执行 docker image build
命令。
(4)等待 Docker 将应用程序构建到 Docker 镜像中。
一旦应用容器化完成(即应用被打包为一个 Docker 镜像),就能以镜像的形式交付并以容器的方式运行了。
使用 docker image build [选项] <上下文路径/URL/->
命令基于上下文目录构建镜像
- 选项
-t
:指定 tag - 选项
-f
:指定 Dockerfile 的路径和名称 - 选项
--nocache=true
可以强制忽略对缓存的使用 - 选项
--squash
可以指定构建一个合并的镜像
在构建 Windows 镜像时,尽量避免使用 MSI 包管理器。因其对空间的利用率不高,会大幅增加镜像的体积。
Dockerfile
在 Docker 当中,包含应用文件的目录通常被称为构建上下文(Build Context)。通常将 Dockerfile 放到构建上下文的根目录下
- Dockerfile 中的注释行以
#
开头。
除注释之外,每一行都是一条指令(Instruction),格式为INSTRUCTION argument
Docker image build
命令会按行解析 Dockerfile 中的指令并顺序执行。
-
FROM
指令指定要构建的镜像的基础镜像,通常是 Dockerfile 中的第一条指令 -
LABEL
以键值对的方式为镜像添加自定义元数据 -
RUN
指令在 FROM 指定的基础镜像之上,新建一个镜像层来存储安装内容通过使用
&&
连接多个命令以及使用反斜杠()换行的方法,将多个命令包含在一个 RUN 指令中
-
COPY
指令将应用相关文件从构建上下文复制到了当前镜像中,并且新建一个镜像层来存储- 选项
--from
从之前阶段构建的镜像中仅复制生产环境相关的应用代码,而不复制生产环境不需要的构件
- 选项
-
WORKDIR
指令为 Dockerfile 中尚未执行的指令设置工作目录。该目录与镜像相关,并且会作为元数据记录到镜像配置中,但不会创建新的镜像层 -
EXPOSE
指令设置向主机暴露的端口号。这个配置信息会作为镜像的元数据被保存下来,并不会产生新的镜像层 -
ENTRYPOINT
指令指定当前镜像的入口程序。ENTRYPOINT 指定的配置信息也是通过镜像元数据的形式保存下来,而不是新增镜像层
查看执行命令历史
使用 docker image history <repository>:<tag>
命令查看在构建镜像的过程中都执行了哪些指令
使用 docker login
命令登录 DockerHub
为镜像添加标签
Docker 默认 Registry=docker.io、Tag=latest。但是Docker并没有给Repository提供默认值,而是从被推送镜像中的REPOSITORY属性值获取,当无权限访问该仓库时,需更改所要推送至的仓库名
使用 docker image tag <current-tag> <new-tag>
命令为指定的镜像添加一个额外的标签,并且不需要覆盖已经存在的标签
推送Docker镜像
使用 docker image push <repository>:<tag>
命令推送镜像至 Docker 仓库
多阶段构建(Multi-Stage Build)
多阶段构建方式使用一个 Dockerfile,其中包含多个 FROM 指令。每一个 FROM 指令都是一个新的构建阶段(Build Stage),并且可以方便地复制之前阶段的构件。
每一个 FROM 指令构成一个单独的构建阶段。各个阶段在内部从 0 开始编号。
利用构建缓存
第一次构建会拉取基础镜像,并构建镜像层,构建过程需要花费一定时间;第二次构建几乎能够立即完成。这就是因为第一次构建的内容(如镜像层)能够被缓存下来,并被后续的构建过程复用。
docker image build 命令会从顶层开始解析Dockerfile 中的指令并逐行执行。而对每一条指令,Docker 都会检查缓存中是否已经有与该指令对应的镜像层。如果有,即为缓存命中(Cache Hit),并且会使用这个镜像层;如果没有,则是缓存未命中(Cache Miss),Docker 会基于该指令构建新的镜像层。缓存命中能够显著加快构建过程。
一旦有指令在缓存中未命中(没有该指令对应的镜像层),则后续的整个构建过程将不再使用缓存。
尽量将易于发生变化的指令置于Dockerfile文件的后方执行,尽量从缓存中获益。
Docker 会计算每一个被复制文件的 checksum 值,并与缓存镜像层中同一文件的 checksum 进行对比。如果不匹配,那么就认为缓存无效并构建新的镜像层。