Docker 从入门到精通实战手册

Docker 从入门到精通实战手册

版本基线:Docker Engine 24+、Docker Compose v2(docker compose)、Harbor 2.x、Docker Swarm(经典模式)


目录


第 1 章 Docker 是什么

1.1 为什么需要容器

传统部署面临的核心问题:

  • 环境不一致:开发环境能跑,测试/生产环境跑不起来(”在我机器上没问题”)。
  • 依赖冲突:多个应用需要不同版本的库、运行时(Python 2/3、OpenSSL、glibc 等)。
  • 资源浪费:虚拟机整套操作系统开销大,启动慢,单机利用率低。
  • 交付复杂:从代码到运行的路径长,依赖部署文档和人工操作。

容器(Container)把应用及其全部依赖打包成标准制品(镜像),在任何装有容器运行时的机器上以几乎一致的方式运行,从而解决上述问题。

1.2 容器 vs 虚拟机

对比项 虚拟机 (VM) 容器 (Container)
隔离级别 硬件级虚拟化(Hypervisor) 操作系统级隔离(namespace + cgroup)
内核 每个 VM 独享一个 Guest OS 内核 共享宿主机内核
启动速度 分钟级 秒级(毫秒~秒)
镜像体积 GB 级 MB ~ 数百 MB
资源占用 高(OS 开销) 低(仅进程 + 依赖)
隔离强度 强(独立内核) 弱(共享内核,逃逸风险更高)
密度 高(一台机器可跑上百个容器)
典型工具 VMware、KVM、VirtualBox、Hyper-V Docker、containerd、Podman

结论:虚拟机更安全、更隔离;容器更轻量、更高效。生产环境中常混合使用(VM 内跑容器)。

1.3 Docker 核心组件

组件 说明
镜像 (Image) 只读模板,由多层文件系统叠加而成,是容器的”模板/类”
容器 (Container) 镜像的运行实例,可读可写,是”对象”
仓库 (Registry) 存放和分发镜像的服务,如 Docker Hub、Harbor、私有 Registry
Dockerfile 描述如何构建镜像的脚本(声明式)
Docker Engine 核心守护进程,负责构建、运行、管理容器
Docker Client 命令行/API 客户端,与守护进程通信
Docker Compose 多容器编排工具(YAML 声明服务)
Docker Swarm / Kubernetes 容器集群编排平台

1.4 Docker 架构

Docker 采用 C/S(客户端/服务端)架构

1
2
3
4
5
6
7
8
9
10
+------------------+        REST API (Unix Socket / TCP)         +---------------------------+
| Docker Client | <----------------------------------------> | Docker Daemon (dockerd) |
| (docker CLI) | unix:///var/run/docker.sock | |
+------------------+ | +---------------------+ |
| | containerd | |
| | +---------------+ | |
| | | runc (OCI) | | |
| | +---------------+ | |
| +---------------------+ |
+---------------------------+
  • dockerd:Docker 守护进程,负责镜像管理、容器生命周期、网络、存储。
  • containerd:行业标准容器运行时,负责容器执行(由 CNCF 托管)。
  • runc:OCI 运行时,真正通过 Linux 内核特性(namespace/cgroup)创建容器。
  • docker CLI:用户操作的命令行工具,也支持 docker context 管理多个远程 daemon。

1.5 Docker 工作原理简述

容器不是”轻量虚拟机”,而是被隔离的普通进程

  • namespace(命名空间):让进程拥有独立的 PID、网络、挂载点、用户、UTS、IPC 视图,实现”看起来像一台独立机器”。
  • cgroup(控制组):限制和统计 CPU、内存、磁盘 IO 等资源使用。
  • overlayfs / 存储驱动:把镜像的只读层和容器的可写层叠加成统一视图,实现分层与写时复制(CoW)。
  • 镜像分层:多容器共享底层只读层,节省磁盘与内存,拉取更快。

详细原理见 第 15 章


第 2 章 安装与基础配置

2.1 环境与版本要求

平台 要求
Linux 内核 ≥ 3.10(推荐 5.x+),x86_64 / arm64
Windows Windows 10/11 专业版/企业版,或 Windows Server 2019+,需开启 WSL2 或 Hyper-V
macOS macOS 11+(Big Sur 以上)
内存 建议 ≥ 4GB(Docker Desktop 默认分配 2GB)

查看内核与系统信息:

1
2
uname -r
cat /etc/os-release

2.2 Ubuntu 安装(推荐方式:官方 apt 源)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 卸载旧版本(可选)
sudo apt-get remove docker docker-engine docker.io containerd runc

# 2. 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release

# 3. 添加官方 GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 4. 添加软件源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 5. 安装 Docker Engine、CLI、Compose 插件
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 6. 验证
sudo docker version
sudo docker run --rm hello-world

2.3 CentOS / Rocky / AlmaLinux 安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 卸载旧版本
sudo yum remove docker docker-client docker-common docker-selinux 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. 安装
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 5. 启动并设置开机自启
sudo systemctl enable --now docker

2.4 Windows / macOS:Docker Desktop

  1. docker.com/products/docker-desktop 下载安装包。
  2. Windows 需先在”启用或关闭 Windows 功能”中开启 WSL2(推荐)或 Hyper-V,并安装 WSL2 内核更新包。
  3. 安装完成后,在 Settings → Resources 中调整 CPU / 内存 / 磁盘配额。
  4. 配置镜像加速(见 2.5),国内网络建议开启。

生产服务器通常是 Linux;Windows/macOS 的 Docker Desktop 主要面向开发。

2.5 配置国内镜像加速

编辑 /etc/docker/daemon.json

1
2
3
4
5
6
7
8
9
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.nju.edu.cn"
],
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "5" }
}

重启生效:

1
2
sudo systemctl restart docker
docker info | grep -A5 Registry

镜像加速只对 Docker Hub 生效;私有仓库不受影响。加速地址稳定性会变化,请以最新可用源为准。

2.6 非 root 用户管理 Docker

1
2
3
4
5
# 将用户加入 docker 组(需重新登录生效)
sudo usermod -aG docker $USER
# 刷新docker 组
newgrp docker
docker ps

安全警告:加入 docker 组等同于获得 root 权限(可以挂载宿主机目录、执行特权操作),生产环境请谨慎授权。

2.7 卸载 Docker

1
2
3
4
5
6
7
# Ubuntu/Debian
sudo apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo rm -rf /var/lib/docker /var/lib/containerd

# CentOS/RHEL
sudo yum remove -y docker-ce docker-ce-cli containerd.io
sudo rm -rf /var/lib/docker /var/lib/containerd

2.8 安装后的基线检查

1
2
3
4
docker version            # 客户端与服务端版本
docker info # 引擎信息、存储驱动、镜像加速、资源配额
docker run --rm hello-world # 端到端验证
systemctl status docker # 服务状态

安装检查清单:

  • docker version 中 Client 和 Server 版本一致
  • docker info 显示 Storage Driver: overlay2
  • hello-world 正常输出
  • 已配置镜像加速且 docker info 可见
  • 非 root 用户可执行 docker ps

第 3 章 镜像管理

3.1 镜像概念与分层

镜像(Image) 是一个只读的、分层的文件系统快照,包含了运行应用所需的一切:操作系统基础文件、运行时、依赖、应用代码、启动命令。

1
2
3
4
5
6
7
8
+-----------------------------+
| Layer 6: 应用代码 | <- 构建时添加
| Layer 5: 依赖安装 |
| Layer 4: 基础工具 |
| Layer 3: 基础镜像更新 |
| Layer 2: 基础镜像 (如 ubuntu)|
| Layer 1: 引导文件系统 (bootfs)|
+-----------------------------+
  • 每一层只记录相对上一层的变更(新增/修改/删除文件)。
  • 多个镜像共享相同底层时,磁盘上只存一份(层共享)。
  • 容器运行时,Docker 在镜像之上加一层可写层(容器层),对文件的修改采用写时复制(CoW)
  • 因此:镜像不可变,任何变更都产生新层;容器退出后修改即丢失(除非写入挂载卷)。

删除镜像时如果有多层被其他镜像引用,只会删除未被引用的层。

3.2 镜像常用命令速查

命令 作用
docker search <关键词> 在仓库搜索镜像
docker pull <镜像> 拉取镜像到本地
docker push <镜像> 推送镜像到仓库
docker images / docker image ls 列出本地镜像
docker image inspect <镜像> 查看镜像详细信息(JSON)
docker history <镜像> 查看镜像构建历史(各层命令)
docker tag <镜像> <新标签> 打标签
docker rmi <镜像> 删除镜像
docker save -o file.tar <镜像> 导出镜像为 tar 文件
docker load -i file.tar 从 tar 导入镜像
docker image prune 清理悬空镜像(dangling)
docker image ls -f dangling=true 查看悬空镜像
docker manifest inspect <镜像> 查看多架构清单
docker buildx 多架构构建工具(见 5.7)

3.3 拉取与查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 拉取最新版(默认 latest 标签)
docker pull nginx

# 拉取指定版本
docker pull mysql:8.0
docker pull redis:7-alpine

# 从私有仓库拉取(需先 login)
docker pull registry.example.com/app/api:v1.2.3

# 按平台拉取(在 x86 机器上拉 arm64)
docker pull --platform linux/arm64 nginx:alpine

# 列出本地镜像
docker images
docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

# 查看镜像详情
docker image inspect nginx | head -50

# 查看构建历史(层指令)
docker history nginx

3.4 标签与删除

1
2
3
4
5
6
7
# 打标签:新标签只是引用,不复制镜像
docker tag nginx:latest myregistry.com/nginx:v1.0

# 删除镜像(若容器仍在引用会报错)
docker rmi nginx:latest
docker rmi $(docker images -q) # 删除全部本地镜像
docker rmi $(docker images -f dangling=true -q) # 删除悬空镜像

3.5 save/load 与 export/import 的区别

操作 内容 保留信息 典型场景
docker save / load 镜像(含全部层、标签、历史) 完整 离线分发、备份、迁移
docker export / import 容器文件系统快照(打平为一层) 丢失历史/标签/层 急救导出容器数据
1
2
3
4
5
6
7
8
9
10
11
12
# 离线分发镜像
docker save -o nginx.tar nginx:latest
scp nginx.tar user@10.0.0.5:/tmp/
ssh user@10.0.0.5 'docker load -i /tmp/nginx.tar'

# 单文件多镜像打包
docker save -o all.tar nginx:latest mysql:8.0 redis:7
docker load < all.tar

# 容器导出为镜像(会丢失分层信息,不推荐用于分发)
docker export mycontainer > rootfs.tar
cat rootfs.tar | docker import - myimage:v1

生产规范:分发走 Registry(仓库),不推荐大文件 tar 传递;save/load 仅用于断网机房等场景。

