news 2026/6/7 6:45:26

AWS Batch构建生产级网络爬虫:IP轮换、反爬绕过与自动伸缩

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AWS Batch构建生产级网络爬虫:IP轮换、反爬绕过与自动伸缩

1. 项目概述:为什么用 AWS 做网络爬虫,而不是本地跑脚本?

我第一次在伦敦租住的公寓里调试一个爬取招聘网站的 Python 脚本时,窗外正下着连绵阴雨。脚本跑了三小时,卡在第 472 条职位信息上——IP 被封了,验证码弹窗像幽灵一样反复出现,本地笔记本风扇嘶吼得像要起飞。那一刻我意识到:单机爬虫不是“能不能跑通”的问题,而是“能不能活过第二天”的生存问题。这正是 AWS 批处理(AWS Batch)真正起作用的地方:它不解决“怎么写代码”,而是解决“怎么让代码在真实世界里稳定、可伸缩、不掉链子地跑下去”。

核心关键词AWS在这里不是指“用个 EC2 虚拟机装个 Python 就叫上云”,而是指一套完整的工程化思路——把爬虫从“个人玩具”升级为“可调度、可监控、可回滚、可计费”的数据采集服务。它天然适配三个现实痛点:第一,IP 池轮换需要多地域出口,AWS 全球 30+ 区域节点就是现成的地理分散代理池;第二,反爬策略升级后,单机重试逻辑容易雪崩,而 Batch 的失败自动重试 + 任务隔离机制能避免一个请求崩掉整个流程;第三,爬取任务有明显波峰波谷(比如每天早九点集中抓新职位),手动启停服务器既浪费钱又容易忘,而 Batch 的按需拉起容器、完成即销毁的模式,让成本和负载完全对齐。

适合谁参考?如果你正面临这些情况中的任意一种:用 Scrapy 写好了逻辑但总被封 IP;用 Selenium 模拟登录却卡在验证码识别环节;或者团队里有人写爬虫、有人管服务器、有人看数据,协作靠微信截图和 Excel 表格——那这篇就是为你写的。它不假设你懂 CloudFormation 或 IAM 策略细节,但会带你亲手把一个本地能跑的 requests+BeautifulSoup 脚本,变成部署在 us-east-1 区域、自动扩缩容、失败自动告警、日志全留存的生产级采集任务。接下来所有内容,都基于我过去三年在金融、招聘、电商三个垂直领域落地的 17 个 AWS 爬虫项目经验,没有理论空谈,只有实测参数和踩坑记录。

2. 整体架构设计与方案选型逻辑

2.1 为什么选 AWS Batch,而不是 Lambda、EC2 或 Fargate?

很多人看到“云上爬虫”第一反应是 AWS Lambda——毕竟按执行时间付费,听起来最省钱。但实际跑起来你会发现:Lambda 默认超时 15 秒,最大内存 10GB,冷启动延迟平均 300ms。而一个带登录态、需渲染 JS、还要等验证码识别接口返回的招聘网站爬虫,光加载页面就可能耗时 8 秒,更别说后续解析和提交表单。我实测过:用 Lambda 抓取 LinkedIn 某类职位页,成功率不足 62%,失败主因全是超时。这不是代码问题,是架构错配。

EC2 看似自由度高,但运维成本陡增。你需要自己维护安全组规则、更新系统补丁、配置日志轮转、处理磁盘满告警——而这些和“获取数据”毫无关系。我曾管理过一个 5 台 EC2 组成的爬虫集群,某天凌晨三点收到磁盘告警,SSH 登上去发现是某次异常退出没清理临时 HTML 文件,占满 200GB 空间。这种半夜救火,不该是数据工程师的日常。

Fargate 是容器无服务器方案,比 Lambda 更适合长任务,但它缺少 Batch 的核心优势:原生任务队列、依赖编排、资源弹性伸缩策略。比如你要实现“先抓列表页 → 解析出 50 个详情 URL → 并发抓取这 50 个详情页 → 最后汇总入库”,Fargate 需要自己搭消息队列(如 SQS)+ 编写状态机(Step Functions),而 Batch 用一行dependsOn参数就能声明任务依赖关系。

