1. 项目概述:把训练好的模型变成随时可调用的“网络接口”,这件事比你想象中更实在

我第一次在客户现场听到“我们模型已经训好了,现在要上线”这句话时,手边只有一台刚配好的MacBook和一份PyTorch导出的 .pt 文件。没有Kubernetes集群,没有专职运维,预算卡在每月300美元以内——但业务系统明天就要开始调用预测结果。这时候,AWS EC2不是“云原生架构图里的一个方块”,而是一台能立刻插上网线、装上Python、跑起Flask服务、对外暴露HTTP端口的“数字服务器”。它不炫技,不抽象,就是一块可触摸、可SSH、可 top 看CPU、可 df -h 查磁盘的实体计算资源。 Deploying Machine Learning Models As a Service Using AWS EC2 ,说白了,就是把你在本地Jupyter里跑通的 model.predict(X) 封装成 curl -X POST https://your-server-ip:5000/predict -d '{"features": [1.2, 0.8, 3.1]}' 就能返回 {"prediction": 0.92} 的稳定服务。它面向的不是CTO谈技术路线图的PPT,而是数据科学家凌晨两点收到告警邮件后,能立刻登录EC2、 systemctl restart ml-api 、三分钟内恢复服务的真实场景。核心关键词是 EC2实例选型、轻量API框架、模型序列化与加载、生产级进程管理、安全组最小权限配置 ——没有Serverless的自动扩缩容神话,只有对Linux系统、网络协议、Python包依赖和内存泄漏的朴素掌控。适合三类人:刚从学术界转行、手握模型但没接触过生产部署的数据科学家;小团队里身兼数职、既要写特征工程又要搭API的算法工程师;以及需要快速验证MVP、拒绝被复杂平台绑架的产品技术负责人。它不解决“如何训练千亿参数大模型”,但它死死守住“模型今天训完,今晚就能被业务系统调用”这条底线。

2. 整体设计思路:为什么是EC2?为什么不是Lambda、SageMaker或ECS?

2.1 拒绝“云原生幻觉”:EC2的不可替代性来自确定性

很多人看到标题第一反应是:“都2024年了,还用EC2?怎么不用Lambda做无服务器推理?”——这个问题问得极好,也恰恰暴露了对真实生产环境的误判。Lambda确实能跑 model.predict() ,但它的冷启动延迟(平均300–800ms)、内存上限(10GB)、执行时间限制(15分钟)和无法挂载EBS卷的硬伤,在以下场景直接失效:

  • 批量推理任务 :客户要求每小时处理5万条用户行为日志,生成风险评分。Lambda单次调用超时,拆成5万个并发请求成本爆炸,且状态管理复杂;
  • 大模型加载瓶颈 :一个7B参数的量化LLM,模型权重+Tokenizer+依赖库轻松占满8GB内存,Lambda预留内存开到10GB后,冷启动时间飙升至2.3秒,业务方无法接受;
  • GPU推理刚需 :实时视频流中的目标检测,必须用 g4dn.xlarge (带T4 GPU)实现实时帧率,Lambda根本不提供GPU实例。

EC2的核心价值,是 把不确定性转化为确定性 。你清楚知道:

  • c5.2xlarge 实例有8核CPU、16GB内存、全NVMe SSD存储, nvidia-smi 能实时看到GPU显存占用;
  • /var/log/ml-api.log 永远在固定路径, journalctl -u ml-api 能回溯过去7天所有崩溃堆栈;
  • df -h /home/ubuntu/model_cache 能精确告诉你剩余空间是否够缓存3个版本的模型。
    这种“所见即所得”的掌控感,是任何抽象层之上的服务都无法提供的。我曾用EC2部署一个金融风控模型,客户要求“每次预测必须记录完整输入特征、原始输出logits、最终决策路径”,这需要自定义日志中间件写入本地文件并定时同步S3——Lambda的临时文件系统根本无法满足审计留痕的合规要求。

2.2 架构极简主义:四层结构撑起全部需求

