Docker 教程
欢迎学习 Docker!本教程将带你从零基础开始,逐步掌握 Docker 容器技术的核心知识和技能。
容器技术发展历史
理解容器技术的历史有助于更好地把握 Docker 的设计理念和演进方向。
早期隔离技术
容器化思想可以追溯到 1979 年 Unix V7 系统引入的 chroot 系统调用。chroot 能够改变进程看到的根目录,实现基本的文件系统隔离。这被称为"改变根目录"(change root),是最早的隔离机制之一,至今仍被用于创建隔离的测试环境。
Linux 容器雏形
2000 年,FreeBSD 推出了 jail 机制,将系统分割成多个独立的环境。2005 年,Sun 公司在 Solaris 10 中引入了 Solaris Zones(也称 Solaris Containers),提供更完整的隔离能力,包括文件系统、网络、进程等。
真正奠定现代容器基础的是 Linux 内核的技术进步:
- Namespaces(2002 年):内核 2.4.19 引入,提供进程、网络、文件系统等资源的隔离
- Cgroups(2007 年):Google 贡献,提供资源限制和审计能力
- LXC(2008 年):LinuX Containers 项目,首次将 namespaces 和 cgroups 整合为易用的容器工具
Docker 的诞生
2013 年 3 月,DotCloud 公司(后更名为 Docker Inc.)在 PyCon 大会上首次发布 Docker。创始人 Solomon Hykes 的目标是解决软件交付的"最后一公里"问题——代码在开发环境能运行,到了生产环境却出问题。
Docker 的创新在于:
- 镜像概念:将应用及其依赖打包为不可变的镜像
- 分层存储:镜像由多个只读层组成,实现高效的存储和传输
- 简洁的 CLI:大幅降低容器技术的使用门槛
- Docker Hub:提供公共镜像仓库,促进生态发展
容器生态的演进
Docker 的成功推动了整个容器生态的发展:
- 2014 年:Kubernetes 项目启动,解决容器编排问题
- 2015 年:OCI(Open Container Initiative)成立,制定容器镜像和运行时标准
- 2016 年:Docker 将核心运行时捐赠给 CNCF,成为 containerd 项目
- 2017 年:Kubernetes 成为容器编排的事实标准
为什么 Docker 能够成功?
回顾历史,Docker 的成功并非偶然:
| 因素 | 说明 |
|---|---|
| 时机 | 云计算兴起,DevOps 理念普及 |
| 简洁性 | 相比 LXC,Docker 的用户体验大幅提升 |
| 标准化 | 镜像格式成为行业标准(OCI 镜像规范) |
| 生态 | Docker Hub 聚合了大量官方和社区镜像 |
| 可移植性 | "一次构建,到处运行" 解决了长期痛点 |
理解这段历史,能帮助你更好地理解为什么 Docker 采用分层镜像设计、为什么强调不可变基础设施,以及容器编排工具存在的意义。
什么是 Docker?
Docker 是一个开源的应用容器引擎,基于 Go 语言开发,遵从 Apache2.0 协议开源。它可以让开发者将应用程序及其依赖打包到一个可移植的容器中,然后发布到任何流行的 Linux、Windows 或 macOS 机器上,也可以实现虚拟化。
Docker 的核心概念
容器(Container):容器是一个轻量级、独立的可执行软件包,包含运行应用程序所需的所有内容:代码、运行时、系统工具、系统库和设置。容器之间相互隔离,确保应用程序在不同环境中以相同方式运行。
镜像(Image):镜像是容器的蓝图,是一个只读模板,包含创建容器所需的指令。镜像由多个层组成,每层代表 Dockerfile 中的一条指令。
仓库(Registry):仓库是存储和分发镜像的地方。Docker Hub 是最大的公共仓库,包含超过 100,000 个镜像。你也可以搭建私有仓库来存储自己的镜像。
Docker 与虚拟机的区别
| 特性 | Docker 容器 | 虚拟机 |
|---|---|---|
| 启动速度 | 秒级 | 分钟级 |
| 资源占用 | MB 级别 | GB 级别 |
| 性能 | 接近原生 | 有损耗 |
| 隔离性 | 进程级别 | 操作系统级别 |
| 操作系统 | 共享宿主机内核 | 独立内核 |
架构对比:
解释:
- 虚拟机需要为每个应用运行完整的操作系统,资源开销大
- Docker 容器共享宿主机内核,只打包应用和依赖,更加轻量
容器底层原理
理解 Docker 的底层原理,有助于更好地排查问题和优化应用。容器本质上是利用 Linux 内核的隔离特性来限制进程的资源访问。
Namespaces:资源隔离
Namespaces 是 Linux 内核提供的一种资源隔离机制,可以让一组进程看到独立的系统资源。Docker 使用多种 namespace 来实现容器的隔离:
| Namespace 类型 | 隔离内容 | 实际效果 |
|---|---|---|
| PID | 进程 ID | 容器内进程从 PID 1 开始编号 |
| NET | 网络栈 | 容器拥有独立的网卡、路由表、防火墙规则 |
| MNT | 挂载点 | 容器有独立的文件系统视图 |
| IPC | 进程间通信 | 容器间信号量、消息队列互不影响 |
| UTS | 主机名 | 容器可以有自己的主机名 |
| USER | 用户和组 | 容器内的 root 可以映射到主机的普通用户 |
PID Namespace 示例:
当你在容器内执行 ps aux 时,只能看到容器内的进程。这是因为容器进程属于一个独立的 PID namespace。在容器内,init 进程(PID 1)是容器的入口进程,而不是系统的 systemd。
# 在容器内查看进程
docker run -it alpine ps aux
# 只显示容器内的进程,PID 从 1 开始
# 在主机上查看同一进程
ps aux | grep <容器名>
# 显示主机视角的 PID(如 12345)
Cgroups:资源限制
Cgroups(Control Groups)用于限制、记录和隔离进程组使用的物理资源。没有 cgroups,一个容器可能耗尽整个主机的内存或 CPU。
主要功能:
- 资源限制:设置进程组使用资源的上限
- 优先级分配:控制进程组分配资源的权重
- 资源统计:统计进程组的资源使用量
- 进程控制:可以冻结进程组或重启进程组
资源限制示例:
# 限制容器使用 512MB 内存
docker run -d --memory="512m" nginx
# 限制容器使用 1.5 个 CPU 核心
docker run -d --cpus="1.5" nginx
这些限制通过 cgroups 实现。在主机上可以查看对应的 cgroup 配置:
# 查看内存限制(cgroup v2)
cat /sys/fs/cgroup/docker/<容器ID>/memory.max
# 查看 CPU 限制
cat /sys/fs/cgroup/docker/<容器ID>/cpu.max
Union File System:分层镜像
UnionFS(联合文件系统)是 Docker 镜像分层存储的基础。它允许多个文件系统"叠加"在一起,呈现为一个统一的文件系统。
分层原理:
Docker 镜像由多个只读层组成,每层对应 Dockerfile 中的一条指令。运行容器时,Docker 在最上层添加一个可写层(容器层)。
┌─────────────────────────┐
│ 容器层(可写) │ ← 运行时修改写到这里
├─────────────────────────┤
│ 镜像层 N(只读) │ ← RUN npm install
├─────────────────────────┤
│ 镜像层 N-1(只读) │ ← COPY package.json
├─────────────────────────┤
│ 镜像层 2(只读) │ ← RUN apk add
├─────────────────────────┤
│ 镜像层 1(只读) │ ← FROM alpine
└─────────────────────────┘
Copy-on-Write(写时复制):
当容器需要修改文件时:
- 从只读层复制文件到容器层
- 在容器层进行修改
- 原始镜像层保持不变
这种设计带来几个好处:
- 镜像共享:多个容器可以共享相同的只读层
- 快速启动:新容器只需创建一个可写层
- 存储效率:只存储差异部分
容器网络原理
Docker 创建容器时,会自动创建一对虚拟网络接口(veth pair):一端在容器内(eth0),一端在主机的网桥上(docker0)。
容器通过 NAT(网络地址转换)访问外部网络,外部网络通过端口映射访问容器。
容器 vs 进程
从内核视角看,容器就是一组被"特殊对待"的进程:
- 它们有独立的 namespace(看起来像独立系统)
- 它们被 cgroup 限制资源(不会失控)
- 它们共享宿主机内核(不是完整的虚拟机)
这也是为什么容器启动快、资源占用少——本质上就是在运行普通进程,只是加了隔离和限制。
为什么学习 Docker?
- 一致性环境:开发、测试、生产环境完全一致,消除"在我机器上能运行"的问题
- 快速部署:容器启动只需几秒,大大加快部署速度
- 资源高效:相比虚拟机,容器占用资源更少,可以在同一硬件上运行更多服务
- 微服务架构:容器是微服务架构的理想载体,每个服务独立部署和扩展
- DevOps 实践:Docker 是 CI/CD 流程的核心组件,实现自动化构建和部署
- 云原生基础:Kubernetes 等容器编排工具都基于 Docker 容器
Docker 的应用场景
应用部署
- 标准化应用交付流程
- 一键部署复杂应用栈
- 版本回滚和灰度发布
开发环境
- 统一团队开发环境
- 快速搭建本地开发栈
- 隔离不同项目的依赖
持续集成/持续部署
- 构建可重复的 CI 环境
- 自动化测试环境
- 流水线标准化
微服务架构
- 服务独立部署和扩展
- 服务隔离和资源限制
- 服务网格基础
云原生应用
- Kubernetes 基础设施
- 云平台部署标准
- 混合云和多云部署
Docker 核心组件
Docker Engine(Docker 引擎)
Docker 引擎是一个客户端-服务器应用程序,包含以下组件:
- Docker Daemon(dockerd):守护进程,负责构建、运行和分发容器
- REST API:用于程序与守护进程通信的接口
- Docker CLI(docker):命令行界面,用于管理容器和镜像
┌─────────────────────────────────────────┐
│ Docker Engine │
├─────────────────────────────────────────┤
│ Docker CLI │
│ (docker 命令) │
├─────────────────────────────────────────┤
│ REST API │
├─────────────────────────────────────────┤
│ Docker Daemon │
│ (dockerd) │
├─────────────────────────────────────────┤
│ containerd │
│ runc │
└─────────────────────────────────────────┘
底层运行时:
Docker 引擎的底层由两个关键组件组成:
- containerd:一个工业级容器运行时,负责管理容器的完整生命周期(镜像传输、容器执行、监控等)。它是 CNCF 毕业项目,被 Kubernetes 等平台广泛使用。
- runc:轻量级容器运行时,负责实际创建和运行容器。它实现了 OCI(开放容器倡议)运行时规范,是容器技术的基础。
Docker Desktop
Docker Desktop 是 Docker 官方提供的桌面应用程序,包含:
- Docker Engine
- Docker CLI
- Docker Compose
- Kubernetes(可选)
- 图形化管理界面
Docker 生态系统
Docker Hub
Docker Hub 是 Docker 官方提供的公共镜像仓库:
- 官方镜像(如 nginx、redis、mysql)经过安全审查
- 自动构建功能,代码提交后自动构建镜像
- 镜像扫描检测安全漏洞
- 支持私有仓库(免费账户 1 个)
Docker Compose
Docker Compose 是用于定义和运行多容器应用的工具:
- 使用 YAML 文件定义服务
- 一键启动多个容器
- 管理容器之间的依赖关系
容器编排
在生产环境中,通常需要容器编排工具来管理大规模容器部署:
- Kubernetes:业界标准的容器编排平台,功能强大,生态丰富
- Docker Swarm:Docker 原生的集群管理工具,简单易用
开放容器倡议(OCI)
OCI 制定了容器镜像格式和运行时规范,确保不同容器工具之间的互操作性。Docker 镜像格式已成为 OCI 镜像规范的基础。
Docker 版本说明
Docker Edition
Docker 提供两个主要版本:
| 版本 | 说明 | 适用场景 |
|---|---|---|
| Docker Community Edition (CE) | 社区版,免费使用 | 开发者、小型团队、学习 |
| Docker Enterprise Edition (EE) | 企业版,付费订阅 | 大型企业、需要技术支持 |
Docker CE 是大多数开发者的选择,功能完整且免费。Docker EE 在 CE 基础上增加了企业级功能,如镜像安全扫描、容器编排管理、统一管理平台等。
版本号命名规则
从 2017 年开始,Docker 采用基于时间的版本命名方案:
Docker 27.x.y
│ │
│ └── 补丁版本(bug修复)
└── 主版本(年度更新)
版本发布节奏:
- Stable 版本:每季度发布一个稳定版本,适合生产环境
- Edge 版本:每月发布,包含最新功能,适合测试环境
长期支持(LTS)
Docker Engine 遵循长期支持策略:
- 每个稳定版本支持约 1 年
- 安全更新和 bug 修复在支持期内持续提供
- 建议生产环境使用 Stable 版本
查看当前版本:
docker version
# 输出包含客户端和服务端的版本信息
docker info | grep "Server Version"
# 只查看服务端版本
版本兼容性:
- Docker 客户端和服务端版本不需要完全一致
- 客户端版本可以比服务端新
- 建议客户端和服务端版本差距不超过一个主版本
教程目录
基础入门
- 环境配置 - 安装 Docker 和配置开发环境
- 基础命令 - 镜像和容器的基本操作
- Dockerfile - 编写 Dockerfile 构建镜像
进阶内容
- Docker Compose - 多容器应用编排
- 网络管理 - 容器网络配置
- 数据卷管理 - 数据持久化存储
- Docker Swarm 集群管理 - 容器编排和集群管理
高级主题
- 安全最佳实践 - 容器安全配置和加固
- 镜像优化详解 - 减小镜像体积和提升构建效率
- 私有仓库 Registry - 搭建和管理私有镜像仓库
- 监控与调试 - 容器监控和故障排查
- 多平台构建 Buildx - 跨平台镜像构建
- 常见问题与故障排除 - 常见问题解决方案
知识速查
- 速查表 - Docker 常用命令速查
学习建议
- 动手实践:每学一个概念,都要动手操作验证
- 理解原理:不只是记住命令,要理解底层原理
- 循序渐进:从基础命令开始,逐步掌握高级功能
- 阅读官方文档:Docker 官方文档是最权威的参考
- 项目练习:将实际项目容器化是最好的学习方式
参考资源
- Docker 官方文档
- Docker Hub
- Docker 官方博客
- Play with Docker - 在线 Docker 实验环境
准备好开始学习了吗?点击下一章开始你的 Docker 学习之旅!