3.6 搭建私有 Registry(简单版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动官方 registry
docker run -d --name registry \
-p 5000:5000 \
-v /data/registry:/var/lib/registry \
--restart=always \
registry:2

# 使用
docker tag nginx:latest localhost:5000/nginx:v1
docker push localhost:5000/nginx:v1
docker pull localhost:5000/nginx:v1

# 查看仓库中的镜像列表
curl -s http://localhost:5000/v2/_catalog | python3 -m json.tool

跨主机推送需配置 HTTP 信任(daemon.json):

1
2
3
{
"insecure-registries": ["192.168.1.10:5000"]
}

单节点 Registry 无认证、无高可用、无漏洞扫描,适合内网小规模;企业级请用 Harbor(见 3.7)。

3.7 Harbor 企业级镜像仓库

Harbor 提供:基于角色的访问控制(RBAC)、镜像复制、漏洞扫描(Trivy)、签名(Notation/Cosign)、审计日志、项目隔离。

1
2
3
4
5
6
7
# 下载离线安装包(选择最新 release)
wget https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-offline-installer-v2.11.0.tgz
tar -xzf harbor-offline-installer-v2.11.0.tgz
cd harbor

# 生成配置并修改
cp harbor.yml.tmpl harbor.yml

harbor.yml 关键配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
hostname: registry.example.com        # 对外访问域名
http:
port: 80
https:
port: 443
certificate: /data/cert/server.crt # 生产必须配置 HTTPS
private_key: /data/cert/server.key
harbor_admin_password: ChangeMe123! # 初始 admin 密码
database:
password: db_pass_123
data_volume: /data/harbor # 数据目录
trivy:
ignore_unfixed: true # 只告警可修复漏洞
1
2
3
4
5
6
7
8
9
10
11
# 执行安装(依赖 docker + docker compose)
sudo ./install.sh --with-trivy --with-chartmuseum

# 验证
docker ps
curl -sk https://registry.example.com/api/v2.0/ping

# 登录并推送
docker login registry.example.com
docker tag nginx:latest registry.example.com/library/nginx:v1
docker push registry.example.com/library/nginx:v1

Harbor 运维要点:

  • 升级前必须备份数据库(PostgreSQL)和 /data 目录。
  • 镜像复制(Replication)可做跨机房/双活同步。
  • 定期执行 GC(垃圾回收)清理未引用的 blob。
  • 为每个项目设置保留策略(如只保留最近 30 个 tag)。

3.8 镜像命名规范

标准镜像名格式:[registry/][namespace/]repository[:tag][@digest]

1
2
3
docker.io/library/nginx:latest          # 官方镜像全名
registry.example.com/devops/api:v1.0.0 # 私有仓库项目
nginx@sha256:3f8d0d1e... # 按摘要引用(不可变、最安全)

团队命名规范建议:

1
2
3
{仓库域名}/{项目组}/{应用名}:{版本}-{环境}
registry.example.com/order/api:2.3.1-prod
registry.example.com/order/api:20260802-001
  • 生产禁用 latest,必须使用语义化版本或 commit hash。
  • 重要镜像同时记录 @sha256 摘要,保证可复现。

第 4 章 容器生命周期管理

4.1 容器状态机

1
2
3
4
5
6
7
Created(已创建) -> Running(运行中) -> Paused(暂停) / Exited(退出)
\ | |
\ v v
\--------> Dead(死亡) <----- Stopped(停止)
|
v
Removed(删除)

常用状态字段:created / running / restarting / exited / paused / dead

4.2 常用命令速查

命令 作用
docker run <镜像> 创建并启动容器
docker create <镜像> 只创建不启动
docker start/stop/restart <容器> 启停/重启
docker pause/unpause <容器> 暂停/恢复(SIGSTOP,不释放资源)
docker rm <容器> 删除已停止容器
docker rm -f <容器> 强制删除运行中容器
docker ps / docker ps -a 列出运行中/全部容器
docker exec -it <容器> bash 进入容器
docker logs [-f] <容器> 查看/跟踪日志
docker top <容器> 查看容器内进程
docker stats 实时资源占用
docker inspect <容器> 查看容器元数据(JSON)
docker cp 宿主机文件 <容器>:路径 宿主机与容器互拷文件
docker rename <旧名> <新名> 重命名
docker container prune 清理已停止容器

4.3 run 参数详解(高频)

1
2
3
4
5
6
7
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

# 最常用参数
docker run -d --name web -p 8080:80 nginx
# -d 后台运行(detached)
# --name web 指定容器名
# -p 8080:80 端口映射:宿主机8080 -> 容器80

参数分类详解:

分类 参数 说明
前台/后台 -d / -it 后台运行 / 交互式终端
端口 -p 8080:80-p 127.0.0.1:8080:80-P 端口映射 / 随机高端口 / 自动映射所有 EXPOSE
数据 -v /host:/container-v myvol:/data--mount 绑定挂载 / 命名卷
环境 -e KEY=value--env-file .env 环境变量
网络 --network mynet--network host--ip 172.20.0.10 网络模式与固定 IP
资源 -m 512m--cpus 1.5--memory-swap 内存/CPU 限制
重启 --restart always--restart unless-stopped--restart on-failure:5 自动重启策略
权限 --user 1000:1000--privileged--cap-add 运行用户/特权
健康 --health-cmd--health-interval 健康检查
主机名 -h web01--add-host a.com:1.2.3.4 主机名/hosts
只读 --read-only 根文件系统只读
其他 --rm--init--restart 退出即删 / 内置 init(僵尸进程回收)

4.4 创建、启动、停止、删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建但不启动
docker create --name web -p 8080:80 nginx

# 启动 / 停止 / 重启
docker start web
docker stop web # 默认等待 10s 优雅停止,可 -t 30 指定
docker restart web

# 删除(-f 强制删运行中的)
docker rm web
docker rm -f web

# 批量清理
docker stop $(docker ps -q) # 停止所有容器
docker rm $(docker ps -aq) # 删除所有容器
docker container prune -f # 清理所有已停止容器

docker stop 先发 SIGTERM(应用可优雅退出),超时后发 SIGKILL;docker kill 直接 SIGKILL。

4.5 进入容器与执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 交互式进入(推荐,等价 bash 登录 shell)
docker exec -it web bash

# 执行单个命令
docker exec web ls /etc/nginx
docker exec -d web touch /tmp/flag # 后台执行

# attach:附着到容器主进程(慎用,Ctrl+C 会终止容器)
docker attach web

# 复制文件(宿主机 <-> 容器)
docker cp ./index.html web:/usr/share/nginx/html/
docker cp web:/var/log/nginx/access.log ./access.log

生产排查原则:优先看日志,尽量不进入容器修改;容器内修改在容器重建后丢失。

4.6 端口映射

1
2
3
4
5
6
7
8
9
# 完整语法:-p [宿主机IP]:[宿主机端口]:[容器端口]/[协议]
docker run -d --name web -p 8080:80 nginx # 所有网卡 8080
docker run -d --name web -p 127.0.0.1:8080:80 nginx # 仅本机回环
docker run -d --name web -p 8080:80/tcp -p 8080:80/udp app # 同时 TCP+UDP
docker run -d --name web -P nginx # 自动分配随机端口

# 查看映射
docker port web
# 输出: 80/tcp -> 0.0.0.0:8080

常见问题:

  • 端口冲突报错:Bind for 0.0.0.0:8080 failed: port is already allocated —— 用 ss -tlnp | grep 8080 查占用并处理。
  • -p 只对 bridge 网络生效;host 模式下不需要也不允许端口映射。

4.7 环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
# 单个/多个环境变量
docker run -d --name db -e MYSQL_ROOT_PASSWORD=root123 -e MYSQL_DATABASE=appdb mysql:8.0

# 使用文件传入(避免密码进 history)
cat > .env <<EOF
MYSQL_ROOT_PASSWORD=root123
MYSQL_DATABASE=appdb
EOF
docker run -d --name db --env-file .env mysql:8.0

# 查看容器环境变量
docker exec db env
docker inspect db --format '{{range .Config.Env}}{{println .}}{{end}}'

生产规范:敏感信息不要硬编码进镜像或命令行,使用 Docker Secrets(Swarm)、K8s Secret,或环境变量注入 + 密钥管理平台(Vault)。

4.8 资源限制

1
2
3
4
5
6
7
8
9
10
11
12
13
# CPU:限制为 1.5 核(按比例),或者 0~100000 的份额
docker run -d --cpus=1.5 --name app myapp

# 内存:硬限制 512M,软限制 384M;超过硬限制会被 OOM Kill
docker run -d -m 512m --memory-reservation 384m --name app myapp
docker run -d -m 512m --memory-swap 1g --name app myapp # 512M 内存 + 最多 1G swap

# 查看限制与当前用量
docker inspect app --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}'
docker stats

# 运行中修改限制
docker update --cpus 2 -m 1g app

内存参数说明:

  • -m 512m = 限制容器最多使用 512MB 内存。
  • --memory-swap 1g = 内存 512MB + swap 可再借 512MB(总量 1G)。
  • 进程被 OOM 杀掉时,docker inspectState.OOMKilledtrue

4.9 重启策略

策略 行为 适用
no(默认) 不自动重启 调试、一次性任务
on-failure[:N] 非零退出码时重启,最多 N 次 短暂崩溃可自愈的任务
always 总是重启(含 daemon 重启后) 常驻服务
unless-stopped 除非手动 stop,否则总是重启 生产常驻服务(推荐)
1
2
docker run -d --restart always --name web nginx
docker update --restart unless-stopped web # 运行中修改

alwaysunless-stopped 的区别:daemon 重启后 always 会拉回手动停止的容器,unless-stopped 不会。

4.10 容器退出码与排障

