爱奇艺大数据离在线混部

混部作为一种提高资源利用率、降低成本的的方案,被业界普遍认可。爱奇艺在云原生化与降本增效的过程中,成功将大数据离线计算、音视频内容处理等工作负载与在线业务进行了混部,并且取得了阶段性收益。本文重点以大数据为例,介绍从 0 到 1 落地混部体系的实践过程。

01

   背景

爱奇艺大数据支持了公司内运营决策、用户增长、广告分发、视频推荐、搜索、会员等重要场景,为业务提供数据驱动引擎。随着业务需求的增长,计算需要的资源量与日俱增,成本管控和资源供给面临着较大的压力。

爱奇艺大数据计算分为离线计算、实时计算两条数据处理链路,其中:

  • 离线计算包括以 Spark 为主的数据处理、以 Hive 为主的小时级乃至天级别的数仓构建及对应的报表查询分析,这类计算多在每天凌晨开始启动计算前一天的数据,在上午结束。每天 0 - 8 点是计算资源需求的高峰期,集群总资源往往不足,任务经常排队、积压,而白天则出现较大空闲导致资源浪费。

  • 实时计算包括 Kafka + Flink 为代表的实时数据流处理,具有比较稳定的资源需求。

为了均衡大数据资源利用,我们将离线计算、实时计算进行混部,一定程度上缓解了白天资源空闲浪费情况,但仍无法有效地削峰填谷,大数据计算资源总体利用情况仍表现出“白天低谷、凌晨高峰”的潮汐现象,如图 1 所示。

e9ff45f0e36396a8d4e75eb834d049b2.png

图 1. 大数据计算集群一天内 CPU 使用变化情况

爱奇艺在线业务则面临另一个问题:服务质量与资源利用率之间的平衡。在线业务主要服务于爱奇艺视频播放等场景,午间和晚间观看视频的用户比较多,资源使用情况具备“白天高峰、凌晨低谷”的潮汐现象(如图 2 所示)。为保证高峰期间的服务质量,在线业务通常会预留较多资源,使得资源利用率非常不理想。

dee4370587e91fe562950a1fb84765dc.jpeg

图 2. 在线业务集群一天内 CPU 使用变化情况

为提高利用率,爱奇艺自研的上一代容器平台采用了 CPU 静态超售策略,该方式虽然对提升利用率有明显效果,但是受限于内核能力等因素,无法避免单机上业务之间偶发的资源竞争问题,也导致了在线业务服务质量不稳定,这个问题始终无法得到妥善解决。

随着云原生化推进,爱奇艺容器平台逐渐转型到 Kubernetes(下文简称“K8s”) 技术栈。最近几年,K8s 社区陆续出现多个混部相关的开源项目,业界也有一些混部实践 [1]。在此背景下,计算平台团队将工作方向由“静态超售”调整到“动态超售 + 混部”。

大数据作为最典型的离线业务,是尝试落地混部的先行者。一方面,大数据有着较大体量,以及较稳定的计算资源需求;另一方面,大数据业务与在线业务在很多维度都能达到互补的效果,通过混部能充分提高资源利用率。

基于上述分析,爱奇艺计算平台团队与大数据团队开始了混部的探索。

02

   混部方案设计

爱奇艺大数据体系基于开源的 Apache Hadoop 生态构建,采用了 YARN 作为计算资源调度系统,而在线业务构建在 K8s 上,如何打通两套不同的资源调度体系,是混部方案首先需要解决的。

业界通常有两种混部方案:

  • 方案一:直接将大数据作业(Spark、Flink 等,不支持 MapReduce)跑在 K8s 上,使用其原生的调度器

  • 方案二:将 YARN 的 NodeManager(下文简称“NM”)运行在 K8s 上,大数据作业仍通过 YARN 来调度

经过慎重的考虑,我们选择了方案二,主要的原因有以下两点:

  1. 目前公司内绝大多数大数据计算作业的调度是基于 YARN 的,YARN 具有强大的调度功能(多租户多队列、机架感知)、优秀的调度性能(5k+ 容器/s)和完善的安全机制(Kerberos、Delegation Tokens),支持 MapReduce、Spark、Flink 等几乎所有大数据计算框架。爱奇艺大数据团队从 2014 年引入 YARN 开始,围绕其构建了一系列平台,用于开发、运维、计算治理等,为公司内部用户提供了便捷的大数据开发流程。因此与 YARN API 的兼容性是混部方案选型的重要考虑点之一

  2. K8s 虽有批处理调度器,但还不够成熟,且调度性能存在瓶颈(<1k 容器/s),还不足以支撑大数据场景需求

