最近接了个咨询,对方公司IT运维一共就四个人,机房里既有VMware这套虚拟化平台,又有Kubernetes容器集群。业务跑在两层“云”上——数据库在虚拟机里,微服务在容器里,日常排查问题要在两个控制台之间来回切换,权限、监控、日志、备份全部是两套。他问我一句话:“能不能只维护一套平台,把VM和容器都管起来?”
答案是能。这篇文章就围绕VM与容器两套平台统一管理这件事做一次完整梳理,从“两套平台为什么折腾”开始,讲到统一架构的设计思路,再到KubeVirt这类容器原生虚拟化方案如何落地,最后给出迁移实操步骤和避坑清单。适合正在纠结“继续加VM还是全面转容器”的运维朋友,也适合刚接手混合环境、想给团队减负的同行参考。先说结论:统一管理不是要把虚拟机全干掉,而是用一套控制面、一套权限、一套监控、一套发布流程,把两种负载都管理好。
1. 为什么会有“两套平台”这种别扭局面
1.1 VM和容器到底差在哪
很多人觉得VM和容器是淘汰关系,其实不是。它们解决问题的层次不同,差异可以从几个维度看清楚。
| 维度 | VM(虚拟机) | 容器(Container) |
|---|---|---|
| 资源隔离边界 | 虚拟硬件层,独立内核 | 进程级隔离,共享宿主内核 |
| 启动速度 | 分钟级 | 秒级 |
| 镜像分发 | 依赖模板和快照 | 依赖镜像仓库,分层复用 |
| 可移植性 | 受虚拟化平台绑定 | 云原生生态更开放 |
| 适合业务 | 有状态、强隔离、老系统 | 无状态、弹性扩缩、微服务 |
容器强在资源效率和密度,几十个Pod可以共享一台物理机的内核;虚拟机强在隔离彻底,老系统需要特定的内核版本、驱动或者Windows桌面环境时,容器根本替代不了。实际操作中,两者通常不是二选一,而是并存互补。
1.2 企业里真实存在的“混合态”
很多企业现在的状态是:数据库跑在VM上,中间件跑在VM上,新业务用微服务架构跑在K8s上,测试环境还有一批Windows虚拟机。这种混合态不是拍脑袋决定的,而是业务约束的结果。
核心业务不能随便容器化,因为涉及有状态数据、持久化存储、网络改造,冒然迁移风险太大;容器平台虽然调度能力强,但面对老旧的Linux版本、特殊内核模块、特定外设依赖时,还是虚拟机更稳。另外,团队技能也是现实因素,如果运维平时只接触VMware,突然要求全面容器化,学习成本和试错成本都高,所以更常见的路径是“新增业务用容器,存量业务继续留VM”。
1.3 两套平台带来的真实痛点
痛点不是“多装了一个软件”,而是整个运维链条被劈成两半。
首先是技能栈分裂。同一个运维工程师,上午要会看VMware集群的HA和DRS,下午要会看K8s的Pod调度和镜像构建,大脑来回切换很容易疲劳。
其次是权限审批双通道。VM那边一套AD域控、一套虚拟化平台账号,容器那边一套Kubernetes RBAC、一套镜像仓库权限,开通一个人要跑两三个流程。
最让人头疼的是故障排查。有一次线上微服务大面积超时,我先打开K8s Dashboard看到Pod一直重启,切到VMware客户端看数据库虚拟机CPU高,再分头查Prometheus和vCenter告警,最后才发现是VM的磁盘IO饱和拖垮了数据库。同一个问题要开四五个窗口,日志对不上、监控对不上、告警时间也对不上,定位全靠猜。
2. 一套架构统一管理的设计思路
2.1 先想清楚:统一到什么层
很多人一听“统一管理”,第一反应是做一个聚合页面,把两个控制台的入口放到一个域名下。这个思路只能说治标不治本,因为底层API、权限、资源模型仍然是两套,运维还是得记两套命令、配两套权限。
真正的统一管理,要从五个维度同时收敛:控制面统一,资源模型统一,身份权限统一,监控日志统一,交付流程统一。用表格展开看更清楚。
| 维度 | 统一前 | 统一后 |
|---|---|---|
| 控制面 | VMware vCenter + K8s API 两套 | 一套Kubernetes API |
| 资源模型 | VM模板 + 容器镜像 | 统一的工作负载抽象 |
| 身份权限 | 虚拟化平台账号 + K8s RBAC | 一套OIDC/LDAP映射 |
| 监控日志 | vCenter告警 + Prometheus | 集中指标和日志收集 |
| 交付流程 | 手工克隆模板 + CI/CD | GitOps声明式交付 |
做到这些,才算真的“一套架构”。
2.2 三条方案路线对比
我梳理过市面上的主流方案,大体有三条路线。
路线A:容器原生虚拟化,代表是KubeVirt和OpenShift Virtualization。思路是把虚拟机当成Kubernetes的一种自定义资源,由K8s统一调度、统一管理。最大优势是如果团队已经跑着K8s,学习曲线很平滑,API、RBAC、网络插件都是现成的。
路线B:云管平台CMP,比如一些商业化云管理平台兼容纳管VMware和K8s。这类产品适合多环境统一展示,但往往停留在“两级管理界面”的层面,底层还是两个控制面。
路线C:自研资源抽象层,在VM和容器之上自己写一套调度和运维系统。这种方案听着很酷,但真正做下来要同时搞定两套底层接口、两套故障域、两套账号体系,开发量巨大且后期维护非常痛苦,我基本不推荐。
三条路线放在一起对比:
| 路线 | 建设成本 | 统一程度 | 学习曲线 | 生态成熟度 |
|---|---|---|---|---|
| KubeVirt容器原生虚拟化 | 中等 | 高 | 中 | 较高 |
| 云管平台CMP | 高(商业采购) | 中 | 中 | 高 |
| 自研抽象层 | 极高 | 取决于实现 | 陡峭 | 低 |
2.3 统一架构的目标状态
我习惯把目标架构画成几层:
- 统一控制层:Kubernetes API,对外提供所有负载的增删改查。
- 资源模型层:Pod、VirtualMachine、Namespace一视同仁,都是K8s对象。
- 调度执行层:根据资源请求、亲和性、污点调度到底层节点。
- 资源池层:物理机启用KVM虚拟化,同时运行容器运行时和虚拟机组件。
在这个架构里,VM和Pod共用一套网络插件,共用一套存储接口,共用一套身份管理。运维创建一个数据库虚拟机,和创建一个Nginx Deployment,用的都是kubectl,权限都走K8s RBAC,日志都进同一个收集管道。用户不需要关心对象背后是虚拟化技术还是容器运行时,只需要看到“业务对象”和“运行状态”。
3. 核心细节解析与实操要点
3.1 KubeVirt:把VM变成Kubernetes资源
KubeVirt的核心机制是通过CRD把虚拟机纳管进K8s。它新增了两类关键资源:VirtualMachine用描述虚拟机模板,VirtualMachineInstance表示实际运行中的虚拟机实例。virt-controller负责编排,virt-handler在节点上执行虚拟机生命周期操作,底层依赖KVM硬件虚拟化。
下面是一段简化后的KubeVirt虚拟机定义:
apiVersion: kubevirt.io/v1 kind: VirtualMachine metadata: name: prod-db-vm namespace: database spec: running: true template: spec: domain: cpu: cores: 4 memory: guest: 8Gi devices: disks: - name: rootdisk disk: bus: virtio interfaces: - name: default masquerade: {} machine: type: q35 networks: - name: default pod: {} volumes: - name: rootdisk persistentVolumeClaim: claimName: prod-db-disk关键参数我展开说一下。cpu.cores和memory.guest定义虚拟机看到的硬件配置,这两个值要和实际业务需要匹配,不能随便写大,否则资源碎片会很严重。disks里指定了bus: virtio,这是性能最好的磁盘模式,但存量虚拟机如果是IDE或SATA磁盘,迁移后要提前装好virtio驱动,否则系统会无法启动。networks里的pod: {}表示虚拟机直接接入K8s的Pod网络,好处是网络策略和DNS规则天然一致;masquerade模式适合对外出方向通信。实际操作中,固定IP需求高的业务建议改用桥接模式,但桥接会带来IP管理成本和二层网络依赖。
3.2 统一网络和存储模型:让VM和Pod一个局域网
两套平台最难统一的其实是网络。传统VM有自己的一套VLAN、分布式交换机;K8s容器用的是CNI插件(常见是Calico、Cilium)。想统一,最稳妥的办法是把网络底座统一到CNI上。
KubeVirt支持几种网络接入方式:默认Pod网络模式,虚拟机像Pod一样获得集群网段IP;桥接模式,VM直接使用节点底层网络;Multus多网卡模式,一块网卡接K8s网络,一块网卡接传统VLAN。我的建议是新区业务直接走Pod网络模式,存量VM迁移初期用桥接过渡,避免大规模改IP导致业务冲突。
存储侧要统一到PVC模型。虚拟机磁盘不再随便放在某个Datastore或本地磁盘上,而是通过StorageClass动态创建PVC。这样快照、备份、迁移都走K8s这套标准。给虚拟机分配PVC时,注意先确认StorageClass的reclaimPolicy和IO性能,数据库这类高IO应用不要图便宜用普通磁盘。
3.3 统一权限与身份认证:只认一套账号
两套平台最容易被忽略的就是权限。VMware里每个管理员账号要单独配权限,K8s里又要单独维护证书和服务账号,忘了收回权限的时候非常头疼。
统一方案是把企业已有的AD/LDAP或OIDC作为身份源,K8s通过OIDC接入,所有管理员和开发都用同一个企业账号登录。KubeVirt虚拟机属于K8s资源后,自动接入RBAC体系,可以精确到Namespace控制谁能创建、删除、访问虚拟机。
需要注意,虚拟机比Pod更容易暴露端口,权限收敛策略要更严格。建议普通开发只给虚拟机控制台权限,不给删除权限;管理员才允许对VirtualMachine做变更操作。至少每季度用kubectl auth can-i --list做一次巡检,比直接在虚拟化平台上手工翻权限靠谱得多。
4. 实操过程:从两套到一套的迁移与落地
4.1 迁移前资产盘点与试点选择
不要一上来就迁移核心业务。先做一份资产盘点表,至少包含五类信息:业务名、当前运行平台、配置规格、网络IP、负责人和业务重要性。把虚拟机按“高依赖、有状态、无状态、可替代”分类。用Excel就能搞定,关键是盘点要细。
试点选择我心里有一条标准:必须是非核心业务,但对团队日常运维有感知价值的系统,比如内部管理系统、测试环境公共组件。最好不要选财务、订单这类一停机就挨骂的业务。试点业务跑通后,再逐步扩大范围,这样整个迁移过程的压力可控。
4.2 部署KubeVirt并打通底层依赖
部署分三步。第一步确认物理节点支持KVM,执行ls -l /dev/kvm,如果没有输出说明没开嵌套虚拟化或没有KVM模块,虚拟机性能会很差。第二步安装KubeVirt Operator和CR,参考命令如下:
export RELEASE=v1.2.0 kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-operator.yaml kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${RELEASE}/kubevirt-cr.yaml kubectl -n kubevirt wait deployment/virt-operator --for=condition=Available --timeout=5m第三步验证组件,执行kubectl get pods -n kubevirt,确认virt-controller、virt-handler、virt-api都处于Running状态。如果发现virt-handler没有调度到某些节点,通常是节点缺KVM模块,可以用kubectl describe node配合系统日志排查。
部署完别急着迁移,先创建一台测试虚拟机,把创建、删除、重启、vnc访问全流程跑一遍,确认基本能力没问题再继续。
4.3 存量VM迁移:从VMware到KubeVirt
存量VM迁移的典型路径是:从VMware导出OVA/OVF,用virt-v2v转换成KubeVirt可识别的镜像格式,再把镜像上传到PVC,最后创建VirtualMachine对象。
转换命令参考:
virt-v2v -i ova myvm.ova -o local -of qcow2 -os /data/images转换完成后,用KubeVirt的virtctl image-upload把镜像上传到PVC:
virtctl image-upload --pvc-name=prod-db-disk --size=100Gi --image-path=/data/images/myvm.qcow2 --storage-class=local-ssd这里有几个坑。第一,VMware导出的虚拟机如果是SATA/IDE磁盘,上传前要手动检查是否含virtio驱动;第二,转换前保证虚拟机已关机并做过快照,避免文件系统不一致;第三,上传时指定--storage-class要和目标存储性能匹配,不要默认落到机械盘上。迁移完成后开机,先验证IP、网络、文件系统挂载情况,再让业务接入流量。
4.4 用GitOps和自动化工具收敛运维操作
统一架构不能只在控制台上“看起来统一”,日常变更也要收口。最可靠的做法是把所有VM和容器配置都声明成YAML,推到Git仓库,再由ArgoCD或Flux自动同步到K8s集群。
Ansible这类自动化运维工具依然有用,但角色变了:不再直接连SSH去改虚拟机里的配置,而是去调kubectl或调用K8s API,把“临时命令”变成“版本化变更”。比如写Ansible playbook定期执行kubectl get vmi -A采集状态,或者通过K8s API回收多余权限。这里的重点不是用什么工具,而是“配置只在仓库里改”,不让人手工在平台上临时敲命令。
5. 常见问题与排查技巧实录
5.1 VM无法调度、启动、迁移问题
KubeVirt上线后,最常见的几类问题我整理成了速查表。
| 问题现象 | 可能原因 | 处理方式 |
|---|---|---|
| 虚拟机一直Pending | 节点没有KVM模块,或CPU模型不兼容 | 检查/dev/kvm,为节点打标签并配置kubevirt.io/schedulable |
| 启动后没有网络 | 网卡模型不对或Pod网络未就绪 | 检查接口类型,改用virtio,确认CNI网络策略放行 |
| 虚拟机迁移卡住 | 源节点和目标节点CPU型号不一致 | 使用统一的CPU型号或开启“CPU静态迁移”策略,目标节点需共享存储 |
| 镜像上传后启动报错 | virtio驱动缺失 | 转换前注入驱动,或在旧平台提前安装virtio |
我在实际中踩得最多的是第一类问题。测试环境里有些旧节点没开嵌套虚拟化,KubeVirt的virt-handler会直接拒绝调度。排查命令可以先用kubectl get vmi <name> -o yaml,看conditions字段有没有LiveMigratable=False之类的提示,再反查节点状态,通常几分钟就能定位。
5.2 容器和VM并存期的网络与DNS冲突
迁移不可能一步到位,总会经历一段时间VM和容器同时服务。这个阶段最常见的故障是DNS解析错乱。容器内默认走CoreDNS,VM走原域控或公司DNS,两边如果都维护同一个业务域名,很容易解析到不同后端的IP,导致流量绕过新平台。
我的处理建议是:并存的过渡期内,用统一命名空间和统一标签管理所有对象,保证同一个业务在VM上的标签和在K8s上的标签一致;DNS调整要放最前面,先把内部域名解析切到统一规则,再逐步下线旧VM的解析记录。另一个容易踩的坑是IP地址冲突,K8s的ClusterIP网段和VM网段必须提前规划好,至少不能在同一个二层网段内重复分配。
5.3 监控告警和日志重复处理
两套平台并存时,监控告警通常是重复的:vCenter在告警,Prometheus也在告警,值班人员同一时间收到两条内容近似的消息,反而容易漏掉真问题。
解决办法是把两层指标汇到同一个监控体系。KubeVirt会暴露虚拟机相关指标,配合node-exporter可以采集宿主机和虚拟机的CPU、内存、磁盘指标;vCenter的事件可以通过对应exporter转成Prometheus格式;日志方面,VM里的journald可以直接接到Loki/ELK这类日志系统,和容器日志进同一个索引。最后用Alertmanager做告警路由,同一类故障只发一条通知,值班体验会好很多。
6. 运维减负的实际收益与经验总结
6.1 算一笔账:效率和人力回报
统一管理到底省不省,我习惯用“运维动作时间”来算账。
| 运维动作 | 原两套平台方式 | 统一后方式 | 人工时间变化 |
|---|---|---|---|
| 开通一个业务环境 | 创建VM再搭容器集群 | Helm/Manifest一键部署 | 从半天缩短到半小时 |
| 排查一次故障 | 切换4个控制台查日志 | 一个日志平台搜索 | 从2小时缩短到40分钟 |
| 回收一个项目权限 | 双重平台手工变更 | Git改权限自动同步 | 从数小时缩短到分钟级 |
这个表格里的数字当然是小团队的真实估算,每家企业不一样,但方向上没有问题。统一架构真正省的不只是操作时间,还有团队成员的学习成本。新入职的同事只要会K8s,就能同时管理VM和容器,不用花两周去熟悉传统虚拟化控制台。
6.2 落地的三条关键经验
第一条经验:别急着“全量迁”,先在容器平台里养一台非核心VM,让团队一点点适应。直接搞大型迁移,业务方一紧张,方案很容易被否决。
第二条经验:网络和存储要前置统一。很多迁移项目卡住,不是KubeVirt不会用,而是底层网络模式和存储类没有提前规划清楚。先把CNI选型、IP段规划、共享存储、备份策略定下来,后面的迁移只是填模板。
第三条经验:权限收敛比“维稳”更重要。两套平台统一后,很容易出现“账号明明从一个入口进来,权限却给了两份”的情况。建议上线前做一次彻底的kubectl auth巡检,把不必要的高危权限全部回收,不让权限成为下一个隐形坑。
6.3 这套架构后面还能往哪里扩展
统一架构跑稳之后,可以继续往几个方向扩展:多集群联邦,让不同地域的K8s集群统一管理;FinOps成本分析,把VM和容器的资源用量统一统计后做成本核算;云原生备份恢复,用Velero这类工具同时备份PVC和虚拟机磁盘。我个人建议扩展顺序是:先做监控日志深化,再做备份演练,最后才考虑多集群调度,别一次性铺太开。
我个人在实际操作中的体会是:别把“统一管理”当成一次技术搬迁,而是当成一次运维习惯重整。如果新架构已经上线,团队还是习惯打开旧控制台、手工临时改配置、在Git仓库之外偷偷漂移配置,那这套架构迟早会变成第二套“历史包袱”。工具只是把两套平台的复杂度收拢了,真正的减负来自流程和习惯的一并收敛。