1、什么是 Docker 镜像?
一个 Docker 镜像 是一个轻量级、可执行的、独立的软件包,它包含了运行某个应用程序所需的一切:代码、运行时环境、系统工具、系统库和设置。
简单来说,镜像就是 一个容器的蓝图或模板。它本身是只读的。当你通过 docker run 命令启动一个镜像时,Docker 会基于这个只读的镜像创建一个可写的容器层,然后在这个隔离的进程中运行指定的应用程序。
一个生动的比喻:类和对象
Docker 镜像 就像面向对象编程中的 “类”。它定义了结构、属性和行为。
Docker 容器 就像这个类的一个 “实例化对象”。你可以创建多个基于同一个类的对象(运行多个基于同一个镜像的容器)。
2、核心特性
a. 分层存储
这是 Docker 镜像最核心、最精妙的特性。镜像由一系列只读层叠加而成,每一层代表 Dockerfile 中的一条指令。
Dockerfile 指令与层的对应关系:
FROM ubuntu:20.04:添加一个基础镜像层。
RUN apt-get update && apt-get install -y python3:在当前镜像之上添加一个新层,包含安装的 Python3。
COPY . /app:添加一个包含你的源代码的层。
CMD [“python3”, “/app/main.py”]:添加一个定义容器启动时运行命令的元数据层。
分层带来的巨大优势:
存储效率:如果多个镜像共享相同的基础层(例如,相同的 ubuntu:20.04 层),那么它们只需要在磁盘上存储一份。这极大地节省了空间。
快速构建:当你重新构建镜像时,Docker 会使用构建缓存。如果某条指令及其之前的层没有变化,Docker 会直接复用缓存中的层,而不是重新执行,这使得构建速度非常快。
可复用性:你可以基于一个已有的镜像添加新的层,来创建更 specialized 的镜像。
b. 内容寻址
现代 Docker 版本使用内容寻址存储。每一层都有一个基于其内容计算出来的唯一加密哈希值(ID)。如果层的内容发生变化,其哈希值也会改变。这保证了镜像内容的完整性和一致性。
c. 写时复制
这是容器能够快速启动的关键。当启动一个容器时,Docker 不会复制整个镜像文件,而是在只读的镜像层之上添加一个薄薄的可写容器层。
读取操作:容器需要读取文件时,直接从底下的只读镜像层读取。
写入操作:容器需要修改文件时,Docker 使用 写时复制 策略。它会将该文件从只读镜像层复制到可写容器层,然后所有修改都作用于这个副本。原始的镜像文件保持不变。
删除操作:当删除一个在只读层存在的文件时,Docker 会在可写层创建一个一种特殊的“白名单”文件来隐藏它。
3、镜像的组成与来源
a. 基础镜像
通常是镜像栈的最底层。它通常是一个精简的操作系统(如 Alpine Linux、Ubuntu、CentOS)或一个运行时环境(如 node:16
, python:3.9-slim
)。使用 FROM
指令指定。
b. 中间层
在基础镜像之上,通过 Dockerfile 中的指令(如 RUN, COPY, ADD)添加的层。
c. 镜像仓库
镜像存储在仓库中,类似于代码存储在 Git 仓库中。
公共仓库:最著名的是 Docker Hub。你可以从这里拉取官方镜像(如 nginx, redis)或社区发布的镜像。
私有仓库:企业可以使用 Docker Trusted Registry 或 Harbor 等搭建自己的私有仓库,用于存储内部镜像。
d. 镜像标签
标签用于标识同一个镜像的不同版本或变体。格式为 <仓库名>/<镜像名>:<标签>。
例如:nginx:1.21, nginx:latest, my-app:v1.2。
如果不指定标签,默认使用 :latest 标签。