1. 概述

这个交付链路把传统部署痛点、镜像构建、仓库分发和容器运行串成一条标准路径。

Docker 是目前最流行的容器化平台,自 2013 年发布以来彻底改变了软件的开发、交付和运行方式。它允许开发者将应用及其所有依赖打包到一个轻量级、可移植的容器中,在任何 Linux 环境上一致地运行。

1.1 为什么需要 Docker?

在传统的软件部署中,有一个经典问题:"在我机器上能跑啊!"

  • 开发者的 Windows/Mac 环境和生产环境的 CentOS 不同
  • 应用依赖的 Python 3.11 版本和生产环境用的 Python 3.6 不一致
  • 数据库版本、系统库、环境变量都有差异

Docker 的解决方案极其简洁:把应用和环境一起打包。这个打包物就是"镜像(Image)",运行时就是"容器(Container)"。

Docker 的出现解决了以下痛点:

痛点 传统方案 Docker 方案
环境不一致 写复杂的部署文档 标准化的 Dockerfile
依赖冲突 虚拟环境隔离 每个容器独立文件系统
部署缓慢 手动配置服务器 docker run 秒级启动
资源浪费 虚拟机占用数 GB 空间 容器共享内核,MB 级开销
扩展困难 重新部署整个服务 容器编排自动扩缩容

1.2 Docker 的应用场景

  • 开发环境标准化:团队统一使用同一镜像,消除"在我机器上能跑"的问题
  • 微服务架构:每个服务独立容器化,独立部署和扩展
  • CI/CD 流水线:Docker 镜像作为标准交付物,从构建到部署路径一致
  • DevOps 实践:开发环境和生产环境一致,减少运维风险
  • 本地测试:快速启动 MySQL、Redis、Nginx 等依赖服务而不污染宿主机

1.3 学习路线

本系列计划从零开始,系统性地学习 Docker 容器化技术:

  1. Docker 简介与核心概念(本文) — 容器、镜像、仓库三大核心概念
  2. Docker 安装与基础命令 — 各平台安装、容器生命周期管理
  3. Dockerfile 编写指南 — 镜像构建的最佳实践
  4. Docker Compose 编排 — 多容器应用管理
  5. 数据持久化 — Volume 和 Bind Mount
  6. 网络配置 — 容器网络模型与通信
  7. 镜像仓库 — Docker Hub 与私有仓库搭建
  8. Docker 安全 — 权限控制和安全最佳实践
  9. Docker 监控与日志 — 容器监控和日志收集
  10. CI/CD 集成 — Docker 在自动化流水线中的应用

前置知识:

  • Linux 基本命令操作
  • 了解进程、文件系统等操作系统的概念

2. Docker 核心概念

Docker 核心技术原理

这个原理视图把容器背后的三类内核能力放在一起:Namespace 提供隔离视图,Cgroup 提供资源边界,UnionFS 提供镜像分层和容器可写层。理解这三点,才能把容器和虚拟机的边界真正分清楚。

2.1 什么是容器?

容器是一种轻量级的操作系统级虚拟化技术。与虚拟机(VM)不同,容器共享宿主机的操作系统内核,但通过 Linux 内核的 Namespace 和 Cgroup 技术实现进程隔离和资源限制。

容器 vs 虚拟机对比:

ASCII
┌─────────────────────────────┐  ┌─────────────────────────────┐
│    虚拟机 (VM)               │  │    容器 (Container)          │
│                             │  │                             │
│ ┌───────┐ ┌───────┐       │  │ ┌───────┐ ┌───────┐       │
│ │ App A │ │ App B │       │  │ │ App A │ │ App B │       │
│ ├───────┤ ├───────┤       │  │ ├───────┤ ├───────┤       │
│ │  Libs │ │  Libs │       │  │ │  Libs │ │  Libs │       │
│ ├───────┤ ├───────┤       │  │ └───┬───┘ └───┬───┘       │
│ │GuestOS│ │GuestOS│       │  │     │         │           │
│ └───┬───┘ └───┬───┘       │  │ ┌───┴─────────┴───┐       │
│     │         │           │  │ │  Docker Engine   │       │
│ ┌───┴─────────┴───┐       │  │ └────────┬─────────┘       │
│ │ Hypervisor      │       │  │          │                 │
│ └────────┬────────┘       │  │ ┌────────┴────────┐       │
│          │                │  │ │  Host OS (Linux) │       │
│ ┌────────┴────────┐       │  │ └─────────────────┘       │
│ │   Host OS       │       │  │                             │
│ └─────────────────┘       │  └─────────────────────────────┘

