2026-01-04,某些文章具有時(shí)效性,若有錯(cuò)誤或已失效,請?jiān)谙路?a href="#comment">留言或聯(lián)系老夜。如何閱讀OpenStack源碼?
點(diǎn)擊▲關(guān)注 “IT168企業(yè)級”給公眾號置頂
更多精彩 第一時(shí)間直達(dá)
本文轉(zhuǎn)自 |??int32bit
注:這篇文章摘抄自O(shè)penStack知乎專欄文章《如何閱讀OpenStack源碼》,針對最新的OpenStack源碼又重新整理了下。
OpenStack簡介
OpenStack是一個(gè)開源的IaaS實(shí)現(xiàn)方案,企業(yè)構(gòu)建私有云的主流選擇之一。截至到2019年4月,OpenStack已經(jīng)有9年的發(fā)展歷史了,最新發(fā)布的版本為第19個(gè)版本,代號為Stein,下一個(gè)版本Train目前已經(jīng)處于開發(fā)階段,預(yù)計(jì)今年10月發(fā)布。
最初OpenStack只有兩個(gè)子項(xiàng)目,分別為Nova和Swift,其中Nova不僅提供計(jì)算服務(wù),還包含了網(wǎng)絡(luò)服務(wù)、塊存儲服務(wù)、鏡像服務(wù)以及裸機(jī)管理服務(wù)。
之后隨著項(xiàng)目的不斷發(fā)展,從Nova中根據(jù)功能拆分為多個(gè)獨(dú)立的項(xiàng)目,如nova-volume拆分為Cinder項(xiàng)目提供塊存儲服務(wù),nova-image拆分為Glance項(xiàng)目,提供鏡像存儲服務(wù),nova-network則是neutron的前身,裸機(jī)管理也從Nova中分離出來為Ironic項(xiàng)目。
最開始容器服務(wù)也是由Nova提供支持的,作為Nova的Hypervisor driver實(shí)現(xiàn),而后容器部分功能遷移到Heat,容器部署在虛擬機(jī)中。現(xiàn)在容器管理功能已經(jīng)獨(dú)立為一個(gè)單獨(dú)的項(xiàng)目Magnum,提供容器編排服務(wù),容器服務(wù)則由Zun項(xiàng)目負(fù)責(zé)。
目前OpenStack幾個(gè)核心基礎(chǔ)組件如下:
-
Keystone:認(rèn)證服務(wù)。
-
Glance:鏡像服務(wù)。
-
Nova:計(jì)算服務(wù)。
-
Cinder:塊存儲服務(wù)。
-
Neutorn:網(wǎng)絡(luò)服務(wù)。
-
Swift:對象存儲服務(wù)。
E版之后,在這些核心服務(wù)之上,OpenStack社區(qū)又不斷出現(xiàn)新的服務(wù),如面板服務(wù)Horizon、編排服務(wù)Heat、數(shù)據(jù)庫服務(wù)Trove、文件共享服務(wù)Manila、大數(shù)據(jù)服務(wù)Sahara、工作流服務(wù)Mistral以及前面提到的容器編排服務(wù)Magnum等,這些服務(wù)幾乎都依賴于以上基礎(chǔ)服務(wù)。比如Sahara大數(shù)據(jù)服務(wù)會調(diào)用Heat模板服務(wù)創(chuàng)建基礎(chǔ)資源,Heat會調(diào)用Nova創(chuàng)建虛擬機(jī),調(diào)用Glance獲取鏡像,調(diào)用Cinder創(chuàng)建數(shù)據(jù)卷,調(diào)用Neutron創(chuàng)建網(wǎng)絡(luò)等。
OpenStack項(xiàng)目越來越多,功能越來越全面,同時(shí)服務(wù)也越來越復(fù)雜,覆蓋的技術(shù)生態(tài)越來越龐大,初次接觸OpenStack感覺面臨一個(gè)龐然大物,總會有種如”盲人摸象”的感覺。
不過不必先過于絕望,好在OpenStack項(xiàng)目具有非常良好的設(shè)計(jì)理念,雖然OpenStack項(xiàng)目眾多,組件繁雜,但幾乎所有的服務(wù)骨架脈絡(luò)基本是一樣的,熟悉了其中一個(gè)項(xiàng)目的架構(gòu),深入閱讀了其中一個(gè)項(xiàng)目源碼,再去學(xué)其他OpenStack項(xiàng)目自然會輕松很多。
本文接下來以Nova項(xiàng)目為例,一步一步剖析源碼結(jié)構(gòu),閱讀完之后,你再去看Cinder項(xiàng)目,發(fā)現(xiàn)會有一種輕車熟路的感覺。
02
工欲善其事必先利其器
要閱讀源代碼首先需要安裝科學(xué)的代碼閱讀工具,圖形界面使用pycharm沒有問題,不過通常在虛擬機(jī)或者測試服務(wù)器是沒有圖形界面的,因此首推vim,需要簡單的配置使其支持代碼跳轉(zhuǎn)和代碼搜索,可以參考我的dotfiles:GitHub – int32bit/dotfiles: A set of vim, zsh, git, and tmux configuration files.。如圖:

03
OpenStack開發(fā)基礎(chǔ)
3.1 OpenStack項(xiàng)目源碼入口導(dǎo)航
OpenStack所有項(xiàng)目都是基于Python語言開發(fā),遵循Python標(biāo)準(zhǔn)Distutils,使用setuptools工具管理項(xiàng)目。
想知道一個(gè)項(xiàng)目有哪些服務(wù)組成,入口函數(shù)(main函數(shù))在哪里,最直接的方式就是查看項(xiàng)目根目錄下的setup.cfg文件,其中console_scripts就是所有服務(wù)組件的入口,它就像一個(gè)十字路口導(dǎo)航,告訴你目的地的入口在哪里,哪條路通向哪里。
比如Nova的setup.cfg的console_scripts如下:

數(shù)了下目前最新的Nova大概有22個(gè)main函數(shù)入口,由此可知Nova項(xiàng)目安裝后會包含22個(gè)可執(zhí)行程序,其中nova-compute服務(wù)的入口函數(shù)為nova/cmd/compute.py(.?->?/)模塊的main函數(shù):

其它服務(wù)依次類推。
3.2 OpenStack開發(fā)測試環(huán)境準(zhǔn)備
由于OpenStack使用Python語言開發(fā),而Python是動態(tài)類型語言,參數(shù)類型只能在運(yùn)行時(shí)確定,不容易從代碼中看出,因此必須部署一個(gè)allinone的OpenStack開發(fā)測試環(huán)境,建議使用RDO部署:Packstack quickstart,當(dāng)然樂于折騰使用DevStack、Kolla也是沒有問題的。
3.3 OpenStack代碼調(diào)試
要想深入研究源碼,最有效的方式就是一步一步跟蹤代碼執(zhí)行,因此會使用debugger工具是關(guān)鍵技能之一。Python的debugger工具有很多,為了簡便起見,pdb工具就夠了。
使用方法也非常簡單,只要在你想設(shè)置斷點(diǎn)的地方,嵌入一行代碼:

然后注意需要通過命令行直接在終端運(yùn)行nova-api服務(wù),而不能通過systemd在后臺啟動:

此時(shí)在另一個(gè)終端創(chuàng)建一個(gè)新的虛擬機(jī),調(diào)用創(chuàng)建虛擬機(jī)API,nova-api進(jìn)程就會馬上彈出pdb shell,此時(shí)你可以通過s或者n命令一步一步執(zhí)行了。更多關(guān)于OpenStack調(diào)試技巧可參考我的另一篇文章《OpenStack斷點(diǎn)調(diào)試方法總結(jié)》。
04
OpenStack項(xiàng)目代碼框架
閱讀源碼的首要問題就是就要對代碼的結(jié)構(gòu)了然于胸,需要強(qiáng)調(diào)的是,OpenStack項(xiàng)目的目錄結(jié)構(gòu)并不是根據(jù)組件嚴(yán)格劃分,而是根據(jù)功能劃分,以Nova為例,nova/compute目錄并不是一定在nova-compute節(jié)點(diǎn)上運(yùn)行,而主要是和compute相關(guān)(虛擬機(jī)操作相關(guān))的功能實(shí)現(xiàn),同樣的,scheduler目錄代碼并不全在scheduler服務(wù)節(jié)點(diǎn)運(yùn)行,但主要是和調(diào)度相關(guān)的代碼。不過目錄結(jié)構(gòu)遵循一定的規(guī)律。
通常一個(gè)OpenStack項(xiàng)目的代碼目錄都會包含api.py、rpcapi.py、manager.py,這三個(gè)是最重要的模塊。
-
api.py:通常是供其它組件調(diào)用的封裝庫。換句話說,該模塊通常并不會由本模塊調(diào)用。比如compute目錄的api.py,通常由nova-api服務(wù)的controller調(diào)用??梢院唵握J(rèn)為是供其他服務(wù)調(diào)用的sdk。
-
rpcapi.py:這個(gè)是RPC請求的封裝,或者說是RPC封裝的client端,該模塊封裝了RPC請求調(diào)用。
-
manager.py:這個(gè)才是真正服務(wù)的功能實(shí)現(xiàn),也是RPC的server端,即處理RPC請求的入口,實(shí)現(xiàn)的方法通常和rpcapi實(shí)現(xiàn)的方法一一對應(yīng)。
比如對一個(gè)虛擬機(jī)執(zhí)行關(guān)機(jī)操作:

前面提到OpenStack項(xiàng)目的目錄結(jié)構(gòu)是按照功能劃分的,而不是服務(wù)組件,因此并不是所有的目錄都能有對應(yīng)的組件。仍以Nova為例:
-
nova/cmd:這是服務(wù)的啟動腳本,即所有服務(wù)的main函數(shù)。看服務(wù)怎么初始化,就從這里開始。
-
nova/db: 封裝數(shù)據(jù)庫訪問,目前支持的driver為sqlalchemy。
-
nova/conf:Nova所有配置項(xiàng)聲明都放在這個(gè)目錄。
-
nova/locale: 本地化處理。
-
nova/image: 封裝Glance接口。
-
nova/network: 封裝Neutron接口。
-
nova/volume: 封裝Cinder接口。
-
nova/virt: 這是支持的所有虛擬化驅(qū)動實(shí)現(xiàn),即compute driver實(shí)現(xiàn),主流的如libvirt、hyperv、ironic、vmwareapi等。
-
nova/objects: 對象模型,封裝了所有Nova對象的CURD操作,相對以前直接調(diào)用db的model更安全,并且支持版本控制。
-
nova/policies:API policy集合。
-
nova/tests: 測試代碼,如單元測試、功能測試。
-
nova/hacking: Nova代碼規(guī)范定義的一些規(guī)則。
以上同樣適用于其它服務(wù),比如Cinder等。
另外需要了解的是,所有的API入口都是從xxx-api開始的,RESTFul API是OpenStack服務(wù)的唯一入口,也就是說,閱讀源碼就從api開始。
而api組件也是根據(jù)實(shí)體劃分的,不同的實(shí)體對應(yīng)不同的controller,比如servers、flavors、keypairs等,controller的index方法對應(yīng)list操作、show方法對應(yīng)get操作、create對應(yīng)創(chuàng)建操作、delete對應(yīng)刪除操作、update對應(yīng)更新操作等。
根據(jù)服務(wù)閱讀源碼并不推薦,因?yàn)楣饫斫夥?wù)如何初始化、如何通信、如何發(fā)送心跳等就很不容易,各種高級封裝太復(fù)雜了。
我認(rèn)為比較好的閱讀源碼方式是追蹤一個(gè)任務(wù)的執(zhí)行過程,比如跟蹤啟動虛擬機(jī)的整個(gè)流程,因此接下來本文將以創(chuàng)建一臺虛擬機(jī)為例,一步步分析其過程。
05
實(shí)踐案例:Nova創(chuàng)建虛擬機(jī)過程分析
這里以創(chuàng)建虛擬機(jī)過程為例,根據(jù)前面的理論基礎(chǔ),一步步跟蹤其執(zhí)行過程。需要注意的是,Nova支持同時(shí)創(chuàng)建多臺虛擬機(jī),因此在調(diào)度時(shí)需要同時(shí)選擇調(diào)度多個(gè)宿主機(jī)。
5.1 nova-api
根據(jù)前面的理論,創(chuàng)建虛擬機(jī)的入口為nova/api/openstack/compute/servers.py的create方法,該方法檢查了一堆參數(shù)以及policy后,調(diào)用compute_api的create()方法。

這里的compute_api即前面說的nova/compute/api.py模塊,找到該模塊的create方法,該方法會創(chuàng)建數(shù)據(jù)庫記錄、檢查參數(shù)等,然后調(diào)用compute_task_api的schedule_and_build_instances方法:

compute_task_api即conductor的api.py。conductor的api并沒有執(zhí)行什么操作,直接調(diào)用了conductor_compute_rpcapi的schedule_and_build_instances方法:

該方法即conductor RPC調(diào)用api,即nova/conductor/rpcapi.py模塊,該方法除了一堆的版本檢查,剩下的就是對RPC調(diào)用的封裝,代碼只有兩行:

其中cast表示異步調(diào)用,schedule_and_build_instances是RPC調(diào)用的方法,kw是傳遞的參數(shù)。參數(shù)是字典類型,沒有復(fù)雜對象結(jié)構(gòu),因此不需要特別的序列化操作。
截至到現(xiàn)在,雖然目錄由api->compute->conductor,但仍在nova-api進(jìn)程中運(yùn)行,直到cast方法執(zhí)行,該方法由于是異步調(diào)用,會立即返回,不會等待RPC返回,因此nova-api任務(wù)完成,此時(shí)會響應(yīng)用戶請求,虛擬機(jī)狀態(tài)為building。
5.2 nova-conductor
由于是向nova-conductor發(fā)起的RPC調(diào)用,而前面說了接收端肯定是manager.py,因此進(jìn)程跳到nova-conductor服務(wù),入口為nova/conductor/manager.py的schedule_and_build_instances方法。
該方法首先調(diào)用了_schedule_instances方法,該方法首先調(diào)用了scheduler_client的select_destinations方法:

