Launch API in the same process as conductor for singleprocess Ironic
Indirection proved to impose very high performance costs. Even though we've identified and fixed a few major contributors to the regression, using RPC for a process to access itself has always been a stopgap measure rather than a proper architecture. This change moves the API from a separate service under the Launcher to a separate thread in the conductor, similarly to how the JSON RPC server is started. This paves the way to remove the local RPC entirely in the next patch. Change-Id: I0f3d854336bcc7ea1062f9a995e6d8979cb0cc22 Signed-off-by: Dmitry Tantsur <dtantsur@protonmail.com>
This commit is contained in:
@@ -19,12 +19,9 @@ from oslo_service import service
|
|||||||
from ironic.command import conductor as conductor_cmd
|
from ironic.command import conductor as conductor_cmd
|
||||||
from ironic.command import utils
|
from ironic.command import utils
|
||||||
from ironic.common import service as ironic_service
|
from ironic.common import service as ironic_service
|
||||||
from ironic.common import wsgi_service
|
|
||||||
from ironic.conductor import local_rpc
|
from ironic.conductor import local_rpc
|
||||||
from ironic.conductor import rpc_service
|
from ironic.conductor import rpc_service
|
||||||
from ironic.console import novncproxy_service
|
from ironic.console import novncproxy_service
|
||||||
from ironic.objects import base as objects_base
|
|
||||||
from ironic.objects import indirection
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@@ -53,19 +50,11 @@ def main():
|
|||||||
|
|
||||||
mgr = rpc_service.RPCService(CONF.host,
|
mgr = rpc_service.RPCService(CONF.host,
|
||||||
'ironic.conductor.manager',
|
'ironic.conductor.manager',
|
||||||
'ConductorManager')
|
'ConductorManager',
|
||||||
|
embed_api=True)
|
||||||
conductor_cmd.issue_startup_warnings(CONF)
|
conductor_cmd.issue_startup_warnings(CONF)
|
||||||
launcher.launch_service(mgr)
|
launcher.launch_service(mgr)
|
||||||
|
|
||||||
# Sets the indirection API to direct API calls for objects across the
|
|
||||||
# RPC layer.
|
|
||||||
if CONF.rpc_transport in ['local', 'none'] or CONF.use_rpc_for_database:
|
|
||||||
objects_base.IronicObject.indirection_api = \
|
|
||||||
indirection.IronicObjectIndirectionAPI()
|
|
||||||
|
|
||||||
wsgi = wsgi_service.WSGIService('ironic_api', CONF.api.enable_ssl_api)
|
|
||||||
launcher.launch_service(wsgi)
|
|
||||||
|
|
||||||
# NOTE(TheJulia): By default, vnc is disabled, and depending on that
|
# NOTE(TheJulia): By default, vnc is disabled, and depending on that
|
||||||
# overall process behavior will change. i.e. we're not going to force
|
# overall process behavior will change. i.e. we're not going to force
|
||||||
# single process which breaks systemd process launch detection.
|
# single process which breaks systemd process launch detection.
|
||||||
|
@@ -21,6 +21,7 @@ from oslo_utils import timeutils
|
|||||||
from ironic.common import console_factory
|
from ironic.common import console_factory
|
||||||
from ironic.common import rpc
|
from ironic.common import rpc
|
||||||
from ironic.common import rpc_service
|
from ironic.common import rpc_service
|
||||||
|
from ironic.common import wsgi_service
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@@ -43,8 +44,11 @@ DRAIN = multiprocessing.Event()
|
|||||||
|
|
||||||
class RPCService(rpc_service.BaseRPCService):
|
class RPCService(rpc_service.BaseRPCService):
|
||||||
|
|
||||||
def __init__(self, host, manager_module, manager_class):
|
def __init__(self, host, manager_module, manager_class,
|
||||||
|
embed_api=False):
|
||||||
super().__init__(host, manager_module, manager_class)
|
super().__init__(host, manager_module, manager_class)
|
||||||
|
self.apiserver = None
|
||||||
|
self._embed_api = embed_api
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def deregister_on_shutdown(self):
|
def deregister_on_shutdown(self):
|
||||||
@@ -57,6 +61,11 @@ class RPCService(rpc_service.BaseRPCService):
|
|||||||
super()._real_start()
|
super()._real_start()
|
||||||
rpc.set_global_manager(self.manager)
|
rpc.set_global_manager(self.manager)
|
||||||
|
|
||||||
|
if self._embed_api:
|
||||||
|
self.apiserver = wsgi_service.WSGIService(
|
||||||
|
'ironic_api', CONF.api.enable_ssl_api)
|
||||||
|
self.apiserver.start()
|
||||||
|
|
||||||
# Start in a known state of no console containers running.
|
# Start in a known state of no console containers running.
|
||||||
# Any enabled console managed by this conductor will be started
|
# Any enabled console managed by this conductor will be started
|
||||||
# after this
|
# after this
|
||||||
@@ -67,6 +76,14 @@ class RPCService(rpc_service.BaseRPCService):
|
|||||||
extend_time = initial_time + datetime.timedelta(
|
extend_time = initial_time + datetime.timedelta(
|
||||||
seconds=CONF.hash_ring_reset_interval)
|
seconds=CONF.hash_ring_reset_interval)
|
||||||
|
|
||||||
|
# Stop serving the embedded API first to avoid any new requests
|
||||||
|
try:
|
||||||
|
if self.apiserver is not None:
|
||||||
|
self.apiserver.stop()
|
||||||
|
self.apiserver.wait()
|
||||||
|
except Exception:
|
||||||
|
LOG.exception('Service error occurred when stopping the API')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.manager.del_host(
|
self.manager.del_host(
|
||||||
deregister=self.deregister_on_shutdown,
|
deregister=self.deregister_on_shutdown,
|
||||||
|
Reference in New Issue
Block a user