Kubespray KubeVirt 镜像构建器实战:为 CI 构建并推送 KubeVirt 虚拟机磁盘镜像
【免费下载链接】kubesprayDeploy a Production Ready Kubernetes Cluster项目地址: https://gitcode.com/GitHub_Trending/ku/kubespray
本文基于 Kubespray 仓库中 test-infra/image-builder/README.md 展开,系统讲解 Kubespray CI 使用的 KubeVirt 虚拟机磁盘镜像构建器:它如何下载上游云镜像、转换为 qcow2、扩容并打包为容器镜像推送至镜像仓库。读完本篇,你可以完整复现"新增一个操作系统镜像、本地验证、构建推送"的全流程,并理解 Makefile 中各目标与 Ansible 角色参数之间的对应关系。
工作原理
KubeVirt Image Builder 位于 test-infra/image-builder/,其定位是为 Kubespray CI 测试构建并推送 KubeVirt 虚拟机磁盘镜像。整体流程由 Makefile 驱动 Ansible playbook cluster.yml 完成,playbook 会调用唯一的核心角色 kubevirt-images:
- 下载:从上游源拉取各操作系统的 cloud image(原始
.img或.qcow2,部分带.xz压缩); - 转换:非 qcow2 格式(如 Ubuntu 的 raw
.img)通过qemu-img convert -O qcow2转为 qcow2; - 扩容:对每块镜像执行
qemu-img resize +8G,为 CI 中的系统安装与测试留出磁盘空间; - 打包:将 qcow2 文件作为
cloud_image构建参数注入 Dockerfile,基于kubevirt/registry-disk-v1alpha基础镜像构建容器镜像; - 推送:默认推送为
quay.io/kubespray/vm-<os-name>:<tag>。受信 CI 任务可通过覆盖 registry 目标实现镜像的分阶段发布(staged publishing)。
Dockerfile 模板极简,见 templates/Dockerfile:
FROM kubevirt/registry-disk-v1alpha ARG cloud_image LABEL org.opencontainers.image.authors="The Kubespray Project <kubespray@googlegroups.com>" COPY $cloud_image /disk即把转换好的磁盘文件直接拷入基础镜像的/disk,KubeVirt 的 registry disk data volume 机制正是通过这种"磁盘即容器镜像"的方式给 VM 挂载磁盘。
受信的分阶段发布路径使用 Cloud Build 认证并跳过docker login:
make push-single-staging image_name=ubuntu-2404前置条件
按 README 要求,构建环境需要:
- Docker、
qemu-img(来自 qemu 工具包)、Ansible; - 对 quay.io 上 kubespray 组织的推送权限(robot 账号
kubespray+buildvmimages)。
角色任务在开始构建前会实际校验这些依赖:tasks/main.yml 中依次执行qemu-img --version(必检)与docker --version(仅当kubevirt_container_builder == 'docker'时检查),BuildKit 路径则额外探测buildctl-daemonless.sh/buildctl+buildkitd的可用性。
镜像定义:所有 OS 的单一事实来源
所有操作系统镜像定义集中在 roles/kubevirt-images/defaults/main.yml。每个条目包含以下字段:
| 字段 | 说明 |
|---|---|
filename | 下载后的文件名 |
url | 上游 cloud image 下载地址 |
checksum | 用于下载校验的校验和(sha256:或sha512:前缀) |
converted | 源镜像已是 qcow2 时为true,需要qemu-img convert时为false |
tag | Docker 镜像标签(通常为latest) |
当前定义覆盖 24 个镜像,例如:
ubuntu-2404: filename: ubuntu-24.04-server-cloudimg-amd64.img url: https://cloud-images.ubuntu.com/releases/noble/release-20260826/ubuntu-24.04-server-cloudimg-amd64.img checksum: sha256:d0fe84bb5f80853425fa6be28e2c106f30104c3cfe8611933f2e65c9b63f0e30 converted: false tag: "latest" fedora-43: filename: Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2 url: https://download.fedoraproject.org/pub/fedora/linux/releases/43/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-43-1.6.x86_64.qcow2 checksum: sha256:846574c8a97cd2d8dc1f231062d73107cc85cbbbda56335e264a46e3a6c8ab2f converted: true tag: "latest"可以从字段取值看出几个实践要点:
- Ubuntu 官方云镜像是 raw 格式的
.img,因此converted: false,角色会执行qemu-img convert;Fedora、Rocky、openEuler 等已是 qcow2,converted: true,角色只做复制; - 校验和前缀随上游提供格式而定:Ubuntu/Fedora/Rocky/openEuler 用
sha256:,Debian 上游提供sha512:,Flatcar 也是sha512:; - 带
.xz压缩的镜像(如 Fedora CoreOS、openEuler)会被角色中的unxz --force步骤自动解压,解压后再按converted标志处理。
使用方式
构建并推送全部镜像
cd test-infra/image-builder/ make docker_password=<quay-robot-token>Makefile 中的deploy目标会调用ansible-playbook -i hosts.ini,把docker_host、docker_login、docker_user、docker_password、docker_password、registry作为额外变量注入 cluster.yml。其中 hosts.ini 定义了一台远程构建主机:
image-builder-1 ansible_ssh_host=xxx.xxx.xxx.xxx [image-builder] image-builder-1playbook 通过hosts: "{{ kubevirt_images_target_host | default('image-builder') }}"指定目标,因此这条deploy路径是面向远程 builder 主机的(需要 SSH 可达)。
新增一个操作系统镜像
在 defaults/main.yml 中添加条目:
new-os-name: filename: cloud-image-file.qcow2 url: https://example.com/cloud-image-file.qcow2 checksum: sha256:<hash> converted: true tag: "latest"构建并推送该镜像:
make docker_password=<quay-robot-token>如果只想构建单个镜像,用
make push-single-docker image_name=new-os-name即可(对应 Makefile 中通过kubevirt_images_selected过滤)。提交包含
defaults/main.yml变更的 PR,使 CI 可以使用新镜像(上游有真实示例 PR #12379 可供参考)。
Makefile 目标全解
Makefile 顶部定义了默认变量,理解它们是理解全部目标的关键:
docker_host ?= quay.io docker_login ?= true docker_user ?= kubespray+buildvmimages registry ?= quay.io/kubespray staging_registry ?= us-central1-docker.pkg.dev/k8s-staging-images/kubespray各目标对应的行为:
| 目标 | 执行方式 | 用途 |
|---|---|---|
deploy | -i hosts.ini,推送至远程 builder | 远程构建全部镜像并推送 |
push-docker | localhost, -c local,kubevirt_images_push=true,builder=docker | 本地构建全部镜像并推送 |
push-single-docker | 同上 +kubevirt_images_selected=["$(image_name)"] | 本地构建并推送单个镜像 |
push-single-staging | 强制docker_host=us-central1-docker.pkg.dev、registry=$(staging_registry)、docker_login=false | 受信分阶段发布到 Google Artifact Registry,走 Cloud Build 认证、跳过 docker login |
validate/validate-single | kubevirt_images_push=false,builder=buildkit,输出目录.image-builder/buildkit-output | CI 本地验证(只构建,不推送) |
validate-docker/validate-single-docker | kubevirt_images_push=false,builder=docker | 用 Docker 路径本地验证 |
validate这条路径在本地运行并使用 BuildKit,因此不依赖 SSH 访问远程构建主机,也不依赖 Docker daemon——这正是 README 中 "CI Validation" 一节强调的特性:
cd test-infra/image-builder/ make validate # 仅构建全部镜像 make validate-single image_name=ubuntu-2404 # 仅构建单个镜像从源码结构看,"不推送"的实现是:BuildKit 构建的输出从type=image,...,push=true切换为type=oci,dest=<输出目录>/vm-<key>-<tag>.tar(见 tasks/main.yml),即以 OCI tar 落地到本地目录完成验证。
运行时变量
三个决定行为模式的关键变量及其默认值(定义于 defaults/main.yml):
kubevirt_images_push(默认true):置为false时跳过 docker login/push/logout;kubevirt_images_selected(默认[]):要构建的镜像键列表,空列表表示构建全部;kubevirt_container_builder(默认docker):设为buildkit可在没有 Docker daemon 访问权限的本地 CI 中做验证。
此外还有几个配套变量:
images_dir(默认/images/base):磁盘镜像的工作目录;docker_user(默认kubespray+buildvmimages)、docker_host(默认quay.io)、docker_login(默认true)、registry(默认quay.io/kubespray);kubevirt_buildkit_output_dir(默认{{ images_dir }}/buildkit-output):BuildKit 验证模式的 OCI tar 输出位置,Makefile 的 validate 目标将其覆盖为$(CURDIR)/.image-builder/buildkit-output。
角色开头有三道参数断言,可以快速定位配置错误:
- 选中的镜像名必须能在
images中找到,否则报No matching images found; kubevirt_container_builder只能是docker或buildkit;- BuildKit 模式当前要求
kubevirt_images_push=false("BuildKit validation currently requires kubevirt_images_push=false")。
构建任务的执行细节
角色任务 tasks/main.yml 的执行顺序值得逐条过一遍,它能解释每个配置字段的实际用途:
- 过滤:
kubevirt_images_to_build依据kubevirt_images_selected是否为空来决定是取全部images还是community.general.keep_keys过滤后的子集——这就是单个镜像构建的底层实现; - 依赖检查:
qemu-img --version、按 builder 检查 Docker 或 BuildKit; - 下载:
get_url直接带checksum校验下载,任何上游文件变化都会在此失败; - 解压:
unxz --force,仅当文件名以.xz结尾时执行; - 转换或复制:
converted: false时执行qemu-img convert -O qcow2 <源文件> <镜像名>.qcow2;converted: true时直接cp为<镜像名>.qcow2。最终所有镜像统一以"镜像键 + .qcow2"命名; - 扩容:
qemu-img resize <镜像名>.qcow2 +8G; - 构建容器镜像:先渲染 Dockerfile 模板到
images_dir/Dockerfile,再按 builder 分支——Docker 路径执行docker build -t {{ registry }}/vm-<key>:<tag> --build-arg cloud_image=<key>.qcow2;BuildKit 路径优先使用buildctl-daemonless.sh(rootless 模式附加--rootless --oci-worker-no-process-sandbox --oci-worker-snapshotter=native标志),否则临时拉起buildkitd守护进程并通过独立 socket 执行构建,失败时还会尝试回退到docker build(未推送时以docker save落 tar); - 推送:仅当 builder 为
docker且kubevirt_images_push=true时执行docker login→ 逐个docker push {{ registry }}/vm-<key>:<tag>→docker logout。
一个值得注意的边界:BuildKit 路径本身不会走 docker login/push 分支,而 Makefile 的push-single-staging目标使用的仍是kubevirt_container_builder: "docker"——即分阶段发布依赖 Docker 客户端与 Artifact Registry 的直连认证,而不是 BuildKit 推送。
受信分阶段发布(Staging Publish)
push-single-staging目标的设计体现在 Makefile:
push-single-staging: ansible-playbook -i localhost, -c local \ -e images_dir=$(CURDIR)/.image-builder \ -e docker_host=us-central1-docker.pkg.dev \ -e registry=$(staging_registry) \ -e '{"docker_login": false, "kubevirt_images_push": true, "kubevirt_container_builder": "docker", "kubevirt_images_target_host": "localhost", "kubevirt_images_selected": ["$(image_name)"]}' \ cluster.yml要点是docker_host被硬切换为us-central1-docker.pkg.dev、registry 切换到staging_registry(默认us-central1-docker.pkg.dev/k8s-staging-images/kubespray),且docker_login: false——认证由 Cloud Build 环境自身提供的凭据完成。
配套的 cloudbuild-staging.yaml 给出了受信任务的完整形态:在 7200 秒超时内,使用 GCB 的 docker-gcloud 基础镜像,安装ansible-core qemu-img与community.generalcollection 后执行:
make -C test-infra/image-builder push-single-staging \ image_name=ubuntu-2404 \ staging_registry=us-central1-docker.pkg.dev/$PROJECT_ID/kubespray该配置的images字段声明了本次构建产出的制品us-central1-docker.pkg.dev/$PROJECT_ID/kubespray/vm-ubuntu-2404:latest,即通过staging_registry变量把发布目标参数化到具体项目名下,实现"先发到 staging 命名空间、验证通过后再由有权任务发到 quay.io"的分阶段发布流程。
总结
Kubespray 的 KubeVirt Image Builder 用一套非常薄的 Ansible 机制(一个 playbook、一个角色、五个 Makefile 目标)解决了 CI 的"裸金属操作系统"供给问题:以 defaults/main.yml 为单一事实来源声明镜像清单,qemu-img完成格式转换与扩容,kubevirt/registry-disk-v1alpha把磁盘封装为容器镜像,再由 Docker/BuildKit 双路径完成构建与推送。新增操作系统只需修改清单文件并走push-single-docker或 staging 通道,本地开发则可用make validate-single在不依赖 Docker daemon 和远程主机的情况下完成端到端验证。
【免费下载链接】kubesprayDeploy a Production Ready Kubernetes Cluster项目地址: https://gitcode.com/GitHub_Trending/ku/kubespray
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考