K8s 层面,双方需要有一套标准的接口来管理和使用混部资源。社区中有很多优秀项目,如阿里巴巴开源的 Koordinator [2]、腾讯开源的 FinOps 项目 Crane [3]、字节跳动开源的项目 Katalyst [4] 等。其中,Koordinator 与龙蜥操作系统(爱奇艺正在尝试的 CentOS 替代者之一)拥有“天然”的适配能力,可以协作实现在线业务负载监测、空闲的资源超分、任务分级调度、在离线工作负载 QoS 保障等,契合爱奇艺需求。

基于上述技术选型,我们通过深度改造,将 YARN NM 容器化后运行在 K8s Pod 中,并可实时感知 Koordinator 动态变化的超分计算资源,从而实现自动的横向及纵向扩缩容,最大化利用混部资源。

03

   混部调度策略演进

大数据和在线业务的混部经历了多个阶段的技术演进,下文我们将详细介绍。

阶段一:夜间分时复用

为了快速验证方案,我们首先完成了 NM  on K8s Pod 的容器化改造(该阶段尚未采用 Koordinator),作为弹性节点扩容到已有的 Hadoop 集群。在大数据层面,这些 K8s NM 与其他物理机上的 NM 一起由 YARN 统一调度。这些弹性节点每天定时启停,只在 0 - 9 点之间运行。

在这个阶段,我们完成了 20 多项改造,下面列举 5 个主要的改造点:

改造点 1:固定 IP 池

传统 NM 部署在物理机上,机器的 IP 和域名固定,在 YARN ResourceManager(下文简称“RM”) 上配置节点白名单(slaves 文件)以允许该节点加入集群。同时 YARN 集群通过 Kerberos 来实现安全认证,在部署前需要在 Kerberos KDC 生成 keytab 文件,并分发到 NM 节点上。

为了适配 YARN 的白名单、安全认证机制,自建集群我们使用了自研的静态 IP 功能,每一个静态 IP 会有对应的 K8s StaticIP 资源,以记录 Pod 和 IP 的对应关系,同时基于公有云集群我们也会部署自研的 StaticIP CRD, 并针对每一个静态 IP 创建 StaticIP 资源,从而对 YARN 提供用法与自建集群一致的固定 IP 池。基于固定 IP 池内 IP 为其提前创建好 DNS 记录、keytab 文件,这样在 NM 启动时便可快速获取所需的配置。

改造点 2:Elastic YARN Operator

为了让用户对引入弹性节点无感知,我们将弹性 NM 加入到已有的 Hadoop YARN 集群。考虑到后期混部动态感知资源的复杂性,我们选择了自研 Elastic YARN Operator,用于更好地管理弹性 NM 的生命周期。

在这一阶段,Elastic YARN Operator 支持的策略有:

  • 按需启动:应对离线任务的突发流量,包括寒暑假、节假日、重要活动等场景

  • 周期性上下线:利用在线服务每天凌晨的资源利用率低谷期,运行大数据任务

改造点 3:Node Label - 弹性与固定资源隔离

由于 Flink 等大数据实时流计算任务是 7x24 小时不间断常驻运行的,对 NM 的稳定性的要求比批处理更高,弹性 NM 节点的缩容或资源量调整会使得流计算任务重启,导致实时数据波动。为此,我们引入了 YARN Node Label 特性 [5],将集群分为固定节点(物理机 NM)和弹性节点(K8s NM)。批处理任务可以使用任意节点,流任务则只能使用固定节点运行。

此外,批处理任务容错的基础在于 YARN Application Master 的稳定性。我们的解决方案是,给 YARN 新增了一个配置,用于设置 Application Master 默认使用的 label,确保 Application Master 不被分配到弹性 NM 节点上。这一功能已经合并到社区:YARN-11084、YARN-11088 。

改造点 4:NM Graceful Decommission

我们采用了弹性节点固定时间上下线,来对在离线资源进行削峰填谷。弹性 NM 的上线由 YARN Operator 来启动,一旦启动完成,任务就可被调度上。弹性 NM 的下线则略微复杂些,因为任务仍然运行在上面,我们需要尽可能保证任务在下线的时间区间内已经结束。

