添加 worker 重试和未来更新支持

Launchpad 蓝图: https://blueprints.launchpad.net/barbican/+spec/add-worker-retry-update-support

Barbican worker 进程需要一种机制来支持重试失败但可恢复的任务(例如,当远程系统不可用时),以及处理长期运行的订单流程(例如证书生成)的更新。此蓝图定义了此重试和更新处理的要求,并提出了一种实现此功能的方案。

问题描述

Barbican 通过数据存储跟踪实体(例如订单,目前是 Barbican 中唯一的跟踪实体)来管理异步任务。这些实体具有一个状态字段,用于跟踪其状态,从新实体的 PENDING 开始,并移动到 ACTIVE 或 ERROR 状态,分别表示异步任务的成功或不成功终止。

Barbican worker 进程实现这些异步任务,如这个 wiki 页面所示:https://github.com/cloudkeep/barbican/wiki/Architecture

如图所示,典型的部署可以包括多个 worker 进程并行运行在一个任务队列上。队列通过 RPC 在 worker 进程上调用任务方法。在某些情况下,这些调用的任务需要实体(例如订单)保持 PENDING 状态,以便允许将来进行后续处理,或者由于临时阻塞条件(例如,远程服务当前不可用)而重试处理。

以下是重试未来任务并因此将跟踪实体保持在 PENDING 状态的要求

R-1) Barbican needs to support extended workflow processes whereby an entity
     might be PENDING for a long time, requiring periodic status checks to
     see if the workflow is completed

R-2) Barbican needs to support re-attempting an RPC task at some point in
     the future if dependent services are temporarily unavailable

请注意,此蓝图不处理对同一实体进行的并发更新,例如对订单执行定期状态检查,以及应用对同一订单的客户端更新。这将在未来的蓝图中解决。

另请注意,此蓝图不处理由于队列中的消息丢失或 worker 在处理实体时崩溃而“卡”在 PENDING 状态的实体。这也会在未来的蓝图中解决。

此外,最终实现还需要以下非功能性要求

NF-1) To keep entity state consistent, only one worker can work on an
      entity or manage retrying tasks at a time.

NF-2) For resilience of the worker cluster:

    a) Any worker process (of a cluster of workers) should be able to
       handle retrying entities independently of other worker processes,
       even if these worker processes are intermittently available.

    b) If a worker comes back online after going down, it should be able to
       start processing retry tasks again, without need to synchronize with
       other workers.

NF-3) In the default standalone Barbican implementation, it should be
      possible to demonstrate the periodic status check feature via the
      SimpleCertificatePlugin class in
      barbican.plugin.simple_certificate-Manager.py.

以下假设成立

A-1) Accurate retry times are not required:

    a) For example, if a task is to be retried in 5 minutes, it would be
       acceptable if the task was actually retried after more than 5
       minutes. For SSL certificate workflows, where some certificate types
       can take days to process, such retry delays would not be
       significant.

    b) Relaxed retry schedules allow for more granular retry checking
       intervals, and to allow for delays due to excessive tasks in queues
       during busy times.

    c) Excessive delays in retry times from expected could indicate that
       worker nodes are overloaded. This blueprint does not address
       this issue, deferring to deployment monitoring and scaling
       processes.

提议的变更

对于要求 R-1 和 R-2,此蓝图建议 worker 任务使用的插件(例如证书插件)确定是否应重试任务以及在未来何时重试。如果插件确定应重试任务,则这些任务将安排进行未来的重试尝试。

为了实现此调度过程,此蓝图建议使用 Oslo 定期任务功能,如下所述

https://docs.openstack.org/developer/oslo-incubator/api/openstack.common.periodic_task.html

这里显示了一个使用较旧代码库的工作示例实现

https://github.com/cloudkeep/barbican/blob/verify-resource/barbican/queue/server.py#L174

每个 worker 节点然后可以执行一个定期任务服务,该服务以可配置的频率(例如,每 15 秒)调用一个方法。然后,该方法将查询哪些任务需要重试(例如,如果当前时间 >= 重试时间),并为每个任务向队列发出重试任务消息。一旦任务入队,该方法将从重试列表中删除重试记录。最终,队列将调用 worker 来实现这些重试任务。

为了提供一种在独立 Barbican 中评估重试功能的手段,符合 NF-3 的要求,barbican.plugin.simple_certificate_manager.py 中的 SimpleCertificatePlugin 类将被修改,使其 issue_certificate_request() 方法返回 5 秒的重试时间(可配置)。check_certificate_status() 方法然后将返回一个成功的执行以在 ACTIVE 状态下终止订单。

此蓝图建议向数据模型添加两个实体:OrderRetryTask 和 EntityLock。

OrderRetryTask 实体将管理哪些任务需要在哪些实体上重试,并将具有以下属性

1) id: Primary key for this record

2) order_id: FK to the order record the retry task is intended for

3) retry_task: The RPC method to invoke for the retry. This method could be
               a different method than the current one, such as to support
               a SSL certificate plugin checking for certificate updates
               after initiating the certificate process

4) retry_at: The timestamp at or after which to retry the task

5) retry_args: A list of args to send to the retry_task. This list includes
               the entity ID, so no need for an entity FK in this entity

6) retry_kwargs: A JSON-ified dict of the kwargs to send to retry_task

7) retry_count: A count of how many times this task has been retried

新的重试记录将添加到需要在未来重试的任务中,由插件在工作流程处理过程中确定。下一个定期任务方法调用然后将此任务发送到队列,供另一个 worker 后续实现。

EntityLock 实体将管理允许从 OrderRetryTask 表中删除的 worker,因为根据上述 NF-1,只有单个 worker 才能从该表中删除。此实体将具有以下属性

