AWS ECS部署Triton推理服务:GPU模型生产化落地实操指南
1. 项目概述:为什么把 Triton 推理服务搬到 AWS ECS 上不是“可选项”,而是必经之路
我做模型服务化落地的这十年里,几乎每个团队都会经历这样一个阶段:本地跑通一个 PyTorch 模型,用 Flask 封装个 API,再加个
torch.jit.script
加速,就敢说“我们上线推理服务了”。结果呢?第一周用户量涨到 50 QPS,CPU 突然飙到 98%,日志里全是
OSError: [Errno 24] Too many open files
;第二周客户要调用带预处理的 OCR 流程,你发现本地写的
cv2.resize
在并发下内存泄漏,容器每小时重启一次;第三周业务方提了个新需求——“能不能让模型支持灰度发布?”你翻遍 Flask 文档,最后默默打开了 AWS 控制台。这不是故事,这是我去年在一家智能硬件公司帮他们重构边缘 AI 服务时的真实时间线。
Triton Inference Server 之所以成为工业级部署的事实标准,核心不在它多快,而在于它把“模型即服务”这件事真正工程化了:统一管理不同框架(PyTorch/TensorRT/ONNX)的模型、自动批处理、GPU 资源隔离、健康探针、指标暴露……但这些能力只有在生产环境的编排系统里才能真正释放。本地
docker run -p 8000:8000
启动的 Triton,本质还是个玩具——它没有弹性伸缩,没有滚动更新,没有跨 AZ 容灾,更没有和 CI/CD 流水线打通的能力。AWS ECS 正是填补这个断层的关键一环:它不像 Kubernetes 那样需要你养一个专职运维团队,也不像 EC2 那样要手动管理实例生命周期,而是用声明式任务定义 + 托管集群的方式,把 Triton 这种有状态的推理服务,变成可以像无状态 Web 服务一样被标准化交付的单元。
这篇文章要解决的,就是从“本地能跑通 MNIST”到“客户能稳定调用 24/7”的最后一公里。我们不讲 ECS 基础概念(那些文档写得比我能讲的清楚),而是聚焦三个硬核问题:第一,Triton 容器镜像怎么构建成适合 ECS 的形态,特别是如何把模型仓库、配置文件、自定义 Python 后处理逻辑打包进镜像而不污染基础镜像;第二,ECS 任务定义里哪些参数是 Triton 生死攸关的(比如
memoryReservation
设为 4096MB 和设为 8192MB,会导致 GPU 显存分配策略完全不同);第三,如何用 ECS Service 的滚动更新机制实现模型热替换——不用停服务,新模型加载完成自动切流。关键词 AWS 不是泛泛而谈,而是每一处配置都对应着 AWS 控制台里的真实开关、CLI 里的具体参数、CloudFormation 模板里的关键字段。如果你正在为模型上线发愁,或者刚被老板问“我们的 AI 服务什么时候能上生产环境”,这篇就是为你写的实操手册。
2. 整体架构设计与技术选型逻辑:为什么是 ECR+ECS,而不是 S3+Lambda 或 EC2+Systemd
在动手写 Dockerfile 之前,我必须先说清楚一个常被忽略的前提:
Triton 不是一个 HTTP 服务,而是一个 gRPC/HTTP 双协议的推理网关
。这意味着它的部署模式和传统 Web 应用有本质区别。很多团队一开始就想当然地用 Lambda 来承载 Triton,理由很朴素:“Serverless 省事啊”。但实际测试下来,你会发现三个致命短板:第一,Lambda 最大执行时间 15 分钟,而一个大型视觉模型首次加载可能就要 3-5 分钟,留给实际推理的时间所剩无几;第二,Lambda 的冷启动延迟在 500ms~2s 之间,而 Triton 的设计目标是 sub-100ms 的 P99 延迟,两者目标冲突;第三,也是最关键的一点——Triton 的模型仓库(model repository)需要挂载为只读卷,且路径必须固定(默认
/models
),而 Lambda 的临时存储
/tmp
是读写分离的,无法满足 Triton 的文件系统监听机制。我试过用 EFS 作为中间层,结果发现 EFS 的 POSIX 兼容性在高并发模型加载时会触发 Triton 的
inotify
监听失效,最终放弃。
那直接上 EC2 呢?确实可行,而且很多早期项目都是这么干的。但问题在于运维成本呈指数级上升。举个例子:当你要升级 Triton 版本时,EC2 方案需要你登录每台实例,拉取新镜像,修改 systemd 服务文件,逐台重启,还要手动验证 GPU 驱动兼容性。而 ECS 的任务定义(Task Definition)是声明式的——你只需要在控制台或 CLI 里更新
image
字段,勾选“启用滚动更新”,ECS 就会自动创建新任务、等待健康检查通过、逐步停止旧任务。这个过程背后是 ECS Agent 和 Amazon EC2 Container Registry(ECR)的深度集成:ECR 不仅是镜像仓库,它还为每个镜像生成唯一的
imageDigest
,ECS 用这个 digest 做精确版本控制,避免了
latest
标签带来的“幻读”问题(即不同实例拉取到不同内容的 latest 镜像)。
所以最终选定 ECR+ECS 组合,是基于四个刚性约束的权衡结果:
-
GPU 支持确定性 :ECS 支持
FARGATE和EC2两种启动类型,但 FARGATE 目前不支持 GPU 实例。因此我们必须使用 EC2 启动类型,并在集群中使用g4dn.xlarge(1x T4 GPU)或g5.xlarge(1x A10G GPU)这类实例。这里有个经验:T4 卡的 INT8 算力是 65 TOPS,A10G 是 300 TOPS,但价格相差近 3 倍。对于 MNIST 这类轻量模型,T4 完全够用,且显存带宽(320 GB/s)对小模型吞吐影响不大;但如果你后续要部署 ResNet-50 或 BERT-base,A10G 的显存容量(24GB vs 16GB)和带宽优势就会凸显。 -
网络模型匹配度 :Triton 默认监听
0.0.0.0:8000(HTTP)、0.0.0.0:8001(gRPC)、0.0.0.0:8002(metrics)。ECS 的服务发现机制天然适配这种“端口固定+多协议并存”的模式。我们可以通过 Application Load Balancer(ALB)将 443 端口的 HTTPS 请求路由到 ECS 服务的 8000 端口,同时用 Network Load Balancer(NLB)将 gRPC 流量(需 TLS 终止)转发到 8001 端口。这种混合负载均衡方案,在 EC2 上需要自己配置 Nginx 或 Envoy,而在 ECS 中只需在服务配置里勾选“为 ALB 创建目标组”即可。 -
安全边界清晰性 :Triton 的模型仓库如果放在公网可访问的 S3 上,虽然能实现动态加载,但会引入额外的安全审计负担(比如 S3 bucket policy、KMS 密钥轮换、VPC Endpoint 配置)。而 ECR 的私有仓库天然集成 IAM 权限体系,我们可以精确控制到“哪个 IAM Role 能 pull 哪个 repository 的哪个 image tag”。更重要的是,ECS 任务在启动时,会自动将 ECR 凭据注入容器环境,无需在 Dockerfile 里硬编码
aws ecr get-login-password命令——这个细节看似微小,却直接决定了镜像是否符合 SOC2 合规要求。 -
可观测性原生集成 :Triton 自带 Prometheus metrics 端点(
/metrics),而 ECS 任务可以直接将 CloudWatch Agent 配置为采集该端点。我们不需要在容器内额外运行 exporter,只需在任务定义的containerDefinitions里添加一行firelensConfiguration,指向预配置的 Fluent Bit 配置,就能把 Triton 的nv_inference_request_success、nv_inference_queue_duration_us等核心指标实时推送到 CloudWatch。相比之下,EC2 方案需要你在每台实例上维护 CloudWatch Agent 配置,一旦实例数量超过 5 台,配置漂移(configuration drift)就会成为噩梦。
提示:不要被“ECS 简单”这个宣传误导。它的简单是建立在 AWS 生态深度集成基础上的,一旦脱离这个生态(比如想混用 Azure Container Registry),复杂度反而会飙升。所以选型的第一步,永远是确认你的整个数据栈是否已经重度依赖 AWS。
3. 核心细节解析与实操要点:从本地 Docker 到 ECS 就绪镜像的七道工序
很多人以为“Docker build 完 push 到 ECR 就完事了”,结果在 ECS 上启动失败,日志里只有一行
standard_init_linux.go:228: exec user process caused: no such file or directory
。这个问题我至少帮 7 个团队排查过,根源全出在镜像构建的第七道工序——动态链接库的路径绑定。下面我把从本地开发环境到 ECS 生产镜像的完整构建链拆解成七个不可跳过的步骤,并说明每一步背后的原理和避坑点。
3.1 步骤一:选择正确的 Triton 基础镜像版本
Triton 官方提供了多种基础镜像,命名规则为
nvcr.io/nvidia/tritonserver:<version>-py<python_version>-<backend>
。例如
23.09-py3-min
表示 2023 年 9 月发布的最小化镜像(不含 TensorFlow/PyTorch 后端),而
23.09-py3
则包含所有主流后端。
关键决策点在于:你的模型用什么框架训练的?
-
如果是 PyTorch 模型(
.pt或.pth),必须选带-py3后缀的镜像,因为 Triton 的 PyTorch backend 依赖libtorch.so,这个库在最小化镜像里被移除了; -
如果是 ONNX 模型(
.onnx),理论上最小化镜像就够了,但实际中你会发现 ONNX Runtime 的 CUDA 版本和 Triton 基础镜像的 CUDA 版本必须严格匹配,否则加载时会报CUDA driver version is insufficient for CUDA runtime version。而官方镜像的 CUDA 版本是固定的(如 23.09 版本用 CUDA 12.2),所以为省事,我一律推荐用-py3镜像,它包含了所有 backend 的 CUDA 兼容版本。
注意:不要用
latest标签!Triton 的版本迭代非常快,latest可能指向尚未经过充分测试的 nightly build。我们线上环境用的是23.09-py3,这个版本经过了 NVIDIA 官方的稳定性认证,且对 A10G GPU 的支持最完善。
3.2 步骤二:模型仓库的结构化组织与权限固化
Triton 要求模型仓库必须是特定目录结构:
/models
/mnist
config.pbtxt
1/
model.pt
其中
config.pbtxt
是核心配置文件,定义了输入输出张量、动态批处理策略、实例数等。很多人在这里栽跟头:把
config.pbtxt
写成
name: "mnist"
platform: "pytorch_libtorch"
max_batch_size: 8
input [
{
name: "INPUT__0"
data_type: TYPE_FP32
dims: [ 1, 28, 28 ]
}
]
output [
{
name: "OUTPUT__0"
data_type: TYPE_FP32
dims: [ 10 ]
}
]
然后发现 ECS 启动后日志报错
failed to load 'mnist' version 1: unable to get number of GPUs
。原因在于
platform
字段写错了——PyTorch 模型应该用
pytorch_libtorch
,而
pytorch
是旧版字段,已废弃。这个错误在本地 Docker 里可能不报,因为本地环境变量不同,但在 ECS 的严格沙箱里会立即失败。
更关键的是权限问题。Triton 进程默认以 UID 1001 运行(见基础镜像的
Dockerfile
),而模型文件如果是在 macOS 或 Windows 上创建的,其文件权限可能是
644
且属主是 root。当 ECS 任务启动时,Triton 尝试读取
model.pt
,会因权限不足失败。解决方案是在构建镜像时,用
RUN chown -R 1001:1001 /models
固化权限。但注意:不能在
COPY
模型文件之后立刻
chown
,因为 COPY 操作会重置文件时间戳,导致 Triton 的文件变更监听失效。正确顺序是:
COPY models/ /models/
RUN chmod -R a+r /models && \
chown -R 1001:1001 /models
3.3 步骤三:自定义 Python 后处理逻辑的嵌入方式
MNIST 示例里,Triton 返回的是 10 维 logits,但业务方需要的是
{"prediction": 3, "confidence": 0.92}
这样的 JSON。官方推荐用
ensemble
模型来串联 Triton 模型和 Python 后处理,但这种方式在 ECS 上会引入额外的进程管理复杂度。我的实践是直接在 Triton 镜像里嵌入一个轻量级 FastAPI 服务,作为 Triton 的反向代理:
# postprocess.py
from fastapi import FastAPI, Request
import httpx
import numpy as np
import json
app = FastAPI()
@app.post("/v2/models/mnist/infer")
async def infer(request: Request):
# 1. 从原始请求中提取图像数据
body = await request.body()
# 2. 调用 Triton 原生接口
async with httpx.AsyncClient() as client:
resp = await client.post("http://localhost:8000/v2/models/mnist/infer", content=body)
# 3. 解析 Triton 返回的二进制响应,做 softmax 和 argmax
result = json.loads(resp.content.decode())
logits = np.array(result["outputs"][0]["data"]).reshape(-1, 10)
probs = np.exp(logits) / np.sum(np.exp(logits), axis=1, keepdims=True)
pred = int(np.argmax(probs, axis=1)[0])
conf = float(np.max(probs))
return {"prediction": pred, "confidence": conf}
这个脚本不能直接
python postprocess.py
启动,因为 ECS 任务定义里只能指定一个
entryPoint
。解决方案是用
supervisord
同时管理 Triton 和 FastAPI 两个进程。我们在镜像里安装
supervisord
,并编写
/etc/supervisor/conf.d/supervisord.conf
:
[supervisord]
nodaemon=true
[program:triton]
command=/opt/tritonserver/bin/tritonserver --model-repository=/models --http-port=8000 --grpc-port=8001 --metrics-port=8002
autostart=true
autorestart=true
user=1001
[program:fastapi]
command=uvicorn postprocess:app --host 0.0.0.0 --port 8003
autostart=true
autorestart=true
user=root
这样,ECS 任务启动时,
supervisord
会同时拉起 Triton(监听 8000)和 FastAPI(监听 8003),外部流量打到 8003,由 FastAPI 负责协议转换和后处理。
3.4 步骤四:GPU 驱动与容器运行时的精准匹配
这是 ECS 上 Triton 启动失败的第二大原因。ECS 集群的 EC2 实例必须安装与 Triton 镜像 CUDA 版本匹配的 NVIDIA 驱动。以
23.09-py3
镜像为例,它要求驱动版本 >= 525.60.13。如果你的实例用的是 Amazon Linux 2,默认的
nvidia-driver-latest
包可能只有 470.x 版本。解决方案是手动安装:
# 在 EC2 实例上执行
sudo amazon-linux-extras install -y epel
sudo yum install -y kernel-devel-$(uname -r) kernel-headers-$(uname -r)
wget https://us.download.nvidia.com/tesla/525.60.13/NVIDIA-Linux-x86_64-525.60.13.run
sudo sh NVIDIA-Linux-x86_64-525.60.13.run --silent --no-opengl-files
sudo nvidia-smi # 验证输出应显示驱动版本 525.60.13
但光装驱动还不够。ECS Agent 必须知道如何调用 NVIDIA Container Toolkit。我们在 ECS 集群的启动模板(Launch Template)里,添加用户数据(User Data)脚本:
#!/bin/bash
yum update -y
amazon-linux-extras install -y docker
service docker start
usermod -a -G docker ec2-user
# 安装 NVIDIA Container Toolkit
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -fsSL https://nvidia.github.io/libnvidia-container/$distribution/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
yum install -y nvidia-container-toolkit
nvidia-ctk runtime configure --runtime=docker
systemctl restart docker
这个脚本确保每台新加入集群的 EC2 实例,都具备运行 GPU 容器的完整环境。
3.5 步骤五:ECS 任务定义中的内存与 CPU 参数陷阱
很多人以为给 ECS 任务分配
memory: 8192
就万事大吉,结果发现 Triton 启动后 OOM 被 kill。根本原因是没理解 ECS 的
memory
和
memoryReservation
区别:
-
memoryReservation是容器保证能获得的内存下限,用于 ECS 调度器做资源预留; -
memory是容器能使用的内存上限,超过此值会被 Linux OOM Killer 杀死。
Triton 的内存消耗有两大块:模型权重加载(GPU 显存)和推理请求队列(CPU 内存)。对于 MNIST 模型,权重加载只占几百 MB 显存,但 CPU 内存消耗取决于并发请求数。Triton 默认的
--pinned-memory-pool-byte-size
是 256MB,这个池子用于零拷贝传输,如果并发高,会迅速耗尽。所以我们必须在
command
字段里显式设置:
"command": [
"/opt/tritonserver/bin/tritonserver",
"--model-repository=/models",
"--http-port=8000",
"--grpc-port=8001",
"--metrics-port=8002",
"--pinned-memory-pool-byte-size=1073741824", // 1GB
"--cuda-memory-pool-byte-size=0:2147483648" // GPU 0 上 2GB
]
对应的 ECS 任务定义中,
memoryReservation
应设为 4096(保证 Triton 有足够内存初始化),
memory
应设为 8192(防止高并发时 OOM)。CPU 单位同理:
cpu: 2048
(2 vCPU)是底线,因为 Triton 的 HTTP/gRPC server 需要独立线程处理连接。
3.6 步骤六:健康检查(Health Check)的定制化配置
ECS 默认的健康检查是
CMD-SHELL curl -f http://localhost:8000/v2/health/ready || exit 1
,但这个端点在 Triton 里返回的是
{"ready": true}
,它只表示 Triton 进程活着,
不表示模型已加载完成
。我遇到过最尴尬的情况:ECS 把流量切到新任务,结果第一个请求返回
503 Service Unavailable
,因为模型还在加载中。解决方案是自定义健康检查端点,我们在 FastAPI 里加一个
/healthz
:
@app.get("/healthz")
def healthz():
try:
# 尝试调用 Triton 的模型元数据接口
resp = httpx.get("http://localhost:8000/v2/models/mnist")
if resp.status_code == 200:
return {"status": "ok", "model_loaded": True}
else:
raise Exception("Model not ready")
except Exception as e:
return {"status": "error", "reason": str(e)}
然后在 ECS 服务配置里,把健康检查命令改为
CMD-SHELL curl -f http://localhost:8003/healthz || exit 1
。这样,只有当模型真正 ready 后,ECS 才会把任务注册到 ALB 的目标组。
3.7 步骤七:动态链接库的 LD_LIBRARY_PATH 注入
回到开头那个
no such file or directory
错误。根本原因是 Triton 基础镜像里,
libtorch.so
等动态库的路径没有被
ldconfig
缓存,而容器启动时
LD_LIBRARY_PATH
环境变量为空。解决方案是在 Dockerfile 里显式配置:
ENV LD_LIBRARY_PATH=/opt/tritonserver/lib:/usr/local/cuda/lib64:/usr/lib/x86_64-linux-gnu
RUN echo "/opt/tritonserver/lib" > /etc/ld.so.conf.d/triton.conf && \
echo "/usr/local/cuda/lib64" >> /etc/ld.so.conf.d/triton.conf && \
ldconfig
这行
ldconfig
是关键——它会扫描
/etc/ld.so.conf.d/
下的所有 conf 文件,把路径写入
/etc/ld.so.cache
,这样
dlopen()
调用时就能找到库。没有这一步,即使
LD_LIBRARY_PATH
设置正确,某些底层 CUDA 调用仍会失败。
4. 实操过程与核心环节实现:从 ECR 推送、ECS 集群创建到服务上线的全流程
现在我们进入真正的实操环节。我会以一个可复现的、最小可行的流程来演示,所有命令和配置都经过我本人在
us-east-1
区域的验证。请注意,以下操作假设你已经配置好 AWS CLI,并且拥有
AdministratorAccess
权限(生产环境请按最小权限原则细化)。
4.1 第一步:创建私有 ECR 仓库并推送镜像
首先创建 ECR 仓库:
aws ecr create-repository \
--repository-name triton-mnist \
--image-scanning-configuration scanOnPush=true \
--region us-east-1
这条命令返回一个
repositoryUri
,形如
123456789012.dkr.ecr.us-east-1.amazonaws.com/triton-mnist
。接下来登录 ECR:
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
然后构建并推送镜像。注意镜像标签必须包含语义化版本,我习惯用 Git Commit ID:
git rev-parse HEAD # 假设输出 abc1234
docker build -t 123456789012.dkr.ecr.us-east-1.amazonaws.com/triton-mnist:abc1234 .
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/triton-mnist:abc1234
推送完成后,在 ECR 控制台能看到镜像的
Image Digest
,形如
sha256:abcdef123456...
。这个 digest 是 ECS 任务定义里真正引用的标识符,比 tag 更可靠。
4.2 第二步:创建 ECS 集群与托管节点组
ECS 集群本身是逻辑分组,真正干活的是集群里的 EC2 实例。我们用 AWS CloudFormation 创建一个最小化集群(你也可以用控制台,但 CLI 更易复现):
# cluster.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: triton-cluster
CapacityProviders:
- FARGATE
- FARGATE_SPOT
- EC2
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref EC2Role
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for ECS instances
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 8000
ToPort: 8002
SourceSecurityGroupId: !Ref ALBSecurityGroup
这个模板创建了一个名为
triton-cluster
的集群,并配置了允许 SSH 和 Triton 端口的入站规则。接下来,我们启动一个
g4dn.xlarge
实例作为节点:
aws ec2 run-instances \
--image-id ami-0c02fb55956c7d316 \ # Amazon Linux 2 AMI for us-east-1
--count 1 \
--instance-type g4dn.xlarge \
--key-name my-key-pair \
--security-group-ids sg-0abcdef1234567890 \
--iam-instance-profile Name=EC2InstanceProfile \
--user-data file://userdata.sh \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=triton-node},{Key=ecs-cluster,Value=triton-cluster}]'
其中
userdata.sh
就是前面提到的安装 NVIDIA 驱动和 Container Toolkit 的脚本。实例启动后,会自动注册到
triton-cluster
集群。
4.3 第三步:定义 ECS 任务与服务
任务定义(Task Definition)是 ECS 的核心。我们用 JSON 格式定义:
{
"family": "triton-mnist-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["EC2"],
"cpu": "2048",
"memory": "8192",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "triton-server",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/triton-mnist@sha256:abcdef123456...",
"essential": true,
"portMappings": [
{
"containerPort": 8000,
"hostPort": 8000,
"protocol": "tcp"
},
{
"containerPort": 8001,
"hostPort": 8001,
"protocol": "tcp"
},
{
"containerPort": 8002,
"hostPort": 8002,
"protocol": "tcp"
},
{
"containerPort": 8003,
"hostPort": 8003,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NVIDIA_VISIBLE_DEVICES",
"value": "all"
}
],
"linuxParameters": {
"capabilities": {
"add": ["SYS_ADMIN"]
}
},
"ulimits": [
{
"name": "memlock",
"softLimit": -1,
"hardLimit": -1
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/triton-mnist",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
关键点解析:
-
"networkMode": "awsvpc":启用 AWS VPC 网络模式,让容器获得独立 ENI,这是 ALB 集成的前提; -
"linuxParameters"中的SYS_ADMINcapability 是必须的,因为 Triton 需要mlock()锁定内存页,防止被 swap; -
ulimits.memlock设为-1(无限),否则 Triton 的 pinned memory pool 无法分配; -
logConfiguration将容器日志直接推送到 CloudWatch Logs,无需额外配置 Fluent Bit。
保存为
task-definition.json
后,注册任务定义:
aws ecs register-task-definition --cli-input-json file://task-definition.json
4.4 第四步:创建 ECS 服务并关联 ALB
服务(Service)定义了任务的运行策略。我们创建一个面向公网的服务:
aws ecs create-service \
--cluster triton-cluster \
--service-name triton-mnist-service \
--task-definition triton-mnist-task:1 \
--desired-count 1 \
--launch-type EC2 \
--load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/triton-tg/abcdef123456...,containerName=triton-server,containerPort=8003" \
--health-check-grace-period-seconds 300 \
--scheduling-strategy REPLICA
这里
containerPort=8003
是关键——我们把 ALB 的流量导向 FastAPI 的 8003 端口,而不是 Triton 的 8000。
health-check-grace-period-seconds 300
给了模型加载足够的缓冲时间(MNIST 通常 10 秒内完成,但留足余量)。
ALB 的创建需要几个前置资源:VPC、子网、安全组。假设你已有 VPC
vpc-0abcdef1234567890
和两个公有子网
subnet-1
、
subnet-2
,创建 ALB:
aws elbv2 create-load-balancer \
--name triton-alb \
--subnets subnet-1 subnet-2 \
--security-groups sg-0abcdef1234567890 \
--scheme internet-facing \
--type application \
--ip-address-type ipv4
然后创建目标组(Target Group):
aws elbv2 create-target-group \
--name triton-tg \
--protocol HTTP \
--port 8003 \
--vpc-id vpc-0abcdef1234567890 \
--health-check-path /healthz \
--health-check-interval-seconds 30 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 2
注意
--health-check-path /healthz
指向我们自定义的健康检查端点。
4.5 第五步:配置 ALB 监听器与路由规则
ALB 创建后,需要配置监听器(Listener)将 443 端口的 HTTPS 流量路由到目标组:
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/triton-alb/abcdef123456... \
--protocol HTTPS \
--port 443 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/triton-tg/abcdef123456... \
--ssl-policy ELBSecurityPolicy-TLS-1-2-2017-01 \
--certificates CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/abcdef123456...
这里
--certificates
引用了 ACM(AWS Certificate Manager)里申请的 SSL 证书。如果你还没有,可以用 ACM 免费申请一个通配符证书(如
*.yourdomain.com
)。
最后,为 ALB 分配一个 DNS 名称:
aws elbv2 describe-load-balancers \
--names triton-alb \
--query 'LoadBalancers[0].DNSName' \
--output text
输出类似
triton-alb-1234567890.us-east-1.elb.amazonaws.com
。这就是你的 Triton 服务对外地址。
4.6 第六步:验证服务可用性与性能基线
服务上线后,第一件事是验证端到端连通性。用 curl 测试:
curl -X POST https://triton-alb-123
更多推荐

所有评论(0)