Docker 系列 | 02 - Docker 核心概念:镜像 (Image) 详解

引言

在上一篇文章中,我们初步认识了 Docker 和容器化技术,了解了容器的轻量、高效和一致性等优势。本篇文章将深入讲解 Docker 的核心基石——**镜像 (Image)**。

如果您将容器比作一个运行中的程序实例,那么镜像就是这个程序的“蓝图”或“模版”。它是容器的静态、只读版本,包含了运行应用程序所需的所有文件、依赖、配置和环境变量。理解镜像的构成和工作原理,是掌握 Docker 的关键一步。

什么是 Docker 镜像?

Docker 镜像是一个轻量级、独立的可执行软件包,包含运行某个软件所需的一切:代码、运行时、库、环境变量和配置文件。它类似于虚拟机中的“ISO 镜像”或面向对象编程中的“类”,而容器就是“实例”或“对象”。

镜像的特点:

  • **只读 (Read-Only)**:镜像一旦创建就不能被修改。
  • **分层 (Layered)**:镜像由一系列只读的文件系统层组成。
  • **可移植 (Portable)**:镜像可以在任何支持 Docker 的环境中运行。
  • **版本化 (Versioned)**:镜像可以有不同的版本(通过 Tag 区分),方便回溯和管理。

镜像与容器的关系

简单来说:镜像是容器的基础,容器是镜像的运行实例。

  • 当你运行一个 Docker 镜像时,Docker 会在镜像的最上层添加一个可写的容器层。所有对容器的修改(例如创建、修改、删除文件)都只发生在这一可写层,而不会影响到基础镜像。
  • 这种分层机制使得多个容器可以共享同一个基础镜像的底层数据,节省了磁盘空间,并加速了容器的启动。

镜像与容器的关系示意图:

+———————————–+

容器(可读写层)
镜像层 D (只读)
镜像层 C (只读)
镜像层 B (只读)
镜像层 A (只读)

+———————————–+

(请您自行根据此描述绘制一个示意图,表示容器是镜像之上的一个可写层,而镜像本身由多个只读层组成。)

镜像的分层存储:UnionFS

Docker 镜像是通过 联合文件系统 (Union File System, UnionFS) 技术实现的。UnionFS 允许多个文件系统目录(称为“分支”或“层”)以一种透明的方式叠加在一起,形成一个单一的连贯文件系统。

  • 只读层:每个 Dockerfile 中的指令(如 FROM, RUN, COPY 等)都会创建一个新的只读层。这些层是堆叠起来的。
  • 可写层:当一个容器启动时,Docker 会在镜像的最顶层添加一个薄薄的可写层。所有容器运行时产生的数据变化(如文件修改、新增、删除)都只发生在这个可写层。

UnionFS 的优势:

  • 存储效率高:多个镜像可以共享相同的底层只读层,只存储一次。例如,所有基于 Ubuntu 镜像的应用都共享 Ubuntu 的基础层。
  • 快速构建:构建镜像时,如果某个层没有变化,可以直接复用缓存,加快构建速度。
  • 节省带宽:推送和拉取镜像时,只传输发生变化的层。

如何获取 Docker 镜像?

获取 Docker 镜像主要有两种方式:

1. 从 Docker Hub 或其他镜像仓库拉取 (Pull)

Docker Hub 是 Docker 官方提供的公共镜像仓库,包含了大量的官方镜像和社区贡献的镜像。

常用命令:docker pull

1
2
3
4
5
6
7
8
9
10
11
# 拉取最新的 Ubuntu 镜像
docker pull ubuntu

# 拉取指定版本的 Nginx 镜像
docker pull nginx:1.21.4

# 拉取 Redis 镜像,默认是最新版本
docker pull redis

# 检查本地已有的镜像
docker images

2. 通过 Dockerfile 构建 (Build)

当官方镜像无法满足需求时,我们可以编写 Dockerfile 来定义自己的镜像构建过程。Dockerfile 是一个包含一系列指令的文本文件,每条指令都会在构建过程中创建一个新的镜像层。

我们将在下一篇文章中详细讲解 Dockerfile,这里先看一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Dockerfile 示例

# 基于官方的 Ubuntu 镜像
FROM ubuntu:22.04

# 设置工作目录
WORKDIR /app

# 拷贝本地文件到镜像中
COPY . /app

# 安装依赖
RUN apt-get update && \
apt-get install -y python3 python3-pip && \
pip3 install flask

# 暴露端口
EXPOSE 5000

# 定义容器启动时执行的命令
CMD ["python3", "app.py"]

构建命令:docker build

1
2
3
# 在包含 Dockerfile 的目录中执行
# . 表示 Dockerfile 位于当前目录,-t 用于指定镜像名称和标签
docker build -t my-flask-app:1.0 .

镜像的标签 (Tag)

镜像的标签(Tag)用于区分同一个镜像的不同版本或不同变体。例如,ubuntu:latestubuntu:22.04 都表示 Ubuntu 镜像,但 latest 通常指向最新稳定版,而 22.04 则指向特定版本。

最佳实践:

  • 避免在生产环境中使用 latest 标签,因为它指向的版本可能会变动,导致环境不稳定。
  • 使用明确的版本标签,例如 nginx:1.21.4my-app:v1.2.3,以确保每次部署使用的都是相同的镜像。

结语

通过本篇文章,我们深入了解了 Docker 镜像的概念、分层原理以及获取镜像的两种主要方式。镜像作为容器运行的基础,其只读、分层的特性极大地提升了容器的效率和可移植性。

在下一篇文章中,我们将聚焦于如何构建自己的 Docker 镜像,详细解析 Dockerfile 的每一个指令,并带您一步步创建您的第一个定制化镜像。