退出码 含义 常见原因
0 正常退出 命令执行完毕
1 一般错误 启动命令失败、配置错误
126 命令无法执行 权限不足、二进制无执行 权限
127 命令不存在 command not found
137 SIGKILL 被 OOM Kill 或 docker kill
139 SIGSEGV 段错误,多为应用崩溃
143 SIGTERM 被优雅停止(docker stop
1
docker inspect web --format '{{.State.ExitCode}} {{.State.Error}} {{.State.OOMKilled}}'

第 5 章 Dockerfile 详解与镜像构建

5.1 Dockerfile 指令全解

FROM —— 指定基础镜像(必选,第一条指令)

1
2
3
FROM ubuntu:24.04
FROM node:20-alpine
FROM scratch # 空镜像,用于静态编译二进制

最佳实践:

  • 优先使用官方镜像;固定版本号,不要用 latest
  • 生产建议同时锁定摘要:FROM node:20-alpine@sha256:xxxx
  • 选用 alpine(小)或 distroless(更安全)来减小体积。

RUN —— 构建时执行命令(每一条 RUN 产生一层)

1
2
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*

关键点:

  • 把多条命令用 && 连接成一条 RUN,减少层数。
  • apt/yum 安装后立即清理缓存,避免镜像膨胀。
  • 避免在 RUN 中写死网络不可达的源;国内构建可换阿里源。

COPY / ADD —— 拷贝文件

1
2
3
COPY ./app /app                    # 只能拷贝构建上下文内的路径
ADD ./jdk.tar.gz /opt/ # ADD 支持 URL 与自动解压 tar.gz
COPY --chown=app:app app.js /app/ # 同时设置属主

区别:

  • COPY:仅本地文件拷贝,推荐首选。
  • ADD:支持 URL 下载和 tar 自动解压;URL 下载不可缓存且不安全,能不用就不用

CMD / ENTRYPOINT —— 启动命令

1
2
3
4
5
CMD ["nginx", "-g", "daemon off;"]          # exec 形式(推荐)
CMD nginx -g "daemon off;" # shell 形式
# entrypoint 作为首要参数,cmd作为entrypoint的附加参数
ENTRYPOINT ["python", "app.py"]
CMD ["--port", "8080"] # 作为 ENTRYPOINT 的参数
  • CMD:容器启动时的默认命令,可被 docker run 后面的参数覆盖
  • ENTRYPOINT:不可被覆盖(可用 --entrypoint 强制覆盖),适合定义固定入口。
  • 推荐组合:ENTRYPOINT 固定程序,CMD 提供默认参数。
  • 两者都支持 exec 形式(推荐,信号可直达)和 shell 形式。

EXPOSE —— 声明端口(文档性,不真正发布)

1
2
EXPOSE 80 443
EXPOSE 3306/tcp

只是元数据;真正发布端口靠 docker run -p 或 Compose 的 ports

ENV / ARG —— 环境变量与构建参数

1
2
3
ARG VERSION=1.0            # 构建参数,仅构建期存在
ENV APP_VERSION=$VERSION # 环境变量,运行期存在
ENV PATH=/usr/local/bin:$PATH
1
docker build --build-arg VERSION=2.0 -t app:2.0 .
  • ARG:构建期,docker build --build-arg 传入,不进镜像(除非被 ENV 引用)。
  • ENV:写入镜像,运行期可用。
  • 敏感信息不要写进 ENV(镜像层可见),用运行时注入。

WORKDIR —— 工作目录

1
2
3
WORKDIR /app      # 相当于 cd,且会自动创建目录
RUN npm install
CMD ["npm", "start"]

后续 RUN/CMD/ENTRYPOINT/COPY 的相对路径都基于 WORKDIR,推荐每个阶段开头设置。

USER —— 运行用户(安全关键)

1
2
RUN useradd -r -u 1001 appuser
USER appuser

默认 root 运行容器是生产环境大忌;尽量用非 root 用户,配合只读根文件系统(--read-only)。

VOLUME —— 声明匿名卷

1
VOLUME /data

作用是防止容器写数据到可写层;实际生产用 -v 或 Compose 显式命名卷更可控。

HEALTHCHECK —— 健康检查

1
2
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
  • 容器状态会显示 healthy / unhealthy / starting
  • Swarm 负载均衡和 K8s 探针都依赖它。

LABEL / MAINTAINER

1
2
LABEL org.opencontainers.image.source="https://github.com/team/app"
LABEL version="1.0" description="订单服务"

MAINTAINER 已废弃,改用 LABEL;给镜像打组织/版本/负责人标签便于审计。

ONBUILD —— 触发指令(了解即可)

1
2
3
FROM node:20-alpine
ONBUILD COPY . /app
ONBUILD RUN npm install

当该镜像被其他 Dockerfile 作为 FROM 时自动触发,常用于制作语言基础镜像。

.dockerignore —— 排除文件

1
2
3
4
5
6
7
8
9
# .dockerignore
node_modules
dist
.git
.gitignore
*.log
Dockerfile*
.dockerignore
.env

不排除会导致构建上下文巨大、慢、甚至把密钥打进镜像。

5.2 完整 Dockerfile 示例:Python Flask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ---------- 依赖阶段 ----------
FROM python:3.12-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt -t /deps

# ---------- 运行阶段 ----------
FROM python:3.12-slim
WORKDIR /app
# 只拷贝依赖,不携带构建工具链
COPY --from=builder /deps /usr/local/lib/python3.12/site-packages
COPY app.py .

# 非 root 用户
RUN useradd -r -u 1001 app && chown -R app:app /app
USER app

EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s CMD python -c "import urllib.request;urllib.request.urlopen('http://127.0.0.1:8000/health')"

CMD ["gunicorn", "-b", "0.0.0.0:8000", "-w", "2", "app:app"]

5.3 多阶段构建(Multi-stage Build)

解决”构建环境大、运行环境小”的矛盾:一个 Dockerfile 里多个 FROM,最终只保留最后一个阶段的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ---------- 阶段1:构建 ----------
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server

# ---------- 阶段2:运行(极简) ----------
FROM alpine:3.20
RUN adduser -D -u 1001 app
COPY --from=builder /app/server /usr/local/bin/server
USER app
EXPOSE 8080
ENTRYPOINT ["server"]

Java 多阶段示例:

1
2
3
4
5
6
7
8
9
10
11
12
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests

FROM eclipse-temurin:17-jre-alpine
RUN adduser -D -u 1001 app
COPY --from=build /build/target/app.jar /app/app.jar
USER app
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

Node 多阶段示例:

1
2
3
4
5
6
7
8
9
10
11
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

5.4 构建流程与缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 基本构建
docker build -t myapp:v1 .
docker build -t myapp:v1 -f docker/Dockerfile.prod .

# 指定构建上下文(注意:上下文影响 COPY 能访问的文件)
docker build -t myapp:v1 -f Dockerfile ./app

# 不使用缓存(排除构建缓存干扰,排查问题用)
docker build --no-cache -t myapp:v1 .

# 查看层信息
docker history myapp:v1

# 构建加速:BuildKit(Docker 24+ 默认启用)
DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp:v1 .

缓存命中规则(逐条):

  1. 从第一条指令开始逐层比对,若某条指令的输入(基础镜像、上下文文件、参数)没变,则复用缓存层。
  2. 文件发生变化会导致该 RUN/COPY 及其之后所有层失效。
  3. 因此把变化少的指令放前面(如 COPY package*.jsonRUN npm ciCOPY . .)。
  4. ADD 的 URL 内容变化可能导致缓存失效;ARG 变化也会使后续层失效。

经典错误:COPY . .RUN npm install —— 任何代码改动都触发重新安装依赖。正确顺序:

1
2
3
COPY package.json package-lock.json ./
RUN npm ci # 依赖层只会在 lock 文件变化时重建
COPY . . # 代码层频繁变化,放最后

5.5 镜像瘦身技巧

方法 效果 做法
多阶段构建 大幅减小 构建阶段与运行阶段分离
alpine/distroless 基础镜像 体积减半以上 FROM alpine / FROM gcr.io/distroless/static
合并 RUN 减少层数 apt-get update && install && rm -rf ...
清理包管理缓存 减几十~上百 MB rm -rf /var/lib/apt/lists/*
静态编译 Go/Rust 可到 10MB 级 CGO_ENABLED=0
--squash(实验性,慎用) 压平所有层 不推荐,破坏层共享
docker buildx --squash 同上 BuildKit 支持
1
2
# 对比各镜像体积
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

5.6 构建多架构镜像(buildx)

1
2
3
4
5
6
7
8
9
10
11
12
# 启用 buildx 与 QEMU 模拟
docker buildx create --name multiarch --use
docker run --privileged --rm tonistiigi/binfmt --install all

# 同时构建 amd64 + arm64,并推送
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.example.com/myapp:v1 \
--push .

# 查看清单
docker buildx imagetools inspect registry.example.com/myapp:v1

5.7 Dockerfile 编写检查清单

  • FROM 固定版本(不用 latest),必要时锁定 digest
  • 先拷贝依赖清单,再安装依赖,最后拷贝源码(最大化缓存)
  • RUN 命令用 && 合并并清理缓存
  • 优先 COPY,少用 ADD
  • 使用多阶段构建
  • 使用非 root 用户(USER
  • 敏感信息不进 ENV/COPY
  • .dockerignore
  • 定义 HEALTHCHECKEXPOSE
  • 镜像 tag 语义化,不打 latest

第 6 章 数据管理

6.1 三种数据存储方式对比

方式 生命周期 位置 适用场景
容器可写层 随容器删除而消失 容器内 临时文件(不建议存重要数据)
绑定挂载 (bind mount) 随宿主机文件存在 宿主机任意路径 配置文件、日志、开发热更新
命名卷 (volume) Docker 管理,独立于容器 /var/lib/docker/volumes/ 数据库等持久化数据(推荐)
tmpfs 仅内存,重启消失 内存 敏感临时数据、性能要求高的缓存

6.2 数据卷(Volume,推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建命名卷
docker volume create mysql-data

# 使用卷(-v 语法)
docker run -d --name mysql -v mysql-data:/var/lib/mysql mysql:8.0

# 更规范的 --mount 语法
docker run -d --name mysql \
--mount type=volume,source=mysql-data,target=/var/lib/mysql \
mysql:8.0

# 匿名卷(不指定名字)
docker run -d -v /var/lib/mysql mysql:8.0

# 查看与管理
docker volume ls
docker volume inspect mysql-data # 显示宿主机挂载点
docker volume rm mysql-data # 删除卷(会删数据!)
docker volume prune # 清理未使用的卷

为什么推荐命名卷:

  • 数据与容器解耦,删除/重建容器数据不丢。
  • Docker 自动管理目录权限与属主。
  • 备份/迁移/跨主机复制(卷驱动)更方便。
  • 支持卷驱动(如 NFS、云盘插件)实现共享存储。

6.3 绑定挂载(Bind Mount)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 把宿主机目录挂进容器
docker run -d --name web -v /srv/www:/usr/share/nginx/html:ro nginx
# 宿主机目录 : 容器目录 : 只读

# --mount 语法
docker run -d --name web \
--mount type=bind,source=/srv/www,target=/usr/share/nginx/html,readonly \
nginx

# 单文件挂载(配置文件)
docker run -d --name nginx \
-v /etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf \
nginx

绑定挂载注意事项:

  • 宿主机目录不存在时,-v自动创建(以 root 创建,可能权限异常);--mount 不会自动创建。
  • 挂载后容器内看到的属主是宿主机文件的属主,注意 UID 对齐(常见坑:数据库权限报错)。
  • 目录内容会覆盖容器内原有目录(例如把宿主机空目录挂到 /var/lib/mysql 会覆盖初始化数据)。
  • 开发环境热更新很方便:改宿主机代码容器立即生效。

6.4 tmpfs 挂载

1
2
3
docker run -d --name cache \
--tmpfs /tmp:rw,noexec,nosuid,size=128m \
redis:7

数据在内存中,容器停止即清空;适合 session、临时缓存。

6.5 数据备份与恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 备份:把卷打包成 tar(利用临时容器)
docker run --rm \
-v mysql-data:/data \
-v /backup:/backup \
ubuntu:24.04 tar czf /backup/mysql-data-$(date +%F).tar.gz -C /data .

# 恢复:解包回卷
docker run --rm \
-v mysql-data:/data \
-v /backup:/backup \
ubuntu:24.04 tar xzf /backup/mysql-data-2026-08-02.tar.gz -C /data

# 数据库建议用官方工具做逻辑备份
docker exec mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /backup/dump.sql

6.6 卷使用规范

  • 生产数据库(MySQL/PostgreSQL/Redis 持久化)必须使用命名卷,并定期备份+验证恢复。
  • 日志目录挂载宿主机路径或卷,配合日志轮转,避免撑爆磁盘。
  • 配置类文件用 bind mount + 只读(:ro)。
  • 命名规则建议:{应用}-{用途}-{环境},如 order-db-prod
  • 定期 docker volume prune 前先确认卷未被引用(docker ps -a 检查)。

第 7 章 网络管理

7.1 网络模式总览

模式 说明 使用场景
bridge(默认) 容器通过虚拟网桥(docker0)与外界通信,可端口映射 单机多容器,最常用
host 容器直接使用宿主机网络栈(无隔离、无端口映射) 高性能场景、依赖本机端口的服务
none 无网络 纯计算任务、安全隔离
container:<其他容器> 与指定容器共享网络栈 调试、代理场景
overlay 跨主机网络(Swarm/K8s 使用) 集群场景
1
2
3
4
5
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# 0f5d4a... bridge bridge local
# 4a2c3b... host host local
# 5b7c9d... none null local

7.2 bridge 网络(默认)

1
2
3
4
5
6
7
宿主机
+--------------------------------------------------+
| docker0 (172.17.0.1/16) |
| | |
| +---- veth-pair ---- 容器 eth0 (172.17.0.2) |
| +---- veth-pair ---- 容器 eth0 (172.17.0.3) |
+--------------------------------------------------+
  • 每个容器有一对 veth 虚拟网卡,一端在容器内(eth0),一端连到 docker0 网桥。
  • 容器间默认可以互通(通过网桥)。
  • 容器访问外网走 NAT(MASQUERADE【SNAT 的动态版本】);外网访问容器需端口映射(DNAT/iptables)。
1
2
3
# 查看容器 IP 与网络细节
docker inspect web --format '{{.NetworkSettings.Networks.bridge.IPAddress}}'
docker network inspect bridge

7.3 host 模式

1
docker run -d --name web --network host nginx   # 直接监听宿主 80 端口

特点与坑:

  • 性能最好(无 NAT 开销),容器内看到的就是宿主机网络。
  • 不能同时用 -p(会警告且无效)。
  • 容器内端口冲突即宿主机端口冲突。
  • 网络隔离丧失,安全性下降。

7.4 none 模式

1
docker run -d --name worker --network none myjob

容器只有 lo 回环接口,适合离线计算任务或需要严格网络隔离的场景。

7.5 自定义网络(生产推荐)

默认 bridge 网络不支持容器名 DNS 解析(老版本),且与宿主网络耦合。生产用自定义 bridge:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建自定义桥接网络
docker network create -d bridge --subnet 172.20.0.0/16 --gateway 172.20.0.1 app-net

# 指定网络启动容器
docker run -d --name web --network app-net nginx
docker run -d --name api --network app-net myapi
docker run -d --name db --network app-net mysql:8.0

# 动态加入/退出网络
docker network connect app-net web
docker network disconnect app-net web

# 指定静态 IP(自定义网络才支持)
docker run -d --name db --network app-net --ip 172.20.0.10 mysql:8.0

# 删除网络(需先断开所有容器)
docker network rm app-net

7.6 容器间通信(服务发现)

同一个自定义网络内,容器可用容器名直接访问(Docker 内置 DNS):

1
2
3
4
5
6
# web 容器内访问同网络的 api 容器
docker exec web curl http://api:8080/health
# 数据库连接串示例:jdbc:mysql://db:3306/appdb

# 验证 DNS 解析
docker exec web getent hosts api

连通性规则:

  • 同自定义网络:容器名互访,推荐。
  • 默认 bridge:可 IP 互访,但无容器名 DNS(需 --link,已废弃,不推荐)。
  • 不同网络:默认隔离,需 docker network connect 加入才能通信。
  • 多网络容器:同时拥有多个网络的 IP,可做”跳板”桥接。

7.7 端口发布原理

1
2
3
4
5
6
7
外网/宿主机 8080 端口
|
v
iptables DNAT 规则 (docker-proxy / hairpin)
|
v
docker0 网桥 -> 容器 eth0:80
  • -p 8080:80 会同时创建 iptables 规则(DOCKER 链)和 docker-proxy 用户态进程。
  • 查看规则:
1
2
iptables -t nat -L -n | grep 8080
ss -tlnp | grep 8080 # 会看到 docker-proxy

7.8 跨主机网络(overlay 简介)

单机 bridge 无法跨主机。Docker Swarm 模式内置 overlay 网络(VXLAN 封装):

1
2
3
4
5
# 集群环境(见第 12 章)
docker network create -d overlay --attachable prod-net

# 不同主机上的服务加入同一 overlay 网络即可互通
docker service create --network prod-net --name api myapp

云原生场景更常用 K8s 的 CNI 插件(Calico/Flannel/Cilium)实现跨主机网络。

7.9 网络排障速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 容器内网络诊断(部分精简镜像没有 ping/curl,可临时装或换镜像)
docker exec web ip addr
docker exec web ip route
docker exec web getent hosts db

# 测试宿主机到容器
curl http://127.0.0.1:8080

# 查看端口占用(宿主)
ss -tlnp | grep -E '8080|3306'

# 常见错误
# 1. 端口已被占用:Bind for 0.0.0.0:8080 failed: port is already allocated
# 2. 找不到容器名:ping: api: Name or service not known(没在同一网络/没起容器)
# 3. 外部访问不通:检查防火墙(ufw/firewalld/安全组)是否放行

第 8 章 Docker Compose 编排

8.1 什么是 Compose

Compose 用一个 YAML 文件声明多个容器(服务)、网络、卷、依赖关系,一条命令完成整栈的启动/停止/重建。适合:开发环境、单机多服务、测试环境、CI 中的集成测试。

注意区分:Compose v1docker-compose,Python 实现,已停更)与 Compose v2docker compose,Go 插件,Docker 24+ 自带)。本文使用 v2。

8.2 安装

1
2
3
4
5
6
7
8
9
10
# Docker 24+ 已内置插件:docker compose version
docker compose version

# 旧版本单独安装插件(以 Linux 为例)
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 \
-o $DOCKER_CONFIG/cli-plugins/docker-compose
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
docker compose version

8.3 compose.yaml 核心语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# compose.yaml(文件命名:compose.yaml / compose.yml / docker-compose.yml 均可)
name: myapp # 项目名(影响容器命名前缀与网络名)

services: # 顶层:服务列表
web:
image: nginx:1.27-alpine # 方式1:直接用镜像
# build: ./web # 方式2:本地构建(可同时指定 image)
container_name: myapp-web # 指定容器名(注意全局唯一)
ports:
- "8080:80" # 端口映射
environment: # 环境变量
- TZ=Asia/Shanghai
- APP_ENV=prod
env_file: # 从文件读环境变量
- .env
volumes:
- ./html:/usr/share/nginx/html:ro
- web-logs:/var/log/nginx
networks:
- app-net
depends_on:
- api
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-fs", "http://localhost/"]
interval: 30s
timeout: 3s
retries: 3

api:
build:
context: ./api
dockerfile: Dockerfile
image: registry.example.com/api:1.0 # 构建后推送名
ports:
- "8081:8080"
environment:
DB_HOST: db
DB_PORT: "3306"
depends_on:
db:
condition: service_healthy # 等待 db 健康后再启动(Compose v2 支持)
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M

db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123} # 支持变量插值
MYSQL_DATABASE: appdb
volumes:
- db-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro # 首次初始化 SQL
ports:
- "3306:3306"
networks:
- app-net
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5

networks: # 顶层:网络
app-net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16

volumes: # 顶层:命名卷
db-data:
web-logs:

关键要点:

  • name 定义项目名;默认项目名 = 目录名。
  • 同一 Compose 项目下的服务自动加入同名默认网络,可用服务名互相访问(如 dbapi)。
  • $$ 表示转义的 $(避免 Compose 提前插值,如密码中的 $)。
  • depends_on 控制启动顺序;condition: service_healthy 可等待健康检查通过。
  • build + image 同时存在时:本地构建,成功后打上 image 标签。

8.4 常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 启动(-d 后台)
docker compose up -d
docker compose up -d --build # 构建后启动
docker compose up -d --scale web=3 # 扩容(仅无端口映射的服务)

# 查看
docker compose ps
docker compose ps -a
docker compose logs -f --tail 100 web
# 需要指定dockerfile
docker compose top

# 执行
docker compose exec web bash
docker compose run --rm web pytest

# 停止/删除
docker compose stop # 停止,保留容器
docker compose down # 停止并删除容器+网络
docker compose down -v # 同时删除卷(会删数据!慎用)
docker compose down --rmi all # 同时删除镜像

# 配置校验/展开
docker compose config
docker compose config --services
docker compose config --volumes

# 查看依赖与健康状态
docker compose ps --format "table {{.Name}}\t{{.Status}}"

8.5 变量与多环境

1
2
# 变量插值(默认值)
image: mysql:${MYSQL_VERSION:-8.0}
1
2
3
4
# .env 文件(与 compose.yaml 同目录,自动读取)
MYSQL_VERSION=8.0
MYSQL_ROOT_PASSWORD=secret123
APP_ENV=prod

多环境方案:

  • 多个 compose 文件叠加:docker compose -f base.yaml -f dev.yaml up -d
  • 每个环境一个目录 + 各自的 .env
  • 不要提交 .env 到 Git;用 .env.example 模板。
1
2
3
docker compose -f compose.yaml -f compose.prod.yaml up -d
# 可以读取 .env 文件后,将变量值替换为真正的值的yaml文件
docker compose -f compose.prod.yaml config > 最终配置校验.yaml

8.6 实战:LNMP 完整栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
name: lnmp

services:
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./www:/usr/share/nginx/html:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./logs/nginx:/var/log/nginx
depends_on:
- php
restart: unless-stopped

php:
build: ./php
volumes:
- ./www:/var/www/html
environment:
- TZ=Asia/Shanghai
depends_on:
- mysql
restart: unless-stopped

mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: appdb
volumes:
- mysql-data:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped

redis:
image: redis:7-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis-data:/data
restart: unless-stopped

volumes:
mysql-data:
redis-data:

8.7 Compose 最佳实践

  • 每个项目一个目录、一份 compose 文件、一份 .env
  • 生产数据库端口不要映射到宿主机(仅容器内访问)。
  • 使用 depends_on + healthcheck 处理启动依赖,而不是 sleep
  • 敏感配置用 env_file 或外部注入,不入库。
  • docker compose config 校验后再 up
  • 版本升级前 docker compose down + 备份卷。
  • Compose 适合单机;多机集群用 Swarm/K8s。

第 9 章 实战部署案例

9.1 Nginx 部署静态网站

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 方案A:直接把静态文件挂载进官方 nginx 镜像
mkdir -p /srv/www
echo "<h1>Hello Docker</h1>" > /srv/www/index.html
docker run -d --name web \
-p 80:80 \
-v /srv/www:/usr/share/nginx/html:ro \
--restart unless-stopped \
nginx:1.27-alpine

# 方案B:自定义配置
cat > /srv/nginx/default.conf <<'EOF'
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain text/css application/javascript;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
EOF
docker run -d --name web \
-p 80:80 \
-v /srv/www:/usr/share/nginx/html:ro \
-v /srv/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
--network app-net \
nginx:1.27-alpine

# 校验
docker exec web nginx -t
curl -I http://127.0.0.1/
docker logs --tail 20 web

Nginx 容器运维要点:

  • 修改配置后 docker exec web nginx -t 校验,再 docker exec web nginx -s reload 平滑重载(无需重启容器)。
  • 日志挂载到宿主机,便于统一采集。
  • HTTPS 证书挂载进 /etc/nginx/ssl,并配置 listen 443 ssl

9.2 MySQL 8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 单实例
docker run -d --name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=Root123! \
-e MYSQL_DATABASE=appdb \
-e MYSQL_USER=app \
-e MYSQL_PASSWORD=App123! \
-v mysql-data:/var/lib/mysql \
--restart unless-stopped \
mysql:8.0

# 首次初始化 SQL(仅在数据目录为空时执行)
mkdir -p /srv/mysql-init
cat > /srv/mysql-init/01_init.sql <<'EOF'
CREATE DATABASE IF NOT EXISTS orders DEFAULT CHARACTER SET utf8mb4;
CREATE USER IF NOT EXISTS 'orders'@'%' IDENTIFIED BY 'Orders123!';
GRANT ALL PRIVILEGES ON orders.* TO 'orders'@'%';
FLUSH PRIVILEGES;
EOF
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=Root123! \
-v mysql-data:/var/lib/mysql \
-v /srv/mysql-init:/docker-entrypoint-initdb.d:ro \
mysql:8.0

# 常用运维命令
docker exec -it mysql mysql -uroot -p
docker exec mysql mysqladmin ping
docker exec mysql sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" appdb' > /backup/appdb.sql

关键参数建议(生产):

1
2
3
4
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--default-authentication-plugin=caching_sha2_password
--max_connections=500
1
2
3
4
5
6
7
8
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=Root123! \
--cpus 4 -m 4g \
-v mysql-data:/var/lib/mysql \
mysql:8.0 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci \
--max_connections=500

重要:MySQL 数据必须挂卷;初始化 SQL 只在数据目录为空时执行一次;升级大版本前先备份再换镜像 tag,并核对数据兼容性。

9.3 Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 开发环境(默认无持久化)
docker run -d --name redis -p 6379:6379 redis:7-alpine

# 生产:AOF 持久化 + 密码 + 数据卷
docker run -d --name redis \
-p 6379:6379 \
-v redis-data:/data \
redis:7-alpine \
redis-server --appendonly yes --requirepass Redis123!

# 连接测试
docker exec -it redis redis-cli -a Redis123! ping
# PONG

# 进入容器交互
docker exec -it redis redis-cli -a Redis123!

Redis 配置建议:

  • 开启 appendonly yes(AOF)或 RDB(默认),生产建议 AOF + RDB 都开。
  • 设置 maxmemory(如 --maxmemory 512mb --maxmemory-policy allkeys-lru),防止内存耗尽宿主机。
  • 哨兵/集群部署复杂,单机用 redis.conf 挂载:
1
2
3
4
5
6
7
8
9
10
11
12
13
cat > /srv/redis/redis.conf <<'EOF'
appendonly yes
requirepass Redis123!
maxmemory 512mb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
EOF
docker run -d --name redis \
-p 6379:6379 \
-v /srv/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro \
-v redis-data:/data \
redis:7-alpine redis-server /usr/local/etc/redis/redis.conf

9.4 PostgreSQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker run -d --name pg \
-p 5432:5432 \
-e POSTGRES_PASSWORD=PG123! \
-e POSTGRES_USER=app \
-e POSTGRES_DB=appdb \
-v pg-data:/var/lib/postgresql/data \
--restart unless-stopped \
postgres:16-alpine

# 进入 psql
docker exec -it pg psql -U app -d appdb

# 备份
docker exec pg pg_dump -U app appdb > /backup/appdb.dump

注意:PostgreSQL 数据目录挂载路径是 /var/lib/postgresql/data(镜像版本不同可能变化,用官方文档为准)。

9.5 Java Spring Boot 应用

Dockerfile(多阶段):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
RUN adduser -D -u 1001 app && chown -R app:app /app
COPY --from=build /build/target/app.jar app.jar
USER app
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -q --spider http://127.0.0.1:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
1
2
3
4
5
6
7
8
9
10
11
12
docker build -t myapp:1.0.0 .
docker run -d --name myapp \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_HOST=db -e DB_PORT=3306 -e DB_PASSWORD=xxxx \
--cpus 2 -m 2g \
--restart unless-stopped \
myapp:1.0.0

# 查看健康状态
curl http://127.0.0.1:8080/actuator/health
docker inspect myapp --format '{{.State.Health.Status}}'

JVM 容器最佳实践:

  • 使用 -XX:MaxRAMPercentage=75.0 让 JVM 感知 cgroup 内存限制(不要写死 -Xmx)。
  • 镜像用 JRE(运行时),不用 JDK(构建时)。
  • 日志输出到 stdout(SLF4J 控制台 appender),由 Docker 收集。

9.6 Node.js 应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
RUN adduser -D -u 1001 app
COPY --from=build --chown=app:app /app/package*.json ./
COPY --from=build --chown=app:app /app/node_modules ./node_modules
COPY --from=build --chown=app:app /app/dist ./dist
USER app
EXPOSE 3000
CMD ["node", "dist/main.js"]
1
2
docker build -t nodeapp:1.0 .
docker run -d --name nodeapp -p 3000:3000 --restart unless-stopped nodeapp:1.0

npm ci 需要 package-lock.json;生产镜像只装 --omit=dev 依赖,或用上面”复制 node_modules”的方式避免重复安装。

9.7 Python Flask/Django

1
2
3
4
5
6
7
8
9
10
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -r -u 1001 app && chown -R app:app /app
USER app
EXPOSE 8000
CMD ["gunicorn", "-b", "0.0.0.0:8000", "-w", "4", "app:app"]
1
2
docker build -t pyapp:1.0 .
docker run -d --name pyapp -p 8000:8000 -e DATABASE_URL=mysql://app:pass@db:3306/appdb pyapp:1.0

9.8 前端项目构建部署(Nginx + 静态资源)

1
2
3
4
5
6
7
8
9
10
11
12
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG VITE_API_BASE=/api
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
1
2
docker build --build-arg VITE_API_BASE=https://api.example.com -t frontend:1.0 .
docker run -d --name frontend -p 80:80 frontend:1.0

9.9 ELK/EFK 日志栈

轻量替代方案:Filebeat → Elasticsearch → Kibana,或用 Loki + Promtail + Grafana(更轻)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
name: elk

services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- xpack.security.enabled=false
volumes:
- es-data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
restart: unless-stopped

kibana:
image: docker.elastic.co/kibana/kibana:8.14.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
restart: unless-stopped

filebeat:
image: docker.elastic.co/beats/filebeat:8.14.0
user: root
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro # 读取容器日志
- /var/run/docker.sock:/var/run/docker.sock:ro # 发现容器元数据
depends_on:
- elasticsearch
restart: unless-stopped

volumes:
es-data:
1
2
3
4
5
6
7
8
9
# filebeat.yml
filebeat.inputs:
- type: container
paths:
- /var/lib/docker/containers/*/*.log

output.elasticsearch:
hosts: ["elasticsearch:9200"]
index: "docker-logs-%{+yyyy.MM.dd}"
1
2
3
4
docker compose up -d
# 验证
curl http://127.0.0.1:9200/_cat/indices
# 打开 http://127.0.0.1:5601 配置索引模式 docker-logs-*

9.10 监控栈:cAdvisor + Prometheus + Grafana

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
name: monitoring

services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
privileged: true
restart: unless-stopped

prometheus:
image: prom/prometheus:v2.53.0
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prom-data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.retention.time=30d
restart: unless-stopped

grafana:
image: grafana/grafana:11.1.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana-data:/var/lib/grafana
restart: unless-stopped

volumes:
prom-data:
grafana-data:
1
2
3
4
5
6
7
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: "docker"
static_configs:
- targets: ["cadvisor:8080"]
1
2
3
docker compose up -d
# Grafana http://127.0.0.1:3000 admin/admin123
# 添加数据源 http://prometheus:9090,导入面板 ID 14282(Docker monitoring)

常用指标:

  • 容器 CPU:rate(container_cpu_usage_seconds_total{name=~".+"}[5m])
  • 容器内存:container_memory_usage_bytes
  • 容器网络:rate(container_network_receive_bytes_total[5m])
  • 容器磁盘:container_fs_usage_bytes

9.11 高频踩坑与对应解法

现象 原因 解法
容器秒退(Exited 0/1) 前台进程不存在/启动失败 docker logs;用 -it 调试
端口已分配 端口冲突 ss -tlnp 找占用进程,改端口或停旧容器
permission denied 挂载目录 宿主目录属主与容器 UID 不一致 --user 对齐 UID,或 chown -R
数据丢失 没用卷/用了匿名卷且删容器 用命名卷,备份
时区错误 镜像默认 UTC -e TZ=Asia/Shanghai 或挂 /etc/localtime
中文乱码 缺 locale 基础镜像装 locales 并设置 LANG=C.UTF-8
DNS 解析不到服务名 不在同一自定义网络 都加入同一 --network
docker.sock 权限 容器内用户无权限 仅可信容器挂载,user=root 或加组
镜像拉取超时 网络/加速问题 配置 registry-mirrors
磁盘被日志/镜像占满 无日志轮转、无清理 max-size/max-file + 定期 docker system prune

第 10 章 安全加固

10.1 安全威胁模型

攻击面 风险
镜像供应链 恶意镜像、带漏洞依赖、被投毒的基础镜像
容器逃逸 内核漏洞、特权容器、危险挂载(docker.sock)
配置不当 root 运行、弱密码、敏感信息入镜像、端口全开
网络 容器间过度信任、暴露不必要的端口
守护进程 docker.sock 未保护、2375 端口裸奔

10.2 镜像安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 只从可信源拉取:官方镜像 + 自己的 Harbor
# 2. 使用固定 digest
docker pull nginx:1.27-alpine@sha256:xxxxxxxx

# 3. 漏洞扫描(Trivy,开源推荐)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image nginx:1.27-alpine
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image --severity HIGH,CRITICAL --ignore-unfixed myapp:1.0

# 4. 扫描私有仓库
trivy repo https://github.com/org/app
trivy fs --scanners vuln,secret,config .

# 5. 检查 Dockerfile 不规范项,把本地带dockerfile文件的文件目录映射到容器内,让容器内的命令去检测dockerfile文件是否规范。
docker run --rm -v $PWD:/project hadolint/hadolint /bin/hadolint /project/Dockerfile

镜像安全清单:

  • 基础镜像固定版本 + digest
  • 使用 alpine / distroless 精简镜像
  • 以非 root 用户运行
  • 安装依赖后清理缓存
  • 无明文密钥/密码(检查 history、ENV、配置文件)
  • 启用签名校验(Cosign/Notation)
  • CI 中强制漏洞扫描门禁(高危以上阻断发布)

10.3 运行时安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 非 root 运行
docker run -d --user 1000:1000 myapp

# 2. 去掉不必要的 Linux capabilities(默认已最小化,可更严)
docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE myapp

# 3. 只读根文件系统 + 临时目录挂内存
docker run -d --read-only --tmpfs /tmp:rw,size=64m myapp

# 4. 安全选项
docker run -d --security-opt no-new-privileges myapp
docker run -d --security-opt seccomp=./myseccomp.json myapp

# 5. 禁止特权模式与危险挂载
# 绝不允许:--privileged
# 绝不允许:挂载 /var/run/docker.sock、宿主根目录 /

# 6. 资源限制(防 DDoS/资源耗尽)
docker run -d -m 512m --cpus 1 --pids-limit 256 myapp

# 7. 网络最小化
docker network create --internal-only # 容器无外网
docker run -d --network none myjob

10.4 守护进程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 默认 Unix Socket 权限
ls -l /var/run/docker.sock
# srw-rw---- 1 root docker -> 仅 root 和 docker 组可访问

# 2. 不要裸开 TCP 2375(无 TLS)
# 禁止:
# dockerd -H tcp://0.0.0.0:2375

# 3. 需要远程管理时用 TLS(推荐直接用企业编排平台)
# 或限制监听地址 + 防火墙 + 认证

# 4. 审计
auditctl -w /var/lib/docker -k docker

10.5 安全检查工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Docker Bench for Security:一键基线检查
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
sudo sh docker-bench-security.sh

# Trivy:镜像/文件系统/仓库/配置扫描
trivy image myapp:1.0
trivy config . # IaC 配置检查
trivy fs --scanners secret . # 密钥泄露扫描

# Grype(Anchore)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
anchore/grype nginx:latest

# 运行时监控:Falco(云原生安全)
docker run -d --name falco \
--privileged \
-v /var/run/docker.sock:/var/run/docker.sock \
falcosecurity/falco

10.6 生产安全基线(对照清单)

  • 不使用 --privileged--cap-drop ALL
  • 非 root 用户运行,no-new-privileges
  • 只读根文件系统(--read-only
  • 资源限制:CPU/内存/PID 数
  • 不挂载 docker.sock / 宿主根目录 / 敏感路径
  • 容器内无密钥;密钥走 Secret 机制
  • 所有镜像过扫描门禁
  • 私有仓库启用认证 + RBAC + 审计
  • 端口最小暴露,数据库不暴露公网
  • daemon 不开放未加密 TCP 端口
  • 定期更新 Docker Engine 与镜像补丁
  • 主机内核与 Docker 版本保持最新(防逃逸漏洞)

第 11 章 日志与监控

11.1 日志驱动

容器日志默认由 json-file 驱动写入宿主机 /var/lib/docker/containers/<id>/<id>-json.log

1
2
3
4
5
6
7
8
# 查看容器日志(默认 STDOUT/STDERR)
docker logs web
docker logs --tail 200 web
docker logs -f --since 5m web # 5分钟之内的日志
docker logs --since 2026-08-02T00:00:00 --until 2026-08-02T08:00:00 web # 固定时间段内的日志

# 查看日志驱动与配置
docker inspect web --format '{{.HostConfig.LogConfig.Type}} {{.HostConfig.LogConfig.Config}}'

配置日志轮转(daemon.json 全局或运行参数):

1
2
3
4
5
6
7
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "5"
}
}
1
2
# 单容器指定
docker run -d --log-driver json-file --log-opt max-size=20m --log-opt max-file=3 web