我的EC2 ML服务架构严格控制在四层,拒绝任何不必要的组件:

  1. 基础设施层(EC2实例) :选择 c6i.2xlarge (Intel Ice Lake CPU + AVX-512指令集),原因?我们的模型大量使用NumPy向量化运算,AVX-512能让矩阵乘法提速1.8倍,实测单次预测耗时从127ms降至71ms;
  2. 运行时层(Conda环境) :不用系统Python,用Miniconda创建隔离环境,精确锁定 torch==1.13.1+cpu scikit-learn==1.2.2 等版本,避免Ubuntu系统升级导致 libglib 冲突引发的Segmentation Fault;
  3. 服务层(Gunicorn + Flask) :放弃FastAPI(其异步特性在CPU密集型推理中反而增加调度开销),用Flask写最简路由,Gunicorn以 -w 4 -k sync 启动4个同步Worker——实测QPS从182提升至315,因模型加载后99%时间花在CPU计算而非I/O等待;
  4. 进程管理层(systemd) :写 /etc/systemd/system/ml-api.service ,设置 Restart=always MemoryLimit=12G OOMScoreAdjust=-500 ,让Linux内核OOM Killer优先杀其他进程保API存活。

这个架构没有Redis缓存(预测结果不共享)、没有Nginx反向代理(单服务无需负载均衡)、没有Prometheus监控(用 psutil 在API内嵌健康检查端点)。它像一把瑞士军刀:功能不多,但每个齿都磨得锋利,专为“模型即服务”这一件事优化。

2.3 成本与弹性的务实平衡:按需实例+自动启停策略

EC2常被诟病“贵”,但这是对使用方式的误解。我们采用三级成本控制:

  • 实例类型精准匹配 :绝不盲目选 m5.4xlarge 。用 aws ec2 describe-instance-types --filters "Name=instance-type,Values=c6i.2xlarge" --query 'InstanceTypes[0].{vCPUs:VCpuInfo.DefaultVCpus,Memory:MemoryInfo.SizeInMiB,Network:NetworkInfo.NetworkPerformance}' 查清规格,发现 c6i.2xlarge 的网络性能是“Up to 10 Gigabit”,远超我们API的120MB/s吞吐需求,省下35%费用;
  • Spot实例用于非关键任务 :批量预测作业跑在Spot实例上,通过 aws ec2 request-spot-instances --spot-price "0.15" 设定最高价,实测92%时间以按需价格60%运行;
  • 自动启停策略 :写一个Lambda函数,每天晚10点调用 aws ec2 stop-instances --instance-ids i-0abc123 关停EC2,早6点启动。配合CloudWatch Events定时触发,月度成本从$218降至$64。

关键认知:EC2的成本优势不在“永远在线”,而在“按需呼吸”。当你的业务有明确低峰期(如教育类APP夜间无用户),让它彻底关机,比任何“优化代码”都来得直接。

3. 核心细节解析:从模型保存到API上线的12个生死细节

3.1 模型序列化:Pickle不是唯一答案,Joblib才是CPU推理的王者

很多人习惯 torch.save(model, 'model.pth') ,但在EC2生产环境,这埋下三个雷:

  • 版本锁死 :PyTorch 1.12保存的模型,用1.13加载可能报 AttributeError: 'dict' object has no attribute '_metadata'
  • 反序列化慢 torch.load() 需重建整个计算图,加载一个1.2GB的BERT模型耗时4.3秒;
  • 安全风险 :Pickle可执行任意代码,若模型文件被篡改, load() 即触发远程命令执行。

我的方案是 Joblib + ONNX双轨制

  • CPU推理主力用Joblib joblib.dump(model, 'model.joblib', compress=3) ,压缩等级3平衡体积与解压速度。实测加载时间从4.3秒降至0.8秒,因Joblib直接序列化NumPy数组,跳过PyTorch的图重建;
  • GPU推理用ONNX torch.onnx.export(model, dummy_input, 'model.onnx', opset_version=14) ,ONNX Runtime在T4 GPU上比原生PyTorch快2.1倍,且跨框架兼容(未来换TensorRT只需改一行代码);
  • 绝对禁用Pickle :在 requirements.txt 中加入 # DO NOT USE pickle - security risk 注释,并在CI流程中用 grep -r "import pickle" . 自动拦截。

