Docker 容器化技术完全教程
从入门到实战的系统化学习指南,帮助程序员从零开始掌握 Docker 核心技术。
目录
- Docker 概述
- Docker 架构与核心概念
- Docker 安装与环境配置
- 镜像管理
- 容器管理
- Dockerfile 编写指南
- 网络配置
- 数据卷与存储
- Docker Compose 多容器编排
- Docker 在生产环境中的应用
- 常见问题与排错指南
- 学习路径与扩展资源
1. Docker 概述
1.1 什么是 Docker
Docker 是一个开源的容器化平台,它允许开发者将应用程序及其所有依赖项打包到一个轻量级、可移植的容器中,然后可以在任何支持 Docker 的环境中以相同的方式运行。
类比理解:想象你正在搬家。传统方式是你把所有家具、衣服、餐具散装打包,到了新家再一件件整理;而 Docker 就像是用标准尺寸的集装箱来装所有东西——无论运到哪儿,打开箱子,一切都在原来的位置。
1.2 容器 vs 虚拟机
| 特性 | 虚拟机 (VM) | 容器 (Container) |
|---|---|---|
| 启动速度 | 分钟级 | 秒级 |
| 资源占用 | 高(完整 OS) | 低(共享宿主内核) |
| 镜像大小 | GB 级别 | MB 级别 |
| 隔离性 | 强(硬件级隔离) | 中等(进程级隔离) |
| 性能开销 | 有虚拟层损耗 | 接近原生性能 |
1.3 Docker 的核心价值
- 环境一致性:开发、测试、生产环境完全一致,消灭"在我机器上能跑"的问题
- 快速部署:秒级启动,便于快速迭代和滚动发布
- 资源高效:一台服务器可以运行更多容器,提高资源利用率
- 易于扩展:横向扩容只需复制容器实例
1.4 本章练习
# 检查 Docker 是否已安装
docker --version
# 查看 Docker 详细信息
docker info
2. Docker 架构与核心概念
2.1 Docker 架构
Docker 采用客户端-服务端(C/S)架构:
┌─────────────────────────────────────────────┐
│ Docker Client │
│ (docker CLI 命令) │
└──────────────────┬──────────────────────────┘
│ Docker API
┌──────────────────▼──────────────────────────┐
│ Docker Daemon (dockerd) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 镜像管理 │ │ 容器管理 │ │ 网络管理 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ │
│ │ 注册中心 │ (Docker Hub) │
│ └──────────┘ │
└─────────────────────────────────────────────┘
2.2 核心概念详解
2.2.1 镜像(Image)
镜像是容器的只读模板,包含了运行应用所需的所有内容:代码、运行时、库、环境变量、配置文件。
- 镜像是分层构建的,每一层代表一条指令的变化
- 镜像只读,容器运行时在镜像之上添加一个可写层
- 可以通过
docker pull从仓库获取,通过docker build自行构建
2.2.2 容器(Container)
容器是镜像的运行实例,是一个独立隔离的运行环境。
- 容器是轻量级的,启动速度快
- 容器之间相互隔离,互不影响
- 容器可以被启动、停止、删除、重启
2.2.3 仓库(Registry)
仓库是存放镜像的地方,分为公开仓库和私有仓库。
- Docker Hub( https://hub.docker.com ):官方公共镜像仓库
- 私有仓库:企业内网部署,用于保护代码安全
2.2.4 三层关系
镜像(模板) → 容器(实例) → 运行中的应用
↑ ↓
仓库(仓库) ←→ 容器管理
2.3 本章练习
# 列出本地所有镜像
docker images
# 查看正在运行的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
3. Docker 安装与环境配置
3.1 macOS 安装
推荐使用 Docker Desktop:
# 方式一:使用 Homebrew 安装
brew install --cask docker
# 方式二:手动下载安装
# 访问 https://www.docker.com/products/docker-desktop/
# 下载 macOS 版本安装包,双击安装并拖入 Applications
安装完成后:
# 启动 Docker Desktop 应用
open -a Docker
# 验证安装
docker --version
# 输出示例:Docker version 27.5.1, build e590761
docker info
# 输出包含服务端版本、存储驱动、运行容器数等信息
3.2 Linux 安装(以 Ubuntu 22.04 为例)
# 卸载旧版本(如有)
sudo apt-get remove docker docker-engine docker.io containerd runc
# 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
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
# 添加 Docker 软件源
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 将当前用户加入 docker 用户组(免 sudo)
sudo usermod -aG docker $USER
newgrp docker
# 验证安装
docker run hello-world
3.3 Windows 安装
- 下载 Docker Desktop for Windows
- 确保已启用 WSL 2(Windows Subsystem for Linux 2)
- 运行安装程序,按提示完成安装
- 重启后打开 Docker Desktop,等待初始化完成
# 验证安装(在 PowerShell 中)
docker --version
docker run hello-world
3.4 常用配置
# 修改镜像加速器(国内用户必备)
# 编辑或创建 ~/.docker/daemon.json
cat > ~/.docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://mirror.baidubce.com",
"https://hub-mirror.c.163.com",
"https://registry.docker-cn.com"
],
"data-root": "/var/lib/docker"
}
EOF
# 重启 Docker 服务
# macOS:通过 Docker Desktop 界面重启
# Linux:
sudo systemctl daemon-reload
sudo systemctl restart docker
# 验证配置
docker info | grep -A 5 "Registry Mirrors"
3.5 本章练习
- 安装 Docker 并验证安装成功
- 配置镜像加速器(如在国内)
- 运行第一个容器:
docker run hello-world - 查看 Docker 系统信息:
docker info
4. 镜像管理
4.1 镜像获取
# 从 Docker Hub 拉取镜像
docker pull nginx # 拉取最新版本的 nginx 镜像
docker pull nginx:1.25 # 拉取指定版本
docker pull nginx:latest # 显式拉取 latest 标签
# 从私有仓库拉取
docker pull registry.example.com/myapp:v1.0
# 搜索镜像(在 Docker Hub 上)
docker search nginx
docker search nginx --limit 5 # 只显示前5个结果
4.2 镜像查看与管理
# 列出本地所有镜像
docker images
# 输出示例:
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx latest abc123def456 2 weeks ago 187MB
# python 3.11 def456abc123 3 weeks ago 920MB
# alpine latest 7a2b3c4d5e6f 1 month ago 7.28MB
# 查看特定镜像的详细信息
docker image inspect nginx:latest
# 查看镜像的历史构建层
docker history nginx:latest
# 删除镜像
docker rmi nginx:latest # 删除指定标签镜像
docker rmi abc123def456 # 通过镜像ID删除
# 强制删除(有容器依赖时)
docker rmi -f nginx:latest
# 清理未使用的镜像
docker image prune # 删除 dangling 镜像
docker image prune -a # 删除所有未被使用的镜像
4.3 镜像标签与版本管理
# 给现有镜像打新标签
docker tag nginx:latest mynginx:v1.0
docker tag nginx:latest registry.example.com/mynginx:v1.0
# 标签命名规范:[registry/]repository[:tag]
# 示例:
# nginx:latest
# python:3.11-slim
# myregistry.com/myproject:v1.2.3
4.4 镜像导出与导入
# 导出镜像为 tar 文件
docker save -o nginx.tar nginx:latest
# 从 tar 文件导入镜像
docker load -i nginx.tar
# 导出容器文件系统(不含镜像元数据)
docker export mycontainer -o container-rootfs.tar
# 导入为镜像
cat container-rootfs.tar | docker import - myimage:latest
4.5 推送镜像到仓库
# 登录 Docker Hub
docker login
# 登录私有仓库
docker login registry.example.com
# 推送镜像
docker push nginx:latest
docker push registry.example.com/myapp:v1.0
4.6 本章练习
# 练习1:拉取一个 Python 镜像
docker pull python:3.11-slim
# 练习2:查看镜像详情和历史
docker image inspect python:3.11-slim | head -20
docker history python:3.11-slim
# 练习3:给镜像打标签并导出
docker tag python:3.11-slim mypython:3.11
docker save -o mypython.tar mypython:3.11
ls -lh mypython.tar
# 练习4:删除旧镜像并重新导入
docker rmi mypython:3.11
docker load -i mypython.tar
docker images | grep mypython
rm mypython.tar
5. 容器管理
5.1 创建与运行容器
# 最简单的方式:后台运行一个容器
docker run nginx
# 前台运行,查看输出
docker run nginx
# 后台运行并返回容器ID
docker run -d nginx
# 分配伪终端并后台运行(交互式)
docker run -d -it --name my-nginx nginx
# 端口映射(主机端口:容器端口)
docker run -d -p 8080:80 --name my-nginx nginx
# 设置环境变量
docker run -d -e NODE_ENV=production --name myapp node:18
# 设置工作目录
docker run -d -w /app --name myapp node:18
# 限制资源
docker run -d --memory=512m --cpus=1 --name myapp nginx
5.2 常用 docker run 参数速查
| 参数 | 说明 | 示例 |
|---|---|---|
-d |
后台运行 | docker run -d nginx |
-it |
交互式终端 | docker run -it ubuntu bash |
-p |
端口映射 | docker run -p 8080:80 nginx |
-P |
随机端口映射 | docker run -P nginx |
-v |
挂载卷 | docker run -v /host/path:/container/path nginx |
-e |
设置环境变量 | docker run -e KEY=VALUE nginx |
--name |
容器名称 | docker run --name myapp nginx |
--rm |
退出后自动删除 | docker run --rm nginx |
--network |
指定网络 | docker run --network host nginx |
--memory |
内存限制 | docker run --memory=512m nginx |
--cpus |
CPU 限制 | docker run --cpus=2 nginx |
-w |
工作目录 | docker run -w /app nginx |
5.3 容器生命周期管理
# 启动已停止的容器
docker start my-nginx
# 停止运行中的容器(发送 SIGTERM,默认10秒后 SIGKILL)
docker stop my-nginx
# 强制停止容器
docker kill my-nginx
# 重启容器
docker restart my-nginx
# 暂停容器(冻结所有进程)
docker pause my-nginx
# 恢复暂停的容器
docker unpause my-nginx
5.4 容器查看与日志
# 查看运行中的容器
docker ps
# 查看所有容器(含已停止)
docker ps -a
# 查看最近创建的容器
docker ps -n 3
# 查看容器详细信息
docker inspect my-nginx
# 查看容器日志
docker logs my-nginx
# 实时跟踪日志输出
docker logs -f my-nginx
# 查看最近 N 行日志
docker logs --tail 100 my-nginx
# 查看时间戳
docker logs -t my-nginx
# 结合时间范围
docker logs --since "2024-01-01T00:00:00" my-nginx
docker logs --until "2024-01-02T00:00:00" my-nginx
5.5 进入容器内部
# 使用 exec 进入运行中的容器
docker exec -it my-nginx bash # 以交互模式进入
docker exec -it my-nginx sh # 某些精简镜像只有 sh
# 在容器内执行单条命令
docker exec my-nginx ls /etc/nginx
docker exec my-nginx cat /etc/nginx/nginx.conf
# 以指定用户进入(需要容器内有该用户)
docker exec -it -u root my-nginx bash
5.6 文件传输
# 从宿主机复制文件到容器
docker cp ./config.json my-nginx:/etc/nginx/conf.d/
# 从容器复制文件到宿主机
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf
# 复制目录
docker cp ./app-dir my-nginx:/app/
docker cp my-nginx:/var/log/app.log ./app.log
5.7 容器删除
# 停止并删除容器
docker rm my-nginx
# 强制删除运行中的容器
docker rm -f my-nginx
# 删除所有已停止的容器
docker container prune
# 删除指定状态的容器
docker rm $(docker ps -q -f status=exited)
5.8 实战:运行 Web 应用
# 1. 运行 Nginx Web 服务器
docker run -d \
--name web-server \
-p 8080:80 \
-v /tmp/html:/usr/share/nginx/html:ro \
nginx
# 2. 创建测试页面
echo '<h1>Hello Docker!</h1>' > /tmp/html/index.html
# 3. 访问验证
curl http://localhost:8080
# 输出:<h1>Hello Docker!</h1>
# 4. 查看容器状态
docker ps
# 5. 查看日志
docker logs web-server
# 6. 清理
docker stop web-server && docker rm web-server
5.9 本章练习
# 练习1:运行 Python 容器并验证环境
docker run -it --rm python:3.11-slim python --version
# 练习2:运行 Nginx 并映射端口
docker run -d --name myweb -p 9090:80 nginx
curl http://localhost:9090
docker stop myweb && docker rm myweb
# 练习3:运行临时容器执行命令
docker run --rm alpine sh -c "apk add --no-cache curl && curl --version | head -1"
# 练习4:运行 MySQL 容器(了解即可,第9章会用 compose 更完整地演示)
docker run -d \
--name mysql-test \
-e MYSQL_ROOT_PASSWORD=root123 \
-p 3306:3306 \
mysql:8.0
# 进入 MySQL 容器验证连接
docker exec -it mysql-test mysql -uroot -proot123 -e "SELECT VERSION();"
docker stop mysql-test && docker rm mysql-test
6. Dockerfile 编写指南
6.1 Dockerfile 概述
Dockerfile 是一个文本文件,包含了构建镜像的所有指令。Docker 读取 Dockerfile 后,逐条指令构建镜像。
6.2 基础指令详解
FROM — 指定基础镜像
# 使用官方 Python 3.11 镜像
FROM python:3.11-slim
# 使用指定版本
FROM nginx:1.25-alpine
# 使用多阶段构建的第一阶段
FROM golang:1.21 AS builder
RUN — 执行命令
# 单次执行
RUN apt-get update && apt-get install -y curl
# 多行命令(使用反斜杠续行)
RUN apt-get update && apt-get install -y \
curl \
wget \
git
# 每条 RUN 都会创建新的镜像层,尽量合并减少层数
CMD — 容器启动时的默认命令
# 首选方式(Shell 形式)
CMD ["python", "app.py"]
# 也可作为 ENTRYPOINT 的默认参数补充
CMD ["--port", "8080"]
注意:一个 Dockerfile 中只能有一个有效的
CMD,后面的会覆盖前面的。
ENTRYPOINT — 配置容器启动入口
# 设置入口点
ENTRYPOINT ["nginx", "-g", "daemon off;"]
# ENTRYPOINT + CMD:CMD 成为 ENTRYPOINT 的参数
ENTRYPOINT ["python"]
CMD ["app.py"]
# 实际执行:python app.py
# 覆盖 CMD:docker run myimage server.py → python server.py
COPY vs ADD
# COPY:简单复制文件(推荐)
COPY requirements.txt /app/
COPY . /app/
# ADD:功能更强,但需谨慎使用
ADD app.tar.gz /app/ # 自动解压 tar 文件
ADD https://example.com/file /app/ # 下载远程文件(不推荐,用 curl/wget 替代)
最佳实践:优先使用
COPY,仅在需要自动解压或远程下载时才用ADD。
ENV — 设置环境变量
ENV APP_HOME=/app
ENV PYTHONUNBUFFERED=1
ENV NODE_ENV=production
# 在 Dockerfile 中使用
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
WORKDIR — 设置工作目录
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
# WORKDIR 会被后续指令继承,且会自动创建父目录
WORKDIR /a
WORKDIR b
WORKDIR c
# 当前工作目录为 /a/b/c
EXPOSE — 声明暴露的端口
EXPOSE 80
EXPOSE 8080 8443
# 注意:EXPOSE 只是声明,并不会实际映射端口
# 实际端口映射需要在 docker run 时使用 -p 参数
VOLUME — 创建数据卷挂载点
VOLUME ["/data"]
VOLUME /var/lib/mysql
ARG — 构建时变量
ARG PYTHON_VERSION=3.11
FROM python:${PYTHON_VERSION}-slim
ARG APP_VERSION=1.0.0
LABEL version=$APP_VERSION
# 构建时传入参数
docker build --build-arg APP_VERSION=2.0.0 .
LABEL — 添加元数据
LABEL maintainer="developer@example.com"
LABEL version="1.0"
LABEL description="A simple web application"
HEALTHCHECK — 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# 禁用健康检查
HEALTHCHECK NONE
USER — 切换用户
# 不使用 root 运行(安全最佳实践)
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
ONBUILD — 触发器(用于构建基础镜像)
# 当其他镜像以此为基础时触发
ONBUILD COPY . /app
ONBUILD RUN pip install -r requirements.txt
6.3 .dockerignore 文件
类似于 .gitignore,用于排除不需要打包进构建上下文的文件。
# .dockerignore
node_modules
.git
*.md
.env
__pycache__
*.pyc
.DS_Store
6.4 完整示例:Python Web 应用
# ===== 多阶段构建示例 =====
# 第一阶段:构建
FROM python:3.11-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# 第二阶段:运行(更小更安全的最终镜像)
FROM python:3.11-slim
WORKDIR /app
# 从 builder 阶段复制已安装的依赖
COPY --from=builder /install /usr/local
# 复制应用代码
COPY . .
# 非 root 用户运行
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# 暴露端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1
# 启动命令
CMD ["python", "app.py"]
6.5 构建与运行
# 构建镜像(. 表示当前目录为构建上下文)
docker build -t myapp:1.0 .
# 指定 Dockerfile 路径
docker build -f Dockerfile.prod -t myapp:prod .
# 构建时传递参数
docker build --build-arg APP_VERSION=2.0.0 -t myapp:2.0 .
# 构建时指定缓存行为
docker build --no-cache -t myapp:latest .
# 运行构建的镜像
docker run -d -p 8080:8080 --name myapp myapp:1.0
# 查看构建历史
docker history myapp:1.0
6.6 Dockerfile 最佳实践
| 实践 | 说明 |
|---|---|
| 使用具体标签 | 避免使用 latest,指定明确版本如 python:3.11-slim |
| 减少镜像层数 | 合并 RUN 指令,使用 && 连接 |
| 利用缓存 | 将不变的层(如依赖安装)放在前面 |
| 使用多阶段构建 | 大幅减小最终镜像体积 |
| 非 root 运行 | 使用 USER 指令切换到普通用户 |
| 使用 .dockerignore | 排除不必要文件,加速构建 |
| 选择精简基础镜像 | 优先使用 -slim 或 -alpine 变体 |
| 不使用 sudo | 镜像内直接用 root 或切换用户 |
| 设置健康检查 | 便于 orchestrator 监控容器状态 |
6.7 本章练习
任务:为一个简单的 Flask Web 应用编写 Dockerfile
项目结构:
my-flask-app/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignore
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from Docker!'
@app.route('/health')
def health():
return 'OK', 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
# requirements.txt
flask==3.0.0
gunicorn==21.2.0
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')" || exit 1
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
# .dockerignore
__pycache__
*.pyc
*.pyo
.git
.env
*.md
# 构建并运行
docker build -t my-flask-app:1.0 .
docker run -d -p 5000:5000 --name my-flask my-flask-app:1.0
curl http://localhost:5000
curl http://localhost:5000/health
docker stop my-flask && docker rm my-flask
7. 网络配置
7.1 Docker 网络模型
Docker 提供四种网络驱动:
| 驱动 | 说明 | 适用场景 |
|---|---|---|
bridge |
默认网络驱动,容器通过虚拟网桥互联 | 单主机多容器通信 |
host |
容器共享宿主机网络栈 | 需要高性能网络的应用 |
none |
容器无网络 | 完全隔离的场景 |
overlay |
跨主机容器通信 | Swarm 集群或多主机 |
macvlan |
为容器分配物理机网络中的独立 MAC 地址 | 需要容器有独立 IP 的场景 |
7.2 网络操作命令
# 查看网络列表
docker network ls
# 查看网络详细信息
docker network inspect bridge
# 创建自定义 bridge 网络
docker network create my-network
# 创建指定子网的网络
docker network create --subnet 172.20.0.0/16 my-network
# 删除网络
docker network rm my-network
docker network prune # 删除未使用的网络
7.3 容器网络连接
# 运行容器并连接到指定网络
docker run -d --name app1 --network my-network nginx
docker run -d --name app2 --network my-network mysql -e MYSQL_ROOT_PASSWORD=pass
# 将已运行的容器连接到网络
docker network connect my-network existing-container
# 从网络断开
docker network disconnect my-network existing-container
7.4 网络间通信
同一网络内的容器可以相互访问,通过容器名作为主机名:
# 在 my-network 网络中的 app1 容器可以访问 app2
docker exec app1 ping app2 # 成功
docker exec app1 curl app2:80 # 成功
# 但不可以通过 localhost 访问
docker exec app1 curl http://localhost:80 # 失败
7.5 实战:多服务通信
# 1. 创建自定义网络
docker network create myapp-net
# 2. 运行数据库(挂载数据卷)
docker run -d \
--name db \
--network myapp-net \
-e MYSQL_ROOT_PASSWORD=root123 \
-e MYSQL_DATABASE=myapp \
-v db-data:/var/lib/mysql \
mysql:8.0
# 3. 运行应用(通过数据库名访问 db 容器)
docker run -d \
--name app \
--network myapp-net \
-p 8080:8080 \
-e DB_HOST=db \
-e DB_PASSWORD=root123 \
myapp:latest
# 4. 验证连通性
docker exec app curl http://db:3306
7.6 本章练习
# 练习1:创建自定义网络并连接两个容器
docker network create test-net
docker run -d --name server --network test-net nginx
docker run -d --name client --network test-net alpine sleep 3600
# 从 client 容器 ping server
docker exec client ping -c 2 server
# 清理
docker network rm test-net
docker rm -f server client
8. 数据卷与存储
8.1 三种存储方式
| 类型 | 说明 | 生命周期 | 典型场景 |
|---|---|---|---|
| 数据卷(Volume) | Docker 管理的存储 | 独立于容器 | 数据库持久化 |
| 绑定挂载(Bind Mount) | 映射宿主机路径 | 与宿主机文件同步 | 代码热更新 |
| tmpfs 挂载 | 内存中的临时存储 | 容器停止即消失 | 敏感数据 |
8.2 数据卷(Volume)
# 创建命名卷
docker volume create my-data
# 运行容器时使用卷
docker run -d \
--name myapp \
-v my-data:/data \
nginx
# 查看所有卷
docker volume ls
# 查看卷的详细信息
docker volume inspect my-data
# 输出包含 Mountpoint(实际存储路径)
# 删除卷(需确保无容器使用)
docker volume rm my-data
# 清理未使用的卷
docker volume prune
docker volume prune -f # 不确认直接清理
数据卷特点:
- 数据存储在
/var/lib/docker/volumes/(Linux)或 Docker Desktop 的 VM 内 - 卷的生命周期独立于容器,删除容器不会删除卷
- 多个容器可以挂载同一个卷(读写或只读)
8.3 绑定挂载(Bind Mount)
# 将宿主机目录挂载到容器
docker run -d \
--name myapp \
-v /host/path:/container/path \
nginx
# 只读挂载
docker run -d \
--name myapp \
-v /host/path:/container/path:ro \
nginx
# 使用环境变量(动态路径)
docker run -d \
--name myapp \
-v "$(pwd)/data":"/app/data" \
nginx
8.4 数据共享与管理
# 方式一:使用数据卷容器(经典方式)
docker create -v /data --name data-container alpine true
docker run --volumes-from data-container myapp
# 方式二:多容器共享同一数据卷(推荐)
docker run -d -v shared-data:/data --name writer alpine sh -c "while true; do echo $(date) >> /data/log.txt; sleep 60; done"
docker run -it --rm -v shared-data:/data alpine cat /data/log.txt
docker rm -f writer
# 方式三:直接绑定挂载(适合开发)
docker run -d -v $(pwd)/logs:/var/log/nginx --name nginx nginx
8.5 实战:MySQL 持久化
# 创建数据卷
docker volume create mysql-data
# 运行 MySQL(数据持久化到卷)
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=root123 \
-e MYSQL_DATABASE=MyDB \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
# 验证数据持久化
docker exec mysql mysql -uroot -proot123 MyDB -e "CREATE TABLE users (id INT, name VARCHAR(50)); INSERT INTO users VALUES (1,'Alice');"
# 停止并删除容器(数据卷保留)
docker stop mysql && docker rm mysql
# 重新运行(数据还在!)
docker run -d \
--name mysql2 \
-e MYSQL_ROOT_PASSWORD=root123 \
-e MYSQL_DATABASE=MyDB \
-v mysql-data:/var/lib/mysql \
-p 3307:3306 \
mysql:8.0
docker exec mysql2 mysql -uroot -proot123 MyDB -e "SELECT * FROM users;"
# 输出:1 | Alice
# 清理
docker stop mysql2 && docker rm mysql2
docker volume rm mysql-data
8.6 本章练习
# 练习1:使用数据卷持久化 Nginx 配置
docker run -d \
--name nginx-vol \
-v nginx-config:/etc/nginx \
nginx
docker inspect nginx-vol | grep -A 5 "Mounts"
# 练习2:使用绑定挂载实现代码热更新
mkdir -p /tmp/webapp
echo '<h1>Hello Bind Mount</h1>' > /tmp/webapp/index.html
docker run -d \
--name nginx-bind \
-p 8081:80 \
-v /tmp/webapp:/usr/share/nginx/html:ro \
nginx
curl http://localhost:8081
echo '<h1>Updated!</h1>' > /tmp/webapp/index.html
curl http://localhost:8081 # 立即看到更新
docker stop nginx-bind && docker rm nginx-bind
9. Docker Compose 多容器编排
9.1 Docker Compose 概述
Docker Compose 是用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 配置文件,可以一键启动整个应用栈。
核心价值:
- 用配置文件替代冗长的
docker run命令 - 管理多容器应用的完整生命周期
- 支持环境变量、网络、卷的统一配置
9.2 安装 Docker Compose
# Docker Desktop 已自带 docker compose(v2 插件方式)
docker compose version
# 或者使用 docker-compose(v1 独立二进制,老项目仍可见)
docker-compose version
9.3 docker-compose.yml 语法详解
version: '3.8'
services:
# 服务名称
web:
# 镜像或构建上下文
image: nginx:latest
# 或
# build: ./web
ports:
- "8080:80" # 端口映射
volumes:
- ./html:/usr/share/nginx/html:ro # 绑定挂载
- nginx-config:/etc/nginx/conf.d # 命名卷
environment:
- NODE_ENV=production
networks:
- frontend
depends_on:
- api
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
api:
build:
context: ./api
dockerfile: Dockerfile
args:
NODE_ENV: production
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
networks:
- frontend
- backend
depends_on:
- db
- redis
db:
image: postgres:15-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
networks:
- backend
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
networks:
- backend
command: redis-server --appendonly yes
volumes:
pgdata:
redis-data:
nginx-config:
networks:
frontend:
backend:
9.4 常用命令
# 启动所有服务(后台)
docker compose up -d
# 启动所有服务(前台,查看日志)
docker compose up
# 停止并删除容器、网络
docker compose down
# 停止并删除容器、网络、卷
docker compose down -v
# 停止服务(保留容器和网络)
docker compose stop
# 启动已停止的服务
docker compose start
# 查看服务状态
docker compose ps
# 查看服务日志
docker compose logs
docker compose logs -f # 实时跟踪
docker compose logs web # 查看指定服务日志
# 重建并启动(更换镜像或配置后)
docker compose up -d --build
# 缩放服务副本数
docker compose up -d --scale web=3
# 执行命令
docker compose exec web ls -la
docker compose exec api bash
9.5 实战:完整的 LAMP 栈
创建项目目录 my-lamp/:
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
networks:
- lamp
restart: unless-stopped
php:
image: php:8.2-fpm-alpine
volumes:
- ./html:/var/www/html
networks:
- lamp
depends_on:
- mysql
restart: unless-stopped
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
volumes:
- mysql-data:/var/lib/mysql
networks:
- lamp
restart: unless-stopped
volumes:
mysql-data:
networks:
lamp:
# nginx.conf
server {
listen 80;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
# 启动
cd my-lamp
docker compose up -d
# 查看状态
docker compose ps
# 访问 http://localhost
# 验证各服务
docker compose exec mysql mysql -uroot -prootpass -e "SHOW DATABASES;"
docker compose logs -f web
9.6 实战:前后端分离应用
# docker-compose.yml
version: '3.8'
services:
# 前端(React/Vue)
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:8080/api
networks:
- appnet
depends_on:
- backend
restart: unless-stopped
# 后端(Node.js/Go/Python)
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- DB_HOST=database
- DB_PORT=5432
- DB_NAME=myapp
- DB_USER=admin
- DB_PASSWORD=secret
- REDIS_HOST=redis
networks:
- appnet
depends_on:
- database
- redis
restart: unless-stopped
# PostgreSQL 数据库
database:
image: postgres:16-alpine
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- appnet
restart: unless-stopped
# Redis 缓存
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
networks:
- appnet
restart: unless-stopped
volumes:
pgdata:
redisdata:
networks:
appnet:
-- init-db.sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com');
# 一键启动整个应用栈
docker compose up -d
# 查看服务状态
docker compose ps
# 查看日志
docker compose logs -f backend
# 验证数据库
docker compose exec database psql -U admin -d myapp -c "SELECT * FROM users;"
# 完整清理
docker compose down -v
9.7 Compose 开发模式与生产模式分离
# docker-compose.yml(开发环境)
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
volumes:
- .:/app # 代码热更新
- /app/node_modules # 避免覆盖容器内 node_modules
environment:
- NODE_ENV=development
command: npm run dev
restart: unless-stopped
# docker-compose.prod.yml(生产环境覆盖)
version: '3.8'
services:
app:
build:
context: .
target: production # 多阶段构建的目标阶段
ports:
- "80:8080"
environment:
- NODE_ENV=production
command: node server.js
restart: unless-stopped
deploy:
replicas: 3
resources:
limits:
memory: 512M
# 组合使用
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
9.8 本章练习
- 使用 Compose 运行一个带数据库的应用(参考上面的 LAMP 或前后端分离示例)
- 使用
docker compose down -v清理环境,理解卷和网络的清理逻辑 - 尝试修改配置后使用
docker compose up -d --build重建
10. Docker 在生产环境中的应用
10.1 镜像优化
目标:减小镜像体积,提升构建和传输效率
# 优化前:镜像约 900MB
FROM python:3.11
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
# 优化后:镜像约 120MB
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
# 多阶段构建进一步优化:镜像约 80MB
FROM python:3.11-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY . .
CMD ["python", "app.py"]
镜像大小对比:
| 基础镜像变体 | 典型大小 | 适用场景 |
|---|---|---|
ubuntu:22.04 |
~77MB | 通用,兼容性好 |
ubuntu:22.04 slim |
~35MB | 需要 apt 但节省空间 |
debian:bookworm-slim |
~30MB | 稳定、轻量 |
alpine |
~7MB | 极致小体积(注意 musl libc 兼容性) |
scratch |
0B | 静态编译语言的空镜像 |
10.2 安全最佳实践
# 1. 不使用 root 运行
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# 2. 不要在内嵌敏感信息(使用 build args 或环境变量)
# ❌ 错误
RUN echo "password=123" > /etc/app.conf
# ✓ 正确:构建时注入或运行时注入
ARG APP_SECRET
RUN echo "secret=${APP_SECRET}" > /etc/app.conf
# 构建:docker build --build-arg APP_SECRET=xxx .
# 3. 使用具体镜像标签而非 latest
FROM nginx:1.25.3-alpine
# 4. 定期扫描镜像漏洞
docker scout cves myapp:latest
# 5. 使用非特权容器
docker run --privileged=false myapp
10.3 CI/CD 集成示例
# .github/workflows/docker.yml
name: Docker Build and Push
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myapp:${{ github.sha }},myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# Jenkinsfile 示例
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'docker build -t myapp:${BUILD_NUMBER} .'
}
}
stage('Test') {
steps {
sh 'docker run myapp:${BUILD_NUMBER} npm test'
}
}
stage('Push') {
when { branch 'main' }
steps {
sh 'docker tag myapp:${BUILD_NUMBER} registry.myco.com/myapp:latest'
sh 'docker push registry.myco.com/myapp:latest'
}
}
}
}
10.4 容器监控与日志
# 查看容器资源使用情况
docker stats
# 查看容器资源限制
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# 日志管理:使用 json-file 驱动(Docker 默认)
docker run -d --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 nginx
# 日志管理:使用 syslog 驱动(接入集中式日志系统)
docker run -d --log-driver syslog --log-opt syslog-address=tcp://log-server:514 nginx
# 使用日志查看工具
docker logs --tail 200 -f myapp
10.5 容器编排平台简介
| 平台 | 适用规模 | 特点 |
|---|---|---|
| Docker Compose | 单主机 | 开发/测试环境首选 |
| Docker Swarm | 中小型集群 | Docker 原生,简单易用 |
| Kubernetes (K8s) | 大型集群 | 行业标准,功能丰富,学习曲线陡 |
| ECS(阿里云) | 云环境 | 托管服务,与阿里云深度集成 |
| AKS/GKE/EKS | 云环境 | 各云厂商的 K8s 托管服务 |
# Swarm 集群初始化(示例)
docker swarm init
docker swarm join-token manager # 获取加入 token
# 部署服务到 Swarm
docker service create --name myapp --replicas 3 -p 80:80 nginx
docker service scale myapp=5
docker service ls
docker service logs -f myapp
# 清理 Swarm
docker swarm leave --force
10.6 本章练习
# 练习1:构建并优化一个镜像
mkdir -p /tmp/optimize-demo
cd /tmp/optimize-demo
cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello(): return 'Hello'
EOF
cat > requirements.txt << 'EOF'
flask==3.0.0
EOF
cat > Dockerfile << 'EOF'
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
EOF
docker build -t myapp:optimized .
docker images | grep myapp
cd /Users/zhouwenjun/Documents/Codex/2026-09-23/role-docker-background-docker-docker-docker
rm -rf /tmp/optimize-demo
11. 常见问题与排错指南
11.1 容器启动失败
# 问题:容器启动后立即退出
# 排查:查看日志
docker logs <container-name>
# 常见原因:
# 1. 没有前台进程运行(容器主进程退出则容器停止)
# 解决:确保 CMD 或 ENTRYPOINT 有持续运行的前台进程
# 2. 启动命令错误
# 解决:docker run -it <image> bash 进入调试
# 3. 配置文件缺失或错误
# 解决:检查挂载的文件是否正确
11.2 端口冲突
# 问题:Error: port is already allocated
# 排查:
lsof -i :8080 # 查看占用端口的进程
docker ps | grep 8080 # 查看是否有其他容器占用
# 解决:
# 1. 修改映射端口:-p 8081:80
# 2. 停止占用容器:docker stop <container-id>
# 3. 使用随机端口:docker run -P nginx
11.3 网络不通
# 问题:容器间无法通信
# 排查:
docker network ls # 查看网络
docker network inspect <network-name> # 查看容器连接情况
docker exec <container> ping <other-container> # 测试连通性
# 常见原因:
# 1. 容器不在同一网络
# 解决:docker network connect <network> <container>
# 2. 容器名解析失败
# 解决:确保使用容器名而非 localhost
# 3. 防火墙规则
# 解决:检查宿主机防火墙设置
11.4 磁盘空间不足
# 查看 Docker 磁盘使用情况
docker system df
# 详细诊断
docker system info | grep "Disk Total"
# 清理未使用的资源
docker system prune # 清理悬挂资源
docker system prune -a # 清理所有未使用资源(谨慎)
docker system prune --volumes # 同时清理未使用的卷
# 查看具体哪些资源占用空间
docker system df -v
docker images --all --digests
docker ps -a --size
11.5 镜像拉取失败(国内网络)
# 配置镜像加速器
# 编辑 ~/.docker/daemon.json
cat > ~/.docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://mirror.baidubce.com",
"https://hub-mirror.c.163.com",
"https://registry.docker-cn.com"
]
}
EOF
# 重启 Docker
# macOS:Docker Desktop → Settings → Docker Engine → 重启
# Linux:sudo systemctl restart docker
# 验证
docker info | grep -i mirror
11.6 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
Cannot connect to the Docker daemon |
Docker 服务未启动 | systemctl start docker 或启动 Docker Desktop |
permission denied |
用户不在 docker 组 | sudo usermod -aG docker $USER 后重新登录 |
image does not exist |
镜像未拉取 | docker pull <image> |
container exited with error code 1 |
应用启动失败 | docker logs <container> 查看详细错误 |
no space left on device |
磁盘满 | docker system prune -a 清理 |
Conflict. The container name is already in use |
容器名重复 | docker rm <container> 或删除旧容器 |
| 容器内时间与宿主机不一致 | 时区未同步 | docker run -e TZ=Asia/Shanghai ... |
12. 学习路径与扩展资源
12.1 建议学习路径
阶段一:基础入门(1-2周)
├── 理解容器化概念与 Docker 架构
├── 掌握 docker run / ps / images 等基本命令
├── 学会运行常见服务(Nginx、MySQL、Redis)
└── 完成本章所有练习
阶段二:镜像与构建(2-3周)
├── 深入理解 Dockerfile 所有指令
├── 学会为实际项目编写 Dockerfile
├── 掌握多阶段构建和镜像优化技巧
└── 完成本章所有练习
阶段三:网络与存储(1-2周)
├── 理解 Docker 网络模型
├── 掌握数据卷和绑定挂载的使用
├── 学会调试容器网络问题
└── 完成本章所有练习
阶段四:编排与部署(2-3周)
├── 熟练使用 Docker Compose
├── 理解容器编排的基本概念
├── 搭建 CI/CD 流水线
└── 完成本章所有练习
阶段五:进阶与生产(持续)
├── 学习 Kubernetes 基础
├── 了解容器安全最佳实践
├── 掌握容器监控与日志方案
└── 结合实际项目持续实践
12.2 推荐扩展资源
官方文档:
- Docker 官方文档:https://docs.docker.com/
- Docker Hub:https://hub.docker.com/
- Docker Blog:https://www.docker.com/blog/
实战练习平台:
- Docker Lab:https://labs.play-with-docker.com/
- Katacoda(部分已迁移)
书籍推荐:
- 《Docker 技术入门与实战》— 韩顺平
- 《Docker 容器与容器云》— 范志水
在线课程:
- Docker 官方入门课程(Docker docs 中有交互式教程)
- Udemy / Coursera 上的 Docker 相关课程
12.3 附录:常用命令速查表
镜像相关:
docker pull <image> # 拉取镜像
docker images # 列出镜像
docker rmi <image> # 删除镜像
docker build -t <name> . # 构建镜像
docker tag <src> <dst> # 标记镜像
docker push <image> # 推送镜像
docker history <image> # 查看镜像历史
docker save/load # 导出/导入镜像
容器相关:
docker run [options] <image> [cmd] # 创建并运行容器
docker ps [-a] # 查看容器
docker stop/start/restart <container> # 控制容器
docker exec -it <container> <cmd> # 进入容器
docker logs <container> # 查看日志
docker rm <container> # 删除容器
docker cp <container>:<path> <local> # 复制文件
网络相关:
docker network ls # 列出网络
docker network create <name> # 创建网络
docker network inspect <name># 查看网络详情
docker network connect/disconnect # 连接/断开网络
卷相关:
docker volume ls # 列出卷
docker volume create <name> # 创建卷
docker volume inspect <name> # 查看卷详情
docker volume rm <name> # 删除卷
系统维护:
docker system df # 磁盘使用情况
docker system prune # 清理资源
docker info # 系统信息
docker version # 版本信息
本教程基于 Docker 27.x 版本编写,涵盖核心概念与实战操作。建议边学边练,在每个章节完成后完成对应练习以巩固知识。

浙公网安备 33010602011771号