1) entity_to_lock: The name of the entity to lock ('OrderRetryTask' here).
                   This would be a primary key.

2) worker_host_name: The host name of the worker that has the
                     OrderRetryTask entity 'locked'.

3) created_at: When this table was locked.

此实体将只有零个或一个记录。因此,上述定期方法将执行以下伪代码

Start SQLAlchemy session/transaction
try:
    Attempt to insert a new record into the EntityLock table
    session.commit()
except:
    session.rollback()
    Handle 'stuck' locks (see paragraph below)
    return

try:
    Query for retry tasks
    Send retry tasks to the queue
    Remove enqueued retry tasks from OrderRetryTask table
    session.commit()
except:
    session.rollback()
finally:
    Remove record from EntityLock table
    Clear SQLAlchemy session/transaction

表锁可能存在问题,如果锁定过程在删除锁之前崩溃。但是,worker 持有锁的时间应该很短,因此上述锁尝试回滚过程应该检查并删除基于锁上的“created_at”时间戳的陈旧锁。

为了分离编码问题,将此过程实现在一个单独的 Oslo “service” 服务器进程中是有意义的,类似于 Keystone listener 方法 此服务将仅运行 Oslo 定期任务方法,以执行重试更新过程。如果该方法未能运行,例如由于另一个 worker 锁定资源,则可以返回/退出。下一个定期调用然后将重新启动该过程。

备选方案

与其让每个 worker 进程管理重试任务,不如指定一个单独的节点来管理这些重试。这将消除对 EntityLock 实体的需求。但是,这种方法需要在 Barbican 网络中配置另一个节点,增加部署复杂性。此管理器节点也将成为管理重试任务的单点故障。

数据模型影响

如上所述,将需要两个新实体。不需要迁移。

REST API 影响

安全影响

通知与审计影响

其他最终用户影响

性能影响

将定期任务添加到识别要重试的任务会给 worker 节点带来额外的负载(假设它们是与正常 worker 处理共置的进程,如预期)。但是,此过程不执行重试工作,而是将任务发布到队列,然后均匀地分配回 worker 进程。因此,给定 worker 上的额外负载应该很小。

本提案包括利用锁来处理多个 worker 节点之间处理重试任务时的并发问题。这可能会导致两种性能影响:(1)多个 worker 可能会争夺锁,导致无法获取锁的 worker 的性能下降,以及(2)如果持有锁的 worker 崩溃,锁可能会“卡住”。

关于 (1),锁仅用于参与处理非时间敏感的异步任务的 worker 节点上。此外,锁的使用时间将非常短,仅足够执行重试任务的查询并将这些任务发送到队列进行后续处理。此外,每个 worker 节点上的定期过程处理这些重试任务,因此如果 worker 节点的部署是交错的,则重试过程不应冲突。另一种选择是随机抖动定期间隔(例如,30 秒 +/- 5 秒),以便 worker 节点不太可能相互冲突。

关于 (2) 关于“卡住”锁的担忧,由于涉及锁的条件是长期运行的订单,这些订单在恢复锁之前可能会受到延迟,或者是在资源不可用的罕见情况下,这种情况不应严重到需要解决。但是,该提案确实建议使用其 created_at 时间戳来删除卡住的锁。

其他部署者影响

Barbican 配置文件需要一个配置参数来定期运行重试查询过程,称为“schedule_period_seconds”,默认值为 15 秒。此参数将放置在一个新的“[scheduler]”组中。

将使用一个名为“retry_lock_timeout_seconds”的配置参数来释放上述“Proposed Change”部分中描述的重试任务表上的“卡住”锁。此参数也将添加到“[scheduler]”组中。

将使用一个名为“delay_before_update_seconds”的配置参数来配置 SimpleCertificatePlugin 从启动演示证书订单到调用更新证书方法的时间量。此参数将放置在一个新的“[simple_certificate]”组中。

一旦部署了修订后的代码库,这些配置将被应用和使用。

开发人员影响

实现

负责人

主要负责人

john-wood-w

其他贡献者

Chelsea Winfree

工作项

  1. 为 OrderRetryTask 和 EntityLock 添加数据模型实体和单元测试

  2. 根据 Approach 部分,向 SimpleCertificatePlugin 添加逻辑,以允许演示重试功能

  3. 修改 barbican.tasks.certificate_resources.py 的 _schedule_retry_task,以将重试记录添加到 OrderRetryTask 表中

  4. 添加 Oslo 定期任务支持

  5. 实现定期方法,执行查询需要重试的任务

  6. 实现 worker 将重试 RPC 消息发送回队列…请参阅下面的注释

  7. 添加新的脚本来启动 Oslo 定期任务,称为 bin/barbican-task-scheduler.py 和 .sh,类似于 bin/barbican-keystone-listener.py 和 .sh

  8. 将 Barbican Devstack gate 功能测试添加到通过上述 SimpleCertificatePlugin 逻辑测试新的重试功能

  9. 添加逻辑来处理 OrderRetryTask 表上的过期锁

请注意,对于 #6,需要对“queue”和“tasks”包进行一些修改,以允许服务器逻辑通过客户端逻辑将消息发送到队列,主要是为了打破循环依赖。再次参见 此处 的工作示例,了解此服务器/客户端/重试处理的示例。

依赖项

测试

除了计划的单元测试外,Barbican 存储库中的基于 Tempest 的功能测试将得到增强,以添加对默认证书插件的新重试功能的测试。

文档影响

开发人员指南需要更新,以包括上述详细的附加定期重试过程。部署指南需要更新,以指定需要执行一个新过程(用于 bin/barbican-task-scheduler.sh 过程)。

参考资料