Openstack Nova 原始碼分析 — RPC 遠端呼叫過程
目錄
Nova Project Services
nova-api:捕獲novaclient傳送過來的HTTP請求,並且將它轉換為AMQP訊息,通過Queue來與別的services通訊。
nova-conductor:為資料庫訪問提供了一層安全保障。
NOTE:除了nova-conductor可以訪問資料庫之外,因為nova-scheduler是隻讀資料庫,而nova-api對資料庫的操作有Policy保護,所以它們也都是可以訪問資料庫的。但是最好還是僅通過nova-conductor來訪問資料庫。nova-scheduler:從可用資源池中選擇最合適的Compute Node來建立虛擬機器例項。
novaclient:是為了簡化使用者使用而將Restful API封裝起來,負責將使用者的請求轉換為標準的HTTP請求。
nova-compute:負責虛擬機器的生命週期管理
Project 的程式入口( setup.py )
每個 Openstack Project 的 setup.cfg(nova/setup.cfg) 檔案,也被稱之為 Openstack Project 的原始碼地圖。其中 [entry_points] 是該檔案最重要的 Section ,它包含了幾個相對特殊的組或稱之為名稱空間,其中下列兩個是非常重要的:
console_scripts =
:其中的每一項都表示一個可執行的指令碼,這些指令碼在 Openstack Project 部署時候就會被安裝到 lib 目錄中,是 Openstack Projec 所提供的各個服務的入口點,在系統希望執行這些程序時,需要找到的位置。 EG.nova-api = nova.cmd.api:main
從console_scripts
名稱空間我們可以看見Nova所提供的許多服務,而這些選項的值就是對應服務的程式入口。
console_scripts =
nova-all = nova.cmd.all:main #啟動所有Nova服務的輔助指令碼
nova-api = nova.cmd.api:main #作為WSGI伺服器對外提供Restful API服務,有三種類型nova-api-ec2/,通過nova.conf中的enable_apis選項值來決定。
nova-api-ec2 = nova.cmd.api_ec2:main #EC2 API支援
nova-api-metadata = nova.cmd.api_metadata:main
nova-api-os-compute = nova.cmd.api_os_compute:main #Openstack API服務
nova-cells = nova.cmd.cells:main
nova-cert = nova.cmd.cert:main #管理X509證書
nova-compute = nova.cmd.compute:main #compute服務,負責虛擬機器的生命週期管理
nova-conductor = nova.cmd.conductor:main #conductor服務,負責與資料庫的互動
nova-console = nova.cmd.console:main
nova-consoleauth = nova.cmd.consoleauth:main
nova-dhcpbridge = nova.cmd.dhcpbridge:main #管理nova-network的DHCP bridge
nova-idmapshift = nova.cmd.idmapshift:main
nova-manage = nova.cmd.manage:main
nova-network = nova.cmd.network:main #已經被Neutron取代,只有在使用Devstack部署的時候才會被使用
nova-novncproxy = nova.cmd.novncproxy:main #支援使用者通過該代理服務以vnc連線到虛擬機器例項
nova-objectstore = nova.cmd.objectstore:main #相容EC2的儲存介面
nova-rootwrap = oslo_rootwrap.cmd:main #在Openstack執行的過程中,支援以root的身份來執行某些shell指令
nova-rootwrap-daemon = oslo_rootwrap.cmd:daemon
nova-scheduler = nova.cmd.scheduler:main #scheduler服務,服務計算節點物理資源的排程
nova-serialproxy = nova.cmd.serialproxy:main
nova-spicehtml5proxy = nova.cmd.spicehtml5proxy:main
nova-xvpvncproxy = nova.cmd.xvpvncproxy:main #允許使用者通過代理的方式來訪問虛擬機器例項的控制檯。
nova.api.v21.extensions
:其中的每一項都對應了一個 API ,starting nova-api service 時會根據nova.api.v21.extensions
名稱空間來進行載入。Extensions 是不同版本之間的過渡,以後更趨向以這種方式來實現路由的Setup ,將資源對映到Controller。如果希望搞清楚一個API的實現過程,那麼這個名稱空間的選項值程式碼路徑將會作為突破口,能更有效的理清API的脈絡。
Nova中RPC(遠端過程呼叫)
RPC:同一個專案內的不同服務程序之間的互動方式。
nova-compute/nova-conductor/nova-scheduler 在啟動時都會註冊一個RPC Server,這幾個服務之間會通過訊息佇列 Queue 和 RPC 來實現互相呼叫,因為 nova-api 負責傳遞請求,並沒有會呼叫它的服務,所以無需要啟動RPC Server。
nova-compute RPC API的實現
nova-compute 主要用於管理虛擬機器的生命週期,所以其RPC API實現的方法多是用於對虛擬機器的操作。
# nova/nova/compute/rpcapi.py
class ComputeAPI(object):
# 這是一個RPC遠端呼叫的方法
def live_migration(self, ctxt, instance, dest, block_migration, host,
migration, migrate_data=None):
args = {'migration': migration}
version = '4.2'
if not self.client.can_send_version(version):
version = '4.0'
# 獲取目標 compute 主機(DEST HOST)的RPC client,即被呼叫的服務程序的HostIP
cctxt = self.client.prepare(server=host, version=version)
# 通過目標主機物件的 RPC cliient 來呼叫遠端過程方法 cast() ,以此來實現遠端呼叫
cctxt.cast(ctxt, 'live_migration', instance=instance,
dest=dest, block_migration=block_migration,
migrate_data=migrate_data, **args)
# cast()非同步遠端呼叫,不會阻塞別的程序,適合於需要長時間進行的執行過程
# cast()的第二個引數是RPC client呼叫的函式名,case()後面的引數會繼續作為引數傳入該呼叫函式
# cast()函式內的live_migration()函式是 manager.live_migration() 視具體實現遷移功能的函式,在manager.py內實現。
注意:在 Nova Project 中大多數的服務都提供了 API 或 RPC API 的實現檔案,這些 API 是服務程序自身為了能被其他的服務程序訪問所提供出來的一種介面,當別的服務程序希望影響另一個服務程序時,就可以通過 import 另一個服務程序的 rpcapi 就可以實現了。
EXAMPLE:當nova-conductor通知nova-compute建立虛擬機器例項時,過程如下:
1. 在 nova-conductor 的程式碼中使用了 import nova-compute rpcapi module 或者類例項化傳參這兩種實現方式來載入 compute rpcapi 物件。這樣 nova-conductor 就擁有了通過 RPC 訪問 nova-compue 的能力。
2. 在 nova-conductor 的程式碼實現中呼叫了 rpcapi 模組的方法,即 nova-conductor傳送了一個請求到 Queue,並等待 nova-compute 接受和響應。
3. nova-compute 接收到 nova-conductor 的請求,並作出響應。
#nova/nova/conductor/tasks/live_migrate.py
class LiveMigrationTask(base.TaskBase):
def __init__(self, context, instance, destination,
block_migration, disk_over_commit, migration, compute_rpcapi,
servicegroup_api, scheduler_client):
super(LiveMigrationTask, self).__init__(context, instance)
...
def _execute(self):
self._check_instance_is_active()
self._check_host_is_up(self.source)
if not self.destination:
self.destination = self._find_destination()
self.migration.dest_compute = self.destination
self.migration.save()
else:
self._check_requested_destination()
# TODO(johngarbutt) need to move complexity out of compute manager
# TODO(johngarbutt) disk_over_commit?
#呼叫 ComputeAPI 類中的 live_migration() RPC介面,以RPC的方式發出一個請求到Queue再被nova-compute接收
return self.compute_rpcapi.live_migration(self.context,
host=self.source,
instance=self.instance,
dest=self.destination,
block_migration=self.block_migration,
migration=self.migration,
migrate_data=self.migrate_data)
注意:**nova-compute RPC Server 接收到別的服務的RPC請求之後(呼叫了ComputeAPI提供的RPC介面),真正完成請求操作的是**nova.compute.manager 模組。
nova.compute.manager 模組
nova.compute.manager 會一直在監聽 Queue ,當Queue中存在相關的 RPC 請求時,實際上是由 manager 來實現的。
# nova/nova/compute/manager.py
5189 def live_migration(self, context, dest, instance, block_migration,
1 migration, migrate_data):
2 """Executing live migration.
3
4 :param context: security context
5 :param dest: destination host
6 :param instance: a nova.objects.instance.Instance object
7 :param block_migration: if true, prepare for block migration
8 :param migration: an nova.objects.Migration object
9 :param migrate_data: implementation specific params
10
11 """
12
13 # NOTE(danms): Remove these guards in v5.0 of the RPC API
14 if migration:
15 migration.status = 'queued'
16 migration.save()
17
18 def dispatch_live_migration(*args, **kwargs):
19 with self._live_migration_semaphore:
20 self._do_live_migration(*args, **kwargs)
21
22 # NOTE(danms): We spawn here to return the RPC worker thread back to
23 # the pool. Since what follows could take a really long time, we don't
24 # want to tie up RPC workers.
25 utils.spawn_n(dispatch_live_migration,
26 context, dest, instance,
27 block_migration, migration,
28 migrate_data)
最後
說到底,從 ComputeAPI 到 ComputeManager 的過程即是 RPC 呼叫過程。