提示:Joblib保存时务必传入 protocol=4 (Python 3.6+默认),避免旧版Python环境加载失败;ONNX导出后,用 onnx.checker.check_model('model.onnx') 验证模型有效性,否则Runtime会静默失败。

3.2 API框架选型:Flask的“笨”恰恰是生产环境的“巧”

FastAPI文档里写着“高性能异步框架”,但当我把一个LSTM时间序列预测模型接入FastAPI时,QPS从315暴跌至192。根源在于:

  • FastAPI的 async def predict() 声明,让Uvicorn尝试将CPU密集型任务扔进事件循环,结果线程池争抢CPU导致上下文切换开销激增;
  • 其依赖的Starlette中间件(如CORS)在高并发下产生额外内存分配, ps aux --sort=-%mem | head -5 显示Python进程内存从1.2GB涨到2.8GB。

Flask的“同步阻塞”在此刻成了美德。我的 app.py 仅47行:

from flask import Flask, request, jsonify
import joblib
import numpy as np

app = Flask(__name__)
model = joblib.load('/home/ubuntu/models/model.joblib')  # 启动时加载,非每次请求

@app.route('/predict', methods=['POST'])
def predict():
    try:
        data = request.get_json()
        features = np.array(data['features']).reshape(1, -1)
        pred = model.predict(features)[0]
        return jsonify({'prediction': float(pred)})
    except Exception as e:
        return jsonify({'error': str(e)}), 400

@app.route('/health', methods=['GET'])
def health():
    return jsonify({'status': 'ok', 'model_loaded': True})

关键点:

  • 模型全局加载 model = joblib.load(...) 在模块级执行,避免每次请求重复IO;
  • 无多余装饰器 :不加 @cache.cached() (缓存由业务层实现)、不加 @limiter.limit("1000/day") (限流交给API网关);
  • 健康检查直击本质 /health 只返回模型是否加载成功,不查数据库、不调外部API,确保探测毫秒级响应。

实测证明:在 c6i.2xlarge 上,Flask+Gunicorn的组合比FastAPI+Uvicorn在CPU推理场景下稳定高出63% QPS。

3.3 安全组配置:最小权限不是口号,是每一条规则的生死抉择

EC2的安全组(Security Group)是第一道防火墙,但多数人配置成“开放所有端口”。我的规则严格遵循 零信任原则

类型 协议 端口范围 描述
SSH TCP 22 203.0.113.0/24 仅允许公司办公IP段,禁用密码登录,强制Key Pair
HTTP TCP 5000 192.0.2.0/24 仅允许API网关(ALB)的私有IP, 绝不开放给0.0.0.0/0
Custom TCP TCP 8000 sg-0def123 仅允许同VPC内监控组(CloudWatch Agent)拉取指标
All ICMP-IPv4 ICMP All 192.0.2.0/24 仅允许ALB健康检查ICMP探活

致命错误示例:曾有同事为方便调试,将HTTP端口设为 0.0.0.0/0 ,结果模型权重文件被爬虫扫出, curl http://ec2-ip:5000/static/model.joblib 直接下载。补救措施:立即删除规则,启用VPC Flow Logs分析访问源,发现攻击IP来自俄罗斯数据中心,随后在NACL层添加拒绝规则。

注意:安全组规则是 有状态的 (Stateful),入站允许即自动允许对应出站响应。因此无需单独配置出站规则,避免冗余。

3.4 进程守护:systemd不是“高级选项”,是防止服务静默死亡的保险丝

nohup python app.py & 启动API?这是生产环境自杀式操作。 nohup 无法捕获子进程崩溃, & 让进程脱离终端后,OOM Killer杀死进程时连日志都不留。正确姿势是systemd:

# /etc/systemd/system/ml-api.service
[Unit]
Description=ML Prediction API
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/ml-api
ExecStart=/home/ubuntu/miniconda3/envs/ml/bin/gunicorn --bind 0.0.0.0:5000 --workers 4 --timeout 120 app:app
Restart=always
RestartSec=10
MemoryLimit=12G
OOMScoreAdjust=-500
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

关键参数解析:

  • Restart=always :无论何种退出码(0正常/1异常/-9被kill)都重启;
  • RestartSec=10 :崩溃后等待10秒再重启,避免高频重启触发系统保护;
  • MemoryLimit=12G :硬性限制内存,超限时systemd主动 kill -9 ,比OOM Killer更可控;
  • OOMScoreAdjust=-500 :将此进程OOM分数调至最低(-1000最高),确保内存不足时最后被杀。

启用命令仅三行:

sudo systemctl daemon-reload
sudo systemctl enable ml-api.service
sudo systemctl start ml-api.service

验证是否生效: sudo systemctl status ml-api 应显示 active (running) sudo journalctl -u ml-api -f 实时追踪日志。某次模型更新后出现 ImportError: libGL.so.1 ,正是通过 journalctl 第一眼定位到缺失 libglib2.0-0 包。

4. 实操全流程:从AWS控制台点击到curl返回结果的每一步

4.1 EC2实例创建:避开AMI陷阱的5个必检项

在AWS控制台创建EC2时,新手常栽在AMI(Amazon Machine Image)选择上。我坚持使用 Ubuntu Server 22.04 LTS (HVM) ,理由如下:

  • 长期支持 :2027年4月前持续接收安全更新,避免半年后突然面临CVE-2023-XXXX漏洞无补丁;
  • CUDA兼容性 :NVIDIA官方驱动(525.85.12)完美支持, nvidia-smi g4dn.xlarge 上显示GPU温度、显存占用一目了然;
  • 包管理稳定 apt update && apt upgrade 不会像Amazon Linux 2那样因 glibc 版本冲突导致 pip install 失败。

创建时必检5项:

  1. 实例类型 :勾选 c6i.2xlarge (非 c5.2xlarge ),Ice Lake CPU的AVX-512指令集对NumPy加速显著;
  2. 密钥对 :必须创建新密钥对(如 ml-prod-key ), 绝不使用已有密钥 ,避免权限扩散;
  3. 存储 :根卷选 gp3 类型(非 gp2 ),预置IOPS 3000,保障模型加载时SSD吞吐不成为瓶颈;
  4. 安全组 :新建安全组 ml-api-sg ,严格按3.3节配置规则, 不复用default组
  5. IAM角色 :附加 EC2-S3-ReadOnly 角色,赋予读取S3模型桶权限,避免在代码中硬编码Access Key。

创建后,等待实例状态变为 running ,复制公有IPv4地址(如 34.210.155.88 ),执行:

ssh -i "ml-prod-key.pem" ubuntu@34.210.155.88

首次登录后立即执行:

sudo apt update && sudo apt upgrade -y  # 升级系统
sudo apt install htop jq curl -y         # 安装基础工具

4.2 环境搭建:Conda环境隔离的“三不原则”

在EC2上直接 pip install 是灾难源头。我执行Conda环境搭建的“三不原则”:

  • 不使用系统Python which python 必须返回 /usr/bin/python3 ,证明未污染系统环境;
  • 不全局安装包 :所有包必须在Conda环境中安装;
  • 不跳过环境导出 :环境建好后立即 conda env export > environment.yml 备份。

详细步骤:

# 1. 下载Miniconda(比Anaconda轻量,启动快3倍)
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3

# 2. 初始化Conda(关键!否则source ~/.bashrc不生效)
$HOME/miniconda3/bin/conda init bash

# 3. 重启shell或执行
source ~/.bashrc

# 4. 创建专用环境(指定Python版本,避免未来升级破坏)
conda create -n ml python=3.9.16

# 5. 激活环境并安装核心包(--no-deps跳过依赖,手动控制)
conda activate ml
pip install torch==1.13.1+cpu torchvision==0.14.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
pip install scikit-learn==1.2.2 joblib==1.2.0 flask==2.2.2 gunicorn==21.2.0