常用日志驱动对比:

驱动 特点 适用
json-file(默认) 落盘 JSON,易轮转 通用,最常用
journald 写入 systemd journal systemd 环境统一收集
syslog 发送到 syslog 服务 有 syslog 中心
fluentd 直接发到 Fluentd 日志管道场景
gelf 发到 Graylog Graylog 用户
awslogs / gcplogs / azurelogs 云厂商日志 云环境
none 丢弃日志 高吞吐、日志走应用直传
1
2
3
# 切到 journald
docker run -d --log-driver journald --name web web
journalctl -u docker -f | grep web

11.2 日志采集架构

1
2
3
4
5
6
7
8
9
10
容器 stdout
|
v
json-file (宿主机)
|
v
Filebeat / Promtail / Fluent Bit
|
v
Elasticsearch + Kibana / Loki + Grafana / ClickHouse

生产建议:应用只写 stdout,由采集器统一收集,避免容器内多份文件日志导致采集混乱。若必须写文件,挂载卷并让采集器读卷。

11.3 容器监控体系

工具 作用
主机层 Node Exporter + Prometheus CPU/内存/磁盘/网络
容器层 cAdvisor 容器资源指标(容器视角)
应用层 应用指标端点(/metrics、/actuator) 业务与性能指标
展示 Grafana 面板与告警
告警 Alertmanager 规则触发通知(邮件/钉钉/企微/Slack)

