简介:这份《OpenStack技术源码模块解读》面向云计算开发与运维人员、源码阅读爱好者,以及希望从IaaS层理解开源云平台架构的中高级学习者。资源以Nova项目为切入点,系统梳理OpenStack从早期Nova、Swift两大组件,到Cinder、Glance、Neutron、Ironic、Keystone、Horizon、Heat等衍生服务的演化脉络,并深入讲解Nova的调度器、计算节点、API服务器与数据库接口等核心结构。内容涵盖setup.cfg服务入口、console_scripts入口函数、api.py与rpcapi.py及manager.py的通用骨骼脉络,还涉及all-in-one开发环境搭建、pdb断点调试、vim代码跳转配置等实用技巧,帮助读者掌握从单组件到整体生态的源码阅读方法。压缩包共1个docx文件,约759KB,以图文文档形式呈现,便于对照源码逐步研读。目前已有105人学习,适合作为深入OpenStack源码与二次开发的入门参考。
1. 从一份 docx 说起:OpenStack 源码模块到底该怎么读
很多人第一次拿到「OpenStack技术源码模块解读.docx」这类资料时,第一反应是把它当成一本电子书从头翻到尾,结果翻到 Nova 的调度器就卡住了——满屏的filter_scheduler、host_manager、resource_tracker,不知道哪个才是真正要改的地方。这份文档的价值不在于「读完」,而在于它给了你一张地图:告诉你 OpenStack 这个庞然大物是由哪些模块拼起来的,每个模块负责什么,源码入口在哪。OpenStack 本身是云平台搭建的底座,Nova 管计算、Neutron 管网络、Cinder 管块存储、Keystone 管认证、Glance 管镜像,而源码解读要解决的核心问题是:当你要做二次开发、排查线上故障、或者只是想搞懂 openstack 部署之后那些进程到底在干什么时,应该从哪个文件、哪个类、哪个方法切进去。这篇笔记面向的是已经能跑起一套 all-in-one 环境、但面对源码不知道从哪下手的工程师,也适合正在做 openstack 云平台搭建、想提前理解内部机制的人。我会按「模块怎么分 → 关键调用链怎么走 → 怎么动手验证 → 坑在哪」的顺序,把这份 docx 里最值得深挖的部分拆开讲。
2. Nova 源码模块拆解:从 API 到 Hypervisor 的调用链
2.1 Nova 的模块分层与源码目录对应关系
Nova 是 OpenStack 里源码量最大、调用链最长的模块,也是「OpenStack技术源码模块解读.docx」里篇幅最重的部分。要读懂它,先得把它的分层和源码目录对上号。Nova 在逻辑上分四层:API 层接收 REST 请求,Conductor 层做数据库操作和任务编排,Scheduler 层决定虚拟机落到哪台计算节点,Compute 层真正调用虚拟化驱动干活。对应到源码目录,nova/api/是 API 层,nova/conductor/是 Conductor,nova/scheduler/是调度器,nova/compute/是计算层,nova/virt/下则是各种 Hypervisor 驱动,比如libvirt、qemu、vmwareapi。
读源码时最容易迷路的地方是 RPC 调用。Nova 各层之间不是直接函数调用,而是通过 oslo.messaging 发 RPC 消息,所以你在nova/compute/api.py里看到self.compute_rpcapi.build_and_run_instance()时,实际执行体在另一端的nova/compute/manager.py。如果不知道这个机制,就会以为代码「断」了。常见做法是先用grep -rn "def build_and_run_instance" nova/找到所有定义和调用点,再顺着 RPC 的topic和server参数确认消息发往哪个服务。
下面这段命令用来快速定位一个 API 请求从入口到 RPC 出口的路径,以创建虚拟机为例:
# 在 nova 源码根目录执行 # 1. 找到创建虚拟机的 API 入口 grep -rn "def create" nova/api/openstack/compute/servers.py # 2. 顺着看它调用了 compute api 的哪个方法 grep -rn "def create" nova/compute/api.py # 3. 找到 RPC 调用点,确认发往哪个 topic grep -rn "build_and_run_instance" nova/compute/api.py nova/compute/rpcapi.py # 4. 在 compute 层找到真正的执行体 grep -rn "def build_and_run_instance" nova/compute/manager.py这几条命令的逻辑是:先定位 HTTP 入口,再定位业务层方法,然后找到 RPC 边界,最后落到执行端。参数上要注意,nova/api/openstack/compute/servers.py里的create方法只做参数校验和策略检查,真正的业务逻辑在nova/compute/api.py的create里;而rpcapi.py是 RPC 的客户端封装,它里面的build_and_run_instance才是跨服务调用的起点。如果你在manager.py里找不到对应方法,大概率是版本差异导致方法名变了,这时候用grep -rn "build_and_run" nova/compute/模糊匹配更稳。
2.2 Scheduler 调度器:Filter 与 Weight 的源码执行顺序
调度器是 Nova 里最容易被「玄学」问题缠上的模块——明明资源够,虚拟机就是调度失败。要排查这类问题,必须读懂nova/scheduler/filter_scheduler.py和nova/scheduler/host_manager.py。调度分两步:先过滤(Filter)掉不满足条件的节点,再称重(Weight)选出最优节点。Filter 在nova/scheduler/filters/下,每个文件是一个过滤器,比如RamFilter看内存、DiskFilter看磁盘、ComputeFilter看节点是否可用。Weight 在nova/scheduler/weights/下,常见的是RAMWeight,内存越大的节点权重越高。
源码里FilterScheduler._schedule方法是总入口,它先调用host_manager.get_filtered_hosts()做过滤,再调用host_manager.get_weighed_hosts()做排序。过滤阶段任何一个 filter 返回 False,该节点就被剔除。这里有个血泪经验:RamFilter默认会考虑ram_allocation_ratio,如果你在nova.conf里把ram_allocation_ratio设成 1.5,那 8G 内存的节点会被当成 12G 来用,调度器认为放得下,但实际可能超分导致虚拟机启动失败。读源码时要把配置项和 filter 逻辑对照看。
下面这段 Python 片段模拟了 filter 的执行逻辑,方便你在本地验证某个节点为什么被过滤掉:
# 模拟 FilterScheduler 的过滤流程,帮助理解源码逻辑 # 实际源码在 nova/scheduler/filter_scheduler.py class FakeHost: def __init__(self, name, ram_mb, disk_gb, vcpus, ram_ratio=1.5): self.name = name self.ram_mb = ram_mb self.disk_gb = disk_gb self.vcpus = vcpus self.ram_ratio = ram_ratio @property def free_ram_mb(self): # 源码中 RamFilter 会用 ram_allocation_ratio 放大可用内存 return self.ram_mb * self.ram_ratio def ram_filter(host, requested_ram_mb): # 对应 nova/scheduler/filters/ram_filter.py 的核心判断 if host.free_ram_mb < requested_ram_mb: return False, f"{host.name} 内存不足: 可用 {host.free_ram_mb}MB, 需要 {requested_ram_mb}MB" return True, "通过" hosts = [ FakeHost("compute01", 8192, 100, 8), FakeHost("compute02", 4096, 50, 4), ] for h in hosts: ok, msg = ram_filter(h, 6000) print(f"{h.name}: {msg}")这段代码的关键参数是ram_ratio,它对应nova.conf里的ram_allocation_ratio。运行后你会看到 compute01 通过、compute02 不通过,因为 4096×1.5=6144 虽然大于 6000,但源码里还会减去已用内存,实际判断更严格。读源码时要注意RamFilter里用的是host_state.free_ram_mb,这个值来自resource_tracker周期上报,有延迟,所以刚创建完虚拟机立刻再调度可能读到旧数据,这就是「明明刚释放了资源却调度不上去」的常见原因。
2.3 用 pdb 在源码里下断点验证调用链
光看代码容易自欺欺人,最有效的验证方式是在源码里下断点,让请求真实走一遍。OpenStack 的服务都是 Python 进程,可以直接用pdb或debugpy挂上去。以 Nova 的build_and_run_instance为例,在nova/compute/manager.py对应方法里插入import pdb; pdb.set_trace(),然后重启nova-compute服务,再发起创建虚拟机请求,进程就会停在断点处。
# 1. 编辑源码,在目标方法第一行插入断点 # 在 nova/compute/manager.py 的 build_and_run_instance 方法内加: # import pdb; pdb.set_trace() # 2. 重启 nova-compute 服务(以 systemd 为例) sudo systemctl restart nova-compute # 3. 另开终端发起创建请求 openstack server create --flavor m1.small --image cirros --nic net-id=<net-id> test-vm # 4. 回到 nova-compute 的日志终端,会看到 (Pdb) 提示符 # 常用命令: # l 查看当前代码上下文 # n 单步执行下一行 # s 进入函数内部 # p var 打印变量值 # c 继续执行这里的关键是pdb会阻塞服务进程,生产环境千万别这么干,只在测试环境用。参数上,l看上下文时注意行号,p self可以看当前对象,p instance能看虚拟机对象的状态。如果断点没生效,先确认改的是不是正在运行的代码路径——OpenStack 部署方式不同,代码可能在/usr/lib/python3/dist-packages/nova/而不是你 clone 的源码目录,用python -c "import nova; print(nova.__file__)"确认实际加载路径。这个习惯能帮你省下大量「改了没反应」的后悔药时间。
3. 避坑与排查:读 OpenStack 源码时最容易翻车的 5 个点
3.1 现象:改了源码重启服务不生效
原因:OpenStack 通过pip安装时,代码在site-packages下,你改的是 clone 的源码目录,两者不是同一份。解决:用python -c "import nova; print(nova.__file__)"确认实际路径,要么改对路径,要么用pip install -e .以开发模式安装,让源码目录直接生效。
3.2 现象:RPC 调用报MessagingTimeout
原因:Nova 各服务通过消息队列通信,nova-compute没起来、rabbitmq连接异常、或者transport_url配置不一致都会导致。解决:先systemctl status nova-compute看服务状态,再检查/etc/nova/nova.conf里所有服务的transport_url是否一致,最后用rabbitmqctl list_queues看队列是否堆积。
3.3 现象:调度失败但资源明明够
原因:resource_tracker上报有周期,默认update_resources_interval是 60 秒,刚释放的资源不会立刻反映到调度器。解决:临时调小该参数方便调试,但生产环境别调太小,会增加消息队列压力。另外检查ram_allocation_ratio和cpu_allocation_ratio是否被改过。
3.4 现象:源码里找不到文档提到的类或方法
原因:OpenStack 版本差异大,N 版和 Yoga 版的模块拆分可能完全不同,比如nova/api/openstack/compute/下的文件在不同版本里增删频繁。解决:先确认版本nova-manage version,再对照对应版本的源码分支看,别拿旧文档套新代码。
3.5 现象:pdb 断点导致服务卡死
原因:pdb.set_trace()会阻塞整个进程,而 OpenStack 服务是多线程/多协程的,一个请求卡住可能拖垮整个服务。解决:只在测试环境用,且用完立刻删掉断点重启服务。更安全的做法是用debugpy远程调试,或者用日志 +oslo.log的DEBUG级别打点。
4. 从源码到落地:用 Kolla 部署一套可调试的 OpenStack 环境
读源码最怕没有可复现的环境。用 Kolla 部署一套容器化的 OpenStack,是当前比较省心的方式,而且容器里可以直接进源码目录调试。Kolla 把每个服务打包成容器,源码在容器内的/var/lib/kolla/venv/lib/python3.x/site-packages/下,你可以docker exec进去直接看、直接改。
# 1. 安装 kolla-ansible(以 pip 为例) pip install kolla-ansible # 2. 准备配置文件 sudo mkdir -p /etc/kolla sudo chown $USER:$USER /etc/kolla cp -r /usr/local/share/kolla-ansible/etc_examples/kolla/* /etc/kolla/ cp /usr/local/share/kolla-ansible/ansible/inventory/all-in-one . # 3. 生成密码 kolla-genpwd # 4. 引导部署(all-in-one 模式) kolla-ansible -i all-in-one bootstrap-servers kolla-ansible -i all-in-one prechecks kolla-ansible -i all-in-one deploy # 5. 部署完成后进入 nova_compute 容器看源码 docker exec -it nova_compute bash python -c "import nova; print(nova.__file__)"这套流程的关键参数在/etc/kolla/globals.yml里,kolla_base_distro选ubuntu或centos,openstack_release选版本号,network_interface填你的网卡名。部署完成后,docker ps能看到一堆容器,nova_compute、nova_api、nova_scheduler各司其职。进容器后源码路径就是python -c "import nova; print(nova.__file__)"输出的那个目录,直接vim改,改完docker restart nova_compute就生效。这比在物理机上折腾依赖省事得多,也方便你反复试错。
提示:Kolla 部署对资源有要求,all-in-one 至少 8G 内存、40G 磁盘,否则 prechecks 阶段就会报资源不足。
5. 进阶技巧:用 osprofiler 追踪跨模块调用耗时
当你已经能读懂单个模块的源码后,下一步是搞清楚一次请求跨了多少模块、每段耗时多少。OpenStack 内置了osprofiler,可以在 API 请求里带上 trace id,把 Nova、Neutron、Cinder 的调用链串起来。开启方式是在/etc/nova/nova.conf里加[profiler]段,enabled = true、trace_sqlalchemy = true,然后发请求时加--os-profiler参数。
# 1. 在 nova.conf 中启用 profiler # [profiler] # enabled = true # hmac_keys = SECRET_KEY # trace_sqlalchemy = true # 2. 重启 nova 相关服务 sudo systemctl restart nova-api nova-compute nova-scheduler # 3. 发起带 trace 的请求 openstack --os-profiler SECRET_KEY server create --flavor m1.small --image cirros --nic net-id=<net-id> trace-vm # 4. 请求返回的 header 里会有 X-Trace-Info,用它去查调用链 # 如果接了 osprofiler 的存储后端(如 MongoDB),可以直接看火焰图osprofiler的价值在于它把 RPC 调用、数据库查询、HTTP 请求都打上了时间戳,你能一眼看出是调度慢、数据库慢还是 Hypervisor 慢。参数上hmac_keys是签名密钥,防止 trace 被伪造;trace_sqlalchemy打开后会记录 SQL 耗时,排查数据库瓶颈特别有用。我一般会在测试环境常开 profiler,生产环境只在排查特定问题时临时开,因为它的性能开销在 5% 到 10% 左右。
最后说个我自己的习惯:读 OpenStack 源码千万别从第一行读到最后一行,而是带着一个具体问题去读,比如「创建虚拟机时安全组规则在哪生效」,然后顺着调用链一路 grep 下去。每读通一条链,就在 docx 上补一张自己的调用图,几个月下来这份文档就变成了你自己的源码地图。希望帮到你。
本文还有配套的精品资源,点击获取