OpenStack Cinderのサービス起動フローについて調査を行いました。cinder-api、cinder-scheduler、cinder-volumeの3つの主要サービスの起動処理に焦点を当て、それぞれの初期化フローを解説します。
1、cinder-apiサービス
エントリーポイントは/usr/bin/cinder-apiで、実際の処理はcinder/cmd/apiモジュールのmain関数から開始されます。
#!/usr/bin/python2
# PBR Generated from u'console_scripts'
import sys
from cinder.cmd.api import main
if __name__ == "__main__":
sys.exit(main())
main関数の核心部分は以下の通りです。
def main():
objects.register_all()
gmr_opts.set_defaults(CONF)
CONF(sys.argv[1:], project='cinder',
version=version.version_string())
config.set_middleware_defaults()
logging.setup(CONF, "cinder")
python_logging.captureWarnings(True)
utils.monkey_patch()
gmr.TextGuruMeditation.setup_autorun(version, conf=CONF)
rpc.init(CONF)
launcher = service.process_launcher()
server = service.WSGIService('osapi_volume')
launcher.launch_service(server, workers=server.workers)
launcher.wait()
14行目でProcessLauncherオブジェクトを生成し、15行目でWSGIServiceインスタンスを作成しています。16行目ではlaunch_serviceメソッドを呼び出してサービスを起動します。
def launch_service(self, service, workers=1):
_check_service_base(service)
wrap = ServiceWrapper(service, workers)
if hasattr(gc, 'freeze'):
gc.freeze()
LOG.info('Starting %d workers', wrap.workers)
while self.running and len(wrap.children) < wrap.workers:
self._start_child(wrap)
_start_childメソッドではos.fork()を呼び出して子プロセスを生成し、_child_processメソッドで実際の処理を開始します。
def _child_process(self, service):
eventlet.hubs.use_hub()
os.close(self.writepipe)
eventlet.spawn_n(self._pipe_watcher)
random.seed()
launcher = Launcher(self.conf, restart_method=self.restart_method)
launcher.launch_service(service)
return launcher
最終的にWSGIServiceのstartメソッドが呼ばれる流れになります。
def start(self):
if self.manager:
self.manager.init_host()
self.server.start()
self.port = self.server.port
このようにしてcinder-apiサービスは正常に起動します。
2、cinder-schedulerサービス
エントリーポイントは/usr/bin/cinder-schedulerで、cinder/cmd/scheduler.pyのmain関数が実行されます。
#!/usr/bin/python2
# PBR Generated from u'console_scripts'
import sys
from cinder.cmd.scheduler import main
if __name__ == "__main__":
sys.exit(main())
main関数の核心部分は以下の通りです。
def main():
objects.register_all()
gmr_opts.set_defaults(CONF)
CONF(sys.argv[1:], project='cinder',
version=version.version_string())
logging.setup(CONF, "cinder")
python_logging.captureWarnings(True)
utils.monkey_patch()
gmr.TextGuruMeditation.setup_autorun(version, conf=CONF)
server = service.Service.create(binary='cinder-scheduler')
service.serve(server)
service.wait()
10-12行目でServiceオブジェクトを生成し、serveメソッドでサービスを起動します。最終的にlaunch_serviceインターフェースを通じてサービスのstartメソッドが呼ばれます。
3、cinder-volumeサービス
エントリーポイントは/usr/bin/cinder-volumeで、cinder/cmd/volume.pyのmain関数が実行されます。
#!/usr/bin/python2
# PBR Generated from u'console_scripts'
import sys
from cinder.cmd.volume import main
if __name__ == "__main__":
sys.exit(main())
main関数の核心部分は以下の通りです。
def main():
objects.register_all()
gmr_opts.set_defaults(CONF)
CONF(sys.argv[1:], project='cinder',
version=version.version_string())
logging.setup(CONF, "cinder")
python_logging.captureWarnings(True)
priv_context.init(root_helper=shlex.split(utils.get_root_helper()))
utils.monkey_patch()
gmr.TextGuruMeditation.setup_autorun(version, conf=CONF)
if not CONF.enabled_backends:
LOG.error('Configuration for cinder-volume does not specify '
'"enabled_backends". Using DEFAULT section to configure '
'drivers is not supported since Ocata.')
sys.exit(1)
if os.name == 'nt':
_launch_services_win32()
else:
_launch_services_posix()
プラットフォームに応じてサービス起動処理が分岐します。Linux環境では_launch_services_posix()が実行され、Serviceオブジェクトが生成されます。
@classmethod
def create(cls, host=None, binary=None, topic=None, manager=None,
report_interval=None, periodic_interval=None,
periodic_fuzzy_delay=None, service_name=None,
coordination=False, cluster=None, **kwargs):
if not host:
host = CONF.host
if not binary:
binary = os.path.basename(inspect.stack()[-1][1])
if not topic:
topic = binary
if not manager:
subtopic = topic.rpartition('cinder-')[2]
manager = CONF.get('%s_manager' % subtopic, None)
service_obj = cls(host, binary, topic, manager,
report_interval=report_interval,
periodic_interval=periodic_interval,
periodic_fuzzy_delay=periodic_fuzzy_delay,
service_name=service_name,
coordination=coordination,
cluster=cluster, **kwargs)
return service_obj
managerはvolume_managerとして初期化され、init_hostメソッドでドライバ初期化が行われます。