构建阶段

前端小茶馆
2025-12-24 22:10
阅读 1972

寒冬里,我靠写爬虫和读源码活了下来

入职新公司两个月,赶上互联网“优化”潮的尾巴。工位还没坐热,隔壁组就被裁了一半。每天打开企业微信,群里不是在讨论 HC(Headcount)冻结,就是在复盘如何“降本增效”。作为刚转正没几天的 DevOps 工程师,我一度焦虑到凌晨三点还在刷 LeetCode——生怕哪天 HR 找上门来,说“你的 OKR 跟组织战略不一致”。

但后来我发现,与其干等着被“毕业”,不如主动把技术栈打穿。尤其在这个前端、后端、运维边界越来越模糊的时代,光会写 Ansible 脚本或调 Helm Chart,已经不够看了。于是,我给自己定了个“寒冬生存计划”:用三个月时间,从前端 JavaScript 到后端服务,再到自动化爬虫,全部亲手撸一遍。

说出来你可能不信——这一切的起点,竟然是一个产品经理甩过来的需求文档。


事情发生在上周五晚上 8 点。刚改完一个 Kubernetes 的 HPA 配置,正准备溜去楼下买杯瑞幸压压惊,钉钉突然弹出一条消息:“小王,能不能搞个工具,自动抓竞品 App 的活动页?我们想监控他们的促销节奏。”

我差点一口咖啡喷出来。这需求听着像前端活,又像后端,还带着点数据采集的味道——典型的“三不管”地带。更离谱的是,对方还补了一句:“最好能跑在 CI/CD 流水线上,每天凌晨自动跑一次。”

行吧,既然没人接,那我这个 DevOps 就硬着头皮上了。毕竟,在我们这种中小厂,DevOps 不就是“啥都得会一点”的代名词吗?

从零开始写一个能跑在流水线里的爬虫

我首先想到的是 Puppeteer。用 JavaScript 写,天然契合前端生态,还能模拟真实用户行为——比如点击“立即领取”按钮、滑动到底部加载更多内容。而且,我们公司的前端团队用的就是 React + TypeScript,代码风格一脉相承,沟通成本低。

但问题来了:Puppeteer 吃内存太狠。本地跑没问题,放到 GitLab CI 里直接 OOM(Out of Memory)。日志里全是:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

当时真的想砸键盘。后来翻了翻 ChatGPT(是的,我重度依赖它,别judge),它建议我加上 --max-old-space-size=4096,并限制并发实例数。但这治标不治本。

真正解决问题的是我周末在家啃 Chromium 源码时悟到的:没必要每次都启动完整浏览器。很多页面其实只需要解析 HTML 结构,完全可以用轻量级的 Axios + Cheerio 组合。

于是我重构了架构:

  • 对于静态页面(比如活动列表页):用 Node.js + Axios 发请求,Cheerio 解析 DOM。
  • 对于需要 JS 渲染的页面(比如带动态价格计算的详情页):才启用 Puppeteer,并且用 browser.disconnect() 显式释放资源。

最终,爬虫脚本压缩到 200 行以内,CI 跑一次只要 37 秒,内存占用不到 300MB。

// 简化版爬虫核心逻辑
const axios = require('axios');
const cheerio = require('cheerio');

async function scrapePromoList(url) {
  const { data } = await axios.get(url);
  const $ = cheerio.load(data);
  const promos = [];
  
  $('.promo-item').each((i, el) => {
    promos.push({
      title: $(el).find('.title').text().trim(),
      link: $(el).attr('href'),
      date: new Date().toISOString().split('T')[0]
    });
  });

  return promos;
}

这段代码现在每天凌晨 3 点准时跑,结果存进 MongoDB,前端同学用 Grafana 做了个看板——产品经理终于闭嘴了。


前端不止是“切图仔”

说到前端,很多人(包括我以前)都觉得 DevOps 不用深究。但这次项目让我意识到:不懂前端,连自动化测试都写不利索

比如,我们的 E2E 测试用的是 Cypress。某次上线后,测试突然全挂,报错是 “Timed out waiting for element .loading-spinner to disappear”。查了半天,发现是前端把 loading 组件从 class 名 .spinner 改成了 .loader,而没人通知测试团队。

那一刻我悟了:DevOps 要做“桥梁”,就必须懂两端

于是我开始啃 React 源码。不是为了写业务组件,而是理解它的渲染机制、生命周期、错误边界(Error Boundary)。比如为什么有时候 useEffect 会在 SSR 环境报错?为什么某些第三方库在 Docker 容器里跑不起来(字体缺失、Canvas 依赖等)?

我还顺手给前端团队提了个 PR:在 CI 里加了一个 eslint-plugin-devops 插件,自动检测是否使用了高危 API(比如 eval()、未加密的 localStorage 存敏感信息)。他们一开始一脸懵,后来发现真能拦住几个安全隐患,立马把我拉进了前端周会。


后端:别只当“环境搭建员”

作为 DevOps,最容易陷入的陷阱是把自己定位成“搭环境的”。装个 Nginx、配个 Redis、调个 JVM 参数就完事了?Too young.

这次爬虫项目的数据要持久化,我本打算直接扔给 DBA。但他们忙得连邮件都不回。没办法,自己上。

我选了 Fastify —— 轻量、高性能,TypeScript 支持好,启动速度比 Express 快 30%。关键是,它自带 OpenAPI 文档生成,前端对接超方便。

但部署时又踩坑了。容器镜像 build 出来 1.2GB,CI 跑一次要 8 分钟。我翻了官方文档,发现是因为 devDependencies 没剔除。于是用多阶段构建:

FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 运行阶段
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

镜像体积降到 120MB,启动时间从 5s 降到 800ms。上线那天,后端同事拍我肩膀:“兄弟,你这比我们老服务还快。”


技术栈打穿之后,心态变了

以前我觉得 DevOps 就是“救火队员”:半夜被 PagerDuty 叫醒,处理数据库主从切换;白天被催着上线,结果因为前端漏传 header 导致 502。

但现在不一样了。我能从前端代码看到性能瓶颈,能从后端日志定位慢查询,还能用爬虫反哺产品决策。我不再是被动响应的角色,而是能主动创造价值的人

更重要的是,我不再焦虑“会不会被裁”。因为我知道,只要持续输出,就有不可替代性。

下面是我这两个月整理的学习路径,分享给同样在寒冬中挣扎的朋友:

领域 学习重点 实践建议
JavaScript 事件循环、Promise、模块系统 用原生 JS 写一个 CLI 工具
前端 React 生命周期、打包原理 手写一个简易 Webpack Plugin
后端 RESTful 设计、中间件机制 用 Fastify/Koa 写个 API 网关
爬虫 反爬策略、IP 代理池 监控公开 API 的变更(比如 GitHub Trending)
DevOps GitOps、可观测性 在本地用 Kind + Prometheus 搭监控

有人说,互联网寒冬是危机,也是洗牌。那些只会“拧螺丝”的人会被淘汰,而愿意向下扎根、向上生长的人,反而能借机弯道超车。

我现在每天下班前都会花 30 分钟读一段源码——可能是 Vite 的插件系统,也可能是 Axios 的拦截器实现。不为别的,就为了下次再遇到“能不能做个工具”的需求时,能笑着说:“行,今晚就能跑起来。”

对了,昨天产品经理又来找我:“那个爬虫能不能加个功能,识别图片里的文字?”
我笑了笑,打开 VS Code,新建了一个 ocr-service.ts 文件。

寒冬很冷,但代码滚烫。

评论 0

最热最新
暂无评论
前端小茶馆Lv.1
0
影响力
0
文章
0
粉丝