stackstorm 25. 源码分析之----stackstorm的actionrunner服务分析

本文深入探讨了StackStorm的actionrunner服务,解析了actionrunner的主入口点、工作流程以及ActionExecutionDispatcher类的角色。ActionExecutionDispatcher监听多个队列,根据消息的routing_key处理不同状态的LiveAction,如调度、取消、暂停和恢复。文章详细介绍了从触发器到执行的整个处理路径,展示了服务间如何通过exchange和routing_key进行消息传递。
摘要由CSDN通过智能技术生成

目标:
弄清楚actionrunner的原理

1 总入口
st2/st2actions/st2actions/cmd/actionrunner.py

def main():
    try:
        _setup()
        return _run_worker()
    except SystemExit as exit_code:
        sys.exit(exit_code)
    except:
        LOG.exception('(PID=%s) Worker quit due to exception.', os.getpid())
        return 1
    finally:
        _teardown()


def _setup():
    common_setup(service='actionrunner', config=config, setup_db=True, register_mq_exchanges=True,
                 register_signal_handlers=True)
    _setup_sigterm_handler()


def _run_worker():
    LOG.info('(PID=%s) Worker started.', os.getpid())

    components = [
        scheduler.get_scheduler(),
        worker.get_worker()
    ]

    try:
        for component in components:
            component.start()

        for component in components:
            component.wait()
    except (KeyboardInterrupt, SystemExit):
        LOG.info('(PID=%s) Worker stopped.', os.getpid())

        errors = False

        for component in components:
            try:
                component.shutdown()
            except:
                LOG.exception('Unable to shutdown %s.', component.__class__.__name__)
                errors = True

        if errors:
            return 1
    except:
        LOG.exception('(PID=%s) Worker unexpectedly stopped.', os.getpid())
        return 1

    return 0

分析:
1.1)关键代码就是
    components = [
        scheduler.get_scheduler(),
        worker.get_worker()
    ]

    try:
        for component in components:
            component.start()

对于worker.get_worker()及其start()放在2的分析
对于scheduler.get_scheduler()以及其start()放在3的分析


2 worker.get_worker()及其start()
st2/st2actions/st2actions/worker.py

def get_worker():
    with Connection(transport_utils.get_messaging_urls()) as conn:
        return ActionExecutionDispatcher(conn, ACTIONRUNNER_QUEUES)

分析:
2.1) 变量打印
(Pdb) p transport_utils.get_messaging_urls()
['amqp://rabbitmq:V6AoJ5TH@rabbitmq.openstack.svc.cluster.local:5672']
ACTIONRUNNER_QUEUES = [
    queues.ACTIONRUNNER_WORK_QUEUE,
    queues.ACTIONRUNNER_CANCEL_QUEUE,
    queues.ACTIONRUNNER_PAUSE_QUEUE,
    queues.ACTIONRUNNER_RESUME_QUEUE
]

对应具体内容如下:
# Used by the action runner service
ACTIONRUNNER_WORK_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.work', routing_key='scheduled')
ACTIONRUNNER_CANCEL_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.cancel', routing_key='canceling')
ACTIONRUNNER_PAUSE_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.pause', routing_key='pausing')
ACTIONRUNNER_RESUME_QUEUE = liveaction.get_status_management_queue(
    'st2.actionrunner.resume', routing_key='resuming')

def get_status_management_queue(name, routing_key):
    return Queue(name, Exchange('st2.liveaction.status', type='topic'), routing_key=routing_key)

分析:
也就是说ActionExecutionDispatcher作为消息处理类,可以监听处理上面4个队列的消息,
exchange经过routing_key绑定到队列关系如下
'st2.liveaction.status'--->'scheduled'--->'st2.actionrunner.work',
'st2.liveaction.status'--->'canceling'--->'st2.actionrunner.cancel', 
'st2.liveaction.status'--->'pausing'--->'st2.actionrunner.pause',
'st2.liveaction.status'--->'resuming'--->'st2.actionrunner.resume'


2.2) 分析ActionExecutionDispatcher(conn, ACTIONRUNNER_QUEUES)
class ActionExecutionDispatcher(MessageHandler):
    message_type = LiveActionDB

    def __init__(self, connection, queues):
        super(ActionExecutionDispatcher, self).__init__(connection, queues)
        self.container = RunnerContainer()
        self._running_liveactions = set()

2.2.1)打印
(Pdb) p connection
<Connection: amqp://rabbitmq:**@rabbitmq.openstack.svc.cluster.local:5672// at 0x4863090>
(Pdb) p queues
[<unbound Queue st2.actionrunner.work -> <unbound Exchange st2.liveaction.status(topic)> -> scheduled>, <unbound Queue st2.actionrunner.cancel -> <unbound Exchange st2.liveaction.status(topic)> -> canceling>, <unbound Queue st2.actionrunner.pause -> <unbound Exchange st2.liveaction.status(topic)> -> pausing>, <unbound Queue st2.actionrunner.resume -> <unbound Exchange st2.liveaction.status(topic)> -> resuming>]