scheduler_client和compute_api以及compute_task_api都是一樣對服務(wù)的client封裝調(diào)用,不過scheduler沒有api.py模塊,而是有個(gè)單獨(dú)的client目錄,實(shí)現(xiàn)在nova/scheduler/client目錄的query.py模塊,select_destinations方法又很直接的調(diào)用了scheduler_rpcapi的select_destinations方法,終于又到了RPC調(diào)用環(huán)節(jié)。

毫無疑問,RPC封裝同樣是在nova/scheduler的rpcapi.py中實(shí)現(xiàn)。該方法RPC調(diào)用代碼如下:

注意這里調(diào)用的是call方法,說明這是同步RPC調(diào)用,此時(shí)nova-conductor并不會退出,而是等待直到nova-scheduler返回。因此當(dāng)前nova-conductor為堵塞狀態(tài),等待nova-scheduler返回,此時(shí)nova-scheduler接管任務(wù)。
5.3 nova-scheduler
同理找到scheduler的manager.py模塊的select_destinations方法,該方法會調(diào)用driver方法:

這里的driver其實(shí)就是調(diào)度驅(qū)動,在配置文件中scheduler配置組指定,默認(rèn)為filter_scheduler,對應(yīng)nova/scheduler/filter_scheduler.py模塊,該算法根據(jù)指定的filters過濾掉不滿足條件的計(jì)算節(jié)點(diǎn),然后通過weigh方法計(jì)算權(quán)值,最后選擇權(quán)值高的作為候選計(jì)算節(jié)點(diǎn)返回。調(diào)度算法實(shí)現(xiàn)這里不展開,感興趣的可以閱讀。
最后nova-scheduler返回調(diào)度的hosts集合,任務(wù)結(jié)束。由于nova-conductor通過同步方法調(diào)用的該方法,因此nova-scheduler會把結(jié)果返回給nova-conductor服務(wù)。
5.4 nova-condutor
nova-conductor等待nova-scheduler返回后,拿到調(diào)度的計(jì)算節(jié)點(diǎn)列表,回到scheduler/manager.py的schedule_and_build_instances方法。
因?yàn)榭赡芡瑫r(shí)啟動多個(gè)虛擬機(jī),因此循環(huán)調(diào)用了compute_rpcapi的build_and_run_instance方法:

看到xxxrpc立即想到對應(yīng)的代碼位置,位于nova/compute/rpcapi模塊,該方法向nova-compute發(fā)起RPC請求:

由于是cast調(diào)用,因此發(fā)起的是異步RPC,因此nova-conductor任務(wù)結(jié)束,緊接著終于輪到nova-compute服務(wù)登場了。
5.5 nova-compute
終于等到nova-compute服務(wù),服務(wù)入口為nova/compute/manager.py,找到build_and_run_instance方法,該方法調(diào)用關(guān)系如下:

這里的driver就是compute driver,通過compute配置組的compute_driver指定,這里為libvirt.LibvirtDriver,代碼位于nova/virt/libvirt/driver.py,找到spawn()方法,該方法調(diào)用Libvirt創(chuàng)建虛擬機(jī),并等待虛擬機(jī)狀態(tài)為Active,nova-compute服務(wù)結(jié)束,整個(gè)創(chuàng)建虛擬機(jī)流程也到此結(jié)束。
06
總結(jié)
本文首先簡單介紹了OpenStack項(xiàng)目,然后介紹了OpenStack的基礎(chǔ)和框架,最后通過Nova創(chuàng)建虛擬機(jī)為例分析OpenStack源碼。
需要注意的是以上創(chuàng)建虛擬機(jī)的各個(gè)服務(wù)的交互過程以及調(diào)用關(guān)系,涉及數(shù)據(jù)庫的操作,比如`instance.save()`以及`update`操作,如果配置`use_local`為`false`,則會向`nova-conductor`發(fā)起RPC調(diào)用,由`nova-conductor`代理完成數(shù)據(jù)庫更新,而不是由`nova-compute`直接訪問數(shù)據(jù)庫,這里的RPC調(diào)用過程在以上的分析中省略了。另外,在調(diào)度過程中調(diào)用placement api也省略了。
如果你對OpenStack的其它服務(wù)以及操作流程感興趣,可以參考我的openstack-workflow項(xiàng)目。


專注于云計(jì)算、大數(shù)據(jù)和分布式技術(shù),分享和學(xué)習(xí)OpenStack 、Docker、Ceph以及kubernetes相關(guān)技術(shù)。
—— 來自

IT168企業(yè)級
讓一部分人先看到企業(yè)IT的未來
微信公眾號ID :IT168qiye
點(diǎn)擊“閱讀原文”查看排版優(yōu)化版本
夜雨聆風(fēng)