关键区别:

特性 虚拟机 容器
启动时间 30-60 秒 毫秒到秒级
磁盘占用 GB 级别(含完整 OS) MB 级别(仅应用 + 依赖)
内核 独立内核 共享宿主机内核
资源开销 高(Hypervisor 层开销) 极低(仅 Namespace/Cgroup)
隔离性 完全隔离 进程级隔离(较弱)
密度 一台服务器数十台 一台服务器数百上千个
迁移性 依赖 Hypervisor 标准 OCI 镜像,跨平台

2.2 三大核心组件

Docker 由三个核心概念构成:

1. 镜像(Image) — 打包规范

镜像是一个只读的、包含应用及其所有依赖的打包文件。它由多层文件系统叠加而成,每一层代表一个 Dockerfile 中的指令。

ASCII
镜像层(只读):
┌─────────────────┐  ← CMD ["python", "app.py"]        (第5层)
├─────────────────┤  ← COPY app.py /app/               (第4层)
├─────────────────┤  ← RUN pip install flask            (第3层)
├─────────────────┤  ← RUN apt-get install python       (第2层)
├─────────────────┤  ← FROM ubuntu:22.04                (第1层/基础层)
└─────────────────┘

每一层只记录与上一层的差异(增量)。这就是 Docker 镜像高效的原因——多个镜像可以共享相同的基础层。

2. 容器(Container) — 运行实例

容器是镜像的运行实例,在只读镜像层之上添加一个可写层:

ASCII
容器运行态:
┌─────────────────┐  ← 可写层(容器运行时修改的内容)       (第6层/临时层)
├─────────────────┤  ← CMD ["python", "app.py"]          (第5层/镜像)
├─────────────────┤  ← COPY app.py /app/                 (第4层/镜像)
├─────────────────┤  ← RUN pip install flask              (第3层/镜像)
├─────────────────┤  ← RUN apt-get install python         (第2层/镜像)
├─────────────────┤  ← FROM ubuntu:22.04                  (第1层/镜像)
└─────────────────┘

容器停止后,可写层的修改默认不会保存。如果要持久化数据,需要使用 Volume 或 Bind Mount。

3. 仓库(Registry) — 镜像分发中心

  • Docker Hub:官方公共仓库(hub.docker.com),包含数百万个公开镜像
  • 私有仓库:企业自建的镜像仓库(如 Harbor、Nexus)
  • 云仓库:阿里云 ACR、AWS ECR、GCR 等

镜像的命名格式:[仓库地址/]镜像名[:标签]

TEXT
docker pull nginx:latest           # Docker Hub 上的 nginx 最新版
docker pull registry.example.com/myapp:v1.0  # 私有仓库的指定版本

3. Docker 核心技术原理

3.1 Namespace — 进程隔离

Linux Namespace 是容器的隔离基础。每个容器拥有独立的 Namespace,使其看起来像是一台独立的机器。

Namespace 类型 隔离内容 Linux 内核版本
PID 进程号空间 2.6.24
Network 网络设备、协议栈、端口 2.6.29
Mount 文件系统挂载点 2.6.19
UTS 主机名和 NIS 域名 2.6.19
IPC System V IPC 和 POSIX 消息队列 2.6.19
User 用户和用户组 ID 3.8

Namesapce 的工作方式: 当 Docker 启动一个容器时,会为这个容器的每个进程创建独立的 Namespace,进程在容器内看到的 PID 是 1(类似在独立操作系统中),但在宿主机上仍有一个真实的 PID。

TEXT
宿主机视角:
PID 10023 → 容器内 PID 1 (nginx master)
PID 10024 → 容器内 PID 2 (nginx worker)
PID 10025 → 容器内 PID 3 (nginx worker)

容器内视角:
PID 1 (nginx master)
PID 2 (nginx worker)
PID 3 (nginx worker)

3.2 Cgroup — 资源限制

Control Groups(cgroup)负责限制容器可以使用的资源量。Docker 通过 cgroup 实现:

bash
# 限制容器最多使用 1 个 CPU 核心和 512MB 内存
docker run --cpus=1 --memory=512m nginx

# cgroup 在宿主机上的对应路径
ls /sys/fs/cgroup/cpu/docker/<container_id>/
ls /sys/fs/cgroup/memory/docker/<container_id>/

