Docker相关

7 minute read

Docker 容器化与微服务 完整系统学习大纲

本文面向测试工程师及后端开发初学者。Docker 早已成为软件交付与测试环境搭建的基础设施:一条命令拉起 MySQL/Redis、在容器里跑被测应用、用镜像保证”环境一致”。掌握 Docker,你的测试环境管理效率会提升一个量级。 文中命令基于 Docker 24+,Windows 用户请在 PowerShell(非 cmd)中执行。

一、Docker 概述

1.1 什么是 Docker?

  • 定义:Docker 是一个开源的应用容器引擎,让开发者把应用及依赖打包到一个可移植的容器中,实现”一次构建,到处运行”。
  • 历史:2013 年由 Solomon Hykes 基于 Linux 容器(LXC)技术发布;2017 年起进入 Moby 开源项目时代。
  • 核心思想:容器 = 操作系统级虚拟化,共享宿主机内核,但拥有独立的文件系统、进程、网络与资源限制。

1.2 容器 vs 虚拟机

维度 容器(Docker) 虚拟机(VM)
隔离级别 进程级(共享宿主机内核) 硬件级(完整独立操作系统)
启动速度 秒级 分钟级
占用空间 镜像 MB 级 镜像 GB 级
资源利用率 高 低(有虚拟化开销)
性能损耗 极小 有一定损耗
隔离强度 较弱(共享内核) 强(完全隔离)
典型代表 Docker、containerd VMware、VirtualBox、KVM

一句话:容器更轻更快,虚拟机更隔离更安全。测试环境用容器足够,涉及内核级别隔离需求时才考虑 VM。

1.3 核心概念:镜像 / 容器 / 仓库

概念 类比 说明
镜像 (Image) 类的定义 / 光盘模板 只读的、分层的应用打包文件,包含代码、运行时、依赖、配置
容器 (Container) 类的实例 / 运行中的程序 镜像运行时的实例,可启动、停止、删除,互相隔离
仓库 (Repository) 应用商店 / 网盘 集中存放镜像的地方(Docker Hub、阿里云 ACR 等)
Dockerfile 制作镜像的”菜谱” 描述如何构建镜像的脚本文件
数据卷 (Volume) U 盘 容器持久化数据的机制

三者的关系链:Dockerfile 构建 → 镜像 → 运行成 容器,镜像可推送到仓库供他人拉取。

1.4 Docker 架构

┌──────────┐   Docker CLI   ┌──────────────┐   REST API   ┌──────────────┐
│  客户端   │ ─────────────▶ │  守护进程     │ ───────────▶ │  容器/镜像/网络 │
│ docker   │                │ docker daemon│              │  (containerd) │
└──────────┘                └──────────────┘              └──────────────┘
     ▼ 镜像下载/上传
┌──────────────┐
│  镜像仓库     │  Docker Hub / 私有 registry
└──────────────┘
  • 客户端 (Client):用户敲 docker 命令。
  • 守护进程 (Daemon):真正干活的后台服务,管理镜像、容器、网络、数据卷。
  • 镜像仓库 (Registry):存放和分发镜像(Docker Hub 是默认公共仓库)。

1.5 应用场景

  1. 测试环境搭建:一条命令拉起 MySQL、Redis、MongoDB、RabbitMQ 等依赖。
  2. 被测应用容器化:把被测系统打成镜像,环境一致、快速复现 Bug。
  3. CI/CD 流水线:构建、测试、发布全部在容器中执行。
  4. 多版本依赖隔离:同一台机器上跑不同版本 JDK/Python/Node 互不干扰。
  5. 微服务架构:每个服务独立容器,配合编排工具(K8s)管理。
  6. 学习与实验:快速体验各种中间件,用完即删,不留垃圾。

二、安装与环境搭建

2.1 Windows 安装(Docker Desktop)

  1. 前置条件:Windows 10/11 专业版/企业版/教育版,开启 WSL 2(推荐)或 Hyper-V。
  2. 下载 Docker Desktop for Windows 并安装,安装时勾选 Use WSL 2 instead of Hyper-V。
  3. 安装完成后启动 Docker Desktop,等托盘图标变为稳定状态(鲸鱼图标不闪烁)。
  4. 验证:
    docker --version
    docker run hello-world     # 能打印 Hello from Docker! 即成功
    
  5. 如果 WSL 未启用,先执行(管理员 PowerShell):
    wsl --install
    

⚠️ Docker Desktop 商业使用需付费授权(个人/小团队教育用途免费)。公司内部使用建议确认授权情况,或用 podman 等替代方案。

2.2 Linux 安装(Ubuntu/Debian 示例)

# 官方一键脚本(适合测试环境)
curl -fsSL https://get.docker.com | sh

