从零构建openEuler 22.03 LTS云镜像:OpenStack黄金镜像制作全流程详解 1. 项目概述与核心价值最近在给公司的私有云平台做镜像标准化选型定在了openEuler 22.03 LTS。这个活儿听起来简单不就是做个系统镜像嘛但真上手了才发现从零开始为OpenStack这样的云平台制作一个“开箱即用”、性能稳定且符合安全基线要求的镜像里头的门道可不少。这不仅仅是装个系统、打个包那么简单它涉及到系统初始化配置、云平台驱动集成、安全加固、性能调优等一系列琐碎但至关重要的步骤。一个制作精良的镜像能极大提升后续虚拟机实例的创建速度和运行稳定性减少运维的重复劳动而一个粗糙的镜像则可能成为后续各种“灵异事件”的源头。今天我就把自己从零开始制作openEuler 22.03 for OpenStack镜像的完整过程、踩过的坑以及总结的最佳实践毫无保留地分享出来。无论你是刚开始接触云平台运维还是想优化现有的镜像流水线相信这篇近万字的实操记录都能给你带来直接的参考价值。2. 镜像制作的整体设计与思路拆解在动手之前我们必须先想清楚要做一个什么样的镜像以及OpenStack对镜像有哪些“隐形”要求。盲目开始往往意味着中途要不断返工。2.1 明确镜像的最终形态与核心需求我们的目标不是做一个能在物理机上安装的ISO而是制作一个QCOW2格式的磁盘镜像文件。这个文件将被上传到OpenStack的Glance镜像服务中作为创建虚拟机实例的“模板”。因此它需要满足几个核心需求云就绪系统必须能适应云环境的动态特性。比如IP地址、主机名通常由云平台通过DHCP或Metadata服务动态分配和注入而不是在镜像中写死。驱动兼容虚拟机在Hypervisor如KVM上运行需要对应的虚拟化驱动如virtio来保证磁盘、网卡等设备的高性能。轻量高效镜像体积要尽可能小以节省存储空间和加快下载、缓存速度。这意味着需要清理不必要的缓存、日志和软件包。安全基线默认配置应符合基本的安全要求例如禁用root密码SSH登录、配置合理的防火墙策略虽然云平台安全组是主要防线但镜像内仍可做基础设置。可维护性镜像中的软件源应配置正确方便后续在实例内部进行软件更新和管理。基于这些需求我选择的方案是在一台临时虚拟机作为构建机中使用qemu-img创建一块虚拟磁盘然后通过virt-install或qemu-system工具将openEuler 22.03 LTS系统安装到这块虚拟磁盘中接着进行一系列“云化”和优化配置最后将磁盘导出为QCOW2格式。这种方法隔离性好可重复性强。2.2 工具链选型与准备工作工欲善其事必先利其器。以下是整个流程中需要用到的核心工具及其作用构建环境一台安装有KVM/QEMU的Linux主机物理机或虚拟机均可。我使用的是另一台CentOS 7的服务器它本身支持虚拟化。你需要确保qemu-kvm、libvirt等包已安装并且CPU支持虚拟化egrep -c ‘(vmx|svm)’ /proc/cpuinfo输出大于0。核心命令qemu-img用于创建、转换磁盘镜像格式。我们用它来创建初始的raw格式磁盘并最终转换为qcow2格式。virt-install一个封装了libvirt的命令行工具能极大地简化从ISO启动并安装系统到指定磁盘的过程。比手动配置qemu-system-x86_64要方便得多。guestfish/virt-customize这是一套“神器”可以在不启动虚拟机的情况下直接对磁盘镜像文件进行读写、注入文件、执行命令等操作。在配置阶段会频繁使用。virt-sysprep同样是libguestfs工具集的一员用于对已安装好的系统镜像进行“通用化”清理例如清除SSH主机密钥、清理日志、清除用户信息等这对于制作模板镜像至关重要。软件源准备好openEuler 22.03 LTS的ISO安装文件。同时为了在构建过程中和最终镜像内部都能高速安装软件建议在构建机内部搭建一个本地镜像源或者配置好可靠的国内开源镜像站如华为云、清华、阿里云的openEuler镜像源。注意构建机本身最好有充足的空间至少50GB空闲因为过程中会产生多个磁盘镜像副本。网络也要畅通以便下载必要的工具和软件包。3. 核心细节解析与实操要点这一部分我们深入到几个最容易出问题也最影响镜像质量的关键环节看看具体怎么做以及为什么要这么做。3.1 虚拟磁盘创建与初始安装的“坑”安装系统听起来是基础操作但为云镜像安装系统有特殊要求。首先创建磁盘。我使用命令qemu-img create -f raw /var/lib/libvirt/images/openEuler-22.03.raw 10G创建了一个10GB的raw格式原始磁盘。为什么先用raw格式因为在安装和初始操作阶段raw格式性能更好兼容性问题最少。等到所有配置都完成后我们再把它压缩转换成qcow2。接下来是使用virt-install进行无人值守安装。这是第一个关键点。如果像平时一样手动交互安装效率太低且无法自动化。我们需要使用Kickstart对于openEuler它继承自RHEL系支持很好或AutoYast对于SUSE系这样的应答文件。我编写了一个精简的Kickstart文件ks.cfg核心内容包括分区方案我采用了云环境常见的简单分区一个大的根分区/和一个交换分区swap。不需要单独的/boot因为云实例通常不需要复杂的多系统引导。软件包选择只安装base和core这两个最基础的包组以及一个至关重要的包cloud-init。cloud-init是云镜像的“灵魂”它负责在实例首次启动时从OpenStack的Metadata服务获取并应用网络配置、主机名、SSH密钥、用户数据等。没有cloud-init的镜像在OpenStack里就是个“半成品”。网络与防火墙在Kickstart中配置网络为DHCP并暂时禁用防火墙firewalld和SELinux。因为在云环境中网络由云平台管理安全主要由安全组控制镜像内过于严格的默认设置可能导致实例初始化失败。root密码设置一个临时密码或者留空不推荐。最佳实践是在cloud-init配置中禁止密码登录仅使用SSH密钥。然后使用如下命令启动自动化安装virt-install \ --name openEuler-builder \ --memory 2048 \ --vcpus 2 \ --disk path/var/lib/libvirt/images/openEuler-22.03.raw,formatraw \ --network networkdefault \ --os-type linux \ --os-variant openeuler22.03 \ --location /path/to/openEuler-22.03-LTS-x86_64-dvd.iso \ --extra-args “inst.ksfile:///path/to/ks.cfg consoletty0 consolettyS0,115200n8” \ --graphics none \ --noautoconsole这里有几个要点--graphics none和console参数配置是为了支持串口控制台这在无图形界面的服务器环境和云平台中非常重要。--noautoconsole让安装后台进行。3.2 驱动与内核模块性能的基石安装完系统后镜像还无法在云平台发挥最佳性能。OpenStack底层默认使用KVM虚拟化其高性能的虚拟设备如网卡、磁盘遵循virtio标准。因此我们必须确保镜像内包含了对应的驱动。磁盘驱动virtio-blk幸运的是openEuler 22.03 LTS的内核默认已经编译了virtio_blk驱动所以对于根文件系统一般无需额外操作。网卡驱动virtio-net同样内核也包含了virtio_net。但为了确保万无一失可以在镜像中显式安装kernel-modules-extra包它包含了更多不常用的内核模块。半虚拟化驱动virtio-pci这是virtio设备的底层PCI驱动也必须存在。如何检查我们可以使用guestfish工具在不启动虚拟机的情况下“潜入”磁盘镜像内部进行检查和操作guestfish --ro -a /var/lib/libvirt/images/openEuler-22.03.raw -i lsmod | grep virtio如果发现缺少关键驱动就需要在镜像内部安装对应的kernel-module包并确保initramfs镜像也包含了这些驱动。更新initramfs的命令需要在镜像内执行dracut -f –add-drivers “virtio virtio_pci virtio_blk virtio_net”。3.3 Cloud-Init的深度配置让镜像“活”起来cloud-init的配置是云镜像制作的重中之重。它的配置文件通常位于/etc/cloud/cloud.cfg及其cloud.cfg.d/子目录下。我们需要定制这个文件使其更好地适配OpenStack和我们的运维习惯。一个常见的优化配置片段我们可以将其制作成一个文件用virt-customize注入到镜像的/etc/cloud/cloud.cfg.d/99_openstack.cfg中如下# 设置默认用户为 openeuler (openEuler的默认用户名) system_info: default_user: name: openeuler lock_passwd: true # 锁定密码禁止密码登录 gecos: openEuler Cloud User groups: [wheel, adm] sudo: [“ALL(ALL) NOPASSWD:ALL”] # 为方便配置sudo无需密码生产环境应收紧 shell: /bin/bash ssh_authorized_keys: [] # 密钥由cloud-init从metadata服务获取后注入 # 配置数据源OpenStack使用ConfigDrive和NoCloudHTTP Metadata datasource_list: [‘ConfigDrive’, ‘NoCloud’] datasource: ConfigDrive: dsmode: local NoCloud: fs_label: “config-2” # 这是OpenStack挂载metadata的卷标签 # 禁用不必要的数据源加速启动 disable_root: true # 禁用root登录 ssh_pwauth: false # 禁用SSH密码认证强制使用密钥 manage_etc_hosts: localhost # 如何管理/etc/hosts preserve_hostname: false # 不保留镜像中的主机名使用metadata提供的 cloud_init_modules: - migrator - bootcmd - write-files - growpart - resizefs - set_hostname - update_hostname - update_etc_hosts - users-groups - ssh cloud_config_modules: - mounts - ssh-import-id - locale - set-passwords - timezone - disable-ec2-metadata - runcmd cloud_final_modules: - scripts-per-once - scripts-per-boot - scripts-per-instance - scripts-user - ssh-authkey-fingerprints - keys-to-console - final-message关键解释lock_passwd: true和ssh_pwauth: false是重要的安全设置强制使用SSH密钥认证。datasource_list指定了cloud-init从哪里获取元数据。OpenStack通常同时提供ConfigDrive一个模拟的配置光盘和HTTP Metadata服务两者都配置上兼容性最好。growpart和resizefs这两个模块非常实用它们允许实例启动时自动扩展根分区以填满创建实例时分配的系统盘大小。比如你用这个10G的镜像创建了一个100G根盘的实例有了这个配置根文件系统会自动扩容到100G无需手动操作。4. 实操过程与核心环节实现下面我将把整个制作过程串联起来形成一个可一键执行的脚本化流程为了可读性省略了部分错误检查。4.1 阶段一基础系统安装准备环境与资源# 假设工作目录为 /data/image_build ISO_PATH“/data/isos/openEuler-22.03-LTS-x86_64-dvd.iso” IMAGE_NAME“openEuler-22.03” IMAGE_RAW“${IMAGE_NAME}.raw” IMAGE_QCOW2“${IMAGE_NAME}.qcow2” KS_FILE“./ks.cfg” # 安装必要工具在构建机上 sudo yum install -y qemu-img libvirt virt-install libguestfs-tools-c sudo systemctl start libvirtd创建磁盘与Kickstart文件# 创建20G的raw磁盘比最终镜像大留出操作空间 qemu-img create -f raw ${IMAGE_RAW} 20G # 编写ks.cfg文件内容参考3.1节此处略 cat ${KS_FILE} ‘EOF’ # … Kickstart配置内容 … EOF执行无人值守安装sudo virt-install \ --name ${IMAGE_NAME}-builder \ --memory 4096 \ --vcpus 2 \ --disk path${IMAGE_RAW},formatraw \ --network networkdefault,modelvirtio \ --os-type linux \ --os-variant openeuler22.03 \ --location ${ISO_PATH} \ --initrd-inject ${KS_FILE} \ --extra-args “inst.ksfile:///ks.cfg inst.repofile:///mnt/iso consoletty0 consolettyS0,115200n8” \ --graphics none \ --noautoconsole \ --wait -1安装完成后虚拟机会自动关闭。使用sudo virsh undefine ${IMAGE_NAME}-builder清理临时虚拟机定义。4.2 阶段二镜像定制与云化配置现在我们有了一个安装了基础系统和cloud-init的raw磁盘镜像。接下来进行深度定制。安装必要软件包与驱动# 使用 virt-customize 进行批量操作 sudo virt-customize -a ${IMAGE_RAW} \ --run-command ‘dnf makecache’ \ --install qemu-guest-agent,cloud-init,cloud-utils-growpart,bash-completion,acpid,net-tools \ --update \ --selinux-relabelqemu-guest-agent安装QEMU Guest Agent它运行在实例内部可以向宿主机报告实例的IP、主机名、磁盘使用情况等信息在OpenStack Horizon控制台能看到这些信息也支持在线调整磁盘。cloud-utils-growpart这是growpart模块的依赖确保分区扩容功能正常。acpid用于响应虚拟机的电源操作如软关机。注入Cloud-Init配置# 将前面写好的99_openstack.cfg注入 sudo virt-customize -a ${IMAGE_RAW} \ --upload 99_openstack.cfg:/etc/cloud/cloud.cfg.d/配置SSH服务sudo virt-customize -a ${IMAGE_RAW} \ --run-command “sed -i ‘s/^#PermitRootLogin.*/PermitRootLogin no/’ /etc/ssh/sshd_config” \ --run-command “sed -i ‘s/^PasswordAuthentication.*/PasswordAuthentication no/’ /etc/ssh/sshd_config” \ --run-command “systemctl enable sshd cloud-init cloud-config cloud-final cloud-init-local”这里直接修改了SSH配置禁止root登录和密码认证并确保相关服务开机自启。4.3 阶段三清理与通用化这是制作模板镜像最关键的一步目的是消除镜像的唯一性信息避免由此引发的网络冲突、主机名混淆等问题。使用virt-sysprep进行深度清理sudo virt-sysprep -a ${IMAGE_RAW} \ --operations defaults,-ssh-userdir \ --network \ --hostname “localhost.localdomain” \ --run-command “truncate -s 0 /etc/machine-id” \ --run-command “rm -f /var/lib/dbus/machine-id ln -s /etc/machine-id /var/lib/dbus/machine-id”–operations defaults执行一系列默认清理操作如清除日志文件、临时文件、yum缓存等。–network清除网络设备持久化规则如70-persistent-net.rules防止网卡MAC地址绑定。–hostname重置主机名。清理machine-id这个ID在系统内应该是唯一的。如果不清理所有从这个镜像创建的虚拟机都会有相同的machine-id可能导致某些依赖此ID的应用程序如Docker、某些集群软件出现问题。dbus的机器ID是/etc/machine-id的软链接也需要处理。清理Shell历史与缓存sudo virt-customize -a ${IMAGE_RAW} \ --run-command “rm -rf /var/cache/dnf/* /tmp/* /var/tmp/*” \ --run-command “rm -f /root/.bash_history /home/*/.bash_history” \ --run-command “journalctl –rotate journalctl –vacuum-time1s” # 清理系统日志4.4 阶段四格式转换与压缩最后我们将优化后的raw镜像转换为OpenStack推荐的qcow2格式并进行空间压缩。# 将raw格式转换为qcow2格式并进行稀疏文件优化 qemu-img convert -c -O qcow2 ${IMAGE_RAW} ${IMAGE_QCOW2} # 检查最终镜像信息 qemu-img info ${IMAGE_QCOW2}-c参数表示进行压缩可以显著减小镜像文件体积。转换完成后原始的raw文件可以删除。现在得到的openEuler-22.03.qcow2就是我们的最终产品了。5. 常见问题与排查技巧实录制作过程中难免会遇到各种问题这里记录几个典型的“坑”和解决方法。5.1 实例启动后网络不通或无法获取IP这是最常见的问题之一。排查思路检查cloud-init日志实例启动后立即通过VNC控制台或日志查看/var/log/cloud-init.log和/var/log/cloud-init-output.log。关注是否有datasource识别错误、网络配置应用失败等信息。检查网络服务确认network.service或NetworkManager服务是否正常运行。openEuler 22.03默认使用NetworkManager。检查/etc/sysconfig/network-scripts/下的网卡配置文件是否被正确生成。检查驱动确认virtio_net内核模块已加载 (lsmod | grep virtio)。我的踩坑记录 有一次制作镜像时为了精简我移除了NetworkManager只保留了network-scripts。结果在某个OpenStack版本上因为metadata路由配置方式不同实例无法通过169.254.169.254获取元数据。教训是除非有充分把握否则不要随意替换或移除发行版默认的网络管理工具。openEuler与NetworkManager集成度更好保持默认即可。快速修复命令在镜像定制阶段注入sudo virt-customize -a ${IMAGE_RAW} \ --run-command “systemctl enable NetworkManager” \ --run-command “echo ‘NOZEROCONFyes’ /etc/sysconfig/network” # 避免某些情况下产生169.254.0.0/16的无效地址5.2 Cloud-Init执行失败用户密钥未注入表现为使用SSH密钥对无法登录实例。排查思路检查metadata服务在实例内部尝试curl -s http://169.254.169.254/openstack/latest/meta_data.json。如果无法连接或返回错误说明实例无法访问OpenStack的metadata服务。检查安全组规则是否放行了实例对169.254.169.254的访问、以及底层网络如Neutron的dhcp-agent是否配置了enable_isolated_metadata True。检查cloud-init配置确认/etc/cloud/cloud.cfg中ssh_pwauth是否为false以及datasource_list是否包含ConfigDrive, NoCloud。检查公钥文件查看/home/openeuler/.ssh/authorized_keys文件是否存在且内容正确。cloud-init会将metadata中的公钥写入此文件。一个隐蔽的坑SELinux。如果SELinux处于强制模式Enforcing且.ssh目录或authorized_keys文件的上下文不正确可能导致SSH服务拒绝读取密钥。可以在cloud-init的runcmd模块中添加命令临时放宽限制或在镜像中设置为宽容模式setenforce 0但更好的做法是确保文件上下文正确。在镜像定制时可以执行restorecon -Rv /home/openeuler/.ssh。5.3 镜像上传后创建实例非常慢可能原因镜像格式如果上传的是raw格式Glance会先将其转换为qcow2如果后端存储支持的话首次创建实例时会有一个转换过程。最佳实践是上传前就转换为qcow2。镜像体积过大虽然我们做了清理但如果初始安装的软件包太多或者包含了调试符号、文档等镜像体积会很大。使用virt-sparsify工具可以进一步“瘦身”它能够识别并丢弃磁盘镜像中未使用的块。sudo virt-sparsify –compress ${IMAGE_QCOW2} ${IMAGE_QCOW2}.sparse mv ${IMAGE_QCOW2}.sparse ${IMAGE_QCOW2}Glance后端存储性能如果使用文件系统后端且磁盘IO性能差也会影响速度。这属于平台运维层面问题。5.4 实例根分区未自动扩容即使配置了growpart和resizefs有时分区也不会扩容。排查步骤检查cloud-init日志看growpart和resizefs模块是否执行是否有报错。检查内核是否支持在线扩容。对于xfs文件系统需要内核和xfsprogs版本支持。最常见原因分区表类型。growpart工具对MBR分区表支持较好但对GPT分区表需要确保磁盘末尾有足够的空闲空间。在创建镜像磁盘时如果分区没有对齐到扇区末尾可能会留出几兆的空间导致growpart认为没有空间可扩。解决方案在Kickstart分区时使用part / –fstype“xfs” –ondiskvda –size8192 –grow中的–grow参数让根分区占据所有剩余空间而不是指定一个固定值。制作一个高质量的云镜像是一个融合了系统知识、云平台理解和运维经验的细致活。从最开始的自动化安装到中间的驱动、服务配置再到最后的深度清理和优化每一步都需要仔细考量。上面分享的流程和配置是我经过多次测试和线上验证后总结出来的已经能够稳定地产出符合生产环境要求的openEuler 22.03镜像。当然根据不同的业务场景你可能还需要在镜像中预装监控Agent、日志采集组件、或者特定的业务运行环境这些都可以在virt-customize的–install和–upload阶段轻松加入。希望这份超详细的指南能帮你少走弯路高效地构建出自己的“黄金镜像”。