AWS Batch 的本质,是把“任务”作为一等公民来管理。它底层调度的是 ECS 容器,但屏蔽了容器编排复杂性,只让你聚焦三件事:任务定义(跑什么)、计算环境(在哪跑)、作业队列(什么时候跑)。我们用它做过最高并发 1200 个容器的任务——全部在 4 分钟内拉起完毕,失败任务自动重试 3 次后进入 FAILED 状态并触发 SNS 告警。这种确定性,是其他方案难以提供的。

提示:Batch 不是万能银弹。如果你的任务是每秒 1000 次的轻量级 API 调用(如查天气),用 API Gateway + Lambda 更合适;但凡涉及页面渲染、文件下载、状态保持、长时间等待,Batch 就是更稳的选择。

2.2 架构全景图:从本地脚本到云上服务的四层跃迁

我把整个架构拆成四个物理隔离又逻辑连贯的层次,每层解决一类问题:

第一层:数据采集层(Batch Job)
这是核心。每个爬虫任务被打包成 Docker 镜像,镜像内预装 ChromeDriver、undetected-chromedriver2、requests-html 等反爬绕过工具,并挂载 AWS Secrets Manager 获取动态 Cookie 和 Token。任务启动时,Batch 自动分配 vCPU 和内存资源(我们常用 4vCPU/16GB 配置,足够跑 3 个并发 Chrome 实例)。

第二层:任务调度层(Job Queue + Compute Environment)
我们创建两个队列:high-priority(用于紧急补数,优先抢占资源)和default(日常增量抓取)。计算环境采用 Spot 实例 + On-Demand 实例混合模式:Spot 实例承担 85% 的常规任务(成本降 60%),On-Demand 实例保障关键任务不被中断。实测下来,Spot 中断率约 5%/天,但 Batch 的自动重试机制让它几乎无感。

第三层:数据流转层(S3 + SQS)
爬取结果不直接写数据库,而是先存入 S3 的raw-scraped/前缀下,文件名含时间戳和任务 ID(如raw-scraped/20231015T092345Z_job-abc123.json)。同时向 SQS 发送一条消息,包含 S3 对象路径和元数据(如抓取 URL 数、成功数、失败数)。这样解耦了采集和处理,下游可以用 Glue 作业做清洗,或用 Lambda 触发入库。

第四层:可观测层(CloudWatch Logs + Metrics + Alarms)
所有容器日志统一推送到 CloudWatch Logs,按/aws/batch/job命名空间分组。我们自定义了 3 个关键指标:JobsFailedPerHour(1 小时内失败任务数)、AvgScrapeTimeMs(平均单页抓取耗时)、SuccessRatePercent(成功率)。当成功率连续 5 分钟低于 85%,自动触发 SNS 邮件告警,并附上最近 10 条失败日志的截取片段。

这个四层设计,让我们把原本需要 3 人轮班盯的爬虫系统,压缩到 1 人每月花 2 小时维护。下文所有实操,都围绕这四层展开。

3. 核心细节解析与实操要点

3.1 爬虫脚本改造:从“能跑”到“能上云”的七处硬编码替换

本地脚本能跑,不等于能上 Batch。我统计过,92% 的首次部署失败,源于脚本里藏着的“本地思维”。以下是必须修改的七个关键点,附修改前后的对比和原理说明:

第一处:硬编码的输出路径 → 改为环境变量驱动
修改前:with open('./data/jobs_20231015.json', 'w') as f:
修改后:output_path = os.environ.get('OUTPUT_BUCKET', 's3://my-scraping-bucket/raw-scraped/')
原理:Batch 任务无法保证本地磁盘持久化,所有输出必须指向外部存储。S3 路径通过环境变量注入,便于不同环境(开发/测试/生产)切换。

第二处:绝对路径的 ChromeDriver → 改为容器内相对路径
修改前:driver = webdriver.Chrome('/usr/local/bin/chromedriver')
修改后:driver = webdriver.Chrome(ChromeDriverManager().install())
原理:Batch 启动的容器每次都是全新实例,ChromeDriver 版本必须与容器内 Chrome 版本严格匹配。用 WebDriverManager 动态下载,比预装更可靠。我们实测过:Chrome 115 需要 chromedriver 115.0.5790.170,版本错一位就会报 session not created 错误。