# 免 sudo 使用 docker(重新登录后生效)
sudo usermod -aG docker $USER

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

# 验证
docker version
docker run hello-world

2.3 配置镜像加速(国内必配)

编辑 /etc/docker/daemon.json(Windows 在 Docker Desktop → Settings → Docker Engine):

{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://dockerproxy.com",
    "https://docker.nju.edu.cn"
  ]
}
sudo systemctl restart docker    # 重启生效
docker info | grep -A 5 "Registry Mirrors"

三、镜像与容器基础操作

3.1 镜像操作

# 搜索镜像
docker search mysql

# 拉取镜像(不写 tag 默认 latest,建议显式写版本)
docker pull mysql:8.0
docker pull nginx:1.24-alpine

# 查看本地镜像
docker images
docker image ls

# 查看镜像详情
docker inspect mysql:8.0

# 给镜像打标签(用于推送到自己的仓库)
docker tag mysql:8.0 myregistry.com/mysql:8.0

# 删除镜像(先删依赖的容器)
docker rmi mysql:8.0
docker image prune       # 清理悬空镜像

3.2 容器操作

# 运行容器(核心命令)
docker run -d --name my-nginx -p 8080:80 nginx:1.24-alpine

# 查看容器列表(-a 包含已停止)
docker ps
docker ps -a

# 启动 / 停止 / 重启 / 删除
docker start my-nginx
docker stop my-nginx
docker restart my-nginx
docker rm my-nginx
docker rm -f my-nginx      # 强制删除运行中的容器

# 进入容器执行命令(调试神器)
docker exec -it my-nginx /bin/sh
docker exec my-nginx nginx -t

# 查看日志(-f 实时跟踪,-n 行数)
docker logs my-nginx
docker logs -f --tail 100 my-nginx

# 查看容器资源占用
docker stats
docker top my-nginx

# 复制文件进出容器
docker cp ./test.sql my-nginx:/tmp/
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf

3.3 docker run 常用参数详解

参数 说明 示例
-d 后台运行 docker run -d nginx
--name 容器命名(唯一) --name my-nginx
-p 宿主机:容器 端口映射 -p 8080:80
-v 宿主机:容器 数据卷/目录挂载 -v /data:/var/lib/mysql
-e KEY=VALUE 设置环境变量 -e MYSQL_ROOT_PASSWORD=123456
--rm 容器退出后自动删除(临时容器) --rm
-it 交互式终端 -it /bin/bash
--network 指定网络 --network my-net
--restart 重启策略 --restart=always
-m / --cpus 内存/CPU 限制 -m 512m --cpus=1

3.4 一次到位的 MySQL 测试环境

docker run -d \
  --name test-mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=edu \
  --restart=always \
  mysql:8.0

💡 测试价值:之前要装半天的 MySQL,现在一条命令即可获得隔离、可丢弃、版本可选的数据库实例。想测 MySQL 8 和 MariaDB 的兼容性,各起一个容器互不影响。

四、Dockerfile 构建镜像

4.1 常用指令速查

指令 作用 示例
FROM 基础镜像(必填,第一行) FROM python:3.11-slim
WORKDIR 设置工作目录 WORKDIR /app
COPY 复制文件进镜像 COPY . /app
ADD 复制文件(支持 URL、自动解压 tar) ADD app.tar.gz /app/
RUN 构建时执行命令(安装依赖) RUN pip install -r requirements.txt
ENV 设置环境变量 ENV TZ=Asia/Shanghai
EXPOSE 声明容器监听端口(仅文档作用) EXPOSE 8000
CMD 容器启动时执行的命令(可被覆盖) CMD ["python", "app.py"]
ENTRYPOINT 容器启动主命令(不可被覆盖,适合固定入口) ENTRYPOINT ["python"]
ARG 构建参数(仅构建期有效) ARG VERSION=1.0
USER 指定运行用户(安全) USER app

4.2 示例 1:Python 应用

FROM python:3.11-slim

WORKDIR /app

# 先复制依赖文件,利用层缓存加速构建
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV TZ=Asia/Shanghai
EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

4.3 示例 2:Java (Spring Boot) 应用

FROM eclipse-temurin:17-jre