关键限制参数:

参数 作用 示例
--cpus 限制 CPU 核心数 --cpus=1.5
--memory 限制最大内存 --memory=2g
--memory-swap 限制 Swap 用量 --memory-swap=4g
--blkio-weight 限制磁盘 I/O 权重 --blkio-weight=500
--pids-limit 限制进程数 --pids-limit=100

3.3 UnionFS — 分层文件系统

Docker 镜像采用 Union 文件系统(如 OverlayFS、AUFS)实现分层存储。核心思想是:多个只读层叠加在一起,对外呈现为一个完整的文件系统。

ASCII
OverlayFS 工作原理:

┌─────────────────────────┐
│     容器视角的文件系统     │
│  (merged)               │
├─────────────────────────┤
│  上层 (upperdir)        │ ← 容器的可写层
├─────────────────────────┤
│  下层 (lowerdir)         │ ← 镜像的多个只读层
│  layer5                  │
│  layer4                  │
│  layer3                  │
│  layer2                  │
│  layer1 (基础镜像)       │
└─────────────────────────┘

写时复制(Copy-on-Write): 当容器需要修改镜像层中的文件时,不会直接修改只读层,而是把文件复制到可写层再进行修改。这样可以:

  1. 节省空间:多个容器共享相同的镜像层
  2. 提高性能:不修改原始层,不需要复制整个镜像
  3. 保持镜像一致性:镜像始终是只读的

4. Docker 架构

Docker 架构:Client、Daemon 与 Runtime

这张图说明 Docker CLI 并不直接运行容器,而是通过 API 与 dockerd 通信,再由 containerd、runc 和 OCI 运行时规范完成镜像拉取、容器创建和生命周期管理。

4.1 C/S 架构

Docker 采用客户端-服务器架构:

ASCII
┌─────────┐      REST API       ┌───────────┐        ┌─────────────┐
│ docker  │ ◄─────────────────► │ dockerd   │ ◄────► │ 容器 1       │
│ (客户端) │   (Unix Socket)   │ (守护进程)  │        ├─────────────┤
└─────────┘                     │           │        │ 容器 2       │
                                │           │ ◄────► ├─────────────┤
                                │           │        │ 容器 3       │
                                └───────────┘        └─────────────┘
  • dockerd:Docker 守护进程,运行在宿主机上,负责管理镜像、容器、网络、数据卷等
  • docker CLI:命令行客户端,通过 REST API 与 dockerd 通信
  • containerd:底层容器运行时(从 Docker 1.11 开始分离),负责容器的生命周期管理
  • runc:OCI 容器运行时规范的标准实现,真正创建和运行容器的组件

4.2 通信方式

Docker 客户端与守护进程通过 REST API 通信,默认使用 Unix Socket:

bash
# 默认通信方式(本地)
/var/run/docker.sock

# 也可以配置为 TCP(远程管理)
dockerd -H tcp://0.0.0.0:2375

# 客户端指定远程守护进程
docker -H tcp://remote-server:2375 ps

4.3 OCI 标准

Docker 推动了 OCI(Open Container Initiative)标准的发展,使得容器技术更加标准化和开放:

  • OCI Runtime Spec:定义容器的运行时规范(runc 是其参考实现)
  • OCI Image Spec:定义镜像的打包规范
  • OCI Distribution Spec:定义镜像的分发规范

这意味着不仅仅 Docker 可以创建和运行容器——Podman、containerd、CRI-O 等都能运行符合 OCI 标准的容器。

5. Docker 常用命令速览

在深入学习之前,先快速了解最常用的命令:

bash
# 镜像管理
docker pull nginx                # 拉取镜像
docker images                    # 列出本地镜像
docker rmi nginx                 # 删除镜像
docker build -t myapp .          # 从 Dockerfile 构建镜像

# 容器生命周期
docker run nginx                 # 创建并启动容器
docker run -d --name web nginx   # 后台运行并命名
docker ps                        # 列出运行中的容器
docker ps -a                     # 列出所有容器(包括已停止的)
docker stop web                  # 停止容器
docker start web                 # 启动已停止的容器
docker rm web                    # 删除容器

# 容器交互
docker logs web                  # 查看容器日志
docker exec -it web bash         # 进入容器执行命令
docker cp file.txt web:/tmp/     # 复制文件到容器
docker top web                   # 查看容器内进程

