Docker 完整技术栈
graph TB
subgraph "基础层"
A1[Linux Kernel]
A2[Namespace]
A3[Cgroups]
A4[UnionFS]
end
subgraph "运行时层"
B1[containerd]
B2[runc]
B3[shim]
end
subgraph "引擎层"
C1[dockerd]
C2[REST API]
end
subgraph "客户端层"
D1[docker CLI]
D2[Docker Compose]
end
subgraph "应用层"
E1[Dockerfile]
E2[镜像]
E3[容器]
E4[网络]
E5[数据卷]
end
subgraph "平台层"
F1[Docker Hub]
F2[Harbor]
F3[Kubernetes]
end
A1 --> B1
A2 --> B2
A3 --> B2
A4 --> B2
B1 --> C1
B2 --> C1
C1 --> D1
C1 --> D2
D1 --> E1
D1 --> E2
D1 --> E3
D2 --> E4
D2 --> E5
E2 --> F1
E2 --> F2
E3 --> F3
style A1 fill:#E1F5FE
style B1 fill:#E8F5E9
style C1 fill:#FFF3E0
style D1 fill:#F3E5F5
style E3 fill:#90EE90
style F3 fill:#FFE4B5
Docker从入门到精通
序章:Docker 是什么?—— 集装箱革命
Docker 的本质是一次构建,到处运行,它通过 Namespace 实现隔离、Cgroups 实现限制、UnionFS 实现高效存储,最终让应用交付变得像运送集装箱一样标准化、高效、可靠!
想象你是一个货运公司老板:
- 传统方式:每艘船装载各种形状、大小的货物,装卸混乱,容易损坏 → 虚拟机时代
- 集装箱方式:所有货物装进标准集装箱,吊车快速装卸,整齐高效 → Docker 时代
graph LR
A[应用部署演进] --> B["物理机时代<br/>一台服务器一个应用"]
A --> C["虚拟机时代<br/>一台服务器多个OS<br/>资源浪费、启动慢"]
A --> D["容器时代<br/>一台服务器多个容器<br/>共享内核、秒级启动"]
B -->|利用率10%| C
C -->|利用率30%| D
D -->|利用率80%+| E[Docker]
style D fill:#90EE90
style E fill:#90EE90
第一章:Docker 简介 —— 开源的容器化平台
1.1 什么是 Docker?
Docker 是一个开源项目,基于 Google 公司推出的 Go 语言实现,遵从 Apache 2.0 协议。
graph TD
A["Docker 是什么"] --> B[开源项目]
A --> C["Go 语言编写"]
A --> D[Apache 2.0 协议]
A --> E[容器化平台]
B --> B1["GitHub 开源社区驱动"]
C --> C1["高性能、跨平台"]
D --> D1[商业友好]
E --> E1["打包、分发、运行应用"]
style A fill:#E1F5FE
1.2 背景与目标
| 背景 | 说明 |
|---|---|
| 云计算兴起 | 服务器硬件扩展非常便利 |
| 软件服务瓶颈 | 部署成为瓶颈,环境不一致问题严重 |
| DevOps 需求 | 开发、测试、运维需要一致的环境 |
目标:实现轻量级的操作系统虚拟化解决方案,Docker 的基础是 Linux 容器技术。
graph LR
A[传统部署痛点] --> B["开发环境<br/>'在我机器上能跑'"]
A --> C["测试环境<br/>配置不同导致 Bug"]
A --> D["生产环境<br/>部署复杂、回滚困难"]
B --> E["Docker 解决方案"]
C --> E
D --> E
E --> F["一次构建<br/>到处运行"]
E --> G["环境一致性<br/>消除'works on my machine'"]
E --> H["快速部署<br/>秒级启动"]
style E fill:#90EE90
第二章:虚拟化技术 —— Docker 的基石
2.1 什么是虚拟化?
定义:在计算机中,虚拟化是一种资源管理技术,是将计算机的各种实体资源(服务器、网络、内存和存储等)予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。
graph TD
A[虚拟化技术] --> B[软件虚拟化]
A --> C[硬件虚拟化]
A --> D[内存虚拟化]
A --> E[网络虚拟化]
A --> F[桌面虚拟化]
A --> G[服务虚拟化]
A --> H[虚拟机]
B --> B1[VMware Workstation]
C --> C1[Intel VT-x<br/>AMD-V]
D --> D1[内存 ballooning]
E --> E1[SDN<br/>软件定义网络]
H --> H1[KVM<br/>Xen<br/>Hyper-V]
style H fill:#FFE4B5
2.2 虚拟化的目的
graph LR
A[虚拟化目的] --> B[资源复用]
A --> C[隔离性]
A --> D[可移植性]
A --> E[灵活性]
B --> B1[一台物理机运行多个虚拟机<br/>提高资源利用率]
C --> C1[每个虚拟机独立运行<br/>互不干扰]
D --> D1[虚拟机可以迁移<br/>从一台服务器到另一台]
E --> E1[动态调整资源<br/>按需分配]
style A fill:#E1F5FE
2.3 Docker 与虚拟机的本质区别
graph TB
subgraph "虚拟机架构"
A1[应用 A] --> B1[Guest OS<br/>CentOS]
A2[应用 B] --> B2[Guest OS<br/>Ubuntu]
A3[应用 C] --> B3[Guest OS<br/>Windows]
B1 --> C1[Hypervisor<br/>VMware/KVM]
B2 --> C1
B3 --> C1
C1 --> D1[Host OS<br/>Linux/Windows]
D1 --> E1[物理硬件]
end
subgraph "Docker 容器架构"
F1[应用 A] --> G1[容器 A<br/>bin/libs]
F2[应用 B] --> G2[容器 B<br/>bin/libs]
F3[应用 C] --> G3[容器 C<br/>bin/libs]
G1 --> H1[Docker Engine]
G2 --> H1
G3 --> H1
H1 --> I1[Host OS<br/>Linux Kernel]
I1 --> J1[物理硬件]
end
style B1 fill:#FFB6C1
style B2 fill:#FFB6C1
style B3 fill:#FFB6C1
style C1 fill:#FFB6C1
style G1 fill:#90EE90
style G2 fill:#90EE90
style G3 fill:#90EE90
style H1 fill:#90EE90
| 对比维度 | 虚拟机 | Docker 容器 |
|---|---|---|
| 启动速度 | 分钟级 | 秒级 |
| 硬盘使用 | GB 级 | MB 级 |
| 性能 | 接近原生,有 Hypervisor 开销 | 接近原生,共享内核 |
| 系统支持量 | 单机几十个 | 单机上千个 |
| 隔离级别 | 操作系统级 | 进程级 |
| 资源利用率 | 低(每个 VM 需完整 OS) | 高(共享宿主机内核) |
第三章:为什么用 Docker?—— 七大优势
mindmap
root((为什么用<br/>Docker?))
1. 秒级启动
容器启动在秒级
比虚拟机快得多
2. 资源利用率高
一台主机运行数千个容器
单机支持量远超虚拟机
3. 开发测试上线一条龙
一次创建配置,到处运行
环境一致性
4. 快速交付和部署
标准化镜像
快速创建容器
5. 更高效的虚拟化
不需要额外 Hypervisor
内核级虚拟化
6. 轻松迁移和扩展
任意平台运行
物理机/虚拟机/公有云/私有云
7. 简单管理
增量修改
替代大量更新工作
3.1 详细解读
| 优势 | 说明 | 场景 |
|---|---|---|
| 秒级启动 | 容器共享宿主机内核,无需启动完整 OS | 弹性伸缩、快速扩容 |
| 资源利用率高 | 一台物理机可运行数千容器 | 微服务架构、高密度部署 |
| 开发测试上线一条龙 | 开发容器 → 测试容器 → 生产容器 | DevOps、CI/CD |
| 快速交付部署 | 镜像标准化,一键部署 | 持续集成、自动化部署 |
| 更高效的虚拟化 | 无需 Hypervisor,接近原生性能 | 高性能计算、边缘计算 |
| 轻松迁移扩展 | 镜像可跨平台运行 | 混合云、多云部署 |
| 简单管理 | 增量更新,版本控制 | 灰度发布、快速回滚 |
第四章:Docker 特性对比 —— 容器 vs 虚拟机
graph LR
A[特性对比] --> B[启动速度]
A --> C[硬盘使用]
A --> D[性能]
A --> E[系统支持量]
B --> B1["容器:秒级<br/>虚拟机:分钟级"]
C --> C1["容器:MB<br/>虚拟机:GB"]
D --> D1["容器:接近原生<br/>虚拟机:略逊"]
E --> E1["容器:上千个<br/>虚拟机:几十个"]
style B1 fill:#90EE90
style C1 fill:#90EE90
style D1 fill:#90EE90
style E1 fill:#90EE90
第五章:Docker 安装部署
5.1 安装步骤
# 1. 卸载旧版本
sudo yum remove docker docker-client docker-client-latest \
docker-common docker-latest docker-latest-logrotate \
docker-logrotate docker-engine
# 2. 安装依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 3. 添加阿里云镜像源
sudo yum-config-manager --add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 安装 Docker CE
sudo yum install -y docker-ce docker-ce-cli containerd.io
# 5. 启动 Docker
sudo systemctl start docker
sudo systemctl enable docker
# 6. 验证安装
docker version
docker run hello-world
安装输出示例:
$ docker version
Client: Docker Engine - Community
Version: 20.10.7
API version: 1.41
Go version: go1.13.15
Git commit: f0df350
Built: Wed Jun 2 11:58:10 2021
OS/Arch: linux/amd64
Server: Docker Engine - Community
Engine:
Version: 20.10.7
API version: 1.41 (minimum version 1.12)
Go version: go1.13.15
Git commit: b0f5bc3
Built: Wed Jun 2 11:56:35 2021
OS/Arch: linux/amd64
Experimental: false
5.2 配置镜像加速
# 创建/编辑 daemon.json
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
第六章:Docker 基本操作 —— 核心命令详解
graph TD
A[Docker 操作] --> B[容器操作]
A --> C[镜像操作]
B --> B1[docker run/create]
B --> B2[docker start/stop/restart]
B --> B3[docker kill]
B --> B4[docker rm]
B --> B5[docker pause/unpause]
B --> B6[docker ps]
B --> B7[docker exec]
B --> B8[docker logs]
B --> B9[docker cp]
B --> B10[docker export/import]
C --> C1[docker images]
C --> C2[docker rmi]
C --> C3[docker tag]
C --> C4[docker build]
C --> C5[docker history]
C --> C6[docker save/load]
style A fill:#E1F5FE
6.1 容器生命周期管理
# ========== 创建并启动容器 ==========
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 常用选项:
# -d: 后台运行(detached)
# -p: 端口映射 hostPort:containerPort
# -v: 挂载卷 hostPath:containerPath
# --name: 指定容器名
# -e: 环境变量
# --rm: 停止后自动删除
# 示例:运行 Nginx
docker run -d \
--name my-nginx \
-p 80:80 \
-v /host/html:/usr/share/nginx/html \
nginx:latest
# 输出:
# 3f8a2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b
# ========== 容器状态管理 ==========
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器(包括停止的)
docker ps -q # 只显示容器 ID
docker start my-nginx # 启动已停止的容器
docker stop my-nginx # 优雅停止(发送 SIGTERM,10秒后 SIGKILL)
docker restart my-nginx # 重启容器
docker kill my-nginx # 强制停止(发送 SIGKILL)
docker rm my-nginx # 删除已停止的容器
docker rm -f my-nginx # 强制删除运行中的容器
docker pause my-nginx # 暂停容器(冻结进程)
docker unpause my-nginx # 恢复容器
# ========== 进入容器 ==========
docker exec -it my-nginx /bin/bash # 交互式进入(推荐)
docker attach my-nginx # 附加到主进程(不推荐)
# ========== 查看日志 ==========
docker logs my-nginx # 查看日志
docker logs -f my-nginx # 实时跟踪日志(tail -f)
docker logs --since="2024-01-01" my-nginx # 查看指定时间后的日志
docker logs --tail 100 my-nginx # 查看最后 100 行
6.2 容器生命周期流程图
6.3 镜像操作
# ========== 查看镜像 ==========
docker images # 列出本地镜像
docker images -q # 只显示镜像 ID
docker images --filter "dangling=true" # 显示悬空镜像
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx latest 605c77e624dd 2 weeks ago 141MB
# ubuntu 20.04 ba6acccedd29 2 months ago 72.8MB
# ========== 删除镜像 ==========
docker rmi nginx:latest # 删除指定镜像
docker rmi $(docker images -q) # 删除所有镜像
# ========== 标记镜像 ==========
docker tag nginx:latest myregistry.com/nginx:v1.0
# ========== 构建镜像 ==========
docker build -t myapp:v1.0 . # 根据 Dockerfile 构建
# ========== 查看镜像历史 ==========
docker history nginx:latest
# IMAGE CREATED CREATED BY SIZE
# 605c77e624dd 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
# ...
# ========== 导出/导入镜像 ==========
docker save -o nginx.tar nginx:latest # 导出为 tar
docker load -i nginx.tar # 从 tar 导入
docker export -o mycontainer.tar my-nginx # 导出容器文件系统
docker import mycontainer.tar myimage:v1.0 # 导入为镜像
6.4 数据拷贝与备份
# ========== 容器与主机间拷贝 ==========
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf # 容器→主机
docker cp ./nginx.conf my-nginx:/etc/nginx/nginx.conf # 主机→容器
# ========== 查看容器信息 ==========
docker inspect my-nginx # 查看详细配置(JSON)
docker inspect -f '{{.NetworkSettings.IPAddress}}' my-nginx # 提取特定字段
docker top my-nginx # 查看容器内进程
docker stats my-nginx # 实时查看资源使用
第七章:Docker 架构 —— 客户端-守护进程模式
graph TB
subgraph "Docker 客户端"
A1[docker CLI]
A2[Docker API 调用]
end
subgraph "Docker 守护进程 (dockerd)"
B1[REST API Server]
B2[容器管理]
B3[镜像管理]
B4[网络管理]
B5[卷管理]
B6[containerd<br/>容器运行时]
end
subgraph "底层技术"
C1[Linux Namespace<br/>隔离]
C2[Linux Cgroups<br/>资源限制]
C3[UnionFS<br/>联合文件系统]
C4[容器运行时 runc]
end
A1 -->|HTTP/Unix Socket| B1
B1 --> B2
B1 --> B3
B1 --> B4
B1 --> B5
B2 --> B6
B6 --> C4
C4 --> C1
C4 --> C2
C4 --> C3
style B6 fill:#90EE90
style C1 fill:#E8F5E9
style C2 fill:#E8F5E9
style C3 fill:#E8F5E9
7.1 Docker 组件详解
| 组件 | 作用 | 类比 |
|---|---|---|
| docker CLI | 用户命令行工具 | 遥控器 |
| dockerd | 守护进程,接收 API 请求 | 电视机主板 |
| containerd | 容器生命周期管理 | 频道管理器 |
| runc | 低级别容器运行时 | 信号解码器 |
| shim | 保持容器运行(dockerd 可退出) | 备用电源 |
sequenceDiagram
participant User as 用户
participant CLI as docker CLI
participant Dockerd as dockerd
participant Containerd as containerd
participant Runc as runc
participant Kernel as Linux Kernel
User->>CLI: docker run nginx
CLI->>Dockerd: POST /containers/create
Dockerd->>Dockerd: 解析参数,准备配置
Dockerd->>Containerd: 创建容器
Containerd->>Containerd: 准备 rootfs(联合文件系统)
Containerd->>Runc: runc create
Runc->>Kernel: clone() 创建 Namespace
Runc->>Kernel: 设置 Cgroups
Runc->>Kernel: pivot_root() 切换根文件系统
Runc->>Kernel: exec() 执行容器进程
Kernel-->>Runc: 容器进程启动
Runc-->>Containerd: 容器创建完成
Containerd-->>Dockerd: 返回容器 ID
Dockerd-->>CLI: 返回成功
CLI-->>User: 容器运行中
Note over Kernel: 容器进程隔离运行<br/>有自己的 PID/NET/IPC/MNT/UTS/User Namespace
第八章:Docker 实现原理 —— 三大核心技术
graph TD
A[Docker 核心技术] --> B[Namespace<br/>隔离]
A --> C[Cgroups<br/>限制]
A --> D[UnionFS<br/>联合文件系统]
B --> B1[PID Namespace<br/>进程隔离]
B --> B2[NET Namespace<br/>网络隔离]
B --> C1[CPU 限制]
C --> C2[内存限制]
C --> C3[IO 限制]
D --> D1[分层存储]
D --> D2[写时复制]
style A fill:#E1F5FE
style B fill:#E8F5E9
style C fill:#FFF3E0
style D fill:#F3E5F5
8.1 Linux Namespace —— 隔离
Namespace 让容器以为自己在一个独立的环境中:
graph TB
subgraph "宿主机视角"
A1[宿主机 PID 1: systemd]
A2[容器 A PID 1: nginx<br/>实际 PID 2345]
A3[容器 B PID 1: mysql<br/>实际 PID 5678]
end
subgraph "容器 A 视角"
B1[PID 1: nginx<br/>以为自己是 1]
B2[PID 2: php-fpm]
B3[看不到宿主机进程]
end
subgraph "容器 B 视角"
C1[PID 1: mysql<br/>以为自己是 1]
C2[PID 2: mysqld_safe]
C3[看不到宿主机进程]
end
style B1 fill:#90EE90
style C1 fill:#90EE90
六种 Namespace:
| Namespace | 隔离资源 | 说明 |
|---|---|---|
| PID | 进程 ID | 容器内 PID 从 1 开始 |
| NET | 网络设备、端口 | 每个容器有自己的 IP |
| IPC | 信号量、消息队列、共享内存 | 进程间通信隔离 |
| MNT | 挂载点 | 文件系统视图隔离 |
| UTS | 主机名、域名 | 可以有自己的 hostname |
| USER | 用户和用户组 | 容器内可以是 root,宿主机不是 |
# 查看进程的 Namespace
ls -l /proc/self/ns/
# lrwxrwxrwx 1 root root 0 Jan 1 00:00 ipc -> ipc:[4026531839]
# lrwxrwxrwx 1 root root 0 Jan 1 00:00 mnt -> mnt:[4026531840]
# lrwxrwxrwx 1 root root 0 Jan 1 00:00 net -> net:[4026531992]
# lrwxrwxrwx 1 root root 0 Jan 1 00:00 pid -> pid:[4026531836]
# lrwxrwxrwx 1 root root 0 Jan 1 00:00 user -> user:[4026531837]
# lrwxrwxrwx 1 root root 0 Jan 1 00:00 uts -> uts:[4026531838]
# 查看容器的 Namespace
docker inspect -f '{{.State.Pid}}' my-nginx
# 2345
ls -l /proc/2345/ns/
8.2 Linux Cgroups —— 限制
Cgroups(Control Groups)限制容器能使用的资源:
graph LR
A[Cgroups 子系统] --> B[cpu]
A --> C[cpuacct]
A --> D[memory]
A --> E[blkio]
A --> F[pids]
A --> G[devices]
A --> H[net_cls]
B --> B1[cpu.cfs_quota_us<br/>限制 CPU 使用率]
D --> D1[memory.limit_in_bytes<br/>限制内存]
E --> E1[blkio.throttle.read_bps_device<br/>限制磁盘 IO]
F --> F1[pids.max<br/>限制进程数]
style B fill:#E1F5FE
style D fill:#E8F5E9
style E fill:#FFF3E0
# 查看容器的 Cgroups 配置
docker run -d \
--name limited-nginx \
--cpus="1.5" \ # 限制使用 1.5 个 CPU
--memory="512m" \ # 限制内存 512MB
--memory-swap="1g" \ # 限制交换空间 1GB
--pids-limit=100 \ # 限制进程数 100
nginx
# 查看 Cgroups 实际配置
cat /sys/fs/cgroup/cpu/docker/<container-id>/cpu.cfs_quota_us
# 150000 (1.5 * 100000)
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes
# 536870912 (512 * 1024 * 1024)
8.3 UnionFS —— 联合文件系统
核心思想:分层存储,写时复制(Copy-on-Write)
graph TB
subgraph "镜像分层结构"
A1["可写层<br/>容器层<br/>Container Layer"] --> B1["修改的数据"]
A2[镜像层 3<br/>nginx:v1.0] --> B2["/usr/share/nginx/html"]
A3[镜像层 2<br/>nginx:base] --> B3["/etc/nginx/nginx.conf"]
A4[镜像层 1<br/>debian:bullseye] --> B4["/bin/bash<br/>基础系统"]
end
subgraph "联合视图"
C1["/bin/bash"] --> C2["/etc/nginx/nginx.conf"]
C2 --> C3["/usr/share/nginx/html"]
C3 --> C4["容器看到的完整文件系统"]
end
A1 -.->|叠加| C4
A2 -.->|叠加| C4
A3 -.->|叠加| C4
A4 -.->|叠加| C4
style A1 fill:#90EE90
style C4 fill:#E1F5FE
写时复制(Copy-on-Write)原理:
sequenceDiagram
participant Container as 容器
participant UnionFS as UnionFS
participant Image as 镜像层(只读)
participant WriteLayer as 可写层
Note over Container,WriteLayer: 读取文件
Container->>UnionFS: read /etc/nginx/nginx.conf
UnionFS->>Image: 查找文件
Image-->>UnionFS: 返回文件内容
UnionFS-->>Container: 返回
Note over Container,WriteLayer: 修改文件(写时复制!)
Container->>UnionFS: write /etc/nginx/nginx.conf
UnionFS->>Image: 查找文件(只读,不能修改)
UnionFS->>WriteLayer: 复制文件到可写层
UnionFS->>WriteLayer: 在可写层修改
WriteLayer-->>UnionFS: 修改完成
UnionFS-->>Container: 返回成功
Note over Container,WriteLayer: 再次读取
Container->>UnionFS: read /etc/nginx/nginx.conf
UnionFS->>WriteLayer: 先查可写层
WriteLayer-->>UnionFS: 找到修改后的版本
UnionFS-->>Container: 返回新版本
Note over Image,WriteLayer: 镜像层保持不变<br/>可被其他容器共享
查看镜像分层:
docker history nginx:latest
# IMAGE CREATED CREATED BY SIZE
# 605c77e624dd 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) COPY file:... in / 4.62kB
# <missing> 2 weeks ago /bin/sh -c #(nop) COPY multi:... in / 16.1MB
# <missing> 2 weeks ago /bin/sh -c set -x ... 54.1MB
# <missing> 2 weeks ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.1 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.5 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) ADD file:... in / 80.4MB
第九章:Dockerfile —— 镜像构建脚本
9.1 Dockerfile 指令详解
# ========== 基础镜像 ==========
FROM centos:7
# 指定基础镜像,必须作为第一个指令
# ========== 维护者信息 ==========
LABEL maintainer="yourname@example.com"
LABEL version="1.0"
LABEL description="This is a sample Dockerfile"
# ========== 环境变量 ==========
ENV NODE_VERSION 14.17.0
ENV APP_HOME /usr/src/app
ENV PATH $APP_HOME/node_modules/.bin:$PATH
# ========== 工作目录 ==========
WORKDIR $APP_HOME
# 后续指令的工作目录,如果不存在会自动创建
# ========== 复制文件 ==========
COPY package*.json ./
# 从构建上下文复制文件到容器
COPY . .
# 复制当前目录所有文件
# ========== 运行命令 ==========
RUN yum install -y gcc-c++ make && \
curl -sL https://rpm.nodesource.com/setup_14.x | bash - && \
yum install -y nodejs && \
yum clean all
# RUN 在构建时执行,每行 RUN 创建一个新层
# ========== 暴露端口 ==========
EXPOSE 3000
# 声明容器运行时监听的端口(只是文档说明,实际映射用 -p)
# ========== 数据卷 ==========
VOLUME ["/usr/src/app/data"]
# 创建挂载点,用于持久化数据
# ========== 健康检查 ==========
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# ========== 启动命令 ==========
CMD ["node", "server.js"]
# 容器启动时执行的命令(只能有一个,可被覆盖)
# 或
ENTRYPOINT ["node"]
CMD ["server.js"]
# ENTRYPOINT 固定执行程序,CMD 提供默认参数
9.2 Dockerfile 构建流程
graph TD
A[Dockerfile] --> B[FROM<br/>基础镜像]
B --> C[RUN yum install...<br/>创建新层]
C --> D[COPY package.json<br/>创建新层]
D --> E[RUN npm install<br/>创建新层]
E --> F[COPY . .<br/>创建新层]
F --> G[EXPOSE 3000<br/>元数据]
G --> H["CMD [node, server.js]<br/>容器启动配置"]
H --> I[最终镜像<br/>多层叠加]
style B fill:#E1F5FE
style C fill:#E8F5E9
style D fill:#E8F5E9
style E fill:#E8F5E9
style F fill:#E8F5E9
style I fill:#90EE90
9.3 构建示例
# 构建镜像
docker build -t mynodeapp:v1.0 .
# 输出:
# Sending build context to Docker daemon 15.36kB
# Step 1/10 : FROM centos:7
# ---> eeb6ee3f44bd
# Step 2/10 : LABEL maintainer="yourname@example.com"
# ---> Running in 3a4b5c6d7e8f
# ---> 1a2b3c4d5e6f
# Step 3/10 : ENV NODE_VERSION 14.17.0
# ---> Running in 4b5c6d7e8f9a
# ---> 2b3c4d5e6f7a
# ...
# Successfully built 9f8e7d6c5b4a
# Successfully tagged mynodeapp:v1.0
# 查看构建缓存
docker build --no-cache -t mynodeapp:v1.0 . # 不使用缓存
# 多阶段构建(减小镜像体积)
docker build -t mynodeapp:production -f Dockerfile.prod .
9.4 多阶段构建示例
# ========== 构建阶段 ==========
FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# ========== 生产阶段 ==========
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
graph TB
subgraph "第一阶段:builder"
A1[node:14] --> B1[npm install]
B1 --> C1[COPY 源码]
C1 --> D1[npm run build]
D1 --> E1[产出 dist 目录]
end
subgraph "第二阶段:production"
F1["nginx:alpine<br/>仅 ~20MB"] --> G1[COPY --from=builder /app/dist]
G1 --> H1[最终镜像<br/>极小体积]
end
E1 -.->|仅复制产物| G1
style E1 fill:#FFE4B5
style H1 fill:#90EE90
第十章:Docker 数据管理 —— 卷与持久化
10.1 数据卷类型
graph TD
A[数据持久化] --> B[Bind Mount<br/>绑定挂载]
A --> C[Volume<br/>数据卷]
A --> D[tmpfs Mount<br/>内存挂载]
B --> B1[主机路径:容器路径<br/>-v /host:/container]
C --> C1["Docker 管理<br/>-v myvolume:/container"]
D --> D1["仅存储在内存<br/>--tmpfs /container"]
B --> B2[适合开发<br/>直接编辑主机文件]
C --> C2["适合生产<br/>备份、迁移方便"]
D --> D2[适合敏感数据<br/>不写入磁盘]
style B fill:#E1F5FE
style C fill:#90EE90
style D fill:#FFF3E0
10.2 数据卷操作
# ========== 创建卷 ==========
docker volume create my-data
# my-data
docker volume ls
# DRIVER VOLUME NAME
# local my-data
docker volume inspect my-data
# [
# {
# "CreatedAt": "2024-01-01T00:00:00Z",
# "Driver": "local",
# "Labels": null,
# "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
# "Name": "my-data",
# "Options": null,
# "Scope": "local"
# }
# ]
# ========== 使用卷 ==========
docker run -d \
--name mysql \
-v my-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8.0
# ========== 数据卷容器 ==========
docker create -v /dbdata --name dbstore busybox
docker run -d --volumes-from dbstore --name db1 mysql
docker run -d --volumes-from dbstore --name db2 mysql
# db1 和 db2 共享 dbstore 的数据卷
# ========== 备份数据卷 ==========
docker run --rm \
--volumes-from mysql \
-v $(pwd):/backup \
busybox \
tar cvf /backup/mysql-backup.tar /var/lib/mysql
# ========== 恢复数据卷 ==========
docker run --rm \
--volumes-from mysql \
-v $(pwd):/backup \
busybox \
tar xvf /backup/mysql-backup.tar -C /
第十一章:Docker 网络 —— 容器通信
11.1 网络模式
graph TB
A[Docker 网络模式] --> B[bridge<br/>桥接模式]
A --> C[host<br/>主机模式]
A --> D[none<br/>无网络]
A --> E[container<br/>容器模式]
A --> F[自定义网络]
B --> B1[默认模式<br/>容器通过 docker0 桥通信<br/>NAT 访问外网]
C --> C1[共享宿主机网络<br/>无隔离<br/>性能最好]
D --> D1[完全隔离<br/>需手动配置网络]
E --> E1[共享另一个容器网络<br/>localhost 互通]
F --> F1[用户定义桥接<br/>DNS 自动解析<br/>推荐用于多容器应用]
style B fill:#90EE90
style F fill:#90EE90
11.2 网络操作
# ========== 查看网络 ==========
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# 3a4b5c6d7e8f bridge bridge local
# 4b5c6d7e8f9a host host local
# 5c6d7e8f9a0b none null local
# ========== 创建自定义网络 ==========
docker network create my-network
docker network create --driver bridge --subnet 172.20.0.0/16 my-subnet
# ========== 容器加入网络 ==========
docker run -d --name web --network my-network nginx
docker run -d --name api --network my-network myapi
# 同一网络内,容器可通过名称互访
docker exec web ping api
# PING api (172.20.0.3): 56 data bytes
# 64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.072 ms
# ========== 端口映射 ==========
docker run -d -p 8080:80 nginx # 主机 8080 → 容器 80
docker run -d -p 127.0.0.1:8080:80 nginx # 仅本机可访问
docker run -d -p 8080:80/udp nginx # UDP 端口
docker run -d -P nginx # 随机映射 EXPOSE 的端口
# ========== 查看端口映射 ==========
docker port my-nginx
# 80/tcp -> 0.0.0.0:8080
11.3 网络架构图
graph TB
subgraph "宿主机"
A1[eth0<br/>192.168.1.100]
A2[docker0<br/>172.17.0.1/16<br/>虚拟网桥]
end
subgraph "容器 A"
B1[eth0<br/>172.17.0.2]
B2[nginx<br/>:80]
end
subgraph "容器 B"
C1[eth0<br/>172.17.0.3]
C2[myapp<br/>:3000]
end
A1 -->|NAT| A2
A2 -->|veth-pair| B1
A2 -->|veth-pair| C1
B1 -.->|内部通信<br/>172.17.0.0/16| C1
B2 -->|暴露端口| D1[主机 8080]
D1 -->|访问| E1[外部用户]
style A2 fill:#E1F5FE
style B1 fill:#E8F5E9
style C1 fill:#E8F5E9
第十二章:Docker Compose —— 多容器编排
12.1 Compose 文件示例
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:latest
container_name: my-web
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
networks:
- frontend
depends_on:
- api
restart: unless-stopped
api:
build: ./api # 从 Dockerfile 构建
container_name: my-api
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_PORT=3306
volumes:
- api-logs:/app/logs
networks:
- frontend
- backend
restart: unless-stopped
db:
image: mysql:8.0
container_name: my-db
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
volumes:
- db-data:/var/lib/mysql
networks:
- backend
restart: unless-stopped
redis:
image: redis:alpine
container_name: my-redis
volumes:
- redis-data:/data
networks:
- backend
volumes:
api-logs:
db-data:
redis-data:
networks:
frontend:
driver: bridge
backend:
driver: bridge
12.2 Compose 架构图
graph TB
subgraph "Docker Compose"
A1[docker-compose.yml]
end
subgraph "Services"
B1[web<br/>nginx<br/>端口 80]
B2[api<br/>Node.js<br/>构建自 ./api]
B3[db<br/>mysql:8.0]
B4[redis<br/>redis:alpine]
end
subgraph "Networks"
C1["frontend<br/>web ↔ api"]
C2["backend<br/>api ↔ db ↔ redis"]
end
subgraph "Volumes"
D1[html<br/>主机目录]
D2[db-data<br/>Docker 卷]
D3[redis-data<br/>Docker 卷]
end
A1 --> B1
A1 --> B2
A1 --> B3
A1 --> B4
B1 -.->|访问| C1
B2 -.->|访问| C1
B2 -.->|访问| C2
B3 -.->|访问| C2
B4 -.->|访问| C2
B1 --> D1
B3 --> D2
B4 --> D3
style A1 fill:#E1F5FE
style C1 fill:#E8F5E9
style C2 fill:#E8F5E9
12.3 Compose 命令
# 启动所有服务
docker-compose up
docker-compose up -d # 后台运行
docker-compose up --build # 重新构建
# 停止服务
docker-compose down
docker-compose down -v # 同时删除卷
# 查看状态
docker-compose ps
docker-compose logs -f # 实时查看日志
docker-compose logs web # 查看特定服务日志
# 扩展服务
docker-compose up -d --scale web=3 # 启动 3 个 web 实例
# 执行命令
docker-compose exec db mysql -u root -p
docker-compose run --rm api npm test # 运行一次性命令
第十三章:Docker 私有仓库 —— Harbor
13.1 为什么需要私有仓库?
graph LR
A[镜像管理需求] --> B[安全性<br/>敏感镜像不公开]
A --> C[速度<br/>内网传输更快]
A --> D["控制<br/>版本管理、权限控制"]
A --> E[合规<br/>企业审计要求]
B --> F[Harbor<br/>企业级私有仓库]
C --> F
D --> F
E --> F
style F fill:#90EE90
13.2 Harbor 架构
graph TB
subgraph "Harbor"
A1[Proxy<br/>Nginx]
A2[Core Services<br/>API/UI]
A3[Registry<br/>镜像存储]
A4[Database<br/>PostgreSQL]
A5[Redis<br/>缓存]
A6[Clair<br/>漏洞扫描]
A7[Notary<br/>镜像签名]
A8[Job Service<br/>异步任务]
end
subgraph "存储后端"
B1[文件系统<br/>/data]
B2[S3/MinIO<br/>对象存储]
B3[Swift<br/>OpenStack]
end
A1 --> A2
A2 --> A3
A2 --> A4
A2 --> A5
A2 --> A6
A2 --> A7
A2 --> A8
A3 --> B1
A3 --> B2
A3 --> B3
style A1 fill:#E1F5FE
style A3 fill:#E8F5E9
13.3 使用私有仓库
# 登录私有仓库
docker login harbor.example.com
# Username: admin
# Password:
# Login Succeeded
# 标记镜像
docker tag myapp:v1.0 harbor.example.com/project/myapp:v1.0
# 推送镜像
docker push harbor.example.com/project/myapp:v1.0
# The push refers to repository [harbor.example.com/project/myapp]
# v1.0: digest: sha256:3f8a2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b size: 1234
# 拉取镜像
docker pull harbor.example.com/project/myapp:v1.0