WORKDIR /app
COPY target/*.jar app.jar

# JVM 时区与参数
ENV TZ=Asia/Shanghai
EXPOSE 8080

ENTRYPOINT ["java", "-jar", "/app/app.jar"]

4.4 构建与推送

# 构建(-t 命名镜像,. 指当前目录的 Dockerfile)
docker build -t myapp:1.0 .

# 构建时传参
docker build --build-arg VERSION=2.0 -t myapp:2.0 .

# 运行验证
docker run -d -p 8000:8000 --name myapp myapp:1.0

# 推送(需先登录 docker login)
docker tag myapp:1.0 yourname/myapp:1.0
docker push yourname/myapp:1.0

# 清理构建缓存
docker builder prune

4.5 镜像分层与优化技巧

原理:Dockerfile 的每条指令生成一个只读层,层被缓存复用。文件被修改时才重建该层及之后所有层。

优化技巧 说明 收益
依赖安装放在代码复制之前 代码改了不用重装依赖 大幅减少构建时间
用 .dockerignore 排除 node_modules、target、.git 等 减小上下文、避免误打包
使用精简基础镜像 -slim、-alpine 减小镜像体积
多阶段构建 编译镜像 → 运行镜像 最终镜像只留运行产物
合并 RUN 指令 RUN a && b && c 减少层数
# 多阶段构建示例(Java)
# 阶段一:编译
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

# 阶段二:运行(只保留 jar)
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=build /build/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

⚠️ 注意:多条 COPY 若顺序颠倒(先 COPY 源码再 COPY 依赖文件),会导致依赖层缓存失效。把变动小的放前面,变动大的放后面。

五、数据管理

容器删除后内部数据随之消失,持久化数据需用数据卷或绑定挂载。

5.1 数据卷 (Volume)

# 创建数据卷
docker volume create mysql-data

# 挂载数据卷
docker run -d --name test-mysql \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

# 查看 / 清理
docker volume ls
docker volume rm mysql-data
  • 数据由 Docker 管理(位于 docker volume inspect 查看的宿主机路径),跨容器共享方便。
  • 容器删除后数据卷默认保留,适合数据库等重要数据。

5.2 绑定挂载 (Bind Mount)

# 把宿主目录挂进容器(前后端联调、热更新场景)
docker run -d --name my-nginx \
  -v D:/www:/usr/share/nginx/html:ro \
  -p 8080:80 nginx
  • 直接映射宿主机路径,修改宿主文件即时生效,适合开发调试。
  • :ro 表示只读挂载。

5.3 对比

维度 数据卷 Volume 绑定挂载 Bind Mount
存储位置 Docker 管理目录 用户指定宿主机路径
推荐程度 生产推荐 开发调试常用
备份迁移 docker run --volumes-from 等方式 直接拷贝目录
跨平台兼容 好 Windows 下路径格式要注意

六、Docker Compose

6.1 为什么用 Compose

多个容器(如 Web + MySQL + Redis)需要协同管理:一条命令同时启动、统一网络、定义依赖关系。docker-compose.yml 就是”多容器编排脚本”。

6.2 示例:Web 应用 + MySQL + Redis

version: "3.8"

services:
  web:
    build: .
    ports:
      - "8080:8080"
    environment:
      DB_HOST: mysql          # 服务名即容器网络内主机名
      DB_PASSWORD: 123456
      REDIS_HOST: redis
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: edu
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  mysql-data:

6.3 常用命令

docker compose up -d            # 后台启动全部服务
docker compose up -d --build    # 重新构建并启动
docker compose ps               # 查看服务状态
docker compose logs -f web      # 跟踪某个服务日志
docker compose exec web /bin/sh # 进入服务容器
docker compose down             # 停止并删除容器(数据卷默认保留)
docker compose down -v          # 连同数据卷一起删除(慎用!)
docker compose config           # 校验并打印生效配置

💡 测试价值:一个 docker-compose.yml 就是一套可重复的测试环境模板——团队共享、版本可控、坏了重建。配合 .env 文件可切换不同环境的配置。

七、网络

7.1 网络模式

模式 说明 使用场景
bridge(默认) 容器通过虚拟网桥与宿主机通信,需端口映射暴露 单机多容器
host 容器直接使用宿主机网络,无端口映射 追求性能、端口大量场景
none 无网络 安全/隔离场景
container:xxx 与指定容器共享网络栈 调试、sidecar 模式

7.2 自定义网络(推荐)

# 创建自定义网络(容器间可用服务名互相访问,并自动 DNS 解析)
docker network create my-net

docker run -d --name mysql8 --network my-net mysql:8.0
docker run -d --name myapp --network my-net -p 8080:8080 myapp:1.0

# 应用内直接连 mysql8:3306,而不是 IP
docker exec myapp ping mysql8    # 能通
  • 自定义网络下容器间无需端口映射即可通信(走虚拟网络),比依赖 IP 更稳定。
  • Compose 会为项目自动创建网络,服务名即主机名。

7.3 端口映射原理

宿主机 8080 端口 ──▶ docker-proxy ──▶ 容器内 80 端口
访问 http://localhost:8080 即可到达容器内 nginx
docker port my-nginx    # 查看映射关系

八、镜像仓库与分发

8.1 Docker Hub

docker login                        # 登录 Docker Hub
docker pull nginx                   # 拉公共镜像
docker push yourname/myapp:1.0      # 推送自己的镜像
docker logout

8.2 私有仓库(离线/内网场景)

# 运行本地 registry(默认端口 5000,仅支持 http)
docker run -d --name registry -p 5000:5000 registry:2

# 打标签并推送
docker tag myapp:1.0 localhost:5000/myapp:1.0
docker push localhost:5000/myapp:1.0

# 验证
curl http://localhost:5000/v2/_catalog

💡 内网测试环境经常无法访问外网拉镜像,可用 registry 或 Harbor(企业级)搭建内网镜像源,或提前 docker pull 后离线导出。

8.3 镜像导出与导入

# 导出(离线迁移场景)
docker save -o myapp.tar myapp:1.0
# 导入
docker load -i myapp.tar

# 容器导出为 tar(含运行状态文件系统,不推荐生产)
docker export -o container.tar my-nginx
docker import container.tar mynginx:imported
命令 对象 是否保留分层/元数据
docker save/load 镜像 ✅ 保留(推荐)
docker export/import 容器 ❌ 仅文件系统

九、测试场景实战(重点)

9.1 秒级搭建测试依赖环境

# MySQL 8
docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

# Redis
docker run -d --name redis -p 6379:6379 redis:7-alpine

# RabbitMQ(带管理面板 http://localhost:15672)
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

# Elasticsearch + Kibana
docker run -d --name es -p 9200:9200 -e "discovery.type=single-node" elasticsearch:8.11.3

9.2 容器化被测应用

# 打包被测系统
docker build -t qa-app:latest .
# 启动被测实例(含数据库依赖)
docker compose up -d
# 验证服务
curl http://localhost:8080/actuator/health

环境一致性:开发环境、测试环境、生产环境用同一个镜像,“在我机器上是好的”这类问题大幅减少。

9.3 多版本依赖隔离

# 同一台机器跑多个 Python 版本互不干扰
docker run --rm -it python:3.9 python --version
docker run --rm -it python:3.12 python --version

# 不同 JDK 版本测试
docker run --rm -it eclipse-temurin:8 java -version
docker run --rm -it eclipse-temurin:17 java -version

9.4 问题排查常用命令

# 1. 容器是否在运行、什么状态
docker ps -a

# 2. 看启动日志(最常见:启动失败/循环重启)
docker logs --tail 200 <container>

# 3. 进入容器手动排查
docker exec -it <container> /bin/sh

# 4. 看端口是否映射成功
docker port <container>
ss -lntp | grep 8080

# 5. 看资源占用(内存爆了?CPU 飙了?)
docker stats

# 6. 看容器完整配置与健康状态
docker inspect <container> | grep -A 5 "Status"

# 7. 重启策略生效吗
docker inspect --format '' <container>

9.5 常见问题速查

现象 可能原因 解决办法
port is already allocated 宿主机端口被占用 换端口或 ss -lntp 找到占用进程
容器启动即退出 前台进程退出、命令错误、端口冲突 docker logs 看日志;用 -it 交互验证
image not found 镜像名/版本写错,或未拉取 检查 tag,docker pull
permission denied 普通用户无 docker 权限 加入 docker 组或加 sudo
容器内无法连外网 DNS 或网络模式问题 检查 /etc/resolv.conf、网络模式
磁盘被占满 镜像/容器/卷堆积 docker system prune -a(慎用)
时区不对 容器默认 UTC 加 -e TZ=Asia/Shanghai

十、资源限制与监控

10.1 资源限制

# 限制内存 512M、CPU 1 核、禁止 Swap
docker run -d --name app \
  -m 512m --memory-swap 512m --cpus 1 \
  myapp:1.0

💡 测试价值:压测时用 -m 限制容器内存,可快速模拟”内存不足/OOM”场景,验证被测应用在资源紧张时的表现(是否优雅降级、是否崩溃)。

10.2 监控

docker stats              # 实时资源监控(Ctrl+C 退出)
docker top <container>    # 查看容器内进程
docker events             # 实时事件流(启停、销毁等)

10.3 日志配置

# 限制日志大小,防止日志撑爆磁盘
docker run -d \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx
// 全局配置 /etc/docker/daemon.json
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }

十一、学习资源

资源 说明
Docker 官方文档 最权威的入门与参考
Docker Hub 公共镜像仓库,找镜像
Play with Docker 在线免费练手环境
Dockerfile 最佳实践(官方) 写 Dockerfile 必读
《Docker 实战》/ 《深入浅出 Docker》 系统化学习书籍
菜鸟教程 Docker 教程 中文快速入门