# 资源管理
docker stats                     # 查看所有容器的资源使用
docker inspect web               # 查看容器详细信息
docker system df                 # 查看 Docker 磁盘使用
docker system prune              # 清理未使用的资源

6. 验证方法

6.1 确认 Docker 已安装

bash
# 检查 Docker 版本
docker --version

# 预期输出
# Docker version 26.0.0, build 2ae903e

# 检查 Docker 守护进程状态
sudo systemctl status docker

# 预期输出(部分)
# ● docker.service - Docker Application Container Engine
#    Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabled)
#    Active: active (running) since ...

6.2 运行 Hello World 验证

bash
# 最经典的 Docker 验证方法
docker run hello-world

# 预期输出:
# Hello from Docker!
# This message shows that your installation appears to be working correctly.

# To generate this message, Docker took the following steps:
#  1. The Docker client contacted the Docker daemon.
#  2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
#     (amd64)
#  3. The Docker daemon created a new container from that image which runs the
#     executable that produces the output you are currently reading.
#  4. The Docker daemon streamed that output to the Docker client, which sent it
#     to your terminal.

6.3 验证 Namespace 隔离

bash
# 启动一个后台容器
docker run -d --name test-vm nginx:alpine

# 查看容器 PID
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' test-vm)
echo "容器在宿主机上的 PID: $CONTAINER_PID"

# 对比宿主机和容器内的 PID 空间
echo "--- 宿主机视角的进程 ---"
ps aux | grep nginx | head -5

echo ""
echo "--- 容器内视角的进程 ---"
docker exec test-vm ps aux

# 查看容器的 Namespace
lsns -p $CONTAINER_PID

7. 常见问题

Q1: Docker 和虚拟机哪个更适合我?

如果需要在不同操作系统上运行应用(如 Linux 上的 Windows 应用),需要完整的内核隔离,选虚拟机。如果应用是 Linux 原生应用,追求快速部署和资源效率,选 Docker 容器。大多数 DevOps 场景同时使用二者——VM 提供隔离,容器提供密度。

Q2: Docker 容器能运行 Windows 应用吗?

Linux 容器不能直接运行 Windows 应用。但 Docker Desktop for Windows 可以使用 Hyper-V 后台运行 Linux 容器,也可以使用 Windows 容器(基于 Windows Server Core)。生产环境建议保持宿主机和容器 OS 类型一致。

Q3: 容器内的 root 用户安全吗?

容器内的 root 并不等同于宿主机的 root(受 User Namespace 限制),但仍有一定的风险。最佳实践是使用 --user 参数以非 root 用户运行容器进程,或者在镜像创建时设置非 root 用户。

Q4: 容器重启后数据会丢失吗?

默认情况下会的。容器的可写层在容器删除后会丢失。要持久化数据,需要使用 Volume(docker run -v mydata:/data)或 Bind Mount(docker run -v /host/path:/container/path)。

Q5: 为什么 Docker 镜像有时候很大?

主要原因是基础镜像过大(如 ubuntu:22.04 约 77MB,但含工具链的镜像更大)。可以通过使用 alpine 版本(约 5MB、含 musl libc)或 distroless 镜像来减小体积。后续 Dockerfile 的文章会详细介绍镜像瘦身技巧。

Q6: Docker 和 containerd 是什么关系?

containerd 是 Docker 从 1.11 版本开始分离出来的底层容器运行时组件。Docker 在 containerd 之上提供了完整的开发者工具链(Dockerfile、docker build、docker-compose 等)。Kubernetes 也可以直接使用 containerd(跳过 Docker 层)。

Q7: Docker 支持哪些架构?

Docker 支持 x86_64(amd64)、ARM64(aarch64)、ARMv7、s390x、ppc64le 等多种架构。拉取镜像时会自动选择对应架构的版本,也可以使用 --platform 参数指定。

8. 从“能运行”到“可交付”的理解方式

很多人第一次接触 Docker,会把它理解成“轻量虚拟机”或者“一个运行应用的命令”。这两个说法都有一点道理,但都不完整。更准确的理解是:Docker 把应用运行所需的文件系统、启动命令、环境变量、依赖和运行边界封装成镜像,再通过统一的容器运行接口在不同宿主机上启动。它解决的核心问题不是“能不能运行”,而是“能不能以同样的方式构建、分发、启动、回滚和验证”。