例如我们周期性部署策略为:0 - 8 点弹性 NM 上线,8 - 9 点为下线时间区间,9 - 24 点为节点离线状态。通过使用 YARN graceful decommission [6] 的机制,将增量 container 请求避免分配到 decommissioning 的节点上,在下线时间区间内等待任务缓慢结束即可。

但是在我们集群中,批处理任务大部分是 Spark 3.1.1 版本,因为 Spark 申请的 YARN container 是作为 task 的 executor 来使用,在大部分情况下,1 个小时的下线区间往往是不够的。因此我们引入了 SPARK-20624 的一系列优化 [7],通过 executor 响应 YARN decommission 事件来将 executor 尽可能快速退出。

改造点 5:引入 Remote Shuffle Service - Uniffle

Shuffle 作为离线任务中的重要一环,我们采用 Spark ESS on NodeManager 的部署模式。但在引入弹性节点后,因为弹性 NM 生命周期短,无法保证在 YARN graceful decommmission 的时间区间内,任务所在节点的 shuffle 数据被消费完,导致作业整体失败。

基于这一点,我们引入了 Apache Uniffle (incubating) [8] 实现 remote shuffle service 来解耦 Spark shuffle 数据与 NM 的生命周期,NM 被转变为单纯的计算,不存储中间 shuffle 数据,从而实现 NM 快速平滑下线。

另外一方面,弹性 NM 挂载的云盘性能一般,无法承载高 IO 和高并发的随机读写,同时也会对在线服务产生影响。通过独立构建高性能 IO 的 Uniffle 集群,提供更快速的 shuffle 服务。

爱奇艺作为 Uniffle 的深度参与者,贡献了 100+ 改进和 30+ 特性,包括 Spark AQE 优化 [9] 、Kerberos 的支持 [10] 和超大分区优化 [11] 等。

阶段二:资源超分

在阶段一,我们仅使用 K8s 资源池剩余未分配资源实现了初步的混部。为了最大限度地利用空闲资源,我们引入 Koordinator 进行资源的超分配。

我们对弹性 NM 的资源容量采用了固定规格限制:10 核 batch-cpu、30 GB batch-memory(batch-cpu 和 batch-memory 是 Koordinator 超分出来的扩展资源),NM 保证离线任务使用的资源总量不会超过这些限制。

为了保证在线业务的稳定性,Koordinator 会对节点上离线任务能够使用的 CPU 进行压制 [12],压制结果由压制阈值和在线业务 CPU 实际用量(不是 request 请求)的差值决定,这个差值就是离线业务能够使用的最大 CPU 资源,由于在线业务 CPU 实际使用量不断变化,所以离线业务能够使用的 CPU 也在不断变化,如图 3 所示:

065afacc4c1ba89ef01457f7289cd451.png

图 3. Koordinator 资源分配策略

对离线任务的 CPU 压制保证了在线业务的稳定性,但是离线任务执行时间就会被拉长。如果某个节点上离线任务被压制程度比较严重,就可能会导致等待的发生,从而拖慢整体任务的运行速度。为了避免这种情况,Koordinator 提供了基于 CPU 满足度的驱逐功能 [13],当离线任务使用的 CPU 被压制到用户指定的满足度以下时,就会触发离线任务的驱逐。离线任务被驱逐后,可以调度到其他资源充足的机器上运行,避免等待。

在经过一段时间的测试验证后,我们发现在线业务运行稳定,集群 CPU 7 天平均利用率提升了 5%。但是节点上的 NM Pod 被驱逐的情况时有发生。NM 被驱逐之后,RM 不能及时感知到驱逐情况的发生,会导致失败的任务延迟重新调度。为了解决这个问题,我们开发了 NM 动态感知节点离线 CPU 资源的功能。

阶段三:从夜间分时复用到全天候实时弹性

与其触发 Koordinator 的驱逐操作,不如让 NM 主动感知节点上离线资源的变化,在离线资源充足时,调度较多任务,离线资源不足时,停止调度任务,甚至主动杀死一些离线 container 任务,避免 NM 被 Koordinator 驱逐。

