AIGC标识 Docker 容器化技术完全教程

从入门到实战的系统化学习指南,帮助程序员从零开始掌握 Docker 核心技术。


目录

  1. Docker 概述
  2. Docker 架构与核心概念
  3. Docker 安装与环境配置
  4. 镜像管理
  5. 容器管理
  6. Dockerfile 编写指南
  7. 网络配置
  8. 数据卷与存储
  9. Docker Compose 多容器编排
  10. Docker 在生产环境中的应用
  11. 常见问题与排错指南
  12. 学习路径与扩展资源

1. Docker 概述

1.1 什么是 Docker

Docker 是一个开源的容器化平台,它允许开发者将应用程序及其所有依赖项打包到一个轻量级、可移植的容器中,然后可以在任何支持 Docker 的环境中以相同的方式运行。

类比理解:想象你正在搬家。传统方式是你把所有家具、衣服、餐具散装打包,到了新家再一件件整理;而 Docker 就像是用标准尺寸的集装箱来装所有东西——无论运到哪儿,打开箱子,一切都在原来的位置。

1.2 容器 vs 虚拟机

特性 虚拟机 (VM) 容器 (Container)
启动速度 分钟级 秒级
资源占用 高(完整 OS) 低(共享宿主内核)
镜像大小 GB 级别 MB 级别
隔离性 强(硬件级隔离) 中等(进程级隔离)
性能开销 有虚拟层损耗 接近原生性能

1.3 Docker 的核心价值

  1. 环境一致性:开发、测试、生产环境完全一致,消灭"在我机器上能跑"的问题
  2. 快速部署:秒级启动,便于快速迭代和滚动发布
  3. 资源高效:一台服务器可以运行更多容器,提高资源利用率
  4. 易于扩展:横向扩容只需复制容器实例

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 安装

  1. 下载 Docker Desktop for Windows
  2. 确保已启用 WSL 2(Windows Subsystem for Linux 2)
  3. 运行安装程序,按提示完成安装
  4. 重启后打开 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 本章练习

  1. 安装 Docker 并验证安装成功
  2. 配置镜像加速器(如在国内)
  3. 运行第一个容器:docker run hello-world
  4. 查看 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 本章练习

  1. 使用 Compose 运行一个带数据库的应用(参考上面的 LAMP 或前后端分离示例)
  2. 使用 docker compose down -v 清理环境,理解卷和网络的清理逻辑
  3. 尝试修改配置后使用 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 技术入门与实战》— 韩顺平
  • 《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 版本编写,涵盖核心概念与实战操作。建议边学边练,在每个章节完成后完成对应练习以巩固知识。

posted @ 2026-09-24 20:43  明明很忙吖  阅读(4)  评论(0)    收藏  举报