第三处:明文账号密码 → 改为 Secrets Manager 动态获取
修改前:login_data = {'username': 'myuser', 'password': 'mypass'}
修改后:

session = boto3.session.Session() client = session.client('secretsmanager', region_name='us-east-1') secret = client.get_secret_value(SecretId='prod/scraping/cookies') cookies = json.loads(secret['SecretString'])

原理:AWS Batch 任务默认拥有执行角色(Execution Role),该角色需有secretsmanager:GetSecretValue权限。此举避免密钥泄露风险,且支持密钥轮换——下次更新密码,脚本无需改一行代码。

第四处:固定 User-Agent → 改为随机 UA + 请求头指纹
修改前:headers = {'User-Agent': 'Mozilla/5.0'}
修改后:

from fake_useragent import UserAgent ua = UserAgent(browsers=['edge', 'chrome', 'firefox']) headers = { 'User-Agent': ua.random, 'Accept-Language': 'en-US,en;q=0.9', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive' }

原理:单一 UA 是反爬第一道防线。fake-useragent 库能实时抓取浏览器市场占比数据,生成符合真实分布的 UA 字符串。我们额外添加 Accept-Language 等字段,模拟真实用户请求头指纹。

第五处:无超时控制的 requests → 改为带分级超时的会话
修改前:response = requests.get(url)
修改后:

session = requests.Session() retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) response = session.get(url, timeout=(3.05, 27)) # (connect, read)

原理:3.05 秒连接超时是行业经验值(TCP 握手通常 <3 秒),27 秒读取超时覆盖了 JS 渲染、验证码识别等长耗时场景。Retry 策略针对 429(限流)和 5xx 错误,避免瞬时网络抖动导致任务失败。

第六处:未处理的异常 → 改为结构化错误捕获
修改前:soup = BeautifulSoup(response.text, 'html.parser')
修改后:

try: soup = BeautifulSoup(response.text, 'html.parser') if not soup.find('div', class_='job-listing'): raise ValueError(f"No job listings found on {url}") except Exception as e: logger.error(f"Parse error on {url}: {str(e)}") # 记录到 S3 的 error 日志文件 s3_client.put_object( Bucket='my-scraping-bucket', Key=f'errors/{datetime.now().isoformat()}_parse_fail.json', Body=json.dumps({'url': url, 'error': str(e), 'status_code': response.status_code}) ) raise

原理:Batch 任务失败时,会自动重试。但若错误是页面结构变更(如网站改版),重试只会重复失败。此处主动捕获结构解析异常,写入独立错误桶,便于人工介入分析,避免无效重试消耗资源。

第七处:无进度反馈的循环 → 改为 CloudWatch 自定义指标上报
修改前:for url in urls: scrape_single_page(url)
修改后:

cloudwatch = boto3.client('cloudwatch', region_name='us-east-1') for i, url in enumerate(urls): try: result = scrape_single_page(url) cloudwatch.put_metric_data( Namespace='Scraping/Jobs', MetricData=[{ 'MetricName': 'PagesScraped', 'Value': 1, 'Unit': 'Count', 'Dimensions': [{'Name': 'JobId', 'Value': os.environ.get('AWS_BATCH_JOB_ID', 'unknown')}] }] ) except Exception as e: cloudwatch.put_metric_data( Namespace='Scraping/Jobs', MetricData=[{ 'MetricName': 'PagesFailed', 'Value': 1, 'Unit': 'Count', 'Dimensions': [{'Name': 'JobId', 'Value': os.environ.get('AWS_BATCH_JOB_ID', 'unknown')}] }] )

原理:CloudWatch 自定义指标是 Batch 任务唯一能实时反馈执行状态的通道。我们用 PagesScraped 和 PagesFailed 两个指标,配合告警策略,实现“爬取进度可视化”。运维人员不用翻日志,看控制台图表就知道任务是否卡住。

这七处修改,看似琐碎,实则是本地脚本与云服务之间的“协议转换”。漏掉任何一处,都可能导致任务在 Batch 上静默失败,排查起来耗时数小时。

3.2 Docker 镜像构建:精简、安全、可复现的三层镜像策略

Batch 任务运行在容器中,镜像质量直接决定稳定性。我们摒弃了“一个 Dockerfile 打天下”的粗放做法,采用三层分层构建策略,兼顾安全性、体积和可维护性:

