ARTICLE DETAIL

资讯详情

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

OpenStack on Kubernetes生产落地实践:架构设计与避坑指南

OpenStack on Kubernetes生产落地实践:架构设计与避坑指南 OpenStack 这套老牌云平台和 Kubernetes 这个新生代编排引擎搭在一起在前两篇里我们把概念验证PoC跑通了基础环境也搭好了。但说句实在话PoC 能跑和能上生产之间隔着的距离比大多数人想象的要大得多。这篇就把我从 PoC 走向生产这一路上踩过的坑、做过的权衡、反复调整过的细节完整摊开来讲。这篇内容的核心就一句话怎么把 OpenStack 控制平面稳定地跑在 Kubernetes 集群之上同时保证数据平面不缩水、不降级经得住真实业务的折腾。文章会从架构设计、存储选型、网络规划、部署编排到监控运维把生产落地必须想清楚的问题过一遍。主要适合两类人。一类是团队里已经有 K8s 运维能力但不想再养一支专职 OpenStack 运维队伍想用 K8s 的声明式能力来管理 OpenStack 生命周期的另一类是正在做技术选型需要在传统虚机方式部署 OpenStack和容器化方式部署 OpenStack之间做个理性对比的。如果你是拿 packstack 在单机上装过 OpenStack、想进一步理解生产级部署思路的人这篇也能帮你补齐很多认知盲区。1. 整体架构设计与资源规划1.1 控制面容器化、数据面裸机化的拆分逻辑先聊架构决策。把 OpenStack 搬上 Kubernetes最容易犯的错误就是什么都想容器化。nova-compute 这种和底层虚拟化强绑定的组件强行塞进容器里跑会遇到设备透传、cgroup 层级、libvirt 套接字共享等一系列麻烦事。生产环境的正确打开方式是分而治之控制面组件全部容器化跑在 K8s 上数据面组件保留裸机部署。控制面指的是 keystone、glance、nova-api、nova-scheduler、neutron-server、horizon 这些无状态或轻状态服务数据面则是 nova-compute带着它的 libvirt 和 KVM、neutron 的 Open vSwitch agent、以及存储节点的 Ceph OSD。为什么这样拆因为无状态服务天然适合 Kubernetes 管理。pod 崩溃了自动拉起滚动升级时先起新的再杀旧的配置变更通过 ConfigMap 统一管理这些能力对于控制面来说就是刚需。而 nova-compute 直接操作 /dev/kvm、需要访问宿主机上的 /run/libvirt/libvirt-sock把它容器化不是不行但每升级一次 OpenStack 版本就要处理一遍 libvirt 和 KVM 的兼容性问题运维成本远大于收益。1.2 资源估算的参考模型资源规划上我给一个经过验证的参考模型。假设你的生产环境规模是 10 个计算节点、每个节点 32 核 256GB 内存总计约 300 台云主机那么 OpenStack 控制面在 K8s 上的资源占用大致如下CPU8 核起步建议 12 核主要是 glance-api 和 nova-api 在镜像上传和并发请求时比较吃 CPU内存32GB 起步其中 MariaDB Galera 三节点各分配 8GBRabbitMQ 三节点各分配 4GB其余服务分摊剩余磁盘系统盘 200GB 足够但建议单独挂一块 SSD 给 etcd 和数据库的 WAL 日志IO 延迟敏感K8s 集群本身建议至少 3 个 master 节点etcd 的节点数跟着 master 走。控制面 pod 的副本数不要少于 2并且一定要设置 podAntiAffinity让同一个服务的多个副本分散在不同节点上。这里有个我在实际环境中踩过的坑第一次部署时没配反亲和rabbitmq 的三个副本全被调度到了同一个 master 节点结果那个节点一维护整个消息总线就断了所有异步任务全部卡死。反亲和配置宁多勿少每个有状态服务都要加。K8s 控制面节点建议 4 核 8GB 起步etcd 所在节点磁盘务必是 SSD机械盘在 etcd fsync 压力下延迟会飙到让集群心跳超时。这个和 OpenStack 本身关系不大但既然 OpenStack 跑在 K8s 上K8s 的稳定性就是 OpenStack 稳定性的基础基础不牢上面全是白搭。2. 存储底座选型与生产配置2.1 为什么存储层最终选了 CephOpenStack 的存储方案选择本质上是在 Ceph、外部 SAN、本地磁盘之间做权衡。外部 SAN 性能好但贵而且和 OpenStack 的对接要额外买驱动本地磁盘方案便宜但对 nova 的疏散和迁移非常不友好。我在生产环境里毫无悬念地选择了 Ceph原因很实在第一glance、cinder、nova 三者的存储需求 Ceph 一个方案全覆盖。glance 镜像存在 Ceph RBD 里cinder 卷从 RBD 池里创建nova 的 ephemeral 盘也落在 RBD 上。统一存储后端意味着备份、扩容、监控只需要维护一套体系。第二Ceph 的 RBD 镜像支持快照和克隆glance 镜像可以直接通过 COW 克隆快速创建虚机创建速度比从镜像文件拷贝快一个量级。第三生产环境普遍需要三副本保证数据安全Ceph 的副本策略是软件层面的不需要额外硬件投入。存储网络必须和业务网络、管理网络物理隔离。我曾在一个测试环境里图省事把存储网络和业务网络混在一起跑性能压测时虚机流量一上来Ceph 的 osd 心跳超时告警瞬间刷屏。生产环境务必给 Ceph 单独配一张网卡甚至一台交换机万兆起步。2.2 关键参数和调优经验Ceph 的 pool 创建有几个参数值得重点说。首先是 pg_num 的计算公式(OSD 总数 × 100) / 副本数然后向上取最接近的 2 的幂。假设你有 9 个 OSD三副本那就是 (9×100)/3 300向上取到 512。pg_num 太小会导致单个 PG 内数据量过大触发迁移时对集群冲击明显太大则会增加 OSD 的内存开销。创建池之后有一个经常被忽略的步骤ceph osd pool application enable volumes rbd不执行这个rbd 命令在创建镜像时会报 application not enabled 的警告而且后续的 rbd 快照、克隆功能可能异常。这个问题在 OpenStack 对接时报错信息非常隐晦我第一次遇到时排查了半天。RBD 的客户端缓存建议开启。在 ceph.conf 里为 OpenStack 节点单独配置[client.openstack] rbd cache true rbd cache size 268435456 rbd cache max dirty 134217728缓存大小 256MB、脏数据上限 128MB 是我在多次压测后得到的平衡点。缓存太大虚机在热迁移时的脏页同步会拖慢迁移流程太小则随机读性能上不去。另外强调一点QEMU 进程崩溃时 RBD 缓存未刷盘的数据会丢失所以这个配置需要在性能和数据安全之间做取舍。对于数据库这类对数据一致性要求极高的虚机可以单独改它的卷属性关闭缓存。3. 网络方案OVN 落地细节3.1 网络层次划分与物理规划OpenStack on Kubernetes 的网络方案生产环境我推荐 OVNOpen Virtual Network。相比传统的 Linux Bridge VLAN 方案OVN 把逻辑网络和物理网络解耦Neutron 创建的网络和子网直接映射到 OVN 的逻辑交换机不需要在每个计算节点上手工维护网桥配合 K8s 的环境特别合适。物理网络上我划分了四个平面管理网络K8s 节点和 OpenStack 控制面通信千兆即可API 网络OpenStack 对外提供 API 和 Horizon 接入千兆租户网络虚机东西向流量万兆存储网络Ceph 数据同步和 rbd 流量万兆租户网络和存储网络建议上 RDMA 或者至少是多网卡 bond。虚机数量过百之后东西向流量会迅速吃满千兆链路我这里在扩容到第三批计算节点时就遇到过一次租户网络交换机端口带宽打满导致虚机之间 SSH 卡顿的事故。3.2 MTU 这个经典大坑MTU 问题是每一个 OpenStack 网络排障者都绕不过去的坎。VXLAN 封装本身要额外占 50 字节外层 Ethernet 14 字节 IP 20 字节 UDP 8 字节 VXLAN 8 字节物理网络 MTU 是 1500 时overlay 网络里的虚机看到的 MTU 就只有 1450。具体操作neutron 的 flat 网络 MTU 设置为 1500VXLAN 网络 MTU 设置为 1450同时在镜像的 metadata 里通过 dhcp 选项把虚机网卡的 MTU 也下发为 1450。这一步必须一致否则会出现一个典型症状虚机能 ping 通网关但 SSH 登录时断断续续、scp 大文件时随机卡死。这个现象的本质是 TCP 的 MSS 协商被中间设备的 DF 标志卡住分片丢失导致重传风暴。排查方法是用 tcpdump 抓包看 ICMP fragmentation needed 报错。还有一点很多人不知道K8s 集群本身的 pod 网络如果有 overlay 需求pod 的 MTU 也要跟着调。我在一次部署中 K8s 的 CNI 用的 VXLAN 模式pod MTU 还是默认的 1500结果 pod 里访问虚机的服务时频繁超时查了半天才发现是 MTU 不匹配。如果你的 K8s CNI 和 OpenStack 网络在同一个物理网络上两边的 MTU 规划一定要拉通来看。3.3 网络排查的实用命令OVN 环境下排查网络问题我建议按这个顺序来# 看 OVN 逻辑流表是否正常下发 ovn-nbctl lr-list ovn-nbctl show # 看计算节点上的 OVS 流表 ovs-vsctl show ovs-appctl bond/show # 看虚机对应的 OVS 端口 ovs-vsctl list interface tap123456最常见的场景是创建了网络和子网但虚机拿不到 IP。先确认 DHCP 端口是否正常创建再看 OVN 的 dhcp 选项是否绑到了逻辑交换机上。如果 DHCP 正常但虚机 ping 不通网关多半是 OVN 网关路由配置问题用ovn-nbctl lr-route-list看路由表重点核对 CIDR 的掩码有没有写错。4. 部署编排与版本升级路径4.1 从 packstack 到 Helm Chart 的思路转变用过 packstack 的人都知道它多方便一条命令装完一个 all-in-one OpenStack。但 packstack 解决的是快速体验问题不是生产可运维问题。生产环境要求部署过程可重复、可版本化、可回滚这正是 Helm Chart 和 Kustomize 这类工具的价值所在。OpenStack 的 Helm Chart 项目把每个核心服务拆成独立的 chart通过 values.yaml 控制镜像版本、副本数、资源配额、存储类等所有参数。我维护的部署仓库里每个环境测试、预发、生产都有独立的 values 文件改动先走测试环境验证再提交生产环境。和 packstack 最大的区别在于升级的可控性。packstack 升级要么全量重装要么手工操作一堆步骤每一步都可能出幺蛾子。在 K8s 上升级就是一个helm upgrade命令配合helm rollback可以快速回滚到上一个版本。这点在 OpenStack 这种组件多、依赖复杂的系统里价值怎么强调都不过分。4.2 数据库迁移和版本兼容OpenStack 升级里最让人头疼的是数据库 schema 变更。每个版本发布时都带数据库迁移脚本nova、neutron、cinder、glance 每个服务的数据库都要按顺序迁移。在容器化场景里数据库迁移一般作为 Job 执行跑完一个再跑下一个。这里有一条血的教训OpenStack 各服务之间的版本兼容是有约束的。比如 nova 和 neutron 的版本差不能超过一个大版本否则会因为 API 版本不一致导致创建虚机时网络配置失败。所以升级时务必看官方文档的版本兼容矩阵把整个控制面统一升到目标版本不要搞混搭。我的升级策略是滚动式先升级 K8s 集群本身如果有必要再升级 Ceph再升级 OpenStack 控制面的底层依赖MariaDB、RabbitMQ最后升级 OpenStack 服务。每升级一个组件就做一轮冒烟测试创建虚机、挂载卷、配置安全组、快照、迁移全部通过才进行下一个。冒烟测试脚本写好后放进 CI 流水线升级过程全程自动化验证比人工操作靠谱得多。4.3 GitOps 管理配置文件OpenStack on K8s 还有一个隐性好处所有配置都变成代码了。以前传统部署方式下/etc/nova/nova.conf 改了什么、谁改的、为什么改全靠口口相传和运气。现在 nova.conf 的模板放在 ConfigMap 里改动有记录、评审有流程、出错能回滚。我把整个部署仓库做成 GitOps 模式Helm Release 的定义、ConfigMap 变更、存储类创建全部通过 Git 提交触发 CI/CD 流水线自动应用到集群。这样团队里任何一个人都能通过查看 Git 历史了解系统的完整变更轨迹这个能力在生产故障排查时价值巨大。5. 监控告警与生产运维实录5.1 监控架构三层覆盖OpenStack on Kubernetes 的监控必须覆盖三个层面缺一不可第一层是 K8s 自身状态。etcd 延迟、apiserver 请求量、节点 NotReady、pod 频繁重启这些指标反映了承载 OpenStack 控制面的底座是否健康。用 Prometheus 加 kube-state-metrics 就能覆盖。第二层是 OpenStack 服务状态。每一个 API 服务的响应时间、错误率、队列积压情况。这里的核心是各服务的 exporter比如 mysqld_exporter 看数据库连接数rabbitmq_exporter 看队列积压HTTP 层面的探针直接打到各服务的 health check 端点。第三层是虚拟化层。计算节点的 CPU 核数、内存超配比、虚机密度、Ceph 集群的健康状态和延迟分布。这层最容易被忽略但往往是最先感知到问题的。Ceph 的 osd 延迟从 10ms 飙到 200ms虚机的 disk IO 立刻就能感觉到但 OpenStack 控制面的指标还一切正常。三层监控之间要建立关联。我的经验是每个 OpenStack 服务 pod 的 label 上同时注明服务名和版本告警时通过 label 快速定位到具体服务、具体版本节省排查时间。5.2 典型故障排查实录记录三个在真实环境遇到过的问题按出现频率排序。第一个是计算节点 kubelet 内存压力导致驱逐 pod。某个计算节点上 OpenStack 控制面 pod 和业务 pod 混部业务压测时把节点内存吃满kubelet 触发驱逐把 keystone 和 glance 的 pod 全干掉了整个 OpenStack API 层瞬间不可用。解决思路是给 OpenStack 控制面单独划分专用节点池打上污点和容忍度确保业务负载不会和核心控制面抢资源。第二个是 RabbitMQ 集群脑裂。三节点 RabbitMQ 部署在 K8s 上网络抖动导致节点间心跳超时触发分区。症状是创建虚机的请求卡在调度阶段不动nova-scheduler 一直等消息响应。处理方式是关闭 RabbitMQ 的自动分区恢复因为它对 K8s 环境下的网络抖动过于敏感改用更保守的心跳配置。同时给 rabbitmq pod 设置更宽松的健康检查阈值避免网络瞬时抖动就触发 pod 重启。第三个是 etcd 容量报警。OpenStack 控制面在 K8s 上运行后etcd 的数据量增长比预期快得多原因是 OpenStack 服务的 ConfigMap 和 Secret 数量庞大加上 Helm Release 的 history 默认保留量大。等 etcd 触发压缩阈值时整个集群的读写延迟剧烈波动。解决方法是压缩历史版本helm upgrade --history-max5同时给 etcd 设置合理的自动压缩策略。5.3 备份策略的优先级备份这件事上我踩过最大的坑就是备份了 OpenStack 服务本身却忘了备份 K8s 层面的关键数据。OpenStack on K8s 架构里备份的优先级是这样排列的etcd 是第一优先级的备份对象它保存了整个 K8s 集群的期望状态。etcd 挂了所有配置和 pod 定义都会丢。第二是 MariaDB里面是 OpenStack 的持久化数据实例信息和卷信息都在这里。第三是 glance 的镜像数据和 Ceph 的 pool 配置镜像数据本身因为 Ceph 三副本的保护单独备份的需求不大但 pool 的配置和 keyring 需要留存。备份策略上我对 etcd 采用 RPO 15 分钟的定期快照对 MariaDB 采用 binlog 实时归档。恢复演练至少每季度做一次别等到真出事了才第一次尝试恢复流程。6. 聊聊我的真实体会跑了一段生产之后回头看把 OpenStack 部署到 Kubernetes 上本质上是在用 K8s 的成熟生态来补 OpenStack 运维体系中的短板。高可用、滚动更新、配置管理、故障恢复这些能力传统 OpenStack 部署方式也能做到但需要很深的 openstack 运维功力用 K8s 之后这些能力变成了平台的默认属性。但反过来说这也引入了一个新的复杂度层。以前 OpenStack 故障排查只需要掌握 OpenStack 一门技术栈现在得懂 K8s、懂容器网络、懂 etcd、懂 Helm。上线初期的定位思路完全不同了。我个人的习惯是遇到 OpenStack 控制面异常先查 K8s 层的 pod 状态和事件再查 OpenStack 服务日志最后才查底层网络和存储——这个排查顺序能省掉大量无用功。如果你的团队规模不大、对 K8s 比较熟但没有专职 OpenStack 运维这个方案值得认真考虑。反过来如果你们已经有成熟的 OpenStack 运维体系贸然迁上 K8s 反而增加负担。技术在精不在新关键看是否匹配团队的实际能力。
返回列表