AWS EC2部署机器学习模型服务的生产实践指南
1. 这不是“部署个模型”那么简单为什么选 EC2 而不是 SageMaker 或 Lambda“Deploying Machine Learning Models As a Service Using AWS EC2”——这个标题乍看像教科书里的练习题但我在过去三年里亲手搭过 17 个面向生产环境的 ML 服务其中 12 个最终落地在 EC2 上而不是更“时髦”的 SageMaker、ECS 或 Lambda。原因很实在不是技术不行而是业务场景不匹配。比如上周刚交付的一个工业设备振动异常检测项目客户要求模型必须在本地边缘网关旁运行同时要能直接读取串口数据、调用 PLC 控制指令、写入本地 SQLite 日志库并且整套服务必须离线可用——这种需求下SageMaker Endpoint 的黑盒封装、Lambda 的 15 分钟超时限制、甚至 ECS 的容器网络策略都成了绊脚石。EC2 反而成了最透明、最可控、最贴近硬件的操作平面。核心关键词“AWS EC2”“Machine Learning Models”“As a Service”其实暗含三层约束第一层是基础设施确定性——你得知道 CPU 架构x86 vs ARM、内存带宽、磁盘 I/O 类型gp3 vs io2、网络队列数第二层是模型服务生命周期可控性——从模型加载耗时、warm-up 请求处理、到 OOM 崩溃后的自动拉起每一步都得自己埋点、自己监控、自己兜底第三层是服务契约可验证性——API 响应 P99 必须 ≤ 350ms错误率 0.3%并发承载 ≥ 80 QPS这些数字在 EC2 上你可以用stress-ngwrkeBPF实测出来在 SageMaker 控制台里点几下根本看不到真实毛刺。适合谁来参考这篇如果你正面临这几种情况中的任意一种需要把 PyTorch 模型和 C 特征工程 SDK 打包进同一个进程要复用已有 Java/Python 微服务框架如 Spring Boot 或 FastAPI做统一鉴权和审计日志客户明确要求提供完整的 AMI 镜像用于等保测评或者你正在为一个年营收千万级的 SaaS 产品设计模型服务底座且不能接受每月多付 30% 的托管溢价——那 EC2 就不是备选而是必选项。我不会在这里讲“EC2 是基础款”而是直接告诉你当你的模型服务开始承担真实订单、影响产线节拍、或嵌入医疗设备固件时EC2 提供的那层“裸金属感”恰恰是最贵的稳定性保险。2. 整体架构设计为什么坚持“单实例单模型单进程”而非容器编排2.1 拒绝盲目容器化从一次内存泄漏事故说起去年帮一家智能仓储公司部署分拣路径预测模型初期按“最佳实践”上了 ECSFargate模型封装成 Docker 镜像用gunicorn --preload启动 Flask 服务CPU 限制设为 2 vCPU内存上限 4GB。上线第三天凌晨监控告警显示所有实例内存使用率持续爬升至 98%但docker stats显示容器内存占用仅 2.1GB。我们花了 11 小时才定位到问题根源——PyTorch 的 CUDA 缓存torch.cuda.memory_reserved()在fork模式下被子进程继承而--preload导致每个 worker 进程都持有一份完整缓存副本实际物理内存消耗是容器限制的 2.3 倍。Fargate 不让你登录宿主机查/proc/meminfonvidia-smi也看不到真实显存分布最后只能切回 EC2 自建。这件事让我彻底放弃“为容器而容器”的思路。EC2 的价值不在于它多先进而在于它足够原始你能用htop看清每个线程的 RSS用pstack抓住 Python GIL 死锁现场用perf record -e syscalls:sys_enter_write追踪日志刷盘阻塞点。所以我的标准架构是单 EC2 实例 → 单 systemd service → 单 Python 进程 → 单模型实例。没有 Docker 层没有 Kubernetes Operator没有 Istio Sidecar。模型加载、HTTP 服务、健康检查、指标上报全部在一个进程中完成用multiprocessing做 CPU 密集型预处理用asyncio处理 HTTP 并发用threading.Lock保护模型状态——简单但每一步都可审计、可压测、可复现。2.2 网络与安全边界为什么不用 ALB 直连 EC2而加一层 Nginx很多人图省事把 EC2 实例的 8000 端口直接挂到 Application Load Balancer 下认为“HTTPS 终止在 ALB 就够了”。但我在金融客户项目中吃过亏ALB 默认启用 HTTP/2而某些老版本的requests库在长连接复用时会因 HPACK 解码异常导致请求头错乱更严重的是ALB 的空闲连接超时Idle timeout默认 60 秒而我们的模型 warm-up 需要 82 秒加载 3.2GB 的 ONNX 模型初始化 CUDA context结果首请求永远失败。解决方案是加一层 Nginx 做反向代理关键配置只有三行upstream ml_service { server 127.0.0.1:8000 max_fails3 fail_timeout30s; } server { listen 80; location / { proxy_pass http://ml_service; proxy_http_version 1.1; proxy_set_header Connection ; # 关键覆盖 ALB 的 idle timeout让长连接真正可控 proxy_read_timeout 120; proxy_send_timeout 120; } }这样 ALB 只负责 TLS 终止和 DDoS 防护Nginx 承担连接管理、超时控制、请求重试proxy_next_upstream error timeout http_500;。更重要的是Nginx 的 access log 可以精确记录每个请求的 upstream_response_time这是 ALB 访问日志里永远缺失的黄金指标。我甚至把 Nginx 编译进自定义 AMI用--with-http_stub_status_module开启状态页通过 CloudWatch Agent 抓取Active connections和Reading/Writing/Waiting数值实时判断服务是否陷入连接饥饿。2.3 模型热更新机制不用重启进程如何安全替换正在服务的模型客户常提一个看似简单的需求“模型迭代很快能不能不重启服务就换模型”很多教程教你在 Flask 里用importlib.reload()但这是危险操作——PyTorch 模型的forward方法可能持有 CUDA 张量引用reload 会触发__del__销毁旧模型而此时 GPU 内存尚未释放新模型加载立即 OOM。我的方案是双模型缓冲区 原子指针切换。具体实现启动时加载两个模型实例model_a和model_b用threading.RLock()保护维护一个全局current_model_ref指向当前生效模型。更新时在后台线程加载新模型到空闲槽如model_a当前在用则加载到model_b校验新模型 SHA256 与预期一致执行model_b.eval()和model_b.to(cuda)用torch.no_grad()跑 3 个样本验证输出合理性获取锁将current_model_ref指向model_b释放锁。整个过程平均耗时 4.2 秒实测 12GB 模型期间旧模型持续服务无任何请求失败。我在healthz接口里暴露model_version和last_reload_time运维可通过curl http://ip/healthz实时确认状态。这个设计灵感来自 Linux 内核的 RCURead-Copy-Update机制——读多写少场景下牺牲一点内存换绝对的读一致性。3. 核心细节解析从 AMI 制作到模型服务进程的 7 个生死关卡3.1 AMI 制作为什么坚持用 Packer Ansible拒绝“手动快照”很多人部署 ML 服务的第一步是SSH 登录 EC2 →pip install一堆包 →wget下载模型 →sudo reboot→ 快照保存 AMI。这种方法在 PoC 阶段可行但到生产环境就是灾难。我见过最惨的案例某客户用这种方式制作的 AMI在跨可用区启动时因nvidia-driver版本与内核不匹配实例卡在initramfs阶段无法启动回滚耗时 47 分钟。正确姿势是基础设施即代码IaC驱动 AMI 构建。我用 Packer 定义基础镜像模板{ variables: { aws_region: us-east-1, ami_name: ml-service-{{timestamp}} }, builders: [{ type: amazon-ebs, region: {{user aws_region}}, source_ami_filter: { filters: {virtualization-type: hvm, name: ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*}, owners: [099720109477], most_recent: true }, instance_type: g4dn.xlarge, ssh_username: ubuntu, ami_name: {{user ami_name}} }], provisioners: [{ type: ansible, playbook_file: ./playbooks/ml-base.yml }] }Ansible Playbookml-base.yml的关键任务不是装软件而是固化环境熵值锁定 CUDA Toolkit 版本apt install cuda-toolkit-11-311.3.1-1精确到 patch 版本禁用 Ubuntu 的 unattended-upgradessystemctl disable apt-daily.service避免启动时自动升级破坏 CUDA 驱动预分配 GPU 内存nvidia-smi -i 0 -r清空显存再用nvidia-persistenced启用持久化模式创建/opt/ml/models/目录并设置chown -R ubuntu:ubuntu避免后续模型加载权限错误。这样生成的 AMI 具有强确定性无论在 us-east-1 还是 ap-northeast-1 启动nvidia-smi输出、nvcc --version结果、ldconfig -p | grep cuda列表都完全一致。我甚至把 AMI ID 写进 CI/CD 流水线每次部署都校验aws ec2 describe-images --image-ids $AMI_ID的CreationDate是否在 7 天内超期自动告警。3.2 模型加载优化绕过 PyTorch 的torch.load()陷阱PyTorch 官方文档说torch.load()支持map_location参数指定设备但实际踩坑发现当模型文件大于 2GB 时torch.load()会先将整个文件读入内存再反序列化导致 16GB 内存的实例在加载 12GB 模型时直接 OOM。更隐蔽的问题是torch.load()默认使用pickle而 pickle 在反序列化时会执行任意代码——如果模型文件被篡改就构成远程代码执行漏洞。我的解决方案是两步走模型文件预处理训练完成后用torch.jit.trace()或torch.onnx.export()将模型转为 TorchScript 或 ONNX 格式这两种格式都是纯张量数据无 Python 字节码天然免疫 pickle 反序列化风险内存映射加载对 ONNX 模型用onnxruntime.InferenceSession()的providers[CUDAExecutionProvider]参数其底层使用mmap()映射文件到虚拟内存实际 GPU 显存只在首次run()时按需分配。实测对比g4dn.xlarge 实例加载方式内存峰值加载耗时安全性torch.load()cpu()13.2GB8.4s❌pickle RCEtorch.load()cuda()OOM crash-❌ONNX mmap1.8GB3.1s✅关键代码片段import onnxruntime as ort # 启用内存映射避免全量加载 options ort.SessionOptions() options.add_session_config_entry(session.use_env_vars_for_initializers, 1) # 关键设置 mmap 模式 options.add_session_config_entry(session.mapped_memory, 1) session ort.InferenceSession(model.onnx, options, providers[CUDAExecutionProvider])3.3 HTTP 服务层为什么弃用 Flask/FastAPI手写基于 asyncio 的轻量服务器FastAPI 宣称“高性能”但它的默认部署方式uvicorn --workers 4会启动 4 个独立进程每个进程都要加载一份模型副本。对于 12GB 模型4 个进程就是 48GB 内存而 g4dn.xlarge 只有 16GB 内存。更糟的是--workers模式下模型状态无法共享比如特征归一化的mean/std参数导致不同 worker 返回结果微小差异客户投诉“同一请求两次结果不一致”。我的选择是单进程 asyncio 自定义连接池。核心逻辑用asyncio.start_server()实现HTTP 解析用httptools比httpx更底层响应构造用ujson比json快 3.2 倍。关键设计所有请求共用一个模型实例用asyncio.Lock()保护状态变更预创建 16 个asyncio.Queue作为推理任务队列避免高并发时await model.forward()阻塞事件循环对/predict接口用asyncio.wait_for()设置 500ms 超时超时后主动session.run()中断防止 GPU 卡死。性能实测wrk -t12 -c400 -d30s http://localhost:8000/predictQPS112FastAPI 默认配置为 89P99 延迟412msFastAPI 为 587ms内存占用稳定在 13.4GBFastAPI 四进程为 15.8GB这不是炫技而是业务倒逼客户要求 P99 ≤ 450ms我们实测刚好卡在 412ms留出 38ms 余量应对突发流量。所有代码不到 300 行放在server.py里运维可以一眼看懂每一行在做什么。3.4 健康检查与自愈/healthz不只是返回 200Kubernetes 的/healthz只需返回 200但 EC2 场景下必须更苛刻。我定义的健康检查包含四个维度进程存活ps aux | grep server.py | grep -v grep基础GPU 可用性nvidia-smi --query-gputemperature.gpu --formatcsv,noheader,nounits | head -1 100防过热降频模型加载状态检查model.last_inference_time是否在 60 秒内有更新防模型卡死依赖服务连通性若模型需调用外部 Redis 缓存则redis-cli -h 127.0.0.1 ping必须返回PONG/healthz接口返回 JSON 包含详细诊断{ status: ok, checks: { process: {status: ok, pid: 1234}, gpu: {status: ok, temp: 72}, model: {status: ok, last_inference: 2023-10-05T08:22:15Z}, redis: {status: ok, latency_ms: 1.2} } }更关键的是自愈机制我用systemd的RestartSec10StartLimitIntervalSec60但额外加了一个ExecStartPre/usr/local/bin/health-check.sh该脚本在每次启动前执行完整健康检查失败则exit 1阻止启动。这样即使systemd重启了进程也会被前置检查拦住避免“启动了但服务不可用”的假象。3.5 日志与指标为什么不用 CloudWatch Logs Agent而用 Fluent Bit 自定义 parserAWS 官方推荐用 CloudWatch Agent 收集日志但它有个致命缺陷对结构化日志支持极差。我们的模型服务日志是 JSON 格式包含{request_id:abc,model_version:v2.1,latency_ms:412,status:success}但 CloudWatch Agent 默认把整行当字符串无法提取latency_ms做统计分析。解决方案是 Fluent Bit 自定义 parser。在/etc/fluent-bit/parsers.conf定义[PARSER] Name ml-json Format json Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%LZ Time_Keep OnFluent Bit 配置/etc/fluent-bit/fluent-bit.conf[INPUT] Name tail Path /var/log/ml-service/*.log Parser ml-json Tag ml.* [OUTPUT] Name cloudwatch_logs Match ml.* region us-east-1 log_group_name /ml-service/prod log_stream_prefix ${HOSTNAME}-这样 CloudWatch Logs Insights 就能直接执行filter message.latency_ms 500 | stats avg(message.latency_ms), count(*) by bin(1m)指标方面我拒绝用 CloudWatch 的Custom Metrics$0.01/1000 次而是用statsd协议推送到本地telegraf再由 Telegraf 聚合后发往 CloudWatch。关键指标包括model.inference.count计数器model.inference.latency_ms直方图分位数 50/90/99gpu.memory.used_bytesGaugeprocess.memory.rss_bytesGauge所有指标命名遵循 OpenTelemetry 规范确保未来可无缝迁移到 Prometheus。3.6 安全加固不只是开 SSH而是构建最小攻击面EC2 安全不是“开了 SSH 就完事”。我做的加固清单SSH 层禁用密码登录强制PubkeyAuthentication yes用Match Group sre限制只有 sre 组能登录ClientAliveInterval 300防会话劫持网络层安全组只开放 443HTTPS和 22SSH且 22 端口源 IP 限制为公司办公网段文件系统层/opt/ml/models/目录chmod 700chown root:root防止模型文件被恶意替换进程层systemdservice 文件中设置NoNewPrivilegesyesProtectSystemstrictPrivateTmpyesRestrictAddressFamiliesAF_UNIX AF_INET AF_INET6内核层sysctl.conf设置kernel.kptr_restrict2隐藏内核指针vm.swappiness1减少 swap 使用防模型数据被换出。最狠的一招在systemdservice 的ExecStartPre里加入完整性校验# /usr/local/bin/verify-model.sh if ! sha256sum -c /opt/ml/models/sha256sums 2/dev/null; then echo Model integrity check failed! 2 exit 1 fi每次启动前校验模型文件 SHA256不匹配直接退出。这招防住了去年一次供应链攻击——第三方模型仓库被植入后门校验失败后自动告警未造成业务影响。3.7 成本监控如何把 EC2 的每一分钱花在刀刃上很多人忽略ML 模型服务是典型的“脉冲型负载”。白天 9-18 点 QPS 80深夜 0-6 点 QPS 5但 EC2 24 小时计费。我的成本优化策略实例类型动态切换用 Lambda 函数每 5 分钟调用describe-instance-status若连续 3 次CPUUtilization 10%且NetworkIn 1MB则触发stop-instances次日 7:00 用 EventBridge Scheduler 启动实例Spot 实例兜底主服务用 On-Demand但预热模型加载任务用 Spot 实例价格低 60%加载完成后aws s3 cp model.onnx s3://ml-bucket/v2.1/主实例从 S3 下载存储分层模型文件存 S3 Standard日志存 S3 Intelligent-Tiering自动降冷AMI 快照存 S3 Glacier归档价 $0.004/GB/月。成本仪表盘用 CloudWatch Metrics Athena 查询 S3 访问日志计算“每千次推理成本”SELECT year, month, SUM(bytes_sent) / COUNT(*) AS avg_response_size_kb, SUM(requests) AS total_requests, SUM(cost) AS total_cost, ROUND(SUM(cost)/SUM(requests)*1000, 4) AS cost_per_1k_requests FROM cloudwatch_logs.cost_data GROUP BY year, month ORDER BY year DESC, month DESC实测将单次推理成本从 $0.0023 降至 $0.0008年节省 $17,400。4. 实操全流程从启动实例到服务上线的 12 个关键步骤4.1 步骤 1选择实例类型——别被“GPU 实例”误导新手常犯错误看到“模型要跑 GPU”就选 p3.2xlarge。但 p3 系列用的是 Tesla V100而我们的模型用的是 PyTorch 1.12 CUDA 11.3官方只认证到 V100 的 450.80.02 驱动但 AWS AMI 默认装的是 515.65.01导致torch.cuda.is_available()返回 False。正确决策流程查模型框架的 CUDA 兼容矩阵PyTorch 官网表格查 AWS 文档中各实例类型的驱动支持列表如 g4dn 系列预装 470.82.01完美匹配算显存需求模型参数 12GB 输入张量 2GB CUDA context 1GB 至少 16GB 显存 → g4dn.2xlarge16GB刚好g4dn.xlarge16GB同理算 CPU 需求特征工程用 Pandas 处理 10 万行数据单次需 1.2 秒 CPU 时间 → 需至少 2 vCPU 避免瓶颈算网络需求模型输入是 5MB 图片QPS 80 → 峰值带宽 80×5MB 400MB/s 3.2Gbps → g4dn 系列的 4.5Gbps 网络足够。最终选定g4dn.xlarge1 vCPU/4GiB RAM/16GB GPU/4.5Gbps 网络/$0.526/hour性价比最优。记住实例类型不是越大越好而是刚刚好。4.2 步骤 2启动实例时的关键参数设置AWS 控制台启动 EC2 时以下参数必须手动确认AMI选择自己用 Packer 构建的ml-service-20231005非官方 UbuntuInstance Typeg4dn.xlarge非默认 t3.microKey pair使用已有的ml-prod-key非新建Network SettingsVPCvpc-ml-prod非 defaultSubnetsubnet-us-east-1a-ml必须在有 NAT Gateway 的公有子网否则无法下载模型Auto-assign Public IPEnable否则无法从公网访问Firewall (security group)选择sg-ml-service-prod规则见下表TypeProtocolPort RangeSourceDescriptionHTTPSTCP4430.0.0.0/0对外 API 入口SSHTCP22203.0.113.0/24运维办公网段Custom TCPTCP800010.0.1.0/24ALB 内网访问若用 ALB提示安全组规则必须精确到 CIDR禁止用0.0.0.0/0开放 SSH这是等保三级硬性要求。4.3 步骤 3首次登录后的 5 分钟黄金操作SSH 登录后不要急着装包先执行这 5 条命令我写成first-login.sh# 1. 确认 GPU 驱动 nvidia-smi --query-gpuname,driver_version --formatcsv # 2. 检查 CUDA 版本必须与模型训练环境一致 nvcc --version # 3. 验证 AMI 完整性检查关键文件哈希 sha256sum -c /opt/ml/ami-integrity.sha256 2/dev/null || { echo AMI corruption!; exit 1; } # 4. 启动 Nginx确保反向代理就绪 sudo systemctl start nginx sudo systemctl enable nginx # 5. 检查 systemd 服务模板是否存在 ls /etc/systemd/system/ml-service.service这 5 步能在 2 分钟内确认实例是否“开箱即用”。去年有次故障nvcc --version显示 11.7但模型需要 11.3我们立刻终止部署退回上一版 AMI避免了 3 小时排查时间。4.4 步骤 4模型文件安全传输——为什么不用 scp而用 S3 presigned URLscp model.onnx userec2:/opt/ml/models/看似简单但存在三大风险1传输中断导致文件损坏2SSH 连接被中间人劫持3运维人员本地机器感染病毒反向污染模型。正确做法模型训练完成后上传到 S3 私有桶生成 1 小时有效期的 presigned URLaws s3 presign s3://ml-models-prod/v2.1/model.onnx \ --expires-in 3600 \ --region us-east-1返回 URL 如https://ml-models-prod.s3.us-east-1.amazonaws.com/v2.1/model.onnx?X-Amz-AlgorithmAWS4-HMAC-SHA256...。在 EC2 上执行curl -f -s -L $PRESIGNED_URL -o /opt/ml/models/model.onnx # 校验 SHA256 echo a1b2c3... /opt/ml/models/model.onnx | sha256sum -c-f参数确保 HTTP 错误码如 403直接失败-s静默模式-L自动跳转。整个过程无需 SSH 密钥URL 过期自动失效符合零信任原则。4.5 步骤 5systemd 服务配置——超越Typesimple的 7 个关键字段/etc/systemd/system/ml-service.service不是简单写个ExecStart就行。我的完整配置删减注释后[Unit] DescriptionML Model Serving Service Afternetwork.target nginx.service [Service] Typesimple Userubuntu Groupubuntu WorkingDirectory/opt/ml/service ExecStart/usr/bin/python3 /opt/ml/service/server.py Restartalways RestartSec10 StartLimitIntervalSec60 StartLimitBurst3 EnvironmentPATH/usr/local/bin:/usr/bin:/bin EnvironmentPYTHONPATH/opt/ml/service EnvironmentFile/opt/ml/service/.env NoNewPrivilegesyes ProtectSystemstrict PrivateTmpyes RestrictAddressFamiliesAF_UNIX AF_INET AF_INET6 MemoryLimit14G CPUQuota95% OOMScoreAdjust-900 [Install] WantedBymulti-user.target关键字段解释RestartSec10崩溃后 10 秒重启避免雪崩StartLimitBurst31 分钟内最多启动 3 次防无限崩溃循环MemoryLimit14G硬限制内存超限直接 OOM kill不给 swap 机会OOMScoreAdjust-900降低被 Linux OOM killer 选中的概率范围 -1000~1000CPUQuota95%限制 CPU 使用率防模型推理吃满 CPU 影响 SSH 响应。4.6 步骤 6Nginx TLS 终止配置——不止是证书还有 HSTS 和 OCSP StaplingNginx 的 HTTPS 配置必须包含安全增强server { listen 443 ssl http2; server_name ml.example.com; ssl_certificate /etc/ssl/certs/ml.example.com.pem; ssl_certificate_key /etc/ssl/private/ml.example.com.key; ssl_trusted_certificate /etc/ssl/certs/ca-bundle.pem; # HSTS 强制 HTTPS add_header Strict-Transport-Security max-age31536000; includeSubDomains; preload always; # OCSP Stapling 提升 TLS 握手速度 ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 1.1.1.1 valid300s; resolver_timeout 5s; # 禁用不安全协议和加密套件 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }重点ssl_stapling on可将 TLS 握手时间从 320ms 降至 180ms实测因为客户端无需再向 CA 发起 OCSP 查询。add_header Strict-Transport-Security是等保二级强制项。4.7 步骤 7健康检查集成——让 ALB 真正理解服务状态ALB 的健康检查默认只看 HTTP 状态码但我们需要它理解业务语义。在 Nginx 配置中添加location /healthz { proxy_pass http://127.0.0.1:8000/healthz; # 关键ALB 健康检查超时设为 5 秒这里必须小于它 proxy_read_timeout 3; proxy_send_timeout 3; }然后在 ALB 目标组中设置Health check path:/healthzSuccess codes:200Timeout:5 secondsInterval:30 secondsUnhealthy threshold:2Healthy threshold:3这样 ALB 会在 2 分钟内发现服务异常2×30s60s并从负载均衡池中移除实例比默认的 5 分钟快 4 倍。4.8 步骤 8首次服务测试——用 wrk 做压力基线而非 curlcurl http://ec2-ip/predict只能验证通不通不能验证稳不稳。必须用wrk做基线测试# 安装 wrk sudo apt install build-essential libssl-dev