传统部署最大的问题是环境漂移。开发机装了某个库,测试机版本不同,生产机路径不同,系统服务启动顺序不同,最后应用故障时很难判断是代码问题、依赖问题还是机器问题。Docker 不能消除所有环境差异,但它把差异收敛到几个可见对象:镜像版本、启动参数、挂载卷、网络、资源限制和宿主机内核能力。只要这些对象被记录下来,部署就从“人工配置一台机器”变成“按声明启动一个容器”。

所以学习 Docker 时,不要只记 docker rundocker run 是入口,但生产交付还需要回答:镜像从哪里构建,基础镜像是否可信,镜像标签如何管理,配置从哪里注入,数据放在哪里,端口暴露给谁,日志如何收集,容器异常退出谁发现,升级失败如何回滚。后续安装、Dockerfile、网络、数据卷和 Compose 的文章,本质上都在回答这些问题。

9. Docker 不是哪些东西

Docker 不是完整虚拟化平台。它不会为每个容器启动独立内核,容器共享宿主机 Linux 内核。这个特性带来轻量和高密度,也意味着内核版本、内核模块、cgroup、namespace、安全模块和宿主机配置会影响容器行为。如果一个应用强依赖不同内核或完整操作系统边界,虚拟机仍然更合适。

Docker 不是安全沙箱的全部。Namespace、Cgroup、Capabilities、seccomp、AppArmor/SELinux、只读文件系统、非 root 用户等能力可以降低风险,但容器逃逸、镜像供应链、Docker socket 暴露、特权容器、宿主机路径挂载都会扩大攻击面。不能因为应用跑在容器里,就默认它安全。

Docker 也不是配置管理系统。容器镜像应该包含应用和运行依赖,但不应该把生产密码、证书、环境差异和业务数据固化进去。配置应通过环境变量、配置文件挂载、secret 管理或编排平台注入;业务数据应通过卷、对象存储、数据库或外部持久化系统保存;运行策略应沉淀到 Compose、Kubernetes、CI/CD 或运维平台里。

Docker 更不是“随便下载镜像就能上线”。公共镜像可能存在过期依赖、未修复漏洞、未知维护者、默认账号、过大攻击面或不适合生产的启动方式。生产镜像要有来源、版本、扫描、构建记录和更新节奏。尤其是数据库、中间件、反向代理和安全敏感服务,不要只因为 Docker Hub 上有现成镜像就直接使用。

10. 一条完整的容器交付链路

一条健康的 Docker 交付链路通常包含七个环节。

第一,选择基础镜像。基础镜像决定包管理器、系统库、默认用户、证书、时区和漏洞面。开发阶段可以使用功能完整的镜像,生产阶段更关注可维护、体积、漏洞和运行权限。

第二,编写 Dockerfile。Dockerfile 不只是把命令写进去,还要考虑缓存顺序、依赖安装、构建产物复制、非 root 用户、启动命令、健康检查和密钥处理。一个糟糕的 Dockerfile 可能能构建成功,但会让镜像巨大、缓存失效、密钥泄露或运行时权限过高。

第三,构建镜像并打标签。latest 适合临时实验,不适合生产发布。生产镜像应该包含版本号、提交号、构建时间或渠道标签,方便回滚和追踪。镜像构建应在 CI 中完成,而不是在某台个人电脑上手工 build 后上传。

第四,推送到仓库。镜像仓库是交付链路的分发中心,需要认证、权限、保留策略、漏洞扫描和镜像清理。企业内部通常需要私有 registry 或云厂商镜像仓库。

第五,拉取并运行容器。运行时要明确端口、网络、卷、环境变量、资源限制、重启策略和日志策略。不要把所有参数散落在命令历史里,至少要写成 Compose 文件、启动脚本或自动化任务。

第六,验证运行状态。验证不是只看容器状态为 Up,还要看应用监听、健康检查、日志、端口访问、数据持久化、依赖服务和监控指标。容器启动成功但应用不可用,是最常见的初学者误判。

第七,升级和回滚。容器化的价值之一是让升级变成可替换的镜像版本。升级前要确认数据兼容、配置兼容和外部依赖;升级失败时,要能回到旧镜像和旧配置。没有回滚路径的容器化,只是换了一种部署格式。

11. Docker 对运维角色的影响

Docker 改变了应用、系统和平台之间的边界。过去应用团队把安装包交给运维,运维在服务器上安装依赖、调整配置、启动服务。容器化之后,应用团队需要对镜像质量负责,平台或运维团队需要对宿主机、运行时、网络、存储、镜像仓库和监控负责。边界不清时,Docker 会把问题从“机器上能不能跑”变成“谁对镜像、配置、数据和运行平台负责”。