第一层:基础镜像(base-scraper:1.0)—— 仅含 OS 和核心依赖
Dockerfile:

FROM public.ecr.aws/amazonlinux/amazonlinux:2 RUN yum update -y && \ yum install -y python3.9 python3.9-devel gcc gcc-c++ make && \ yum clean all RUN pip3.9 install --upgrade pip setuptools wheel # 预装 Chrome(关键!避免每次启动下载) RUN curl -sSL https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm -o /tmp/chrome.rpm && \ yum localinstall -y /tmp/chrome.rpm && \ rm -f /tmp/chrome.rpm # 预装字体(解决中文乱码) RUN yum install -y fontconfig dejavu-sans-fonts

构建命令:docker build -t base-scraper:1.0 .
体积:387MB(比 Ubuntu 镜像小 42%)
原理:Amazon Linux 2 是 AWS 官方优化镜像,内核和 glibc 与 EC2 实例完全一致,避免兼容性问题。Chrome 预装节省了 20 秒启动时间,且版本锁定(当前为 115.0.5790.170),杜绝运行时版本冲突。

第二层:运行时镜像(runtime-scraper:1.0)—— 加入 Python 包和反爬工具
Dockerfile:

FROM base-scraper:1.0 COPY requirements.txt . RUN pip3.9 install -r requirements.txt --no-cache-dir # 安装 undetected-chromedriver2(绕过 Selenium 检测) RUN pip3.9 install undetected-chromedriver2==3.5.5 # 安装 playwright(备用渲染引擎) RUN pip3.9 install playwright && playwright install chromium

requirements.txt 关键内容:

requests==2.31.0 beautifulsoup4==4.12.2 lxml==4.9.3 boto3==1.28.50 fake-useragent==1.4.0 cloudwatch-logging==1.0.0 # 自研日志库,自动打 Batch Job ID 标签

构建命令:docker build -t runtime-scraper:1.0 .
体积:621MB(比单层镜像小 28%)
原理:分离基础层和运行时层,使 Python 包更新无需重建整个 OS 层。我们固定 requests 和 beautifulsoup4 版本,因为它们的 minor 版本升级常引发解析逻辑变更(如 requests 2.31.0 修复了 HTTP/2 连接复用 bug)。

第三层:应用镜像(job-scraper-reed:2023.10)—— 注入业务代码和配置
Dockerfile:

FROM runtime-scraper:1.0 WORKDIR /app COPY . . # 设置非 root 用户(安全强制要求) RUN useradd -m -u 1001 scraper && \ chown -R scraper:scraper /app USER scraper CMD ["python3.9", "main.py"]

构建命令:docker build -t job-scraper-reed:2023.10 .
体积:635MB(仅增加 14MB 业务代码)
原理:最后一层只含业务逻辑,体积最小,推送 ECR 最快。强制使用非 root 用户,满足 AWS Batch 安全最佳实践——否则任务会因权限不足被拒绝启动。

注意:我们禁用docker build --no-cache,因为缓存能加速 CI/CD 流程。但每次发布新版本时,会在镜像 Tag 中加入日期(如2023.10),确保可追溯。所有镜像推送到私有 ECR 仓库,并开启扫描功能,自动检测 CVE 漏洞。

这套三层策略,让我们在 2023 年全年未发生一次因镜像问题导致的批量任务失败。相比单层镜像,构建时间从 12 分钟降至 3 分钟,推送时间从 8 分钟降至 90 秒。

4. 实操过程与核心环节实现

4.1 从零开始:创建 Batch 计算环境、队列和任务定义

现在进入实操阶段。以下所有命令均在 AWS CLI v2 下执行,假设你已配置好us-east-1区域的管理员凭证。我会给出完整命令、参数解释和避坑提示,而非只贴代码。

第一步:创建计算环境(Compute Environment)
这是 Batch 的“工厂车间”,决定任务在哪跑、用什么资源。

