OpenStack Cinderサービスの起動プロセス解析

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メソッドでドライバ初期化が行われます。

タグ: OpenStack Cinder WSGI Server oslo_service eventlet LVM Volume Driver

5月27日 01:23 投稿