一个实用分工可以这样划分:

对象 主要责任 验收问题
Dockerfile 应用/平台共同维护 是否可复现、无密钥、非 root、体积可控
镜像仓库 平台团队 是否有权限、扫描、保留和回滚
容器启动参数 应用/运维共同维护 环境变量、端口、卷、资源限制是否明确
宿主机 运维/平台团队 Docker Engine、磁盘、网络、安全基线是否正常
数据卷 应用/运维共同维护 数据是否可备份、可恢复、可迁移
监控日志 运维/平台团队 容器、应用、宿主机是否都可观测

这张表不是组织架构要求,而是提醒团队:Docker 不会自动解决责任边界。容器化越深入,越需要把构建、运行、数据和平台的责任写清楚。

12. 初学 Docker 的实验路径

建议按五个小实验学习 Docker,而不是一次性学习所有命令。

第一个实验是运行现成镜像,例如 hello-worldnginx,理解镜像拉取、容器启动、端口发布和容器删除。第二个实验是进入容器查看进程、文件系统和网络,理解容器不是虚拟机,而是宿主机上的隔离进程。第三个实验是写一个最小 Dockerfile,把自己的静态页面或小应用打成镜像。第四个实验是使用命名卷保存数据,删除容器后重建,确认数据还在。第五个实验是创建自定义网络,让两个容器用服务名互相访问。

这五个实验分别对应后续章节:安装与基础命令、Dockerfile、网络、数据卷和 Compose。每做完一个实验,都要留下验证命令和失败记录。比如端口访问失败时,记录是应用没监听、容器没启动、端口没发布,还是防火墙拦截;数据丢失时,记录是写到了容器可写层,还是卷挂载路径错了。学习时就养成证据习惯,后续生产排障会轻很多。

13. 生产使用前的最低检查清单

在把 Docker 用到生产前,至少要完成以下检查:

检查项 为什么重要
镜像来源明确 避免使用未知维护者或过期镜像
不使用 latest 部署生产 保证版本可追踪、可回滚
容器不以特权模式运行 降低宿主机风险
应用尽量非 root 运行 降低容器内进程权限
数据不写入容器可写层 避免删除容器导致数据丢失
日志有大小限制或集中采集 避免磁盘被日志打满
Docker 数据目录容量受监控 避免镜像、容器、卷撑爆磁盘
Docker socket 不暴露给普通容器 避免容器控制宿主机 Docker
配置和密钥不写入镜像层 避免泄露和环境固化
升级和回滚路径已验证 避免发布失败无法恢复

这个清单不要求初学阶段全部自动化,但至少要知道每一项对应的风险。Docker 的学习曲线看似平缓,真正的复杂度往往出现在生产治理阶段。

14. 三个典型应用场景

第一个场景是开发环境一致化。过去一个新同事加入项目,需要安装语言运行时、数据库、缓存、消息队列和各种系统依赖,任何一步版本不一致都可能导致“我这里能跑,你那里不行”。Docker 可以把这些依赖封装成镜像和 Compose 配置,让团队用更接近生产的方式启动本地环境。但这里要注意,开发环境容器化不代表生产环境已经准备好。开发环境可以为了方便暴露端口、挂载代码、使用弱口令;生产环境必须重新审视权限、数据、网络和监控。

第二个场景是交付中间件和基础服务。Nginx、Redis、PostgreSQL、Prometheus、Grafana 等服务都有成熟镜像,适合快速搭建测试环境或小规模内部服务。这里的重点不是“镜像能启动”,而是配置、数据和升级。比如数据库容器必须有持久化卷和备份策略;Nginx 配置要挂载或构建进镜像;监控服务要考虑数据保留和容量;Redis 要明确是否允许持久化、是否需要密码、是否可以被外部访问。

第三个场景是应用发布。应用团队把代码打包成镜像,CI 构建后推送到镜像仓库,运行环境只负责拉取指定版本镜像并启动。这个场景最能体现 Docker 的价值:发布产物可追踪,回滚可以切换镜像版本,环境依赖封装在镜像中,运行参数由平台管理。但它也要求团队具备 Dockerfile 质量、镜像扫描、版本标签、日志输出、健康检查和数据外置等能力。

