跳转至

25. 容器与虚拟化基础

本章介绍 RHEL 10 上的容器化与虚拟化技术:Podman(无守护进程容器引擎,RHEL 10 官方默认)和 KVM/libvirt(内核级硬件虚拟化)。RHEL 10 以 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 10 支持 不在官方仓库 官方默认容器工具

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 实践任务

  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 --all 和 virsh capabilities | head -10 验证环境就绪。
  7. (进阶)使用 virt-install 创建虚拟机,练习 virsh start/shutdown/snapshot-create-as/snapshot-revert 操作。

25.6 自测问题

Q1:Podman 与 Docker 在架构上的核心区别是什么,为何 RHEL 10 选择 Podman?

Docker 采用 client-server 架构,dockerd 守护进程以 root 身份常驻运行,所有容器操作须经此进程代理,守护进程崩溃则所有容器受影响(单点故障)。Podman 无守护进程,每次 podman run 直接 fork 子进程启动容器,支持 rootless 模式(容器以普通用户身份运行),隔离性更强。RHEL 10 选择 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 10 容器与虚拟化的核心配置:Podman 无守护进程容器管理(镜像操作、卷持久化、SELinux :Z 集成、systemd 开机自启、Compose 多容器编排)和 KVM/libvirt 虚拟化(环境验证、VM 创建与生命周期管理、快照与克隆)。Podman 和 KVM 是现代数据中心基础设施的两大核心技术,也是向微服务架构和私有云迁移的基础能力,为下一章云与混合环境集成提供了本地化的容器和虚拟化支撑。