根据这个思路,我们通过 YARN Operator 动态感知节点所能利用的资源,来纵向伸缩 NM 可用资源量。分两步实现:1)提供离线任务 CPU 压制指标;2)让 NM 感知 CPU 压制指标,采取措施。如图 4 所示:

9d5009ba6c111fd99b4ae4a1435d4970.png

图 4. NM 动态感知资源

CPU 压制指标

Koordinator 的 Koordlet 组件,运行于 K8s 的节点上,负责执行离线任务 CPU 压制、Pod 驱逐等操作,它以 Prometheus 格式提供了 CPU 压制指标,经过采集后就可以通过 Prometheus 对外提供。CPU 压制指标默认每隔 1 秒更新 1 次,会随着在线业务负载的变化而变化,波动较大。而 Prometheus 的指标抓取周期一般都大于 1 秒,这会造成部分数据的丢失,为了平滑波动,我们对 Koordlet 进行了修改,提供了 1 min、5 min、10 min CPU 压制指标的均值、方差、最大值和最小值等指标供 NM 选择使用。

YARN Operator 动态感知和纵向伸缩

在 NM 常驻的部署模式下,YARN Operator 提供了新的策略。通过在 YARN Operator 接收到当前部署的节点 10 min 内可利用的资源指标,用来决策是否对所在宿主机上的 NM 进行纵向伸缩。

对于扩容,一旦超过 3 核,则向 RM 进行节点的资源更新。扩容过程如图 5 所示:

e4c67f05ec7ac54c17a6f4b3ca733fa4.png

图 5. NM 动态扩容资源

缩容的话,如果抑制率控制在 10% 以内的波动,我们默认忽略。一旦超过阈值,则会触发缩容操作,分为两个步骤:1)更新节点在 RM 上的可用资源,用来堵住增量的 container 分配需求;2)将缩容请求下发给 NM 的 guarder sidecar 容器,来对部分资源超用的 container 的平滑和强制下线,避免因占用过多 CPU 资源导致整个 NM 被驱逐。

guarder 在拿到目标可用资源后,会对当前所有的 YARN container 进程进行排序,包括框架类型、运行时长、资源使用量三者,决策拿到要 kill 的进程。在 kill 前,会进行 SIGPWR 信号的发送,用来平滑下线任务,Spark Executor 接收到此信号,会尽可能平滑退出。缩容过程如图 6 所示:

f3176bf64751cff208b7a8b24e4efac8.png

图 6. NM 动态缩容资源

通常节点的资源量变动幅度不是很大,且 NM 可使用的资源量维持在较高的水平(平均有 20 core),部分 container 的存活周期为 10 秒级,因此很快就能降至目标可用资源量值。涉及到变动幅度频繁的节点,通过 guarder 的平滑下线和 kill 决策,container 失败数非常低,从线上来看,按天统计平均 force kill container 数目为 5 左右,guarder 发送的平滑下线信号有 500+,可以看到效果比较好。

在离线 CPU 资源感知功能全面上线后,NM Pod 被驱逐的情况基本消失。因此,我们逐步将混部时间由凌晨的 0 点至 8 点,扩展到全天 24h 运行,并根据在线业务负载分布情况,在一天的不同时段采用不同的 CPU 资源超分比,从而实现全天候实时弹性调度策略。伴随着全天 24h 的稳定运行,集群 CPU 利用率再度提升了 10%。从线上混部 K8s 集群来看(如图 7 所示),弹性 NM 的 vcore 使用资源量(绿线)也是动态贴合可超分的资源(黄线)。

e9fa46733b43b495a6a123b9cf7519c4.png

图 7. 混部资源分配及使用情况

阶段四:提升资源超分率

为了提供更多的离线资源,我们开始逐步调高 CPU 资源的超分比,而 NM Pod 被驱逐的情况再次发生了,这一次的原因是内存驱逐。我们将物理机器的内存超分比设置为 90%,从集群总体情况看,物理机器上的内存资源比较充足,刚开始我们只关注了 CPU 资源,没有关注内存资源。而 NM 的 CPU 和内存按照 1:4 的比例来使用,随着 CPU 超分比的提高,YARN 任务需要的内存也在提升,最终当 K8s 节点内存使用量超过设定的阈值时,就会触发 Koordinator 的驱逐操作。