这三个场景难度逐步提升。学习时可以先从开发环境和测试中间件开始,理解容器生命周期;再进入应用镜像构建,理解交付链路;最后再考虑生产治理和平台化。不要一开始就把 Docker 当成生产平台,也不要因为几个本地实验成功就忽略安全和运维边界。

15. Docker 常见风险地图

Docker 的风险并不神秘,大多来自几个固定位置。

风险位置 典型问题 控制方法
镜像来源 使用未知镜像、过期镜像、latest 可信仓库、固定版本、扫描
构建过程 密钥写入镜像层、构建不可复现 BuildKit secret、CI 构建、锁定依赖
运行权限 root、privileged、过多 capability 非 root、最小权限、只读文件系统
Docker socket 容器挂载 socket 后控制宿主机 避免暴露 socket,使用受控代理
数据存储 数据写入容器可写层 命名卷、bind mount、外部数据库
网络暴露 数据库端口直接暴露到公网 最小端口发布、防火墙、内网访问
日志磁盘 容器日志无限增长 日志轮转、集中采集、容量监控
配置密钥 .env、证书、token 进入镜像 secret 管理、只读挂载、脱敏审计

掌握这张风险地图后,学习 Docker 命令就不会变成孤立操作。每一个参数都能对应到一个风险控制点:-p 对应网络暴露,-v 对应数据边界,--user 对应运行权限,--restart 对应异常恢复,--memory 对应资源边界,--read-only 对应文件系统保护。

16. 容器化是否适合当前应用

不是所有应用都适合立刻容器化。适合优先容器化的应用通常具备几个特征:运行依赖清晰,启动命令明确,配置可以外置,日志能输出到标准输出,数据可以放到外部数据库或卷,服务可以通过端口暴露,健康状态可以被探测。

暂时不适合直接容器化的应用也很常见。比如强依赖桌面环境、依赖宿主机特定驱动、需要复杂 systemd 服务编排、数据路径混乱、配置散落在多个目录、无法前台运行、许可证绑定硬件或主机名。这些应用不是永远不能容器化,而是需要先整理运行边界。

判断时可以问六个问题:应用怎么启动?依赖哪些文件和系统库?配置从哪里读取?数据写到哪里?日志输出到哪里?异常时如何判断健康?如果这六个问题答不清,先不要急着写 Dockerfile。把运行手册补清楚,再容器化会更稳。

17. 从 Docker 到后续技术栈

Docker 是容器化入口,但不是终点。单机 Docker 能帮助理解镜像、容器、网络、卷和运行参数;Compose 能把多容器应用写成声明式配置;Kubernetes、Nomad、Swarm 或云平台容器服务则进一步处理调度、服务发现、滚动更新、弹性伸缩和集群级治理。

学习顺序不应该跳太快。没有理解容器网络,就很难理解 Kubernetes Service 和 CNI;没有理解数据卷,就很难理解 PV/PVC 和存储类;没有理解 Dockerfile,就很难做好镜像供应链;没有理解日志和健康检查,就很难做好平台可观测性。后续系列如果进入 Compose 或 Kubernetes,本文的概念仍然是基础。

18. 团队落地 Docker 的路线

团队引入 Docker,最好不要从“所有系统立刻容器化”开始。更稳的路线是先选一个低风险内部应用,完成镜像构建、容器运行、日志、配置、数据和回滚的完整闭环。这个应用不一定要很重要,但要能代表真实交付流程。只有跑通一个完整闭环,团队才知道自己缺的是 Dockerfile 能力、镜像仓库、配置管理、网络规划、数据备份,还是监控告警。

第二阶段可以把通用中间件和开发环境容器化。比如为开发团队提供统一的数据库、缓存、消息队列、对象存储模拟服务,减少本地环境差异。这一阶段要强调“开发便利”和“生产标准”分开,不要把开发 Compose 文件直接当生产部署文件。

第三阶段再进入核心应用容器化。核心应用往往涉及数据库连接、文件上传、定时任务、证书、外部系统、灰度发布和回滚。此时 Docker 只是基础,真正挑战是发布流程和运维边界。团队需要明确镜像谁构建、镜像谁扫描、配置谁维护、数据谁备份、发布谁批准、事故谁回滚。

第四阶段才考虑平台化。平台化可以是 Compose 管理多容器应用,也可以是 Kubernetes、Nomad、云容器服务或内部 PaaS。不要在团队还没有镜像质量标准、日志规范和数据边界时直接上平台。平台会放大标准,也会放大混乱。