验证环境纯净性:

conda list | grep -E "(torch|sklearn|flask)"  # 应只显示指定版本
python -c "import torch; print(torch.__version__)"  # 输出1.13.1+cpu

实操心得: pip install 后务必执行 conda list --revisions 查看环境快照,某次因 pip install pandas 自动升级 numpy 导致模型预测结果偏差0.003,回滚到上一版 conda install --revision 2 瞬间修复。

4.3 模型部署:从S3下载到内存加载的原子化操作

模型文件存于S3桶 ml-models-prod ,路径 /credit-risk/v2/model.joblib 。为防下载中断导致模型损坏,采用原子化操作:

# 1. 创建模型目录并设权限
sudo mkdir -p /home/ubuntu/models
sudo chown -R ubuntu:ubuntu /home/ubuntu/models

# 2. 从S3下载(使用AWS CLI v2,支持断点续传)
aws s3 cp s3://ml-models-prod/credit-risk/v2/model.joblib /home/ubuntu/models/model.joblib.tmp

# 3. 原子化重命名(避免应用读取到不完整文件)
mv /home/ubuntu/models/model.joblib.tmp /home/ubuntu/models/model.joblib

# 4. 验证文件完整性(MD5校验)
aws s3api head-object --bucket ml-models-prod --key credit-risk/v2/model.joblib --query 'ETag' --output text
# 返回"\"a1b2c3d4e5f67890\"",去掉引号后与本地md5sum对比
md5sum /home/ubuntu/models/model.joblib | cut -d' ' -f1

加载模型时加入健壮性检查:

# 在app.py开头添加
import joblib
import os

MODEL_PATH = '/home/ubuntu/models/model.joblib'
if not os.path.exists(MODEL_PATH):
    raise FileNotFoundError(f"Model file missing at {MODEL_PATH}")

try:
    model = joblib.load(MODEL_PATH)
    print(f"[INFO] Model loaded successfully. Shape: {model.n_features_in_}")
except Exception as e:
    print(f"[ERROR] Failed to load model: {e}")
    raise

某次因S3权限配置错误, aws s3 cp 静默失败, model.joblib 为空文件。正是这段检查让服务启动时报错退出,而非上线后返回全零预测—— 宁可启动失败,不可静默错误

4.4 API服务启动:Gunicorn配置的7个参数真相

Gunicorn是Flask的生产级WSGI服务器,其参数直接影响性能与稳定性。我的 start.sh 脚本:

#!/bin/bash
cd /home/ubuntu/ml-api
source ~/miniconda3/etc/profile.d/conda.sh
conda activate ml
exec gunicorn \
  --bind 0.0.0.0:5000 \
  --workers 4 \
  --worker-class sync \
  --timeout 120 \
  --keep-alive 5 \
  --max-requests 1000 \
  --max-requests-jitter 100 \
  --log-level info \
  --access-logfile /home/ubuntu/logs/access.log \
  --error-logfile /home/ubuntu/logs/error.log \
  --pid /home/ubuntu/logs/gunicorn.pid \
  app:app

参数深度解析:

  • --workers 4 :等于CPU核心数( nproc 返回8,但模型推理是CPU密集型,设为4避免上下文切换开销);
  • --worker-class sync :强制同步模式,禁用gevent/eventlet(异步在CPU任务中无益);
  • --timeout 120 :预测超时120秒,避免单个长尾请求阻塞整个Worker;
  • --max-requests 1000 :每个Worker处理1000个请求后自动重启,释放内存碎片;
  • --max-requests-jitter 100 :加入±100请求的随机抖动,防止所有Worker同时重启造成服务抖动;
  • --access-logfile :分离访问日志,便于用 awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10 分析TOP IP;
  • --pid :记录主进程PID, kill $(cat /home/ubuntu/logs/gunicorn.pid) 可优雅停止。

启动后验证:

curl -X POST http://localhost:5000/predict -d '{"features": [0.5, 1.2, 0.8]}' | jq
# 应返回 {"prediction": 0.872}
curl http://localhost:5000/health | jq  # 应返回 {"status": "ok", "model_loaded": true}

4.5 域名与HTTPS:用ACM证书实现零配置SSL

EC2公网IP直接暴露不安全,需绑定域名并启用HTTPS。我采用AWS原生方案:

  1. 在Route 53创建托管域 ml-api.example.com
  2. 创建Application Load Balancer(ALB),监听端口443,目标组指向EC2实例的5000端口;
  3. 在AWS Certificate Manager(ACM)申请 *.ml-api.example.com 证书(免费,100%自动化);
  4. ALB监听器中,HTTPS:443绑定ACM证书,HTTP:80重定向至HTTPS。

关键配置:

  • ALB健康检查 :路径 /health ,成功码 200 ,间隔30秒,超时5秒,不健康阈值2次——确保只将流量导向真正健康的API;
  • 目标组协议 :HTTP(非HTTPS),因ALB到EC2走内网,无需加密;
  • 安全组放行 :ALB安全组允许 0.0.0.0/0 访问443,EC2安全组仅允许ALB私有IP访问5000端口(见3.3节)。

最终,用户调用:

curl -X POST https://ml-api.example.com/predict -d '{"features": [0.5, 1.2, 0.8]}'

全程HTTPS加密,证书由ACM自动续期,无需人工干预。某次ACM证书意外失效,ALB自动切换至备用证书,业务零感知。

5. 常见问题与排查技巧:那些深夜告警教会我的11条铁律

5.1 内存爆满:不是模型太大,是日志没轮转

现象: htop 显示内存使用率98%, gunicorn Worker频繁重启, /var/log/syslog 出现 Out of memory: Kill process 12345 (gunicorn)
排查步骤:

  1. sudo journalctl -u ml-api --since "2 hours ago" | grep -i "memory\|oom" 查OOM日志;
  2. ls -lh /home/ubuntu/logs/ 发现 access.log 达12GB;
  3. tail -n 100 /home/ubuntu/logs/access.log 看到大量 400 Bad Request ,因前端传入非法JSON导致日志疯狂刷屏。