aws batch create-compute-environment \ --compute-environment-name scraping-ce-spot \ --type MANAGED \ --state ENABLED \ --compute-resources image-id="ami-0c02fb55956c7d316" \ --compute-resources instance-role="arn:aws:iam::123456789012:instance-profile/ecsInstanceRole" \ --compute-resources instance-types="c5.2xlarge,c5.4xlarge" \ --compute-resources minvCpus=0 \ --compute-resources maxvCpus=1000 \ --compute-resources desiredvCpus=0 \ --compute-resources ec2-key-pair="" \ --compute-resources security-group-ids="sg-0a1b2c3d4e5f67890" \ --compute-resources subnets="subnet-0123456789abcdef0,subnet-0987654321fedcba0" \ --compute-resources type=SPOT \ --compute-resources bid-percentage=70 \ --service-role "arn:aws:iam::123456789012:role/aws-service-role/batch.amazonaws.com/AWSServiceRoleForBatch"

参数详解:

  • image-id: 使用 Amazon Linux 2 AMI(ami-0c02fb55956c7d316),与我们的 Docker 基础镜像完全匹配,避免内核不兼容。
  • instance-types: 限定 c5 系列(计算优化型),c5.2xlarge(8vCPU/16GB)是性价比之王,c5.4xlarge(16vCPU/32GB)用于重负载任务。
  • bid-percentage=70: 出价为 On-Demand 价格的 70%,实测中断率可控在 5%/天。低于 60% 中断率飙升至 20%+。
  • subnets: 必须指定至少两个可用区的子网(如 us-east-1a 和 us-east-1b),确保 Spot 实例中断时,Batch 能在另一可用区快速拉起新实例。

提示:创建后需等待 2-3 分钟,状态从 CREATING 变为 VALID。可通过aws batch describe-compute-environments --compute-environments scraping-ce-spot查看。

第二步:创建作业队列(Job Queue)
这是任务的“调度中心”,决定优先级和资源分配。

aws batch create-job-queue \ --job-queue-name scraping-queue-default \ --state ENABLED \ --priority 10 \ --compute-environment-order computeEnvironment="scraping-ce-spot",order=1

参数详解:

  • priority 10: 优先级数值越小越高。我们另建一个scraping-queue-high,priority 设为 1,用于紧急任务。
  • computeEnvironment-order: 指定该队列只使用 Spot 计算环境。若需混合 Spot+On-Demand,可添加第二个computeEnvironment-order条目,order 设为 2。

第三步:注册任务定义(Job Definition)
这是任务的“说明书”,定义容器如何运行。

aws batch register-job-definition \ --job-definition-name scraping-reed-job \ --type container \ --container-properties '{ "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/scraping-reed:2023.10", "vcpus": 4, "memory": 16384, "privileged": false, "ulimits": [ {"name": "nofile", "softLimit": 65536, "hardLimit": 65536}, {"name": "nproc", "softLimit": 65536, "hardLimit": 65536} ], "environment": [ {"name": "OUTPUT_BUCKET", "value": "s3://my-scraping-bucket/raw-scraped/"}, {"name": "SECRETS_MANAGER_REGION", "value": "us-east-1"} ], "secrets": [ {"name": "COOKIES", "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/scraping/cookies-AbCdEf"} ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group-name": "/aws/batch/job", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "scraping-reed" } } }'

参数详解:

  • vcpusmemory: 必须与容器内 Chrome 启动参数匹配。我们设为 4vCPU/16GB,Chrome 启动时加--no-sandbox --disable-dev-shm-usage --disable-gpu --single-process参数,实测稳定。
  • ulimits: 关键!nofile 限制必须设为 65536,否则并发 100 个请求时会报OSError: [Errno 24] Too many open files
  • secrets: 直接绑定 Secrets Manager 密钥,Batch 会自动注入环境变量,无需脚本内调用 SDK。
  • logConfiguration: 指定日志组和前缀,所有任务日志自动归集到/aws/batch/job/scraping-reed/下,便于 CloudWatch Insights 查询。

注意:任务定义注册后,会生成一个 ARN(如arn:aws:batch:us-east-1:123456789012:job-definition/scraping-reed-job:1),后续提交任务必须引用此 ARN 的最新版本(:1)。

4.2 提交任务:参数化、可重试、带依赖的作业提交

任务提交不是简单的一行命令,而是工程化操作。我们封装了一个 Python 脚本submit_job.py,支持三种模式:

模式一:单次提交(ad-hoc)
适用于调试和补数:

python submit_job.py \ --job-name "reed-scrape-20231015" \ --job-queue "scraping-queue-default" \ --job-definition "scraping-reed-job:1" \ --parameters '{"start_date":"2023-10-15","end_date":"2023-10-15","max_pages":"50"}' \ --vcpus 4 \ --memory 16384

原理:--parameters传入 JSON 字符串,会被注入容器环境变量AWS_BATCH_JOB_PARAM_start_date等。脚本内用os.environ.get('AWS_BATCH_JOB_PARAM_start_date')读取,实现参数化爬取。

模式二:定时提交(cron)
每日早 8 点抓取新增职位:

# 创建 EventBridge 规则 aws events put-rule \ --name "scraping-reed-daily" \ --schedule-expression "cron(0 8 * * ? *)" \ --state ENABLED # 创建目标(调用 Batch SubmitJob) aws events put-targets \ --rule "scraping-reed-daily" \ --targets "Id"="1","Arn"="arn:aws:batch:us-east-1:123456789012:job-definition/scraping-reed-job:1","Input"='{"jobName":"reed-daily-","jobQueue":"scraping-queue-default","jobDefinition":"scraping-reed-job:1","parameters":{"start_date":"$(date -u +%Y-%m-%d)","end_date":"$(date -u +%Y-%m-%d)"}}'

原理:EventBridge 规则触发时,会自动调用 Batch 的 SubmitJob API。$(date ...)是 EventBridge 的内置函数,确保每次触发都用当天日期。

模式三:依赖提交(dependsOn)
实现“列表页 → 详情页 → 汇总”的三阶段流水线:

# 提交列表页任务(job1) JOB1_ID=$(aws batch submit-job \ --job-name "reed-list-20231015" \ --job-queue "scraping-queue-default" \ --job-definition "scraping-reed-job:1" \ --parameters '{"mode":"list","date":"2023-10-15"}' \ --query 'jobId' --output text) # 提交详情页任务(job2),依赖 job1 aws batch submit-job \ --job-name "reed-detail-20231015" \ --job-queue "scraping-queue-default" \ --job-definition "scraping-reed-job:1" \ --parameters '{"mode":"detail","date":"2023-10-15"}' \ --depends-on "[{\"jobId\":\"'$JOB1_ID'\",\"type\":\"SEQUENTIAL\"}]" \ --query 'jobId' --output text

原理:--depends-on参数指定 job2 必须在 job1 成功(SUCCEEDED)后才启动。Batch 内部会轮询 job1 状态,状态变为 SUCCEEDED 后立即拉起 job2。我们实测过 50 个依赖任务链,最长链耗时 22 分钟,无一错乱。

实操心得:提交任务后,不要立刻去 CloudWatch 看日志。先用aws batch describe-jobs --jobs $JOB_ID查看状态。常见状态:SUBMITTED(已接收)、PENDING(排队中)、RUNNABLE(等待资源)、STARTING(拉起容器)、RUNNING(执行中)、SUCCEEDED/FAILED(结束)。若卡在 PENDING 超过 5 分钟,大概率是计算环境资源不足,需检查maxvCpus设置或 Spot 出价。

4.3 监控与告警:用 CloudWatch Metrics 实现分钟级故障感知

Batch 本身提供基础监控,但要达到“分钟级故障感知”,必须自定义指标并配置智能告警。我们搭建了一套三级告警体系:

一级告警:任务失败率(Critical)
JobsFailedPerHour> 5 且SuccessRatePercent< 80% 连续 5 分钟,触发最高优先级告警。

aws cloudwatch put-metric-alarm \ --alarm-name "scraping-reed-failure-rate-high" \ --alarm-description "Reed scraping job failure rate exceeds threshold" \ --alarm-actions "arn:aws:sns:us-east-1:123456789012:scraping-alerts" \ --metric-name "JobsFailedPerHour" \ --namespace "Scraping/Jobs" \ --statistic "Sum" \ --period 300 \ --threshold 5 \ --comparison-operator "GreaterThanThreshold" \ --evaluation-periods 1 \ --datapoints-to-alarm 1 \ --dimensions "Name=JobId,Value=*" \ --unit "Count"

