采集架构的三次升级:脚本、Docker 与 Kubernetes

0 阅读4分钟

很多人在第一次听到“用 Kubernetes 管理爬虫”时,都会产生疑问:

爬虫不就是一个脚本吗?
引入 Kubernetes 会不会显得过于复杂?

这个问题本身并没有错,真正的关键在于:
你的爬虫系统目前处在什么阶段。

这篇文章不试图先给结论,而是通过一个真实、可落地的爬虫任务,完整演示爬虫架构的三次演进过程:

  • Python 脚本级爬虫
  • Docker 化爬虫
  • Kubernetes Job 化爬虫

通过这个过程,你会自然判断出:
什么时候 Kubernetes 是奢侈,什么时候开始变成刚需。

一、阶段一:Python 脚本级爬虫

1. 业务背景

假设我们有一个非常常见的采集需求:

  • 采集某电商网站的商品列表页
  • 需要并发请求
  • 必须使用代理IP
  • 对请求成功率有一定要求

这是大量爬虫项目最初的起点。

2. 最基础的 Python 爬虫实现

import requests

# 16YUN爬虫代理配置
PROXY_HOST = "proxy.16yun.cn"
PROXY_PORT = 8000
PROXY_USER = "your_username"
PROXY_PASS = "your_password"

proxies = {
    "http": f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}",
    "https": f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
}

def fetch(url):
    headers = {
        "User-Agent": "Mozilla/5.0"
    }
    resp = requests.get(url, headers=headers, proxies=proxies, timeout=10)
    resp.raise_for_status()
    return resp.text

if __name__ == "__main__":
    html = fetch("https://example.com")
    print(html[:500])

3. 这个阶段的典型问题

在项目初期,这种方式通常能够满足需求,但随着使用频率增加,很快会遇到一些现实问题:

  • 脚本异常退出后,没有自动恢复机制
  • 代理异常时,请求容易被阻塞
  • 并发能力主要依赖线程或进程
  • 部署到服务器往往依赖人工操作

在这个阶段,引入 Kubernetes 并不会带来明显收益,反而会增加理解和维护成本。

二、阶段二:Docker 化爬虫

当爬虫开始出现以下情况时,问题会逐渐显现:

  • 不同服务器上的 Python 运行环境不一致
  • 需要频繁复制多个爬虫实例
  • 希望将任务统一部署到云服务器或集群中

此时,Docker 通常是下一步选择。

1. Dockerfile 示例

FROM python:3.10-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY spider.py .

CMD ["python", "spider.py"]

requirements.txt

requests==2.31.0

2. 构建与运行容器

docker build -t spider-demo .
docker run spider-demo

3. Docker 带来的改进

容器化之后,爬虫在工程层面有了明显改善:

  • 运行环境高度一致
  • 部署方式标准化
  • 可以在多台机器上快速启动多个实例

4. 新问题开始出现

当爬虫任务规模继续扩大时,仅靠 Docker 仍然存在明显不足:

  • 容器失败后需要人工介入
  • 资源使用缺乏统一约束
  • 多任务并行时缺乏整体调度能力
  • 无法很好地管理任务生命周期

Docker 解决的是“如何运行程序”,但并不关心“程序应该如何被管理”。

三、阶段三:Kubernetes Job 化爬虫

在这个阶段,一个关键的认知转变是:

绝大多数爬虫并不是长期运行的服务,而是一次性或周期性任务。

因此,Kubernetes 中最合适的资源类型不是 Deployment,而是 Job 或 CronJob。

1. Kubernetes Job 示例

apiVersion: batch/v1
kind: Job
metadata:
  name: spider-job
spec:
  backoffLimit: 3   # 失败重试次数
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: spider
        image: spider-demo:latest
        resources:
          limits:
            cpu: "500m"
            memory: "512Mi"
        env:
        - name: PROXY_HOST
          value: "proxy.16yun.cn"
        - name: PROXY_PORT
          value: "8000"
        - name: PROXY_USER
          value: "your_username"
        - name: PROXY_PASS
          value: "your_password"

2. 爬虫代码适配 Kubernetes

import os
import requests

PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")
PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASS = os.getenv("PROXY_PASS")

proxies = {
    "http": f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}",
    "https": f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
}

def fetch(url):
    headers = {"User-Agent": "Mozilla/5.0"}
    resp = requests.get(url, headers=headers, proxies=proxies, timeout=10)
    resp.raise_for_status()
    return resp.text

if __name__ == "__main__":
    print(fetch("https://example.com")[:300])

3. Kubernetes 带来的本质变化

引入 Kubernetes 后,爬虫系统的关注点发生了变化:

  • 任务失败后可以自动重试和回收
  • 每个爬虫实例的资源使用受到限制
  • 爬虫任务的生命周期变得可控、可观测

此时,爬虫不再依赖人工“盯着跑”,而是由系统自动托管。

四、什么时候是奢侈,什么时候是刚需

在以下情况下,引入 Kubernetes 往往并不划算:

  • 个人或实验性质的爬虫
  • 请求规模较小
  • 代理 IP 成本不敏感

但当出现以下信号时,Kubernetes 往往开始变得必要:

  • 同时运行多个采集任务
  • 代理 IP 成本明显
  • 爬虫失败会影响业务节奏
  • 对稳定性和可恢复性有明确预期

可以总结为一句话:

Kubernetes 并不会让爬虫跑得更快,但可以避免系统在出问题时整体失控。

五、结语

很多团队最终引入 Kubernetes,并不是因为技术追求,而是因为现实压力:

人工维护已经跟不上爬虫规模的增长。

如果你还停留在脚本阶段,没有必要急于升级;
但如果你已经频繁处理失败重跑、资源争抢和代理浪费的问题,那么 Kubernetes 往往不是炫技,而是一种止损手段。