我把 cron 迁到 FunctionGraph 的返工记录

举报
yd_272744321 发表于 2026/10/26 10:33:34 2026/10/26
【摘要】 我把一台专门跑 cron 的 ECS 下线,用 FunctionGraph 承接每 10 分钟的对账任务。过程里连续踩了依赖包体积、VPC 连通和超时三个坑,下面是我实际遇到的报错、配置改动、耗时和账单变化,当前每月成本从约 97 元降到 2.68 元。

我为什么动 cron

5 月 19 日补丁夜,02:13 值班电话把我叫醒,告警名是 reconcile_lag_gt_15m,Prometheus 里 lag 指标已经到 19m42s。我们订单服务在华为云 ECS,业务机 s6.large.2(2 vCPU / 4 GiB),旁边单独一台 s6.small.1(1 vCPU / 2 GiB)只跑 crond。系统 CentOS 7.9,内核 3.10.0-1160.108.1.el7.x86_64,cronie 1.4.11,脚本解释器 Python 3.9.18。

*/10 * * * * /usr/bin/python3 /srv/jobs/reconcile.py >> /var/log/reconcile.log 2>&1

任务逻辑是固定的,每 10 分钟拉支付网关 CSV,再调内网 /internal/reconcile 写状态。4 月漏跑 3 次,间隔分别是 17 分钟、24 分钟、47 分钟。我把日志和系统事件对了一遍,命令是 journalctl -u crond --since '2026-04-14 03:00:00' --until '2026-04-14 04:30:00',03:20 还有执行记录,03:30 没有新子进程,内核日志同时出现目录索引写满。

Apr 14 03:20:01 crond[2187]: (root) CMD (/usr/bin/python3 /srv/jobs/reconcile.py >> /var/log/reconcile.log 2>&1)
Apr 14 03:21:02 kernel: EXT4-fs warning (device vda1): ext4_dx_add_entry: Directory index full
Apr 14 03:30:01 CROND[2334]: (root) CAN'T OPEN OR CREATE /var/log/reconcile.log: No space left on device

那次我手工补了 612 条流水,先在 MySQL 跑 select channel_txn_id from pay_txn where biz_date='2026-04-14' and status='PENDING',再拿脚本逐条重放,1 小时 20 分才把对账抹平。

这个方案我一开始是拒绝的。

迁到 FunctionGraph 的返工细节和现状

我在 FunctionGraph(华北-北京四)先建了 Python3.9 函数,内存 256 MB,超时 3 秒,触发器填 0 */10 * * * ?。第一次上传只放 index.py,执行 1 分钟就报 Runtime.ImportModuleError: Unable to import module 'index': No module named 'requests'。我图快,把本地 .venv 整个 zip 上去,控制台拒绝,包 126.4MB,平台上限 40MB。

Code package size exceeds limit, current size 126.4MB, max size 40MB

第二次我又翻车,命令写成 zip -r fg-reconcile.zip package,压缩包多了一层 package 目录,入口找不到,报 No module named 'index'。我还做过一次错误尝试,在 Mac 本地打包后直接传,运行时出现 ImportError: /var/task/charset_normalizer/md.cpython-39-darwin.so: invalid ELF header,这个坑我以前见过,二进制轮子架构不对。

我改成在一台 x86 Linux 构建机打包,ECS 规格 c6.large.2,系统 Ubuntu 22.04,pip 23.3.2,步骤拆得很死,避免再靠运气。

python3.9 -m venv .venv
source .venv/bin/activate
mkdir -p package
pip install -r requirements.txt -t package --no-cache-dir --upgrade
# requirements.txt
requests==2.31.0
pymysql==1.1.1
urllib3==2.2.1
charset-normalizer==3.3.2
cp index.py package/
cd package && zip -r ../fg-reconcile.zip .

这次包大小 9.6MB,上传 37 秒,发布版本 6 后函数能进 handler,但日志还是一片红,核心报错是内网地址连不上。

ConnectTimeoutError: HTTPConnectionPool(host='10.23.17.8', port=8080): Max retries exceeded with url: /internal/reconcile (Caused by ConnectTimeoutError(..., 'Connection to 10.23.17.8 timed out. (connect timeout=2)'))

原因很直白,我忘了绑 VPC,接口只开了内网 8080。我给函数挂了 vpc-prod、subnet-app-a,新建安全组 sg-fg-job,出方向只放行 10.23.17.8/32:8080,DNS 和 NTP 保留默认。网络打通后又撞上平台超时,监控里连续 6 次 Task timed out after 3.00 seconds,最大 Duration 3008 ms。

我把改动写进变更单,能对上问题来源。

  • timeout 从 3 秒改到 15 秒,因为支付网关高峰响应在 4 到 6 秒,3 秒等于必炸
  • 内存从 256 MB 调到 512 MB,CPU 配额跟着翻倍,单次 CSV 解析从 780 ms 降到 430 ms
  • requests 从 timeout=2 改成 timeout=(2,10),加 Retry(total=2, backoff_factor=0.6, status_forcelist=[502,503,504])

我还试过把 timeout 直接拉到 60 秒想兜底,这个方案我现在看是错的,坏请求挂更久,p95 从 5.8 秒涨到 9.1 秒,09-23 那天计算费单日 0.21 元涨到 0.47 元,第二天就回滚到 15 秒。

幂等这块我没上复杂组件,还是 MySQL 唯一键 (biz_date, channel_txn_id),SQL 从 insert ignore 改到 ON DUPLICATE KEY UPDATE updated_at=NOW(),因为我要追重复写入的时间波峰,ignore 会把证据吃掉。失败通知走 SMN 主题 prod-reconcile-alert,订阅企业微信机器人,最近 30 天失败到消息落地平均 8 秒,最慢 19 秒。

周六 01:07 我把 cron 机关了。

账单我按服务筛了 2026-09-01 到 2026-09-30,原来那台 s6.small.1 折月 78.6 元,40GB 高 IO 盘 18.4 元,公网带宽分摊 6.1 元,合计 103.1 元;迁到 FunctionGraph 后执行 4467 次,请求费 0.09 元,计算费 1.86 元,LTS 0.73 元,SMN 0.04 元,总计 2.72 元。4467 次里失败 2 次,一次 9 月 12 日 10:20 网关 502,原始体 {'code':'UPSTREAM_BAD_GATEWAY'};另一次 9 月 21 日 03:10 主从切换,报 pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query'),都在 5 分钟内靠重试补齐。

现在还有个尾巴,定时触发偶发延迟,控制台最大 94 秒,日志里 scheduled_time=14:00:00,actual_start_time=14:01:34,报表会短时间缺口。下周我准备把关键对账改成事件驱动,定时任务只做兜底扫描,去重窗口先按 10 分钟跑一周,再看要不要改成 30 分钟。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。