十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OpenStack on Kubernetes生产实践:控制平面容器化后的关键问题与填坑指南

OpenStack on Kubernetes生产实践:控制平面容器化后的关键问题与填坑指南 这个系列写到第十三篇前面十二篇我们把环境准备、Kubernetes 集群搭建、OpenStack 各个核心组件的容器化镜像制作、以及初始化的编排逻辑都过了一遍。这一篇我想集中聊一个很多人问过我的问题当 OpenStack 控制平面真正跑在 Kubernetes 之上以后生产环境里到底还有哪些坑等着你以及这些坑应该怎么填。先说清楚这篇的定位。它不是一个从零开始的安装教程也不会把 Helm Chart 的每一行 values.yaml 再贴一遍。我假设你已经有一个能跑起来的 Kubernetes 集群也已经用容器化的方式把 OpenStack 的核心服务Keystone、Nova、Neutron、Cinder、Glance、Horizon 这些调度上去了。这篇文章要解决的是更现实的问题控制平面容器化之后数据平面怎么和它对接网络怎么打通存储怎么不拖后腿以及出问题的时候怎么快速定位。换句话说这是把 OpenStack 搬上 Kubernetes 之后真正进入生产运维阶段的那一套东西。如果你正好处于“容器化 OpenStack 已经能跑通 demo但一上生产就心里没底”的阶段这篇文章应该能帮到你。1. 整体设计思路为什么选择 OpenStack on Kubernetes以及架构上怎么拆1.1 这种架构到底解决了什么问题传统 OpenStack 部署方式里控制节点就是几台物理机或虚拟机每个节点上跑着十几个 Python 进程由 systemd 托管。这种方式最大的问题不是不能跑而是“变更”很痛苦。升级一个组件要小心翼翼回滚更是灾难节点硬件故障意味着业务中断而且不同组件的依赖冲突经常会让你在 pip 依赖地狱里爬不出来。把控制平面搬上 Kubernetes 以后这些痛点被换了一种方式解决。镜像不可变版本升级就是换镜像滚动更新进程崩溃了有 kubelet 自动拉起节点挂了 Pod 会调度到其他节点声明式配置让整个环境的期望状态都写在 Git 里。听起来很美好但实际落地的时候你会发现OpenStack 并不是天生为 Kubernetes 设计的尤其是那些有状态的服务和依赖主机网络的组件需要你花很多心思去做适配。我之前在社区里看到过一个比喻说 OpenStack on Kubernetes 就像“把一辆老式手动挡汽车的动力系统装进一台自动挡新能源车的底盘里”。比喻不一定精确但意思是对的你能开但离合器得自己想办法。1.2 控制平面与数据平面的职责划分生产级 OpenStack on Kubernetes 架构一定要在脑子里把两个平面分清楚。控制平面跑在 Kubernetes 集群里包含 Keystone、Glance、Nova API、Neutron Server、Cinder API、Horizon 这些无状态或半无状态的服务。它们的共同特点是通过数据库和消息队列通信自身不直接处理底层硬件资源所以非常适合容器化调度。数据平面则是真正干活的那些组件典型代表是 Nova Compute也就是 nova-compute、Neutron Agent包括 L2 Agent、DHCP Agent、Metadata Agent以及可选的 Cinder Volume 的 LVM 后端、Manila Share 的存储节点。这些组件必须贴近物理主机运行因为它们要操作宿主机上的网桥、网卡、存储卷组如果让它们跑在随机的 Pod 里网络和存储直接就断掉了。这里有一个很多人第一次做架构设计时会犯的错误试图把 nova-compute 也做成一个普通的 Deployment 丢进集群里。这样做的结果通常是计算节点上的虚拟网桥和 Pod 网络互相打架流量绕来绕去性能差不说还特别难排查。正确的做法是让数据平面组件以特权 DaemonSet 的方式调度到指定计算节点或者直接用 systemd 在宿主机上托管Kubernetes 只负责控制平面。生产环境里我见过两种主流方案一种是把 nova-compute 用 DaemonSet 部署Pod 直接共享宿主机的网络命名空间并且挂载宿主机的 /var/lib/libvirt、/run 等目录另一种是干脆保持传统方式计算节点上的组件用容器运行但不纳入 Kubernetes 调度而是由单独的容器运行时管理。这两种没有绝对的好坏取决于你的团队更熟悉哪套运维体系。1.3 为什么我最终选了 Helm 作为交付方式在控制平面容器化的落地过程中你一定会碰到一个选择用 Kolla-Kubernetes、OpenStack-Helm还是自己写一套 Deployment 和 Service。Kolla-Kubernetes 项目很早就不太活跃了而且它和 OpenStack 版本的绑定关系比较紧升级路径不灵活。OpenStack-Helm 是 OpenStack 社区里相对活跃的方案用 Helm Chart 管理整个控制平面提供了比较完整的 values.yaml 参数体系。如果团队里没人愿意从零维护几百个 YAMLOpenStack-Helm 是一个可以接受的起点。但我要多说一句OpenStack-Helm 的上手成本并不低。它把很多运维细节封装起来了一旦出问题你面对的是层层嵌套的 Helm 模板想定位一个参数从 values.yaml 到最终的 ConfigMap 是怎么传的需要花不少时间。我个人建议的做法是把 OpenStack-Helm 当作参考实现在它的基础上裁剪出自己团队需要的 Chart。不要盲目全量部署因为默认配置里有很多服务在生产环境根本用不到它们白白占用资源不说还会增加故障面。控制平面组件其实就那么核心的七八个其他边缘组件按需开启就行。2. 生产部署前的关键决策资源规划、版本选型和存储网络底座2.1 版本搭配不是越新越好OpenStack 和 Kubernetes 的版本兼容性是生产部署里第一个需要认真对待的问题。OpenStack 社区每半年出一个版本Kubernetes 每三个月出一个小版本两者的发布节奏完全对不上所以你必须选一个经过验证的组合而不是两手一摊直接 latest。以我目前环境为例OpenStack 用的是 Yoga 版本Kubernetes 用的是 1.24 到 1.26 之间的某个稳定版本。为什么选这个组合因为 Yoga 版本的容器镜像对 Python 版本、gRPC 库的依赖相对稳定和 Kubernetes 1.24 之后的稳定 API 配合没什么兼容性问题。另外要注意的是 Kubernetes 的 API 版本弃用策略。新版本集群会把一些旧 API 直接移除比如 networking.k8s.io/v1beta1 这种如果你的 OpenStack-Helm Chart 还在用旧 API部署的时候就会直接报错。所以生产环境选型时最好先看一下 Chart 里用到的 API 版本再反推 K8s 集群的版本范围。2.2 资源规划的计算逻辑控制平面的资源规划经常被低估。很多人觉得 OpenStack 控制服务都是 API 进程吃不了多少内存实际上 Keystone、Nova API、Neutron Server 这些服务加起来再加上数据库和消息队列资源消耗是相当可观的。分享一个我在生产环境用来估算资源的经验公式。假设你的云平台规模在 50 台计算节点以内控制平面至少需要 16 核 CPU、64GB 内存这是底线。其中数据库独占 8GB 到 16GB消息队列独占 4GB 到 8GB剩下的分给各个 API 服务。如果你的计算节点规模超过 100控制平面的资源要按比例往上加尤其是数据库的连接池和消息队列的吞吐能力会成为瓶颈。存储方面etcd 是 Kubernetes 的大脑必须使用高性能磁盘建议 SSD 起步。OpenStack 自己的数据库通常是 MariaDB同样要放在高性能存储上因为每次虚拟机创建、删除、热迁移背后都是大量的数据库读写。我见过一个生产事故数据库跑在机械盘上虚拟机创建并发一高整个控制平面全部卡死最后排查下来 IO 延迟已经高到不可接受。网络方面Kubernetes 集群的管理网络、OpenStack 内部 API 网络、OpenStack 的数据网络要尽量物理隔离。即使不能物理隔离也要用 VLAN 或 VXLAN 做逻辑隔离。否则OpenStack 的 Neutron 数据流量会把 Kubernetes 的 etcd 心跳和 Pod 间通信全部打爆。2.3 存储后端选型Ceph 依然是最稳的选择OpenStack 的存储后端我个人强烈建议直接上 Ceph不要在这个环节省事。Ceph RBD 可以作为 Cinder 的后端给虚拟机提供块存储也可以作为 Glance 的后端存镜像还能作为 Nova 的临时磁盘后端。一套 Ceph 解决所有存储问题运维上也只用维护一套分布式存储系统。Ceph 和 OpenStack on Kubernetes 的配合有一个需要注意的点Ceph 集群建议独立部署不要和 Kubernetes 节点混部。除非你非常清楚 Ceph 和 K8s 的资源隔离边界否则混部会导致 IO 竞争和故障域重叠。生产环境的稳定性需要的是故障隔离而不是硬件的“充分利用”。Cinder 的 Ceph 后端配置里有一个参数很容易被忽略就是rbd_connect_timeout和rbd_operations_timeout。默认值可能只有几十秒但生产环境里 Ceph 集群偶尔会有高负载如果超时时间太短Cinder 会频繁报错甚至把卷状态置为 error。我一般会把这两个参数调大到 60 秒以上给自己留出容错空间。Glance 后端用 Ceph 的话镜像上传会直接通过 RBD 写入 Ceph不走 Glance 自身的本地文件系统。这个配置很关键因为它让 Glance 变成无状态服务Pod 随便怎么调度都不会丢数据。如果你使用 Glance 本地文件存储那 Glance 的 PVC 就成了单点Pod 一迁移就找不到镜像了。2.4 网络方案Calico 与 OpenStack 数据平面的边界Kubernetes 集群内部的 Pod 网络我用的是 Calico原因很简单稳定、成熟、支持 BGP 路由通告而且和 OpenStack 的 Neutron 网络没有直接的地址冲突问题。但有一点要特别注意OpenStack 的 Neutron 网络和 Kubernetes 的 Pod 网络是两套完全独立的网络体系。Neutron 管理的是租户的虚拟网络VXLAN、VLAN、Flat 等由 Neutron Agent 在计算节点上通过 Linux Bridge 或 Open vSwitch 实现Kubernetes 的 Pod 网络则是 Calico 等 CNI 插件管理的 Overlay 网络。两者之间不能互相打通也不应该有路由交叉。如果你需要在 OpenStack 虚拟机里访问 Kubernetes 里的服务或者反过来正确的做法是通过负载均衡器或 API Gateway 暴露服务而不是试图在 Neutron 和 Calico 之间做路由打通。强行打通会带来不可控的 ARP 风暴和路由环路我见过不止一次因为这个设计失误导致的整个网络瘫痪。还有一个经验是Neutron 的 Open vSwitch 代理要使用宿主机网络不要走 Kubernetes 的 Pod 网络。Neutron Agent 需要操作宿主机上的 ovsdb、网桥和 tunnel 接口这些操作必须发生在宿主机网络命名空间里。如果 Pod 网络隔离了这些操作agent 会一直处于异常状态。3. 控制平面容器化的核心实操环节3.1 数据库与消息队列的高可用部署OpenStack 控制平面里最需要认真对待的就是 MariaDB 和 RabbitMQ。这两个服务一旦出问题整个 OpenStack 控制平面就会瘫痪。在 Kubernetes 里部署高可用的 MariaDB和部署一个普通 Web 服务完全不是一个复杂度。MariaDB 的高可用我用的是 MariaDB Galera Cluster它支持多主同步复制任何一个节点挂掉都不会影响数据读写。在 Kubernetes 里跑 Galera最关键的是每个 Pod 必须有一个稳定的标识符通常用 StatefulSet 来实现。StatefulSet 的 Pod 名称是固定的比如 mariadb-0、mariadb-1Galera 集群的节点地址就靠这些固定的 Pod 名称来互相发现。这里有个实操细节Galera 集群的 SSTState Snapshot Transfer对网络延迟非常敏感。当新节点加入集群时它需要从现有节点同步全量数据如果网络抖动SST 可能会超时失败。所以 MariaDB 的 Pod 应该通过节点亲和性绑定到同一个机架或者同一批高性能网络互通的节点上避免跨地域部署。RabbitMQ 的高可用方案是镜像队列。在 Kubernetes 里部署 RabbitMQ一般用 StatefulSet 加 Headless Service 来保证每个实例的稳定网络标识然后在 RabbitMQ 内部配置镜像队列策略让队列在主节点挂掉后能自动切换到镜像节点。有一个常见的配置坑是 RabbitMQ 的autocluster插件和 Kubernetes 的 DNS 解析配合问题。RabbitMQ 节点启动时需要通过 DNS 解析其他节点的地址如果 Kubernetes 的 CoreDNS 出问题RabbitMQ 集群会一直起不来。我建议在 RabbitMQ 的 StatefulSet 里配置一个独立的 Headless Service并且把 DNS 解析超时时间调大一些。3.2 Keystone 与各个服务的交互配置Keystone 作为 OpenStack 的身份认证中心它的稳定性决定了整个控制平面的可用性。容器化部署 Keystone 时最重要的配置文件是keystone.conf里的数据库连接串和 Fernet Key。Fernet Key 是用来签发和验证用户 token 的它必须在一个环境中保持一致。如果你用 Deployment 部署 Keystone 并且有多个副本每个副本必须挂载同一个包含 Fernet Key 的 Secret。如果 Fernet Key 不一致用户在一个 Keystone 节点上获取的 token在另一个节点上验证会直接失败表现为用户时不时收到 401 Unauthorized。实际操作中我会把 Fernet Key 放到 Kubernetes Secret 里然后挂载到 Keystone 的 Pod 中确保所有副本读取到的是同一份 Key。同时Fernet Key 的轮换需要专门安排维护窗口不能随意操作因为新旧 Key 需要共存一段时间才能平滑过渡。其他服务Nova、Neutron、Cinder 等与 Keystone 的交互主要通过 service account 和 endpoint 两种方式。容器化部署时各个服务的*.conf文件里需要配置 Keystone 的地址这个地址应该是 Kubernetes 内部的 Service 地址而不是外部 LoadBalancer 的地址这样可以减少网络延迟并且避免额外的故障点。3.3 Endpoint 注册与内部访问地址管理OpenStack 的各个服务之间通信靠的是 Keystone 里注册的 Endpoint。容器化部署时Endpoint 的地址设置需要格外小心。我在生产环境中的做法是把 Endpoint 分为两种内部 Endpoint 和公共 Endpoint。内部 Endpoint 使用 Kubernetes 的 Service 名称比如http://keystone.openstack.svc.cluster.local:5000供 OpenStack 各服务之间内部调用公共 Endpoint 则使用外部负载均衡器的地址比如https://keystone.example.com:5000供用户和运维工具访问。这个设计的好处是内部流量不走外部负载均衡器减少了延迟和故障暴露面同时也让各服务之间的调用不依赖外部 DNS 解析。如果你只用一套 Endpoint内部所有服务都走外部地址一旦负载均衡器抖动整个 OpenStack 控制平面都会出现间歇性不可用。这里有一个常见的问题是Nova 的nova.conf里配置的oslo_messaging_rabbit_hosts需要指向 RabbitMQ 的服务地址如果你在多个 Region 或多套环境中用同一个 RabbitMQ 集群要确认地址是否正确否则服务之间无法通信虚拟机创建会一直卡在调度阶段。3.4 镜像构建与 ConfigMap 管理控制平面容器化之后镜像构建的规范就变得非常重要。我在生产环境中维护了一套基于 Kolla 镜像的定制化构建流程。Kolla 提供了非常完整的 OpenStack 组件镜像但我不会直接用它官方的最新镜像而是会在它的基础上加上我们团队自己的配置和补丁构建出私有镜像仓库里的版本。镜像tag 的管理也很关键。我建议使用带有版本号和构建日期的 tag比如nova-api-yoga-2024.01.15。这样回滚时只需要改一下镜像 tag 重新部署不用去猜某个镜像到底是什么时候构建的。ConfigMap 是用来管理 OpenStack 服务配置的主要方式。所有服务的*.conf文件都可以通过 ConfigMap 挂载到 Pod 中。但这里有一个容易踩的坑OpenStack 服务启动时如果 ConfigMap 挂载的配置文件里有语法错误服务不会启动但 Kubernetes 不会给你明显的报错只是 Pod 一直处于 CrashLoopBackOff。我通常会准备一套配置校验脚本在构建镜像或者发布前先跑一遍用oslo-config-validator检查配置文件的合法性。这个工具会帮你检查配置项的类型、范围、是否在配置文件中被正确识别能省掉大量调试时间。4. 数据平面服务调度与底层资源打通4.1 Nova Compute 的调度策略Nova Compute 是 OpenStack 数据平面最核心的组件它直接负责虚拟机的生命周期管理。容器化部署 Nova Compute 时调度策略是首先要考虑的问题。我的做法是给计算节点打上专门的标签比如openstack-compute-nodetrue然后让 Nova Compute 的 DaemonSet 只调度到这些节点上。同时Nova Compute 的 Pod 需要配置hostNetwork: true使用宿主机的网络命名空间。这是因为 libvirt 创建的虚拟机网卡要直接挂到宿主机的 Linux Bridge 或 OVS 网桥上如果 Pod 使用独立的网络命名空间虚拟机网络根本无法工作。另外Nova Compute 的 Pod 需要挂载宿主机的/var/lib/libvirt、/etc/libvirt、/run等目录。一开始你可能会觉得挂载这么多目录有安全风险但在生产环境里这是没办法的事libvirt 管理虚拟机必须要访问这些宿主机的资源。Privileged 模式也需要打开因为没有它nova-compute 无法创建和管理虚拟机。这里有一个严重的性能问题需要注意Nova Compute 的 Pod 里运行的 libvirt 进程会直接使用宿主机的 CPU 和内存来跑虚拟机。如果 Kubernetes 的 kubelet 开启了 CPU Manager 或者给 Pod 设置了 CPU limit可能会影响虚拟机的性能。我在生产环境里建议不要让 Nova Compute 的 Pod 设置 CPU limit让它直接使用宿主机的全部资源。4.2 Neutron Agent 与宿主机网络的协同Neutron 的各个 Agent 是数据平面和网络平面的桥梁它们和宿主机的网络栈紧密耦合。和 Nova Compute 类似Neutron Agent 的 Pod 也需要使用hostNetwork: true和特权模式。DHCP Agent 和 Metadata Agent 相对简单它们只需要监听宿主机的网络命名空间里对应网桥的 IP提供 DHCP 和 metadata 服务。L2 AgentOpen vSwitch Agent 或 Linux Bridge Agent则要复杂得多它需要管理宿主机上的各种网桥和隧道接口。有一个经验是Neutron 的 L2 Agent 不要和 Nova Compute 放在同一个 Pod 里跑即使它们调度到同一个节点。分开跑的坏处是通信要经过额外的网络栈好处是故障隔离更清晰一个组件崩溃不会影响另一个。我的建议是分开跑虽然看起来多占了一点资源但排查问题时你会感谢这个决定。Neutron Agent 的配置里tunnel_type和local_ip参数非常重要。local_ip必须是宿主机上用于 VXLAN 隧道通信的真实 IP 地址而且这个 IP 对应的网卡必须稳定可靠。如果这个 IP 配置错了虚机之间的东西向流量会完全不通而且故障排查起来非常困难因为控制平面一切正常就是虚拟机之间 ping 不通。4.3 Cinder Volume 如何在容器里管理 LVM 和 CephCinder Volume 负责管理和提供块存储。如果你的后端是 Ceph情况相对简单因为 Cinder Volume 只需要通过 librbd 库连接 Ceph 集群不需要直接操作宿主机的存储设备。但如果后端是 LVM情况就复杂了。Cinder Volume 的 Pod 必须挂载宿主机的/dev目录才能看到宿主机上的物理卷和卷组。你需要在宿主机上预先创建好 Volume Group然后在 Cinder 的配置里指定这个 Volume Group 的名称。Cinder Volume 会在运行时通过 LVM 命令创建逻辑卷然后把它们作为 iSCSI 或 FC 设备导出给 Nova。有个坑是Cinder Volume 容器里跑的 LVM 命令可能和宿主机的 LVM 版本不一致导致无法识别卷组。这个问题比较隐蔽我遇到过几次后来直接在构建镜像时锁定了 LVM 版本避免这种兼容性问题。Cinder 的后端配置中volume_backend_name参数要特别注意。创建 Volume Type 时Extra Specs 里的volume_backend_name必须和 Cinder 配置中的volume_backend_name完全一致否则创建卷时会报 “No valid backend was found” 的错误。这个错误信息很常见但让人头疼的是它不会告诉你是哪个参数不匹配。4.4 如何优雅地处理计算节点下线生产环境里计算节点总会有维护、硬件更换的时候。在 OpenStack on Kubernetes 架构里计算节点退出集群的操作要格外谨慎因为你不仅要处理 Kubernetes 节点还要处理 OpenStack 数据平面的状态。第一步是先把计算节点上的虚拟机迁移走使用nova host-evacuate或nova live-migration把虚拟机热迁移到其他节点。迁移完成之后把该节点上的 OpenStack 服务禁用比如nova service-disable、neutron agent-disable。第二步才是处理 Kubernetes 节点。把节点标记为不可调度清理 Pod。这里要注意Nova Compute 的 DaemonSet 在节点下线后会自动在新节点上启动新的 Pod但如果你没有预留足够的计算资源新的 Pod 会处于 Pending 状态影响整个环境的可用性。所以节点下线前的容量评估非常关键。我在实际运维时会先做一个kubectl drain的演练确认所有数据平面的 Pod 都迁移干净了再真正执行节点下线。演练能发现很多意外情况比如某个 PVC 没有设置合适的回收策略导致节点下线时数据被删掉。5. 常见问题与故障排查实录5.1 数据库连接池耗尽导致的控制平面卡死一个典型场景虚拟机创建并发突然升高控制平面所有 API 请求变慢甚至超时。排查下来发现是 Nova API 连接 MariaDB 的连接池被打满了新请求全部在等待数据库连接。这个问题在传统部署里也存在但容器化部署更容易被忽略因为每个 API 服务的副本数可能不止一个连接池的总数是各个副本的叠加。如果你的 Nova API 有 5 个副本每个副本连接池上限是 50那么数据库端的最大连接数就是 250很容易超过 MariaDB 的max_connections限制。解决方案分两步。第一步调整 Nova API 的oslo_db连接池大小把max_pool_size和max_overflow控制在一个合理范围第二步调整 MariaDB 的max_connections并且监控连接数增长趋势提前扩容数据库节点。5.2 RabbitMQ 节点重启后集群脑裂Kubernetes 滚动更新或节点重启时RabbitMQ 集群偶尔会出现脑裂或分区状态。如果你用的是镜像队列表现是部分队列的消息无法被消费OpenStack 里的虚拟机创建任务会一直卡在等待状态。排查方法很简单登录 RabbitMQ 管理界面看每个节点的分区状态。如果出现{partitions, [nodehost1]}之类的提示说明集群已经分区了。生产的解决思路是先手动恢复网络连通性然后选择保留哪个分区的数据。如果两个分区的数据差异不大可以尝试把分区节点逐个重启让它们重新加入集群。如果数据差异较大就只能选择保留数据较新的分区然后把旧分区的数据丢弃。这个过程一定要谨慎操作最好先用快照备份一遍数据。为了防止出现这种情况我建议在配置 RabbitMQ 时开启pause_minority模式。这个模式会让集群在分区时自动暂停少数派节点的服务避免脑裂导致的数据不一致虽然会在分区时牺牲一小段可用性但总比数据损坏好。5.3 Neutron Agent 状态假死的问题Neutron Agent 的状态在控制平面显示为 UP但实际上已经无法处理网络请求。这种情况通常发生在 OVS Agent 上表现为虚拟机的网络不通但neutron agent-list里 Agent 的状态依然是活的。原因通常是 OVS Agent 和 Neutron Server 之间的 RPC 通信断开但 Agent 进程本身没有退出。它还在向 Neutron Server 发送心跳包但实际的工作线程已经卡住了或者 OVS 网桥上的流表已经和 Neutron Server 上的期望状态不一致。遇到这种问题第一件事是重启 OVS Agent 的 Pod然后观察流表是否重新同步。如果频繁出现假死就要检查宿主机上的 OVS 进程是否有异常日志以及 Neutron Server 是否频繁重启。还有一种可能是宿主机的 ovsdb 和 Open vSwitch 内核模块版本不匹配导致流表下发失败。5.4 Ceph 超时导致卷创建失败的排查Cinder 创建卷超时失败是 OpenStack on Kubernetes 里比较常见的存储问题。表现为创建卷的请求一直处于creating状态最终超时变成error。排查思路是看 Cinder Volume 的日志确认是连接 Ceph 超时还是 RBD 操作超时。如果是连接超时检查 Cinder Volume Pod 到 Ceph Monitor 的网络连通性尤其要看防火墙是否放行了 Ceph 的 6789 端口。如果是 RBD 操作超时多半是 Ceph 集群本身负载过高需要查看 Ceph 的健康状态和 IO 延迟。另外容器里跑的 Cinder Volume 需要能够解析 Ceph Monitor 的地址。如果你的 Ceph 集群启用了域名解析要确认容器里的 DNS 配置正确否则会报 “Name or service not known” 的错误。5.5 常见问题速查表问题可能原因快速处理方法Keystone 401 间歇性出现Fernet Key 多个副本不一致检查 Secret 挂载统一 Fernet Key虚拟机创建卡在调度RabbitMQ 队列拥塞或分区查看 RabbitMQ 队列状态和节点分区状态虚拟机网络不通Neutron Agent 状态假死或流表异常重启 Agent Pod检查 OVS 流表Cinder 卷创建超时Ceph 连接超时或集群负载过高检查 Ceph 健康状态调整超时参数API 请求普遍变慢数据库连接池耗尽调整连接池参数监控数据库连接数Pod 一直 CrashLoopBackOff配置文件语法错误用 oslo-config-validator 检查配置6. 生产上线前的检查清单与运维建议6.1 检查清单生产环境不像测试环境出问题可以推倒重来。上线前把下面的清单过一遍能避免大部分低级事故。第一确认所有 Secret 和 ConfigMap 都已经正确创建并且内容没有遗漏。尤其是 OVS 和 Nova 需要使用的各种密钥和凭据少一个都没法正常工作。第二确认各个服务的 Endpoint 已经注册到 Keystone并且通过openstack endpoint list验证一下。注意检查公开 Endpoint 是否可以通过外部地址访问。第三确认 Cinder 的 Volume Type 和 Extra Specs 配置正确。先用 Ceph 创建几个测试卷然后挂载到虚拟机上实际跑一下读写确认存储链路没有隐藏问题。第四验证 Neutron 的网络功能。创建一个测试网络和子网启动两台虚拟机关联到这个网络测试它们之间的网络通信是否正常。同时测试安全组规则是否生效浮动 IP 是否能正常绑定。第五确认监控和告警已经接入。至少要监控宿主机 CPU、内存、磁盘、网络以及 MariaDB 和 RabbitMQ 的关键指标。同时要对 OpenStack 的服务进程做健康检查建议接入 Prometheus 加 Grafana。6.2 日常运维里的两个小技巧先说备份。数据库和消息队列是 OpenStack 控制平面的神经中枢备份不能省。我建议每天全量备份 MariaDB 的数据目录并且至少保留最近 14 天的备份。RabbitMQ 的配置和用户定义也要备份虽然它本身存储的是临时数据但配置丢了重建起来很麻烦。这些备份应该放在独立于 Kubernetes 集群之外的存储里防止集群故障时备份也一起丢了。再说日志。容器化部署的日志采集和传统方式不太一样。OpenStack 各服务的日志打到 stdout由容器运行时收集再通过 fluentd 或 Filebeat 发送到 Elasticsearch。如果你用 Loki也可以直接把 Pod 日志拉取到 Loki 里。关键是日志检索能力生产环境排查问题时能在一个地方同时搜索 Nova、Neutron、Cinder 的日志会让你心里的踏实程度完全不一样。最后要提的一点是在 K8s 里跑 OpenStack 控制平面你仍然要把 OpenStack 本身的知识和运维经验学扎实。容器化只是把进程的运行环境规范化了OpenStack 组件之间的依赖关系、配置项的作用、消息队列里的交互逻辑这些底层的东西跟传统部署没有区别。工具换了但理解问题的框架没有换。把这一点想透你在生产环境里遇见任何奇怪的问题都能有一套可靠的思路去拆解它而不是被容器那一层障眼法带着走。
返回列表