相关新闻

最新新闻

大模型Function Calling实战:从原理到架构,构建高效智能体系统

大模型Function Calling实战:从原理到架构,构建高效智能体系统

1. 项目概述:从“聊天”到“做事”的范式跃迁 如果你最近在折腾大模型应用开发,尤其是想搞点能真正“干活”的智能体,那“Function Calling”这个词你一定绕不过去。它听起来有点技术化,但说白了,就是让大模型从一个“…

2026/8/13 12:44:36
清华开源AI智能体OpenClaw:从零部署到实战应用全指南

清华开源AI智能体OpenClaw:从零部署到实战应用全指南

1. 项目概述:当AI私人导师走进你的电脑 最近在AI圈子里,一个由清华团队开源的项目 OpenMAIC 和它的核心执行引擎 OpenClaw 讨论度很高。简单来说,你可以把它理解为一个能帮你“干活”和“学习”的AI私人助理。它不是另一个聊天机器人&…

2026/8/13 12:44:36
SPT-AKI 存档编辑器使用指南:如何快速搞定塔科夫离线版角色、商人、任务与技能

SPT-AKI 存档编辑器使用指南:如何快速搞定塔科夫离线版角色、商人、任务与技能

SPT-AKI 存档编辑器使用指南:如何快速搞定塔科夫离线版角色、商人、任务与技能 【免费下载链接】SPT-AKI-Profile-Editor Программа для редактирования профиля игрока на сервере SPT-AKI 项目地址: https://g…