告警规则示例(Prometheus):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
groups:
- name: docker-alerts
rules:
- alert: ContainerDown
expr: time() - container_last_seen{name=~".+"} > 60
for: 1m
labels:
severity: critical
annotations:
summary: "容器 {{ $labels.name }} 已停止响应"

- alert: HighContainerMemory
expr: container_memory_usage_bytes{name=~".+"} / container_spec_memory_limit_bytes{name=~".+"} > 0.85
for: 5m
labels:
severity: warning

11.4 事件与审计

1
2
3
4
# 实时查看 Docker 事件(创建/启动/停止/删除/健康状态变化)
docker events
docker events --filter type=container --filter event=die
docker events --since 1h

第 12 章 Docker Swarm 集群

12.1 Swarm 架构

1
2
3
4
5
6
7
8
+------------------+        +------------------+        +------------------+
| Manager 节点 | | Manager 节点 | | Manager 节点 |
| (Raft, 调度) | <----> | (Raft, 调度) | <----> | (Raft, 调度) |
+------------------+ +------------------+ +------------------+
| | |
+------------------+ +------------------+ +------------------+
| Worker 节点 | | Worker 节点 | | Worker 节点 |
+------------------+ +------------------+ +------------------+
  • Manager 节点:Raft 共识存储集群状态,负责任务调度、服务发现、集群管理(建议奇数台,≥3 高可用)。
  • Worker 节点:只运行任务(Task),把任务结果上报。
  • Service:长期运行的服务声明(镜像、副本数、网络、更新策略)。
  • Task:Service 的一个运行实例(一个容器)。