二级告警:单页耗时异常(Warning)
AvgScrapeTimeMs> 30000(30 秒)连续 10 分钟,说明页面加载或 JS 渲染变慢,可能是网站前端变更。

aws cloudwatch put-metric-alarm \ --alarm-name "scraping-reed-slow-page" \ --alarm-description "Average scrape time exceeds 30 seconds" \ --alarm-actions "arn:aws:sns:us-east-1:123456789012:scraping-warnings" \ --metric-name "AvgScrapeTimeMs" \ --namespace "Scraping/Jobs" \ --statistic "Average" \ --period 600 \ --threshold 30000 \ --comparison-operator "GreaterThanThreshold" \ --evaluation-periods 2 \ --datapoints-to-alarm 2 \ --dimensions "Name=JobId,Value=*" \ --unit "Milliseconds"

三级告警:S3 输出缺失(Info)
当 CloudWatch Logs 中 15 分钟内未出现S3 upload completed日志,说明任务可能卡死在最后一步。

# 创建日志过滤告警 aws logs put-metric-filter \ --log-group-name "/aws/batch/job" \ --filter-name "scraping-reed-s3-upload" \ --filter-pattern "S3 upload completed" \ --metric-transformations metricName="S3Uploads",metricNamespace="Scraping/Jobs",metricValue="1" aws cloudwatch put-metric-alarm \ --alarm-name "scraping-reed-s3-missing" \ --alarm-description "No S3 upload log in last 15 minutes" \ --alarm-actions "arn:aws:sns:us-east-1:123456789012:scraping-info" \ --metric-name "S3Uploads" \ --namespace "Scraping/Jobs" \ --statistic "Sum" \ --period 900 \ --threshold 0 \ --comparison-operator "LessThanOrEqualToThreshold" \ --evaluation-periods 1 \ --datapoints-to-alarm 1 \ --unit "Count"

实操心得:告警不是越多越好。我们最初设置了 12 个告警,结果每天收 30+ 邮件,真正有用的不到 3 条。现在只保留这 3 个,配合 CloudWatch Dashboard 一张图看全貌。Dashboard 上我们固定展示:任务总数、成功数、失败数、平均耗时、S3 输出量(GB)、Spot 中断次数。运维同学每天早上花 30 秒扫一眼,就能掌握全局。

5. 常见问题与排查技巧实录

5.1 典型问题速查表:从现象到根因的 7 类高频故障

现象可能根因排查命令解决方案
任务卡在 RUNNABLE 状态超过 10 分钟Spot 实例出价过低,或子网 IP 耗尽aws batch describe-jobs --jobs JOB_ID查看statusReasonaws ec2 describe-subnets --subnet-ids SUBNET_ID查看AvailableIpAddressCount提高bid-percentage至 75%;为子网扩容 IP 地址段;或添加 On-Demand 实例作为后备
容器启动后立即 EXITED,Exit Code 137内存溢出(OOM Killer 杀死进程)aws logs get-log-events --log-group-name "/aws/batch/job" --log-stream-name "scraping-reed/..." --limit 5查看 OOM 日志增加任务定义中的memory值;Chrome 启动时加--disable-dev-shm-usage;减少并发请求数
**CloudWatch Logs 显示 `WebDriverException: Message
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/7 6:39:44

从F1赛车到无人机避障:聊聊脉冲雷达‘距离模糊’那些事儿

从F1赛车到无人机避障&#xff1a;聊聊脉冲雷达‘距离模糊’那些事儿想象一下F1赛车场上&#xff0c;工程师们正用雷达测量车速。突然&#xff0c;屏幕上出现两个重叠的速度值——这不是系统故障&#xff0c;而是雷达遇到了多普勒模糊。与此同时&#xff0c;一架无人机在树林中…

作者头像 李华
网站建设 2026/6/7 6:36:34

N皇后遗传算法Python实战:从编码设计到适应度函数调优

1. 这不是教科书&#xff0c;而是一次真实的GA项目复盘&#xff1a;从Matlab到Python的N皇后实战手记 你点开这篇文章&#xff0c;大概率不是为了背诵“遗传算法是模拟生物进化过程的优化方法”这种定义。你真正想搞清楚的是&#xff1a;当一个真实项目摆在面前——比如用遗传算…

作者头像 李华