2.2.2)分析ActionExecutionDispatcher处理入口
ActionExecutionDispatcher本身是消息处理类。

class ActionExecutionDispatcher(MessageHandler):

    def process(self, liveaction):
        """Dispatches the LiveAction to appropriate action runner.

        LiveAction in statuses other than "scheduled" and "canceling" are ignored. If
        LiveAction is already canceled and result is empty, the LiveAction
        is updated with a generic exception message.

        :param liveaction: Action execution request.
        :type liveaction: ``st2common.models.db.liveaction.LiveActionDB``

        :rtype: ``dict``
        """

        if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELED:
            LOG.info('%s is not executing %s (id=%s) with "%s" status.',
                     self.__class__.__name__, type(liveaction), liveaction.id, liveaction.status)
            if not liveaction.result:
                updated_liveaction = action_utils.update_liveaction_status(
                    status=liveaction.status,
                    result={'message': 'Action execution canceled by user.'},
                    liveaction_id=liveaction.id)
                executions.update_execution(updated_liveaction)
            return

        if liveaction.status not in ACTIONRUNNER_DISPATCHABLE_STATES:
            LOG.info('%s is not dispatching %s (id=%s) with "%s" status.',
                     self.__class__.__name__, type(liveaction), liveaction.id, liveaction.status)
            return

        try:
            liveaction_db = action_utils.get_liveaction_by_id(liveaction.id)
        except StackStormDBObjectNotFoundError:
            LOG.exception('Failed to find liveaction %s in the database.', liveaction.id)
            raise

        if liveaction.status != liveaction_db.status:
            LOG.warning(
                'The status of liveaction %s has changed from %s to %s '
                'while in the queue waiting for processing.',
                liveaction.id,
                liveaction.status,
                liveaction_db.status
            )

        dispatchers = {
            action_constants.LIVEACTION_STATUS_SCHEDULED: self._run_action,
            action_constants.LIVEACTION_STATUS_CANCELING: self._cancel_action,
            action_constants.LIVEACTION_STATUS_PAUSING: self._pause_action,
            action_constants.LIVEACTION_STATUS_RESUMING: self._resume_action
        }

        return dispatchers[liveaction.status](liveaction)

分析:
2.2.2.1) 打印变量
(Pdb) p liveaction
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7b88d8b4a73700014aa920', u'name': None}, u'trace_context': {u'id_': u'5e7b88d8b4a73700014aa921', u'trace_tag': u'st2.IntervalTimer-8c13d9a3-12a3-4cff-88d3-66c804a47590'}, u'rule': {u'id': u'5e660433c75b50001c06b822', u'name': u'network-check'}, u'user': u'admin@example.org'}, end_timestamp=None, id=5e7b88d9b4a73700014aa922, notify=None, parameters={u'cmd': u'curl busybox:80/cmd/network/check.sh%20cn', u'email_to': [u'chao.ma@easystack.cn'], u'email_from': u'noreply@easystack.cn'}, result={}, runner_info={}, start_timestamp="2020-03-25 16:37:45.132682+00:00", status="scheduled")>

2.2.2.2)逻辑分析
这里实际上是根据liveaction的不同状态分别调用不同的处理方法,
例如如果状态是scheduled,就会调用ActionExecutionDispatcher类的_run_action方法。
重点分析_run_action方法,具体参见2.3)的分析