与 Kubernetes 对应关系:Service≈Deployment,Task≈Pod,Manager≈Control Plane,Worker≈Node。

12.2 初始化集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 在 Manager-1 上初始化
docker swarm init --advertise-addr 192.168.1.10

# 输出包含加入令牌,保存:
# docker swarm join --token SWMTKN-1-xxxx 192.168.1.10:2377

# 2. 加入 Worker
# 在 Worker 节点执行(使用 Manager 输出的令牌)
docker swarm join --token SWMTKN-1-xxxx 192.168.1.10:2377

# 3. 加入 Manager(高可用)
# 获取 manager 加入令牌
docker swarm join-token manager
docker swarm join --token SWMTKN-1-yyyy 192.168.1.10:2377

# 4. 查看节点
docker node ls
docker node inspect node2
docker node update --availability drain node2 # 摘除节点(排空任务)
docker node rm node2 # 移除节点(先 drain)

安全要点:

  • 2377 端口用于集群管理通信;4789(VXLAN)、7946(gossip)用于 overlay 网络。
  • 定期轮换令牌:docker swarm join-token --rotate worker
  • 生产环境控制台(Manager)不要跑业务负载。

12.3 部署服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 创建服务
docker service create \
--name web \
--replicas 3 \
--publish published=8080,target=80 \
--update-delay 10s \
--restart-condition any \
--with-registry-auth \
registry.example.com/web:1.0.0

# 查看
docker service ls
docker service ps web
docker service inspect web

# 查看服务日志(从所有副本收集)
docker service logs -f web

# 扩容/缩容
docker service scale web=5
docker service scale web=2

# 滚动更新(默认逐个更新,可加 --update-parallelism)
docker service update --image registry.example.com/web:1.0.1 web
docker service update --env-add TZ=Asia/Shanghai web

# 回滚到上一个版本
docker service rollback web

# 删除服务
docker service rm web

服务更新策略:

1
2
3
4
5
6
7
8
docker service create \
--update-order start-first \ # 先起新副本再停旧副本(零停机)
--update-parallelism 1 \
--update-delay 30s \
--rollback-parallelism 1 \
--rollback-delay 10s \
--restart-condition any \
--name app app:2.0

12.4 Swarm 网络与数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# overlay 网络:跨主机互通
docker network create -d overlay --attachable prod-net

# 服务加入网络
docker service create --name api --network prod-net myapi
docker service create --name web --network prod-net --publish published=80,target=80 nginx

# 服务间用服务名互访(内置 DNS)
# web -> http://api:8080

# 数据:Swarm 使用 volume 时需存储可被多节点访问
# 方案1:NFS 卷
docker volume create --driver local \
--opt type=nfs --opt o=addr=192.168.1.20,rw \
--opt device=:/srv/nfs/share shared-vol

# 方案2:使用支持多主机的卷插件(RexRay、Portworx、云盘插件)

# 方案3:全局服务 + 节点本地卷(数据落在运行节点)
docker service create --mode global \
--mount type=volume,source=node-local,target=/data \
--constraint node.hostname==worker1 \
--name db mysql:8.0

需要状态的数据(数据库)放 Swarm 前务必想清楚存储方案;无状态应用最适合 Swarm。

12.5 约束与标签

1
2
3
4
5
6
7
8
9
10
11
12
# 给节点打标签
docker node update --label-add disk=ssd node2
docker node update --label-add role=db node3

# 服务约束:只调度到满足条件的节点
docker service create \
--constraint node.labels.disk==ssd \
--constraint node.role==worker \
--name web nginx

# 全局模式:每台(满足条件的)节点各跑一个副本
docker service create --mode global --name exporter prom/node-exporter

12.6 Swarm 运维