这条路线的核心是循序渐进。Docker 是工具,不是组织流程的替代品。没有清楚的构建、发布、监控和责任边界,容器化只会把原来散落在服务器上的问题换一种形式继续存在。

19. 读完本篇后的复盘问题

可以用下面几个问题检查自己是否真正理解 Docker 核心概念:镜像和容器的区别是什么?删除容器会不会删除镜像?删除容器会不会删除数据?容器为什么比虚拟机轻?Docker daemon、containerd、runc 分别处在哪一层?为什么生产不推荐只用 latest?为什么 Docker socket 很危险?为什么日志应该输出到 stdout/stderr?为什么配置不应该写死在镜像里?

如果这些问题都能回答清楚,再进入安装和基础命令会更顺。否则很容易只会执行命令,不知道命令背后的边界。后续每篇文章都会围绕这些问题继续展开。

20. 最小生产样板

如果要把本文概念落到一个最小生产样板,可以从一个只读静态网站开始。应用代码通过 Dockerfile 构建为 Nginx 镜像,镜像推送到私有仓库,运行时只暴露 80 或 443 端口,配置文件只读挂载,日志输出到标准输出,容器使用明确版本标签,宿主机监控 Docker daemon、磁盘和容器状态。这个样板不复杂,但已经覆盖镜像、容器、仓库、网络、配置、日志和监控。

下一步可以把样板换成 API 服务。API 服务需要环境变量、健康检查、数据库连接、资源限制和回滚策略。再下一步才是带状态服务,例如数据库或文件上传服务,此时必须引入数据卷、备份恢复和容量巡检。按这种顺序推进,团队能逐步理解 Docker 的边界,而不是一开始就把无状态、有状态、网络和安全问题混在一起。

这个样板也适合作为团队培训。让每个人完成同样的构建、运行、验证、删除、重建和回滚练习,比单纯讲概念更有效。Docker 的核心不是命令数量,而是能不能用一套一致的方法把应用从源码交付到可运行状态,并且在失败时知道如何恢复。

21. 概念文章的验收方式

学完 Docker 概念后,可以用一次小型复盘验收。先画出一条从源码到容器运行的链路:源码进入 Dockerfile,Dockerfile 构建镜像,镜像推送仓库,宿主机拉取镜像,容器启动时注入配置、网络和卷,日志进入采集系统,监控发现异常后可以回滚到上一镜像版本。画完后再逐项标出哪些内容属于镜像,哪些属于运行时,哪些属于宿主机,哪些属于外部平台。

如果一条链路里没有镜像标签、配置来源、数据位置、日志路径和回滚方法,就说明概念还没有落到交付。Docker 的价值不是让命令变短,而是让交付对象更清楚、验证方式更标准、失败恢复更可控。

这也是本系列后续文章的主线:安装解决运行入口,Dockerfile 解决镜像交付,网络解决访问边界,数据卷解决状态保存,Compose 解决多服务编排。每一步都不是孤立知识点,而是在补齐容器交付链路。

因此,判断一个人是否真正入门 Docker,不是看他记住多少命令,而是看他能否解释清楚镜像、容器、仓库、网络、卷、配置和日志之间的关系,并能在容器删除、主机重启、镜像升级时判断哪些状态会保留、哪些状态会丢失。

这些判断能力会直接决定后续排障效率和交付质量。

也会决定团队能否安全推广容器化,并持续复盘改进落地执行闭环。

22. 总结

本文从零开始介绍了 Docker 的核心概念和基本原理:

  • 容器是轻量级的进程隔离技术,通过 Namespace 和 Cgroup 实现
  • 镜像是只读的打包规范,采用分层存储和写时复制机制
  • 仓库是镜像的分发中心,Docker Hub 是最流行的公共仓库
  • Docker 采用 C/S 架构,客户端通过 REST API 与守护进程通信
  • OCI 标准保证了不同容器运行时之间的互操作性

核心要点:

  1. 容器共享宿主机内核,比虚拟机更轻量、启动更快
  2. 镜像分层存储,多个镜像可共享基础层
  3. 容器运行态在镜像层之上叠加可写层,修改不持久
  4. 数据持久化需要使用 Volume 或 Bind Mount
  5. Docker 是 OCI 标准的推动者和主要实现者

下篇预告: Docker 容器化实战 02 — 将详细介绍 Docker 在各平台的安装方法、容器生命周期管理命令,以及常见的开发环境配置。

参考资料