2026/8/13 12:44:36
继电器原理、选型与应用全解析:从电磁驱动到电路设计实战

继电器原理、选型与应用全解析:从电磁驱动到电路设计实战

1. 从“开关”到“桥梁”:继电器到底是什么?如果你拆开过家里的老式空调遥控器、汽车的控制盒,或者任何一个工业控制柜,大概率会看到一些黑色或透明的小方块,上面印着“12VDC”、“10A 250VAC”之类的字样,…

2026/8/13 12:44:36
Windows Defender完全移除终极指南:3种模式轻松提升系统性能

Windows Defender完全移除终极指南:3种模式轻松提升系统性能

Windows Defender完全移除终极指南:3种模式轻松提升系统性能 【免费下载链接】windows-defender-remover A tool which is uses to remove Windows Defender in Windows 8.x, Windows 10 (every version) and Windows 11. 项目地址: https://gitcode.com/gh_mirro…

2026/8/13 12:44:36
股权激励行权条件解析Python实战识别假激励信号

股权激励行权条件解析Python实战识别假激励信号

股权激励行权条件解析:Python 实战识别"假激励"信号 去年下半年我把所有 A 股的股权激励方案拉出来做系统性分析,371 家公司,做了一个反常识的发现——股权激励行权条件越低的股票,未来 3 个月的平均涨幅反而越小&#…

2026/8/13 12:39:36