1
2
3
4
5
6
7
8
9
10
# 集群状态检查
docker node ls
docker service ls
docker stack ls

# 用 Compose 文件部署整个栈(Swarm 模式)
docker stack deploy -c stack.yaml myapp
docker stack services myapp
docker stack ps myapp
docker stack rm myapp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# stack.yaml(Swarm 版 compose:不支持 build,需先推镜像;支持 secrets/configs)
version: "3.8"
services:
web:
image: registry.example.com/web:1.0.0
ports:
- "80:80"
networks:
- prod-net
deploy:
replicas: 3
update_config:
order: start-first
restart_policy:
condition: any
api:
image: registry.example.com/api:1.0.0
environment:
- DB_HOST=db
networks:
- prod-net
deploy:
replicas: 2
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_pw
secrets:
- db_root_pw
volumes:
- db-data:/var/lib/mysql
networks:
- prod-net
deploy:
replicas: 1

networks:
prod-net:
driver: overlay
volumes:
db-data:
secrets:
db_root_pw:
file: ./secrets/db_root_pw.txt

故障演练与巡检:

1
2
3
4
5
6
7
# 模拟节点故障:直接关机/停 docker,观察任务是否迁移到其他节点
docker service ps --no-trunc web

# 巡检
docker node ps $(docker node ls -q) # 各节点任务分布
docker stats --no-stream
docker service ls --format "table {{.Name}}\t{{.Replicas}}\t{{.UpdateStatus.State}}"

Swarm 定位:轻量集群,适合中小规模、无状态/少状态服务;大型复杂场景(自动扩缩容、存储、Ingress、多集群)建议 Kubernetes。


第 13 章 CI/CD 集成

13.1 标准流水线

1
2
代码提交 -> 触发流水线 -> 单元测试 -> 构建镜像 -> 漏洞扫描
-> 推送私有仓库 -> 部署(SSH/Ansible/Swarm/K8s)-> 健康检查 -> 通知

13.2 GitLab CI 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# .gitlab-ci.yml
stages:
- test
- build
- scan
- deploy

variables:
IMAGE: registry.example.com/myapp:$CI_COMMIT_SHORT_SHA

test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

build:
stage: build
image: docker:27
services:
- docker:27-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE .
- docker push $IMAGE
rules:
- if: $CI_COMMIT_BRANCH == "main"

scan:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 $IMAGE
needs: ["build"]
rules:
- if: $CI_COMMIT_BRANCH == "main"

deploy:
stage: deploy
image: docker:27
before_script:
- apk add --no-cache openssh-client
- mkdir -p ~/.ssh && echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
script:
- ssh deploy@10.0.0.5 "docker login ... && docker pull $IMAGE && docker service update --image $IMAGE myapp"
environment:
name: production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual

要点:

  • docker:27-dind(Docker in Docker)执行构建。
  • 凭据用 CI 变量/受保护变量,禁止写死在代码里。
  • 部署阶段加 when: manual 人工确认,或接审批流。

13.3 GitHub Actions 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# .github/workflows/deploy.yml
name: Build & Deploy
on:
push:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Login to Registry
uses: docker/login-action@v3
with:
registry: registry.example.com
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
registry.example.com/myapp:${{ github.sha }}
registry.example.com/myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: registry.example.com/myapp:${{ github.sha }}
severity: HIGH,CRITICAL
exit-code: "1"

13.4 Jenkins 流水线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pipeline {
agent { label 'docker' }
environment {
IMAGE = "registry.example.com/myapp:${env.BUILD_NUMBER}"
}
stages {
stage('Test') {
steps { sh 'npm ci && npm test' }
}
stage('Build & Push') {
steps {
sh "docker login -u ${env.REG_USER} -p ${env.REG_PASS} registry.example.com"
sh "docker build -t ${IMAGE} . && docker push ${IMAGE}"
}
}
stage('Deploy') {
steps {
sh "ssh deploy@10.0.0.5 'docker pull ${IMAGE} && docker service update --image ${IMAGE} myapp'"
}
}
}
post {
failure { slackSend color: 'danger', message: "部署失败: ${env.BUILD_NUMBER}" }
}
}

13.5 部署策略对比

策略 实现 特点
重建(Recreate) stop 旧的 → 起新的 简单、有停机窗口
滚动更新(Rolling) 逐个替换副本 Swarm/K8s 默认,无停机
蓝绿(Blue-Green) 两套环境切流量 回滚快、成本高
金丝雀(Canary) 先放少量新版本 风险最小,配合网关/服务网格

Swarm 蓝绿示例:

1
2
3
docker service create --name myapp-blue --replicas 3 --publish published=8081,target=8080 --network prod-net myapp:1.0
docker service create --name myapp-green --replicas 3 --publish published=8082,target=8080 --network prod-net myapp:1.1
# 验证 green 后,把负载均衡切到 8082 或直接改 published 端口

13.6 镜像拉取凭据(K8s 场景提一下)

1
2
3
4
5
6
7
8
# K8s 从私有仓库拉取需要 secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=robot$project \
--docker-password=xxxx

# Pod 中指定
# spec.imagePullSecrets: [{name: regcred}]

第 14 章 性能调优与故障排查

14.1 排障方法论

1
2
3
4
5
1. 收集信息:docker ps -a、docker logs、docker inspect、系统日志
2. 定位阶段:创建失败?启动失败?运行崩溃?性能问题?
3. 缩小范围:对比正常/异常容器、检查依赖与网络
4. 修复验证:最小改动,验证后固化(更新配置/镜像/编排)
5. 记录复盘:更新文档与监控告警

信息收集命令:

1
2
3
4
5
6
7
8
docker ps -a
docker logs --tail 200 -f <容器>
docker inspect <容器> --format '{{json .State}}'
docker inspect <容器> --format '{{json .HostConfig}}'
docker top <容器>
docker stats --no-stream
journalctl -u docker -n 100 # daemon 日志
dmesg -T | tail -50 # 内核 OOM 记录

14.2 常见故障与处置

场景1:容器一直重启(Restarting)

1
2
3
4
5
6
7
8
9
docker ps -a                       # 看 STATUS 的 Restarting
docker logs <容器> --tail 100 # 看报错
docker inspect <容器> --format '{{.State.ExitCode}} {{.State.Error}}'

# 常见原因:
# - 启动命令/入口缺失或报错(127/126)
# - 配置挂载错误导致进程起不来
# - 端口/文件权限问题
# - OOM(137 + OOMKilled=true)