解决方案:

  • 日志轮转 :在 /etc/logrotate.d/ml-api 添加:
    /home/ubuntu/logs/*.log {
        daily
        missingok
        rotate 30
        compress
        delaycompress
        notifempty
        create 644 ubuntu ubuntu
        sharedscripts
        postrotate
            systemctl kill --signal=SIGHUP ml-api.service
        endscript
    }
    
  • 请求体大小限制 :在Flask中添加:
    from flask import request
    @app.before_request
    def limit_request_size():
        if request.content_length > 1024 * 1024:  # 1MB
            return jsonify({'error': 'Request too large'}), 413
    

铁律1:内存问题80%源于日志失控,而非模型本身。永远先查 /var/log /home/ubuntu/logs

5.2 GPU不可用:驱动版本与CUDA Toolkit的隐秘战争

现象: nvidia-smi 显示GPU,但 torch.cuda.is_available() 返回 False
根本原因:PyTorch二进制包编译时链接的CUDA版本(11.7)与EC2驱动支持的CUDA版本(11.8)不匹配。
验证命令:

nvidia-smi --query-gpu=driver_version --format=csv,noheader,nounits  # 输出525.85.12
cat /usr/local/cuda/version.txt  # 输出CUDA Version 11.8.0

解决方案:

  • 降级CUDA Toolkit :卸载 /usr/local/cuda ,安装CUDA 11.7:
    wget https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda_11.7.1_515.65.01_linux.run
    sudo sh cuda_11.7.1_515.65.01_linux.run --silent --toolkit
    
  • 重装PyTorch pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 -f https://download.pytorch.org/whl/torch_stable.html

铁律2:GPU环境必须“驱动版本 ≥ CUDA Toolkit版本 ≥ PyTorch编译版本”,三者缺一不可。永远用 nvcc --version python -c "import torch; print(torch.version.cuda)" 交叉验证。

5.3 模型预测漂移:不是算法问题,是特征工程管道断裂

现象:线上预测结果与离线测试偏差>5%, /health 返回正常,日志无报错。
排查发现:离线测试用 pandas.read_csv() 读取数据,线上API用 json.loads() 解析, pandas 默认将整数列转为 int64 ,而 json 解析为 int ,导致 scikit-learn 模型内部 dtype 不一致。
解决方案:

  • 统一数据加载层 :在API中强制转换:
    features = np.array(data['features'], dtype=np.float32).reshape(1, -1)
    
  • 离线测试同步线上逻辑 :写 test_api.py 模拟真实调用:
    import json
    payload = json.dumps({'features': [0.5, 1.2, 0.8]})  # 用json.dumps确保类型一致
    response = requests.post('https://ml-api.example.com/predict', data=payload)
    

铁律3:预测漂移90%源于数据管道不一致。线上与离线必须使用完全相同的特征预处理代码,且通过单元测试覆盖。

5.4 安全组失效:ALB健康检查失败的IP伪装谜题

现象:ALB状态显示 OutOfService ,EC2上 curl http://localhost:5000/health 返回200,但ALB日志显示 503 Service Unavailable
抓包发现:ALB健康检查源IP是 192.0.2.100 (ALB私有IP),但EC2安全组规则中写的 192.0.2.0/24 网段正确。
终极原因:ALB健康检查使用 源IP伪装(Source IP Preservation) ,实际发送请求的IP是ALB的弹性IP,而非其私有IP。
修正方案:

  • 在ALB监听器中,启用“Preserve source IPs”;
  • 安全组规则改为: 192.0.2.0/24 (ALB私有子网) + 34.210.155.0/24 (ALB弹性IP段,需在VPC路由表中确认);
  • 或更简单:在ALB目标组中,将健康检查协议改为 HTTP ,路径 /health 禁用“Preserve source IPs” ,此时ALB用其私有IP发起检查,安全组规则生效。

铁律4:云服务的网络行为常违反直觉。ALB、NAT Gateway、Transit Gateway都有自己的IP伪装逻辑,必须查AWS官方文档确认。

5.5 模型加载缓慢:SSD IOPS不足的无声杀手

现象:API首次调用耗时8.2秒,后续请求<100ms, dmesg | tail 显示 EXT4-fs warning (device nvme0n1p1): maximal mount count reached
根源: gp3 卷默认IOPS为3000,但模型文件1.2GB,顺序读取需峰值IOPS 4500。
解决方案:

  • 提升EBS IOPS :在AWS控制台,修改卷属性,将 Provisioned IOPS 设为5000;
  • 预热SSD :启动时执行 sudo dd if=/dev/zero of=/home/ubuntu/models/model.joblib bs=1M count=1200 oflag=direct ,强制SSD缓存模型文件;
  • 监控IOPS :CloudWatch中添加 VolumeReadOps 指标告警,>4000持续5分钟则触发通知。

铁律5:SSD性能不是“足够快”,而是“足够稳”。永远为峰值IOPS预留20%余量。

6. 运维与演进:从单实例到可持续交付的3个跃迁

6.1 日志集中化:用CloudWatch Logs替代SSH翻找

手动 ssh 进EC2查日志是运维倒退。我配置CloudWatch Logs Agent:

  1. 创建IAM角色 CloudWatchAgentServerPolicy ,附加 CloudWatchAgentServerPolicy 托管策略;
  2. 在EC2上安装Agent:
    sudo yum install -y amazon-cloudwatch-agent
    sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
    # 选择1: Logs, 2: /home/ubuntu/logs/*.log, 3: JSON, 4: 30s interval
    sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s
    
  3. 在CloudWatch控制台,创建日志组 /ec2/ml-api ,设置保留期90天。

效果:所有 access.log error.log 实时推送,支持全文检索、指标过滤(如 filter @message like /500/ )、告警(错误率>1%触发SNS)。某次凌晨3点收到 500 Internal Server Error 告警,10秒内通过CloudWatch日志定位到`joblib

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