2. 实验环境搭建:虚拟机与容器化环境(Docker/KVM)的配置与快照管理
做故障注入实验,环境搭建是第一步,也是最容易踩坑的一步。我个人习惯把实验环境分成两类:虚拟机(KVM)和容器(Docker)。两者各有千秋,咱们得根据实验场景来选。
2.1 为什么需要虚拟化?
说白了,故障注入实验本质上就是「搞破坏」。你想想看,直接在物理机上搞内存位翻转、磁盘 I/O 错误、网络丢包——万一恢复不了,那可就真「炸」了。虚拟化技术给了我们一个安全沙箱:
- 隔离性:实验环境与宿主机完全隔离,搞坏了也不影响生产
- 快照回滚:这是故障注入实验的「后悔药」,一键回到干净状态
- 资源管控:精确控制 CPU、内存、磁盘,模拟资源受限场景
- 网络拓扑:轻松搭建多节点集群,模拟分布式系统故障
核心原则:故障注入实验的环境,必须做到「可重复、可回滚、可观测」。做不到这三点,实验数据就是废纸。
2.2 KVM 虚拟机环境配置
KVM 是 Linux 内核原生的虚拟化方案,性能损耗小,适合做系统级的故障注入。我在项目中遇到过好几次,用 VirtualBox 做内核调试时各种兼容性问题,换成 KVM 就稳了。
2.2.1 安装与基础配置
# 检查 CPU 是否支持虚拟化
grep -E '(vmx|svm)' /proc/cpuinfo
# 安装 KVM 及相关工具
sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
# 启动 libvirt 服务
sudo systemctl enable --now libvirtd
# 将当前用户加入 libvirt 组
sudo usermod -aG libvirt $USER
小技巧:安装完成后,记得用 virsh list --all 验证服务是否正常。我曾经因为忘记重启终端,导致新用户组没生效,折腾了半小时。
2.2.2 创建实验虚拟机
我建议用 virt-install 命令行创建,比图形化界面更可控,也方便写脚本批量部署。
# 创建 Ubuntu 22.04 实验虚拟机
virt-install \
--name fault-injection-lab \
--ram 4096 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/fault-lab.qcow2,size=40 \
--os-variant ubuntu22.04 \
--network bridge=virbr0 \
--graphics vnc,listen=0.0.0.0 \
--cdrom /path/to/ubuntu-22.04-server.iso
| 参数 | 说明 | 我的建议 |
|---|---|---|
--ram |
内存大小(MB) | 故障注入实验建议 4GB 起步 |
--vcpus |
虚拟 CPU 核心数 | 2 核足够,太多反而掩盖资源竞争问题 |
--disk |
磁盘路径与大小 | qcow2 格式支持快照,必须用这个 |
--network |
网络模式 | 默认 NAT,需要集群实验时改用 bridge |
2.2.3 快照管理——实验的「后悔药」
快照是故障注入实验的灵魂。没有快照,你每次实验都得重装系统,那效率太低了。KVM 的快照分两种:
- 磁盘快照:只保存磁盘状态,速度快,占用空间小
- 系统快照:保存整个虚拟机状态(内存+磁盘),恢复时完全回到那一刻
# 创建磁盘快照(实验前必须做)
virsh snapshot-create-as fault-injection-lab \
--name "clean-state" \
--description "干净的 Ubuntu 22.04 初始状态"
# 查看快照列表
virsh snapshot-list fault-injection-lab
# 恢复快照(实验搞坏了?一键恢复)
virsh snapshot-revert fault-injection-lab --snapshotname "clean-state"
# 删除快照
virsh snapshot-delete fault-injection-lab --snapshotname "old-snapshot"
⚠️ 重要提醒:快照不是备份!快照文件与原始磁盘文件是依赖关系,删了原始文件,快照就废了。我曾经吃过这个亏,实验数据全丢了。建议定期导出完整镜像。
2.3 Docker 容器环境配置
Docker 适合做应用层的故障注入。比如模拟微服务之间的网络延迟、CPU 资源争抢、磁盘 I/O 限速。相比 KVM,Docker 启动快、资源占用小,但隔离性弱一些。
2.3.1 安装 Docker
# 安装 Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 将当前用户加入 docker 组
sudo usermod -aG docker $USER
# 验证安装
docker --version
docker run hello-world
2.3.2 构建故障注入实验镜像
我习惯把实验工具直接打包进镜像,省得每次启动容器都要安装。
# Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
stress-ng \ # CPU/内存压力工具
iproute2 \ # 网络故障注入
iptables \ # 防火墙规则
tc \ # 流量控制
fio \ # 磁盘 I/O 测试
sysbench \ # 系统基准测试
&& rm -rf /var/lib/apt/lists/*
CMD ["/bin/bash"]
# 构建镜像
docker build -t fault-injection-lab:1.0 .
# 启动容器(带资源限制)
docker run -it --rm \
--name fault-container \
--cpus="1.5" \
--memory="2g" \
--memory-swap="2g" \
--blkio-weight=500 \
fault-injection-lab:1.0
| 资源限制参数 | 作用 | 故障注入场景 |
|---|---|---|
--cpus |
限制 CPU 使用率 | 模拟 CPU 资源争抢 |
--memory |
限制内存上限 | 触发 OOM Killer |
--blkio-weight |
限制磁盘 I/O 优先级 | 模拟磁盘性能瓶颈 |
--cap-add |
添加系统权限 | 注入网络/内核故障 |
2.3.3 Docker 快照与镜像管理
Docker 没有 KVM 那种「一键快照」的功能,但我们可以用 docker commit 保存容器状态为镜像,效果类似。
# 在容器内做完配置后,保存为镜像
docker commit fault-container fault-injection-lab:experiment-1
# 查看镜像列表
docker images
# 从保存的镜像启动新容器
docker run -it --rm fault-injection-lab:experiment-1
# 导出镜像为 tar 文件(备份用)
docker save fault-injection-lab:experiment-1 -o experiment-1.tar
# 导入镜像
docker load -i experiment-1.tar
我的经验:Docker 的 commit 操作会丢失容器运行时的内存状态,所以它只适合「配置快照」,不适合「运行时快照」。如果你需要保存完整的运行时状态,还是得用 KVM。
2.4 KVM vs Docker:怎么选?
很多同学问我:「老师,到底用哪个?」我的回答是:看你要注入什么故障。
| 对比维度 | KVM 虚拟机 | Docker 容器 |
|---|---|---|
| 隔离级别 | 完全隔离(独立内核) | 进程级隔离(共享内核) |
| 启动速度 | 分钟级 | 秒级 |
| 资源占用 | 高(完整 OS) | 低(仅进程) |
| 快照能力 | 强(磁盘+内存) | 弱(仅文件系统) |
| 适用故障类型 | 内核故障、硬件故障模拟 | 应用故障、网络故障、资源争抢 |
| 网络拓扑 | 复杂(多网卡、桥接) | 简单(Docker Network) |
我的建议:做内核级别的故障注入(比如内存位翻转、系统调用劫持),用 KVM。做微服务、分布式系统的故障注入(比如网络延迟、服务熔断),用 Docker。两个都学会,才是完整的安全测试工程师。
2.5 实验环境拓扑图
下面这张图展示了我们课程中会用到的实验环境架构。KVM 负责底层系统故障注入,Docker 负责上层应用故障注入,两者通过虚拟网络连接,形成完整的故障注入实验平台。
2.6 环境验证脚本
环境搭好了,怎么知道能不能用?我写了个简单的验证脚本,跑一遍就知道环境是否就绪。
#!/bin/bash
# 环境验证脚本
echo "=== 1. 检查 KVM ==="
virsh list --all || echo "KVM 未安装或服务未启动"
echo "=== 2. 检查 Docker ==="
docker info --format '{{.ServerVersion}}' || echo "Docker 未安装或服务未启动"
echo "=== 3. 创建测试虚拟机 ==="
virt-install --name test-vm --ram 512 --vcpus 1 \
--disk size=5 --os-variant ubuntu22.04 \
--graphics none --print-xml > /dev/null && \
echo "KVM 创建虚拟机成功"
echo "=== 4. 创建测试容器 ==="
docker run --rm alpine echo "Docker 容器运行成功"
echo "=== 5. 测试快照功能 ==="
virsh snapshot-create-as test-vm --name "test-snapshot" && \
echo "KVM 快照创建成功"
virsh snapshot-delete test-vm --snapshotname "test-snapshot"
echo "=== 环境验证完成 ==="
⚠️ 注意:验证脚本中的测试虚拟机和容器会在验证后自动清理。但如果你手动创建了实验环境,记得用 virsh undefine 和 docker rm 清理,别让测试环境占用资源。
2.7 避坑指南
最后,分享几个我踩过的坑,希望能帮你省点时间:
- 磁盘格式:KVM 一定要用 qcow2 格式,raw 格式不支持快照。我曾经图省事用了 raw,结果做实验时没法回滚,重装了三次系统。
- 网络模式:做分布式故障注入实验时,记得用 bridge 模式,NAT 模式会导致容器间网络隔离,没法模拟真实的微服务通信。
- 资源预留:给虚拟机分配资源时,别把宿主机资源全占满。留 1 个 CPU 核心和 2GB 内存给宿主机,否则宿主机卡死,连恢复快照都操作不了。
- 快照命名:养成好习惯,快照名要带时间戳和实验编号。比如
exp-20240615-memory-flip-before,不然一周后你根本分不清哪个快照是哪个。