一台新机器从开机到能跑任务,要装驱动、配代理、导流程、调浏览器,大半天就过去了。
如果每次扩容都这样,自动化规模永远上不去。
店群矩阵在持续扩张,我们从3台执行节点扩到5台,又从5台扩到8台。每次加机器都变成一场小折腾——新机器环境配置靠人工,稍有不慎就和线上不一致,跑出来的流程莫名报错,排查才发现是某个系统补丁版本不同导致的。
这让我想起后端服务的发展历程。十年前部署应用还要手动装依赖,现在Docker一拉镜像就起来。RPA执行节点虽然做不到纯容器化(后面会讲原因),但标准化和可复制化的思路完全可以借鉴。
这篇文章讲我们怎么把执行节点的交付从“手工打造”变成“模板化生产”,并在这个过程中踩过的技术坑和做出的妥协。
一、执行节点的环境构成到底有多复杂
一个典型的影刀RPA执行节点,依赖的东西远比一个微服务多:
- Windows 10/11或Windows Server操作系统
- 特定版本的Chromium或Chrome
- 影刀客户端(特定版本,与流程文件兼容)
- Python环境(影刀内置的 + 外部独立环境)
- 代理客户端或系统级代理配置
- 店铺的
user-data-dir环境目录 - 各类运行时库(VC++ Redistributable、.NET Framework等)
- 系统级的性能配置(虚拟内存、电源管理、防火墙规则)
这些东西只要有一个版本对不上,流程就可能在某个步骤上表现出微妙的不兼容。我们早期用Ghost或者系统还原点来备份环境,但还原后经常需要手动修复影刀的授权绑定和浏览器驱动匹配,效率很低。
二、容器化:最理想的方案,也是最大的挑战
如果这是Linux服务器跑后端服务,我闭着眼就上Docker了。但RPA执行节点的情况完全不同。
我们花了两周时间认真研究了Windows容器(Windows Server Containers和Hyper-V隔离容器)来跑影刀和浏览器,结论是:当前阶段,纯容器化带来的成本远超收益。
核心问题有三个:
第一,图形界面依赖。 影刀和Chromium需要真实的Windows桌面环境来渲染页面。虽然可以用虚拟显示器或者headless模式跑一部分流程,但很多平台会检测无头浏览器特征,合规运营不能冒这个风险。容器不提供原生桌面,需要额外的XServer或虚拟显卡配置,稳定性堪忧。
第二,影刀的授权与安装限制。 影刀客户端绑定机器指纹,容器重建后指纹变化,授权可能失效。虽然可以申请浮动授权,但每次重建容器都需要重新激活,达不到“即用即弃”的理想状态。
第三,浏览器实例的性能损耗。 Chromium在容器内通过虚拟化GPU渲染时,页面加载速度和交互响应时间明显劣于裸机运行。对于依赖页面元素实时交互的RPA流程,这个延迟累积下来,任务超时率会显著上升。
容器化是一个方向,但不能为了容器化而容器化。
在RPA领域,目前更务实的路径是追求环境交付的标准化,而非运行时的完全隔离。
三、我们的务实方案:可复制化模板 + 自动化交付管线
既然不能做到理想化的容器即用即弃,我们退而求其次,目标是:一台裸Windows机器,30分钟内从零变成可执行任务的节点,全程无需人工远程桌面。
这个目标靠一套自动化交付管线来实现,包含三个阶段:
阶段一:系统初始化
基于Windows应答文件(Autounattend.xml)定制系统安装镜像,预装必要的运行时库、代理配置脚本和节点注册服务。机器开机后自动进入OOBE(开箱体验)但跳过用户交互步骤,直接进入一个最小化的桌面环境。
阶段二:核心软件部署
一个Python编写的 node_bootstrap 脚本在机器首次登录后自动启动,依次执行:
- 从内网制品仓库下载指定版本的Chrome和Chromedriver
- 下载并静默安装影刀客户端(命令行参数指定安装路径和授权Token)
- 配置Python虚拟环境和插件库依赖
- 注册节点到调度中心
这个脚本不依赖任何手动操作,所有下载都走内网代理,避免公网波动影响。
def bootstrap_node(config):
logger.info("Starting node bootstrap...")
install_chrome(config.chrome_version)
install_yingdao(config.yingdao_version, config.yingdao_token)
setup_python_env(config.python_packages)
register_to_scheduler(config.scheduler_url, config.node_spec)
logger.info("Bootstrap completed, node is ready")
阶段三:店铺环境部署
节点就绪后,调度中心根据分配计划,调用环境工厂在节点上创建店铺的 user-data-dir 并绑定代理。这个过程在之前的环境隔离文章里详细讲过,不再展开。区别在于,现在是批量自动化执行的,不需要人工一个个配置。
四、配置漂移检测与自动修复
即使有了标准化交付,运行中的节点还是会因为各种原因产生配置漂移——某个更新被意外安装、某个依赖库被其他程序覆盖、磁盘空间被临时文件塞满。
我们写了一个 node_doctor 巡检服务,定期检查节点的各项配置是否与基线一致,包括:
- 系统版本和关键补丁列表
- Chrome和Chromedriver版本匹配
- 影刀客户端版本
- Python插件库的文件哈希
- 防火墙规则和代理配置
- 磁盘剩余空间和
user-data-dir目录完整性
一旦检测到漂移,node_doctor 会尝试自动修复(比如重新下载被修改的库文件),如果无法自动修复,则将该节点标记为 degraded,限制新的任务分配,并推送告警。
def run_health_check(baseline_config):
drifts = []
if get_chrome_version() != baseline_config.chrome_version:
drifts.append(("chrome_version", "mismatch"))
if not verify_file_hash(baseline_config.plugin_path):
drifts.append(("plugin_hash", "mismatch"))
if drifts:
handle_drifts(drifts)
这个机制和上一篇账号健康度评分类似,区别是对象换成了执行节点本身。工程化就是这样,健康检查的思路可以复用到任何需要长期运行的资源上。
五、节点退役与无状态化回收
有新增就有退役。当一台执行机老化需要下架,或者某个节点需要被替换时,不能粗暴关机了事——上面还绑着大量店铺环境,直接关机意味着那些店铺的自动化全部中断。
我们的退役流程被标准化为几个步骤:
- 标记退役:调度中心将该节点状态设为
draining,不再分配新任务,但已运行的任务继续执行。 - 任务排空:等待节点上所有进行中的任务完成或超时。对于长流程,调度中心会提前将任务路由到其他节点。
- 环境迁移:将该节点绑定的店铺环境按批次迁移到其他健康节点。迁移过程就是环境工厂重建和登录态重新验证的过程,需要人工协助扫码。
- 数据清除:确认所有环境已迁移完毕后,执行
node_wipe脚本,安全擦除所有user-data-dir数据和本地日志,确保不泄露任何店铺信息。 - 从集群移除:调度中心删除该节点注册信息,节点关机。
整个过程编排为一个 decommission_plan,每一步都有状态追踪和失败回滚能力。退役不再是突发事件,而是一个可以安排在周末从容执行的标准运维操作。
六、多版本执行环境的并行管理
店群的自动化流程不是全部运行在同一个影刀版本上。新流程可能依赖新版影刀的某个功能,老流程还没完成兼容验证,需要在旧版本上继续跑。
这就要求同一集群内,不同节点可以运行不同的影刀版本,甚至可以同一节点上并存多个版本(通过安装路径隔离)。
我们在节点注册时上报其影刀版本信息,调度中心在任务路由阶段根据流程的 min_yingdao_version 要求来匹配节点。这就实现了类似于Kubernetes里节点亲和性调度的能力,只不过更轻量。
当旧版本节点退役后,调度中心会检查该版本是否还有残留任务,如有则阻止退役并告警。这样避免了版本碎片化导致的僵尸流程。
七、那些在实际交付中逼疯人的小细节
标准化交付里最大的障碍不是大软件,是小细节。
细节一:Windows更新。 自动更新会在任务执行期间强制重启。我们通过组策略关闭了自动更新,改为每月一个固定维护窗口手动安装补丁并重启。维护窗口期间,节点从集群中临时摘除。
细节二:时区和时间同步。 店群涉及跨境平台,服务器时区必须是UTC,但RPA流程里有些时间戳需要展示为北京时间。我们统一了所有执行节点的时区为UTC,业务流程里的时间转换在Python插件层处理。另外强制开启NTP同步,否则时间漂移会导致平台API验签失败。
细节三:代理客户端的DNS泄漏。 某些代理客户端在连接断开时没有正确回退DNS设置,导致浏览器在不走代理的情况下直接解析域名,IP漏出。我们的 node_doctor 增加了一个外部IP探测项,如果检测到非预期的出口IP,直接封锁网络并告警。
这些小问题,每一个单独拿出来都不值一提,但堆在一起足以让新节点变成一颗哑弹。
八、这条路的尽头是基础设施即代码
回头看,我们做的事情本质上就是把执行节点的基础设施管理向IaC(基础设施即代码)靠拢。节点配置由脚本定义,版本受Git管理,变更走审批流程,交付自动化执行。
虽然现在跑的还是Windows裸机,但管理思路已经和后端服务的基础设施管理没有本质区别。这让我们的运维人员从“手工装机工”变成了“配置策展人”,工作方式完全不同。
未来如果Windows容器生态成熟到足以承载图形界面应用,或者影刀支持更轻量的运行模式,我们这套交付管线可以无缝切换到容器化底版上。脚本还是那些脚本,只是底层从物理机变成了容器实例。
九、最后聊几句交付之外的事情
有一次我们需要紧急扩容两台机器应对大促,如果按照以前的手工装机方式,至少需要一整个下午。那次我们用了新交付管线,从机器上架到开始跑任务,一共用了35分钟,其中包括等待Windows自动安装的时间。
那一刻我真正感受到工程化的力量——它不只是在正常情况下把事情做好,而是在压力面前也能保持从容。
自动化工程最难的不是让机器替你做事,而是让机器本身可以被复制。
当执行环境从手工艺品变成工业标品,规模化的最后一公里才算真正打通。
作者:林焱
一个想把装机这件事从生活里彻底抹掉的工程师