场景2:磁盘被占满(经典事故)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
df -h /var/lib/docker
du -sh /var/lib/docker/* | sort -h | tail

# 1. 日志占空间
du -sh /var/lib/docker/containers | sort -h
# 排查大日志容器并配置 max-size/max-file 轮转

# 2. 悬空镜像/无用容器/构建缓存
docker system df
docker system prune -a -f --volumes # 谨慎:-a 会删未使用镜像,--volumes 会删未用卷
docker builder prune -f # 清构建缓存

# 3. 容器可写层膨胀
docker ps -s
# 对写入量大的容器使用卷,并定期清理应用日志

# 4. 数据卷占满
du -sh /var/lib/docker/volumes/* | sort -h | tail

预防措施(写进制度):

  • 全局配置日志轮转(max-size/max-file)。
  • 部署定时清理任务:docker system prune -f(保留在用资源)。
  • 数据卷与日志目录规划好容量并监控告警(>80% 告警)。
  • 镜像瘦身,避免拉取大镜像撑满磁盘。

场景3:OOM(内存被杀死)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 现象:容器退出码 137,或频繁 Restarting
docker inspect <容器> --format '{{.State.OOMKilled}}' # true
dmesg -T | grep -i "out of memory"

# 排查
docker stats --no-stream
# 进入容器看内存大户
docker exec <容器> top
# 或看应用日志:Java 看 GC 日志,Node 看 heap

# 处理
docker update -m 2g --memory-swap 2g <容器> # 调大限制(宿主机要有余量)
# 优化应用内存:JVM 用 MaxRAMPercentage,Node 用 NODE_OPTIONS=--max-old-space-size
# 定位泄漏:结合监控面板观察内存曲线

场景4:网络不通

1
2
3
4
5
6
7
8
9
10
11
12
13
# 分层定位
curl http://127.0.0.1:8080 # 宿主机侧
docker exec web curl http://api:8080 # 容器间
docker exec web getent hosts api # DNS
docker network inspect app-net # 网络成员
iptables -t nat -L -n | grep 8080 # 端口规则
ss -tlnp | grep 8080 # 端口监听

# 常见:
# - 容器名 DNS 不可用 -> 用自定义网络
# - 防火墙/安全组没放行
# - host 模式 + -p 冲突
# - 代理/NAT 问题

场景5:镜像拉取慢/失败

1
2
3
4
5
6
7
8
docker pull nginx
# 失败:net/http: TLS handshake timeout / dial tcp ... i/o timeout

# 处理:
# 1. 换镜像加速(daemon.json registry-mirrors)
# 2. 用代理(dockerd 支持 HTTPS_PROXY)
# 3. 断网机房:docker save/load 离线导入
# 4. 检查 DNS / hosts

场景6:docker 命令报权限

1
2
3
# permission denied while trying to connect to the Docker daemon socket
# 处理:用户加入 docker 组(usermod -aG docker $USER && 重新登录)
# 或 sudo docker ...(临时)

14.3 性能调优

daemon 层:

1
2
3
4
5
6
7
8
{
"storage-driver": "overlay2",
"max-concurrent-downloads": 5,
"max-concurrent-uploads": 5,
"live-restore": true,
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "5" }
}
  • live-restore:daemon 重启不杀容器(生产推荐,注意与 swarm 的兼容)。
  • overlay2 是当前主流存储驱动,性能与稳定性最佳。
  • 日志驱动默认 json-file 对高吞吐应用有 I/O 开销,可换 journald 或 fluentd。

容器层:

1
2
3
4
5
6
7
# CPU 亲和/配额
docker run --cpus 2 --cpu-shares 1024 --cpuset-cpus 0-1 app
# IO 限制(需 cgroup v2 / io 子系统)
docker run --device-read-bps /dev/sda:50mb --device-write-bps /dev/sda:50mb app

# 内核参数(sysctl)
docker run --sysctl net.core.somaxconn=1024 app

应用层:

  • JVM:-XX:MaxRAMPercentage=75,避免 -Xmx 写死。
  • Nginx:worker_processes auto,打开 epoll
  • MySQL:innodb_buffer_pool_size 设为内存的 60%~70%(在容器限制内)。
  • Redis:关闭持久化或调低 save 频率可大幅提升写性能(按需取舍)。

基准测试:

1
2
3
4
5
6
# 网络吞吐
docker run --rm --network host appropriate/curl -s http://127.0.0.1:8080 | wc -c
# 磁盘 IO(对比宿主与容器)
docker run --rm alpine:3.20 dd if=/dev/zero of=/tmp/test bs=1M count=1024
# 压测工具:wrk / hey / ab(在容器内跑)
docker run --rm --network host williamyeh/wrk -t4 -c100 -d30s http://127.0.0.1:8080/

14.4 清理大法(系统回收)

1
2
3
4
5
6
7
8
9
docker system df                     # 查看各类占用
docker system prune -f # 清理停止容器、悬空镜像、无用网络、构建缓存
docker system prune -a -f # 再加:所有未被使用镜像
docker system prune -a -f --volumes # 再加:未使用卷(有数据风险,慎用)
docker builder prune -f # 构建缓存
docker container prune -f
docker image prune -f
docker network prune -f
docker volume prune -f # 未使用卷(数据风险)

14.5 应急处理流程(示例)

1
2
3
4
5
6
告警:某服务不可用
1. docker service ps <svc> # 看任务状态
2. docker service logs <svc> # 看错误
3. 判断:配置问题 / 资源问题 / 数据问题 / 代码问题
4. 紧急回滚:docker service rollback <svc>
5. 止血后修复并复盘:更新镜像、加监控、加预案

第 15 章 深入原理

15.1 namespace:隔离视图

容器通过 Linux namespace 让进程看到”独立的系统”:

namespace 隔离内容 相关参数
PID 进程号空间 CLONE_NEWPID
Network 网络栈(网卡/IP/路由/iptables) CLONE_NEWNET
Mount 挂载点视图 CLONE_NEWNS
UTS 主机名与域名 CLONE_NEWUTS
IPC 进程间通信(消息队列等) CLONE_NEWIPC
User 用户与 UID 映射 CLONE_NEWUSER
Cgroup cgroup 根视图 CLONE_NEWCGROUP
1
2
3
# 在宿主机查看容器进程的 namespace
docker top <容器> # 拿到容器内 PID
ls -l /proc/<PID>/ns/ # 查看该进程所属的 namespace inode

注意:namespace 只是”视图隔离”,容器内看到的所有进程都在宿主机内核管理下;PID namespace 里是 1 的进程在宿主上可能是几千。

15.2 cgroup:资源限制

cgroup(control group)控制并统计进程组的资源使用:

  • cgroup v1:CPU、内存、blkio、net_prio 等各自独立子系统。
  • cgroup v2(内核 5.2+ 主流):统一层次结构,接口更简洁(/sys/fs/cgroup/)。
1
2
3
4
5
6
7
# 查看容器所属 cgroup(cgroup v2)
cat /proc/$(docker inspect -f '{{.State.Pid}}' <容器>)/cgroup
ls /sys/fs/cgroup/system.slice/docker-<容器ID>.scope/

# 内存限制写入处
cat /sys/fs/cgroup/system.slice/docker-<id>.scope/memory.max
cat /sys/fs/cgroup/system.slice/docker-<id>.scope/memory.current

关键事实:

  • docker run -m 对应 cgroup 的 memory.max(v2)或 memory.limit_in_bytes(v1)。
  • --cpus 对应 CPU 权重/配额;cgroup v2 用 cpu.max
  • OOM Killer 由 cgroup 触发,杀掉组内进程(退出码 137)。
  • 容器内看到的 /proc/meminfo 等已被 cgroup 视角修正(Docker 会处理),但部分应用仍会读到宿主值,这是老问题,注意结合 --memory 与应用自身配置。

15.3 存储驱动 overlayfs

1
2
3
4
5
6
7
8
镜像层:下层镜像层,只读(Docker 基础镜像、各镜像分层都在这里)
lowerdir: /var/lib/docker/overlay2/<layer1>/diff
/var/lib/docker/overlay2/<layer2>/diff
...
容器层:上层容器读写层,可写
upperdir: /var/lib/docker/overlay2/<container>/diff
合并视图:对外呈现的统一视图(容器内看到的根目录)
merged: /var/lib/docker/overlay2/<container>/merged
  • 读文件:从上往下找第一个存在的层。
  • 写文件:写时复制 —— 文件在低层则先拷贝到上层再改(CoW),删除文件则在上层放”白名单”标记。
  • 目录修改:overlayfs 的目录合并是”浅合并”,删目录行为有特殊语义。如果上下层出现同名普通文件:上层文件直接遮蔽下层文件,只展示 upper 的文件,下层文件完全隐藏。
  • 优点:层共享省磁盘、启动快;缺点:大量小文件写有开销。
1
2
docker info | grep "Storage Driver"
df -h | grep overlay

15.4 容器隔离的局限(为什么容器不等于虚拟机)

  • 共享内核:容器无法运行与宿主不同内核(如 Linux 容器不能在 Windows 内核上跑,需 WSL2 虚拟机)。
  • 内核级漏洞可逃逸:历史逃逸漏洞(CVE-2019-5736 runc、CVE-2022-0492 cgroup 等)说明容器边界不是安全边界。
  • 资源争抢:CPU 缓存、磁盘 IO 等仍会互相影响(cgroup 对 IO 控制弱)。
  • 时间与系统调用:共享系统时钟;部分系统调用未隔离。
  • 结论:容器是”进程隔离”而非”安全隔离”,安全设计要按共享内核前提来做。

15.5 从 Docker 到 Kubernetes

能力 Docker 单机 Docker Swarm Kubernetes
容器编排 ✓(轻量) ✓(重量级)
自动扩缩容 手动 scale HPA 自动
服务发现/负载均衡 手动 内置 Service + Ingress
存储编排 卷(依赖插件) PV/PVC 生态
配置/密钥 env secrets ConfigMap/Secret
滚动更新/回滚 手动 ✓(策略丰富)
故障自愈 restart 策略 任务重调度 全面(节点/容器/驱逐)
生态与插件 - 极丰富(CNI/CSI/CRD/Operator)
运维复杂度

学习路线建议:

1
2
3
4
Docker 基础(镜像/容器/网络/卷)
-> Docker Compose(单机多服务)
-> Docker Swarm(轻量集群,理解编排概念)
-> Kubernetes(生产主流:Pod/Deployment/Service/Ingress/Storage/Helm)

第 16 章 常用命令速查表

16.1 镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker search nginx                      # 搜索
docker pull nginx:alpine # 拉取
docker images # 列出
docker image inspect nginx # 详情
docker history nginx # 构建历史
docker tag nginx nginx:v1 # 打标签
docker rmi nginx:v1 # 删除
docker build -t myapp:1.0 . # 构建
docker save -o nginx.tar nginx # 导出
docker load -i nginx.tar # 导入
docker push registry/x/nginx:v1 # 推送
docker login registry.example.com # 登录仓库
docker system df # 占用统计
docker image prune -f # 清理悬空

16.2 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker ps                                # 运行中
docker ps -a # 全部
docker run -d --name web -p 80:80 nginx # 运行
docker start/stop/restart web # 启停
docker pause/unpause web # 暂停
docker rm -f web # 删除
docker exec -it web bash # 进入
docker logs -f web # 日志
docker top web # 进程
docker stats # 资源
docker inspect web # 元数据
docker cp web:/etc/nginx/nginx.conf ./ # 拷出
docker cp ./index.html web:/tmp/ # 拷入
docker port web # 端口映射
docker diff web # 可写层变更
docker events # 事件流
docker container prune -f # 清理停止容器

16.3 网络 / 卷 / Compose / Swarm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 网络
docker network ls
docker network create app-net
docker network connect app-net web
docker network disconnect app-net web
docker network rm app-net
docker network inspect app-net

# 卷
docker volume ls
docker volume create data-vol
docker volume inspect data-vol
docker volume rm data-vol
docker volume prune -f

# Compose
docker compose up -d
docker compose ps
docker compose logs -f
docker compose exec web bash
docker compose down
docker compose down -v
docker compose config

# Swarm
docker swarm init --advertise-addr 192.168.1.10
docker swarm join --token xxx manager:2377
docker node ls
docker service create --name web --replicas 3 -p 80:80 nginx
docker service ls
docker service ps web
docker service scale web=5
docker service update --image app:2.0 web
docker service rollback web
docker service logs -f web
docker service rm web
docker stack deploy -c stack.yaml app
docker stack rm app

16.4 系统级

1
2
3
4
5
6
7
docker version                  # 版本
docker info # 引擎信息
docker system df # 磁盘占用
docker system prune -a -f # 深度清理
docker system events # 事件
docker context ls # 多环境上下文
docker context use prod # 切换远端 daemon

附录

A. 术语表

术语 含义
Image 只读分层模板
Container 镜像的运行实例
Registry 镜像仓库(Docker Hub / Harbor)
Dockerfile 镜像构建脚本
Layer 镜像中的一层变更
Volume Docker 管理的数据卷
Bind Mount 宿主机目录挂载
Dockerfile ARG/ENV 构建参数 / 运行环境变量
Multi-stage 多阶段构建
Compose 单机多容器编排
Swarm Docker 内置集群
Overlay 网络 跨主机网络(VXLAN)
OCI 开放容器标准(image/runtime spec)
containerd 标准容器运行时
runc OCI 运行时实现

B. 官方与推荐资料

C. 常见错误信息对照表

报错 含义 处理
Cannot connect to the Docker daemon daemon 未启动/权限不足 systemctl start docker;加 docker 组
port is already allocated 端口被占用 ss -tlnp 定位,改端口
No such image 镜像不存在/名写错 docker images 核对;重新 pull
OCI runtime exec failed 容器内没有该命令 换镜像/改用 sh
permission denied 权限不足 检查用户/挂载属主/只读挂载
no space left on device 磁盘满 清理日志/镜像/卷(见 14.2)
tls handshake timeout 网络问题 换加速源/代理/离线导入
Exec format error 架构不匹配 拉对应 --platform 镜像
exit status 137 被 OOM/强杀 检查内存限制与 OOMKilled
conflict: container name is already in use 容器名重复 --name 或删旧容器
join token expired Swarm 令牌过期 docker swarm join-token 重新获取

D. 进阶学习清单(掌握程度自测)

  • 能独立编写多阶段 Dockerfile 并瘦身镜像
  • 熟练使用 Compose 编排一套 LNMP/前后端栈
  • 能解释镜像分层与 CoW 原理
  • 会做数据卷备份/恢复
  • 能完成自定义网络下的容器间服务发现
  • 掌握日志轮转、资源限制、健康检查
  • 能搭建 Harbor 并配置 RBAC、复制、扫描
  • 会用 Trivy + Bench Security 做安全基线
  • 能部署 Swarm 集群并完成滚动更新/回滚
  • 掌握常见故障的排查套路(OOM/磁盘/网络/端口)
  • 理解 namespace/cgroup/overlayfs 与容器安全边界
  • 能设计一条从提交到生产的 CI/CD 流水线

祝你在容器化的路上越走越稳。学完本手册后,建议到 Kubernetes 继续深入:Pod、Service、Ingress、Helm、Kubeadm/K3s 部署实践。


Docker 从入门到精通实战手册
https://blog.t-ao.cn/2026/08/02/Docker从入门到精通实战手册/
作者
TAO
发布于
2026年8月2日
许可协议