跳转至

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 拒绝容器访问挂载目录 确认挂载参数含 :Zausearch -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 实践任务

  1. 登录 linuxdc(SecureCRT 端口 2222,ops 用户,密钥认证,sudo -i 切换 root)。
  2. 安装 Podman,拉取 registry.access.redhat.com/ubi9/httpd-24,运行容器,验证 curl http://localhost:8080 正常返回。
  3. 创建 Volume webdata,挂载到容器(注意 :Z 后缀),在容器内写入 HTML 文件,删除容器后用相同卷重建,验证数据持久化。
  4. 使用 podman generate systemd 生成服务文件,安装到 /etc/systemd/system/,启用并验证开机自启。
  5. 验证 VMware 嵌套虚拟化已启用:egrep -c '(vmx|svm)' /proc/cpuinfo 输出 > 0。
  6. 安装 KVM 工具集(@virtualization),启动 libvirtd,执行 virsh list --allvirsh capabilities | head -10 验证环境就绪。
  7. (进阶)使用 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 shutdownvirsh 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 是现代数据中心基础设施的两大核心技术,也是向微服务架构和私有云迁移的基础能力,为下一章云与混合环境集成提供了本地化的容器和虚拟化支撑。