25. 容器与虚拟化基础
本章介绍 RHEL 9 上的容器化与虚拟化技术:Podman(无守护进程容器引擎,RHEL 9 官方默认)和 KVM/libvirt(内核级硬件虚拟化)。RHEL 9 以 Podman 全面替代 Docker,其无守护进程架构和 rootless 运行模式更符合企业安全规范;KVM 是 Linux 内核原生的 I 型虚拟化方案,是企业私有云基础设施的核心组件。
25.1 前提条件
- 已完成第四阶段前序配置(第 20–23 章)。
- 通过 SecureCRT(SSH 端口 2222)以
ops用户、密钥认证方式登录192.168.1.100,执行sudo -i切换至 root 环境。 - 官方仓库和 EPEL 仓库已启用(第 7 章)。
- KVM 需要宿主机 CPU 支持硬件虚拟化,VMware 需启用嵌套虚拟化:VMware 虚拟机设置 → 处理器 → 勾选"虚拟化 Intel VT-x/EPT 或 AMD-V/RVI"。
本章全局参数
| 参数 | 值 |
|---|---|
| 主机名 / IP | linuxdc / 192.168.1.100 |
| 容器镜像 | registry.access.redhat.com/ubi9/httpd-24 |
| 容器挂载卷名 | webdata |
| 容器映射端口 | 宿主机 8080 → 容器 8080 |
| KVM 镜像目录 | /var/lib/libvirt/images/ |
25.2 Podman 容器管理
25.2.1 安装 Podman
dnf install -y podman podman-compose
podman --version
Podman 与 Docker 的核心差异
| 维度 | Docker | Podman |
|---|---|---|
| 架构 | 需要 dockerd 守护进程 |
无守护进程(daemonless) |
| 权限 | 守护进程以 root 运行 | 支持 rootless(以普通用户运行) |
| SELinux 集成 | 需额外配置 | 原生集成,自动处理上下文 |
| 命令兼容性 | Docker CLI | 兼容 Docker CLI 语法 |
| RHEL 9 支持 | 不在官方仓库 | 官方默认容器工具 |
25.2.2 运行第一个容器
# 从 Red Hat Universal Base Image 拉取 httpd 镜像
podman pull registry.access.redhat.com/ubi9/httpd-24
# 后台运行,映射端口
podman run -d --name web01 \
-p 8080:8080 \
registry.access.redhat.com/ubi9/httpd-24
# 查看运行中的容器
podman ps
# 查看容器日志
podman logs web01
# 验证服务
curl http://localhost:8080
开放防火墙(从外部访问时):
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
检查点
podman ps 显示 web01 处于 Up 状态;curl http://localhost:8080 返回 HTML 内容。
25.2.3 容器基础操作
# 进入运行中容器的交互式 Shell
podman exec -it web01 bash
# 在容器内执行单条命令(不进入 Shell)
podman exec web01 cat /etc/os-release
# 停止与启动
podman stop web01
podman start web01
# 删除容器(须先停止)
podman stop web01 && podman rm web01
# 查看所有容器(含已停止)
podman ps -a
# 查看本地镜像列表
podman images
# 查看容器详细配置(JSON 格式)
podman inspect web01
25.2.4 持久化存储(卷管理)
容器默认无状态,删除后数据随之消失。使用 Named Volume 实现跨容器生命周期的数据持久化:
# 创建命名卷
podman volume create webdata
# 挂载卷运行容器(:Z 自动设置 SELinux 上下文,必须添加)
podman run -d --name web-persist \
-p 8080:8080 \
-v webdata:/var/www/html:Z \
registry.access.redhat.com/ubi9/httpd-24
# 在容器内写入测试数据
podman exec web-persist \
bash -c 'echo "<h1>Persistent Data on linuxdc</h1>" > /var/www/html/index.html'
# 停止并删除容器
podman stop web-persist && podman rm web-persist
# 用相同卷创建新容器,验证数据依然存在
podman run -d --name web-new \
-p 8080:8080 \
-v webdata:/var/www/html:Z \
registry.access.redhat.com/ubi9/httpd-24
curl http://localhost:8080
检查点
新容器 curl http://localhost:8080 仍返回 Persistent Data on linuxdc,证明数据持久化有效。
SELinux 与容器卷挂载
挂载宿主机目录或卷到容器时,必须添加 :Z(私有重标签)或 :z(共享重标签)后缀::Z 将目录 SELinux 上下文设为仅该容器可访问(container_file_t,更安全);:z 允许多个容器共享访问(container_share_t)。不添加后缀时,SELinux 会拒绝容器进程访问宿主机目录,即使 Unix 权限为 777 也无效,且错误表现为 403 Forbidden,容易误判为权限配置问题。
25.2.5 配置容器开机自启(systemd 集成)
# 运行目标容器
podman run -d --name web-autostart \
-p 8080:8080 \
-v webdata:/var/www/html:Z \
registry.access.redhat.com/ubi9/httpd-24
# 生成 systemd 服务文件(--new 确保重启时重新创建容器)
podman generate systemd \
--name web-autostart \
--files \
--new \
--restart-policy always
# 安装到系统级 systemd
cp container-web-autostart.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now container-web-autostart
# 验证
systemctl status container-web-autostart
25.2.6 Podman Compose(多容器编排)
dnf install -y podman-compose
# 创建项目目录和示例 Compose 文件
mkdir -p ~/compose-demo/html
echo "<h1>Compose Demo on linuxdc</h1>" > ~/compose-demo/html/index.html
tee ~/compose-demo/docker-compose.yml << 'EOF'
version: "3"
services:
web:
image: registry.access.redhat.com/ubi9/httpd-24
ports:
- "8080:8080"
volumes:
- ./html:/var/www/html:Z
restart: always
EOF
cd ~/compose-demo
podman-compose up -d
podman-compose ps
curl http://localhost:8080
# 停止并清理
podman-compose down
25.3 KVM 虚拟化
25.3.1 验证硬件虚拟化支持
# 检查 CPU 虚拟化指令集(vmx=Intel,svm=AMD)
egrep -c '(vmx|svm)' /proc/cpuinfo
# 输出大于 0 表示支持
# 检查 KVM 内核模块是否已加载
lsmod | grep kvm
嵌套虚拟化
在 VMware 虚拟机中使用 KVM 需要启用嵌套虚拟化。若 egrep -c '(vmx|svm)' /proc/cpuinfo 输出为 0,请在 VMware 设置中开启"虚拟化 Intel VT-x/EPT"选项后重启虚拟机。
25.3.2 安装 KVM 工具集
# 安装虚拟化完整组(含 KVM、QEMU、libvirt、virt-install、virt-manager 等)
dnf install -y @virtualization
# 启动 libvirtd 守护进程
systemctl enable --now libvirtd
systemctl status libvirtd
# 将 ops 用户加入 libvirt 组(无需 root 即可管理 VM)
usermod -aG libvirt ops
# 需重新登录使组权限生效
# 验证安装
virsh version
virsh list --all
25.3.3 创建 KVM 虚拟机
virt-install \
--name rhel9-vm01 \
--ram 2048 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/rhel9-vm01.qcow2,size=20,format=qcow2 \
--os-variant rhel9.0 \
--cdrom /root/rhel-9.x-x86_64-dvd.iso \
--network network=default \
--graphics none \
--console pty,target_type=serial \
--extra-args 'console=ttyS0,115200n8'
退出 virsh console
在 virsh console 中,按 Ctrl + ] 退出控制台回到宿主机,虚拟机继续在后台运行。
25.3.4 管理 KVM 虚拟机
# 查看所有虚拟机(含关机状态)
virsh list --all
# 生命周期管理
virsh start rhel9-vm01 # 启动
virsh shutdown rhel9-vm01 # 优雅关机(需 guest agent)
virsh destroy rhel9-vm01 # 强制停止(等同于断电)
virsh reboot rhel9-vm01 # 重启
# 连接虚拟机控制台
virsh console rhel9-vm01
# 快照管理
virsh snapshot-create-as rhel9-vm01 snap01 "初始安装完成"
virsh snapshot-list rhel9-vm01
virsh snapshot-revert rhel9-vm01 snap01
# 查看虚拟机详情
virsh dominfo rhel9-vm01
virsh domifaddr rhel9-vm01 # 查看 VM 获取的 IP 地址
# 克隆虚拟机(源 VM 须处于关机状态)
virt-clone \
--original rhel9-vm01 \
--name rhel9-vm02 \
--file /var/lib/libvirt/images/rhel9-vm02.qcow2
25.4 常见问题与排查
| 问题现象 | 排查步骤 |
|---|---|
| 容器镜像拉取失败 | 检查网络连通性;podman pull --tls-verify=false <image> 跳过 TLS 验证测试 |
| SELinux 拒绝容器访问挂载目录 | 确认挂载参数含 :Z;ausearch -m avc -ts recent 查看具体拒绝日志 |
virt-install 报不支持虚拟化 |
egrep -c '(vmx|svm)' /proc/cpuinfo 应 > 0;VMware 启用嵌套虚拟化后重启 |
virsh list 无法连接 libvirtd |
systemctl status libvirtd;确认 ops 用户在 libvirt 组(需重新登录) |
| 容器 systemd 服务启动失败 | journalctl -u container-web-autostart -n 20;检查镜像是否存在 podman images |
# Podman 综合诊断
podman info # Podman 系统配置信息
podman inspect web01 | head -40 # 容器详细配置
journalctl -t podman -n 20 # Podman 系统日志
# KVM / libvirt 诊断
journalctl -u libvirtd -n 30
virsh capabilities | grep -E "emulator|arch" # 查看支持的虚拟化能力
25.5 实践任务
- 登录
linuxdc(SecureCRT 端口 2222,ops用户,密钥认证,sudo -i切换 root)。 - 安装 Podman,拉取
registry.access.redhat.com/ubi9/httpd-24,运行容器,验证curl http://localhost:8080正常返回。 - 创建 Volume
webdata,挂载到容器(注意:Z后缀),在容器内写入 HTML 文件,删除容器后用相同卷重建,验证数据持久化。 - 使用
podman generate systemd生成服务文件,安装到/etc/systemd/system/,启用并验证开机自启。 - 验证 VMware 嵌套虚拟化已启用:
egrep -c '(vmx|svm)' /proc/cpuinfo输出 > 0。 - 安装 KVM 工具集(
@virtualization),启动 libvirtd,执行virsh list --all和virsh capabilities | head -10验证环境就绪。 - (进阶)使用
virt-install创建虚拟机,练习virsh start/shutdown/snapshot-create-as/snapshot-revert操作。
25.6 自测问题
Q1:Podman 与 Docker 在架构上的核心区别是什么,为何 RHEL 9 选择 Podman?
Docker 采用 client-server 架构,dockerd 守护进程以 root 身份常驻运行,所有容器操作须经此进程代理,守护进程崩溃则所有容器受影响(单点故障)。Podman 无守护进程,每次 podman run 直接 fork 子进程启动容器,支持 rootless 模式(容器以普通用户身份运行),隔离性更强。RHEL 9 选择 Podman 的核心原因:符合 SELinux 和 rootless 的企业安全要求;与 systemd 深度集成(podman generate systemd);无 Docker 守护进程的单点故障风险;Red Hat 作为其维护方提供官方支持。
Q2:podman run -v /host/path:/container/path:Z 中 :Z 和 :z 各自的含义是什么?
:Z(大写)将挂载目录的 SELinux 类型标签修改为 container_file_t 并设置为私有(MCS 类别唯一),仅当前容器可访问,同一宿主机上其他容器无法读取,更安全,推荐单容器场景使用。:z(小写)将标签设为 container_share_t,标记为共享,多个容器可以同时访问同一挂载目录,适合多容器共享数据场景。不加任何标签时,宿主机目录保持原有 SELinux 上下文,容器进程(container_t 类型)无权访问,即使 Unix 权限是 777 也会被拒绝。
Q3:KVM virsh shutdown 和 virsh destroy 的本质区别及适用场景是什么?
virsh shutdown 通过 ACPI 信号通知虚拟机内部操作系统执行正常关机流程(同按物理电源键),OS 有机会刷新缓存、写入日志、优雅停止服务;需要虚拟机内运行 qemu-guest-agent 才能可靠触发。virsh destroy 直接停止 QEMU 进程(等同于拔电源),OS 无任何清理时间,可能导致磁盘文件处于不一致状态。适用场景:正常操作始终使用 shutdown;虚拟机内核崩溃(无响应)时使用 destroy,但之后应对虚拟机磁盘执行 fsck。
25.7 章节总结
本章完成了 RHEL 9 容器与虚拟化的核心配置:Podman 无守护进程容器管理(镜像操作、卷持久化、SELinux :Z 集成、systemd 开机自启、Compose 多容器编排)和 KVM/libvirt 虚拟化(环境验证、VM 创建与生命周期管理、快照与克隆)。Podman 和 KVM 是现代数据中心基础设施的两大核心技术,也是向微服务架构和私有云迁移的基础能力,为下一章云与混合环境集成提供了本地化的容器和虚拟化支撑。