经过观察,我们发现内存驱逐在某些节点上发生的概率特别高,这些节点的内存比其他节点内存小,而 CPU 数量是相同的,因此这些节点在 CPU 超分比相同的情况下,更容易因为内存原因被驱逐,它们能提供的离线内存更少。因此,guarder 容器也需要感知节点的离线内存资源用量,并根据资源用量采取相应的措施,这个过程与 CPU 离线资源的感知一样的,不再赘述。

内存感知功能上线后,我们又逐步提升了 CPU 的超分比,当前在线业务集群的 CPU 利用率已经提升到全天平均 40%+、夜间 58% 左右。

e10dc4d1c1088ba23f1b60ea10e64192.jpeg

图 8. 混部集群 CPU 利用率


04

   效果

通过大数据离线计算与在线业务的混部,我们将在线业务集群 CPU 平均利用率从 9% 提升到 40%+,在不增加机器采购的同时满足了部分大数据弹性计算的资源需求,每年节省数千万元成本。

同时,我们也将这套框架应用到大数据 OLAP 分析场景,实现了 Impala/Trino on K8s 弹性架构,满足数据分析师日常动态查询需求,支持了寒暑假、春晚直播、广告 618 与双 11 等重要活动期间临时大批量资源扩容需求,保障了广告、BI、会员等数据分析场景的稳定、高效。


05

   未来计划

当前,大数据离在线混部已稳定运行一年多,并取得阶段性成果,未来我们将基于这套框架进一步推进大数据云原生化:

  1. 完善离在线混部可观测性:建立精细化的 QoS 监控,保障在线服务、大数据弹性计算任务的稳定性。

  2. 加大离在线混部力度:K8s 层面,继续提高宿主机资源利用率,提供更多的弹性计算资源供大数据使用。大数据层面,进一步提升通过离在线混部框架调度的弹性计算资源占比,节省更多成本。

  3. 大数据混合云计算:目前我们主要使用爱奇艺内部的 K8s 进行混部,随着公司混合云战略的推进,我们计划将混部推广到公有云 K8s 集群中,实现大数据计算的多云调度。

  4. 探索云原生的混部模式:尽管复用 YARN 的调度器能让我们快速利用混部资源,但它也带来了额外的资源管理和调度开销。后续我们也将探索云原生的混部模式,尝试将大数据的计算任务直接使用 K8s 的离线调度器进行调度,进一步优化调度速度和资源利用率。


参考资料

[1] 一文看懂业界在离线混部技术. https://www.infoq.cn/article/knqswz6qrggwmv6axwqu

[2] Koordinator: QoS-based Scheduling for Colocating on Kubernetes. https://koordinator.sh/

[3] Crane: Cloud Resource Analytics and Economics in Kubernetes clusters. https://gocrane.io/

[4] Katalyst: a universal solution to help improve resource utilization and optimize the overall costs in the cloud. https://github.com/kubewharf/katalyst-core

[5] Apache Hadoop YARN - Node Labels. https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/NodeLabel.html

[6] Apache Hadoop YARN - Graceful Decommission of YARN Nodes. https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/GracefulDecommission.html

[7] Apache Spark - Add better handling for node shutdown. https://issues.apache.org/jira/browse/SPARK-20624

[8] Apache Uniffle: Remote Shuffle Service. https://uniffle.apache.org/

[9] Apache Uniffle - Support getting memory data skip by upstream task ids. https://github.com/apache/incubator-uniffle/pull/358

[10] Apache Uniffle - Support storing shuffle data to secured dfs cluster. https://github.com/apache/incubator-uniffle/pull/53

[11] Apache Uniffle - Huge partition optimization. https://github.com/apache/incubator-uniffle/issues/378

[12] Koordinator - CPU Suppress. https://koordinator.sh/docs/user-manuals/cpu-suppress/

[13] Koordinator - Eviction Strategy based on CPU Satisfaction. https://koordinator.sh/docs/user-manuals/cpu-evict/

700634db9afed707dbd32356f950df3d.jpeg

也许你还想看

爱奇艺大数据加速:从Hive到Spark SQL

  后Hadoop时代,爱奇艺如何有效整合大数据和AI平台?

  爱奇艺大数据生态的实时化建设

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/194638.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法萌新闯力扣】:旋转字符串