2.3) 分析ActionExecutionDispatcher类的_run_action方法
class ActionExecutionDispatcher(MessageHandler):

    def _run_action(self, liveaction_db):
        # stamp liveaction with process_info
        runner_info = system_info.get_process_info()

        # Update liveaction status to "running"
        liveaction_db = action_utils.update_liveaction_status(
            status=action_constants.LIVEACTION_STATUS_RUNNING,
            runner_info=runner_info,
            liveaction_id=liveaction_db.id)

        self._running_liveactions.add(liveaction_db.id)

        action_execution_db = executions.update_execution(liveaction_db)

        # Launch action
        extra = {'action_execution_db': action_execution_db, 'liveaction_db': liveaction_db}
        LOG.audit('Launching action execution.', extra=extra)

        # the extra field will not be shown in non-audit logs so temporarily log at info.
        LOG.info('Dispatched {~}action_execution: %s / {~}live_action: %s with "%s" status.',
                 action_execution_db.id, liveaction_db.id, liveaction_db.status)

        extra = {'liveaction_db': liveaction_db}
        try:
            result = self.container.dispatch(liveaction_db)
            LOG.debug('Runner dispatch produced result: %s', result)
            if not result:
                raise ActionRunnerException('Failed to execute action.')
        except:
            _, ex, tb = sys.exc_info()
            extra['error'] = str(ex)
            LOG.info('Action "%s" failed: %s' % (liveaction_db.action, str(ex)), extra=extra)

            liveaction_db = action_utils.update_liveaction_status(
                status=action_constants.LIVEACTION_STATUS_FAILED,
                liveaction_id=liveaction_db.id,
                result={'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))})
            executions.update_execution(liveaction_db)
            raise
        finally:
            # In the case of worker shutdown, the items are removed from _running_liveactions.
            # As the subprocesses for action executions are terminated, this finally block
            # will be executed. Set remove will result in KeyError if item no longer exists.
            # Use set discard to not raise the KeyError.
            self._running_liveactions.discard(liveaction_db.id)

        return result

分析:
2.3.1) 打印变量
(Pdb) p liveaction_db
<LiveActionDB: LiveActionDB(action="email.mistral-network-check", action_is_workflow=True, callback={}, context={u'trigger_instance': {u'id': u'5e7b88d8b

### 回答1: StackStorm是一个开源自动化工具,用于创建、管理和执行工作流和自动化任务。Docker Compose是一个工具,用于定义和运行多个Docker容器的应用程序。StackStorm提供了一个Docker Compose配置文件(docker-compose.yaml),用于快速部署StackStorm服务。 这个docker-compose.yaml文件中包含了StackStorm的各个组件的配置信息。首先,它定义了要使用的基础镜像,如StackStorm工作者(worker)和队列服务(rabbitmq)。然后,它指定了各个容器的名称、端口映射和网络配置。 在docker-compose.yaml文件中,还定义了stackstorm服务容器的环境变量,用于指定配置参数,如数据库连接、日志级别等。此外,还可以定义其他容器的环境变量,以满足不同组件的需求。 通过运行docker-compose命令,可以根据这个配置文件来创建和启动StackStorm服务容器。Docker Compose会自动解析和处理配置文件中的依赖关系,并按照定义的顺序启动容器。这可以让我们快速部署和管理StackStorm服务,并且方便地进行扩展和升级。 总之,StackStorm的docker-compose.yaml文件是用来定义和管理StackStorm服务容器的配置文件。它提供了一种简便的方式来部署和管理StackStorm,使得自动化工作流和任务的创建和执行更加方便和高效。 ### 回答2: StackStorm 是一个开源自动化平台,可以用于自动化各类任务和流程。Docker Compose 是一个用于定义和运行多个 Docker 容器的工具。一般来说,使用 Docker Compose 可以轻松地将 StackStorm 部署到 Docker 容器中。 stackstorm docker-compose.yaml 是一个用于在 Docker Compose 中定义 StackStorm 的配置文件。这个文件包含了 StackStorm 中的各个组件以及它们的配置。在编写这个文件时,我们可以指定 StackStorm 需要运行的容器数量、容器之间的依赖关系等。 通常,stackstorm docker-compose.yaml 包含以下几个部分: 1. Services 部分:这里定义了 StackStorm 中的各个组件的容器。比如,可以指定一个容器运行 StackStorm Web UI,另一个容器运行 StackStorm 后台服务,还可以有其他的容器用于运行 StackStorm 的数据库、消息队列等。 2. Networks 部分:这里定义了容器之间的网络连接方式。可以指定容器使用的网络模式,如桥接模式或者主机模式,以及容器之间的连接关系。 3. Volumes 部分:这里定义了容器中的数据卷。可以将 StackStorm 需要的数据持久化保存到宿主机上的指定目录,以便容器重启后能够恢复数据。 通过编写 stackstorm docker-compose.yaml 文件,并使用 Docker Compose 工具进行部署,可以快速搭建 StackStorm 环境,并保证该环境的可复制性和可移植性。同时,使用 Docker Compose 也方便了 StackStorm 的管理和维护工作,简化了容器之间的依赖关系管理。 ### 回答3: StackStorm是一个开源的自动化平台,它允许用户将不同的任务组织成工作流,并通过事件触发来自动化执行。Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。 在StackStorm中使用Docker Compose,可以帮助我们更方便地部署和管理StackStorm的相关容器和服务。通过编写docker-compose.yaml文件,我们可以定义StackStorm容器的配置和依赖关系。 Docker Compose使用一种声明性的语法来定义应用程序的配置。在docker-compose.yaml文件中,我们可以指定StackStorm容器的镜像、环境变量、端口映射等配置。我们还可以定义其他依赖容器,例如数据库容器,用于支持StackStorm的运行。 以下是一个示例的docker-compose.yaml文件的片段: ``` version: '3.8' services: stackstorm: image: stackstorm/stackstorm:3.3.0 ports: - 443:443 - 80:80 environment: - ST2_VERSION=3.3.0 - ST2_AUTH_USERNAME=admin - ST2_AUTH_PASSWORD=MyPassword depends_on: - postgres postgres: image: postgres:12 environment: - POSTGRES_USER=stackstorm - POSTGRES_PASSWORD=stackstorm ``` 在这个示例中,我们定义了两个服务stackstorm和postgres。stackstorm服务使用了stackstorm/stackstorm:3.3.0镜像,将443端口和80端口映射到宿主机。我们还指定了一些环境变量,用于配置StackStorm的版本和管理员账号密码。在depends_on部分,我们指定了stackstorm服务依赖于postgres服务。 通过运行docker-compose up命令,Docker Compose将会根据docker-compose.yaml文件创建并启动相应的容器。这样,我们就能够方便地使用Docker Compose来部署和管理StackStorm的相关容器和服务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值