力扣热题&#xff1a;796.旋转字符串 开篇 今天下午刷了6道力扣算法题&#xff0c;选了一道有多种解法的题目与大家分享。 题目链接:796.旋转字符串 题目描述 代码思路 完全按照题目的要求&#xff0c;利用StringBuffer中的方法对字符串进行旋转&#xff0c;寻找相同的一项 …

类和对象(8):explicit,static成员,友元,内部类

一、explicit class Date { public:Date(int year 2023, int month 1, int day 1):_year(year),_month(month),_day(day){}private:int _year;int _month;int _day; };int main() {// Date d1(1); // 这是正常初始化Date d1 1;return 0; }不妨猜测一下&#xff0c;d1的初始…

两栏布局:左侧固定,右侧自适应

左侧宽度固定&#xff0c;右侧宽度自适应剩余空间 方法一&#xff1a;float margin 方法二&#xff1a;flex布局 相关HTML代码 <div class"container"><div class"left"></div><div class"main"></div> </d…

C#入门(1):程序结构、数据类型

一、C#程序结构 第一个C#程序 using System;namespace base_01 {class Program{#region 代码折叠块static void Main(string[] args){//控制台输出Console.WriteLine("Hello World!");Console.Write("C#是微软的编程语言"); //不换行输出//Console.Rea…

python表白程序,无法拒绝

# codinggbk import tkinter as tk import random import tkinter.messagebox as messagebox# 创建主窗口并隐藏 root tk.Tk() root.attributes(-alpha, 0) # 设置主窗口为不可见# 表白内容 message "做我女朋友好不好&#xff1f;"# 获取屏幕宽度和高度 screen_w…

linux网络——HTTPS加密原理

目录 一.HTTPS概述 二.概念准备 三.为什么要加密 四.常⻅的加密⽅式 1.对称加密 2.⾮对称加密 五.数据摘要&#xff0c;数字签名 六.HTTPS的加密过程探究 1.方案一——只使用对称加密 2.方案二——只使⽤⾮对称加密 3.方案三——双⽅都使⽤⾮对称加密 4.方案四——⾮…

设计模式(二)-创建者模式(2)-工厂模式

一、为何需要工厂模式&#xff08;Factory Pattern&#xff09;? 由于简单工厂模式存在一个缺点&#xff0c;如果工厂类创建的对象过多&#xff0c;使得代码变得越来越臃肿。这样导致工厂类难以扩展新实例&#xff0c;以及难以维护代码逻辑。于是在简单工厂模式的基础上&…

QFile文件读写操作QFileInFo文件信息读取

点击按钮选择路径&#xff0c;路径显示在lineEdit中 将路径下的文件的内容放在textEdit中 最后显示出来 &#xff01;file.atend()//没有读到文件尾就一直读 file.readline表示按行进行读 追加的方式进行写 要是重新写的话用file.open(QIODevice::write) 用QFileInFo来读取…

2023年【安全员-A证】报名考试及安全员-A证新版试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-A证报名考试是安全生产模拟考试一点通总题库中生成的一套安全员-A证新版试题&#xff0c;安全生产模拟考试一点通上安全员-A证作业手机同步练习。2023年【安全员-A证】报名考试及安全员-A证新版试题 1、【多选…

【MySQL--->用户管理】

文章目录 [TOC](文章目录) 一、用户管理表二、基本操作三、用户权限分配给用户某个数据库中某个表的某个权限. grant 权限 on 库.表名 to 用户名主机名. ![在这里插入图片描述](https://img-blog.csdnimg.cn/fe8eb171ef9343c3a09bd64d4f0db5c1.png)分配给用户某个数据库中全部表…

指南:关于帮助中心需要注意的一些细节

在现代商业环境中&#xff0c;帮助中心已经成为企业提供客户支持和解决问题的重要方式之一。然而&#xff0c;建立一个高效的帮助中心并不简单。除了选择合适的软件平台和工具之外&#xff0c;还需要注意一些细节&#xff0c;以确保能够真正帮助客户并提高客户满意度。 | 1.设计…

RobotFramework框架之导入自己打包的python程序(十五)

引言 RobotFramework自动化框架&#xff08;以下简称RF&#xff09;之前文章我们讲了通过import第三方的library&#xff08;RequestsLibrary等&#xff09;&#xff0c;在实际项目中第三方的包并不能满足我们的需要&#xff0c;此时我们可自己编写python模块&#xff08;.py文…