ECS部署Rust应用后服务连接失败,健康检查未通过求助
问题:ECS Fargate上的Rust应用API无法访问
我希望在ECS中运行一个包含健康API和WebSocket API的Rust应用,该应用在本地运行正常,但无论是从本地机器连接ECS上的两个API,还是ECS内部通过localhost调用健康API均失败。我已确认安全组允许所有流量,相关信息如下:
部署步骤
- 开发提供WebSocket和HTTP端点的Rust应用;
- 构建镜像并上传至ECR;
- 在ECS中创建含1个公有子网的集群,采用Fargate作为基础设施;
- 在任务定义中映射3014、3015端口为TCP协议,配置健康检查命令
CMD-SHELL curl -f localhost:3015/health; - 将任务部署为集群服务,日志显示服务启动成功,但无法通过公网IPv4地址连接任一API。
Rust核心代码(main.rs)
use std::net::SocketAddr; use tokio::net::TcpListener; use warp::Filter; use warp::hyper::StatusCode; #[tokio::main] async fn main() { println!("Starting up..."); let url = "0.0.0.0:3014"; let listener = TcpListener::bind(url) .await .unwrap(); println!("Listing on default URL"); tokio::spawn(async move { run_health_check().await; }); loop { match listener.accept().await { Ok((stream, _)) => { let addr = stream.peer_addr().expect("connected streams should have a peer address"); println!("Peer address: {}", addr); let ws_stream = tokio_tungstenite::accept_async(stream) .await .expect("Error during the websocket handshake occurred"); println!("New WebSocket connection: {}", addr); drop(ws_stream); } Err(e) => panic!("{:#?}", e), } } } async fn run_health_check() { let routes = warp::get() .and(warp::path("health")) .map(move || Ok(warp::reply::with_status("", StatusCode::OK))) .with(warp::cors().allow_any_origin()); let socket_address: SocketAddr = "0.0.0.0:3015".to_string().parse().unwrap(); warp::serve(routes).run(socket_address).await; }
ECS任务定义配置
{ "taskDefinitionArn": "arn:aws:ecs:eu-central-1:XXXX:task-definition/websocket:3", "containerDefinitions": [ { "name": "websocket", "image": "XXX.dkr.ecr.eu-central-1.amazonaws.com/hello3", "cpu": 0, "links": [], "portMappings": [ { "name": "websocket-3015-tcp", "containerPort": 3015, "hostPort": 3015, "protocol": "tcp", "appProtocol": "http" }, { "name": "websocket-3014-tcp", "containerPort": 3014, "hostPort": 3014, "protocol": "tcp", "appProtocol": "http" } ], "essential": true, "entryPoint": [], "command": [], "environment": [], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "secrets": [], "dnsServers": [], "dnsSearchDomains": [], "extraHosts": [], "dockerSecurityOptions": [], "dockerLabels": {}, "ulimits": [], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-create-group": "true", "awslogs-group": "/ecs/websocket", "awslogs-region": "eu-central-1", "awslogs-stream-prefix": "ecs" }, "secretOptions": [] }, "healthCheck": { "command": [ "CMD-SHELL curl -f localhost:3015/health" ], "interval": 30, "timeout": 5, "retries": 3 }, "systemControls": [] } ], "family": "websocket", "executionRoleArn": "arn:aws:iam::XXX:role/ecsTaskExecutionRole", "networkMode": "awsvpc", "revision": 3, "volumes": [], "status": "ACTIVE", "requiresAttributes": [ { "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.24" }, { "name": "ecs.capability.execution-role-awslogs" }, { "name": "com.amazonaws.ecs.capability.ecr-auth" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17" }, { "name": "ecs.capability.container-health-check" }, { "name": "ecs.capability.execution-role-ecr-pull" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" }, { "name": "ecs.capability.task-eni" }, { "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29" } ], "placementConstraints": [], "compatibilities": [ "EC2", "FARGATE" ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "1024", "memory": "3072", "runtimePlatform": { "cpuArchitecture": "ARM64", "operatingSystemFamily": "LINUX" }, "registeredAt": "2022-12-11T11:19:54.622Z", "registeredBy": "arn:aws:iam::465984508737:root", "tags": [ { "key": "ecs:taskDefinition:createdFrom", "value": "ecs-console-v2" }, { "key": "ecs:taskDefinition:stackId", "value": "arn:aws:cloudformation:eu-central-1:XX:stack/ECS-Console-V2-TaskDefinition-c246faa1-4ba5-4b54-ac4a-49ae00ab2f0d/8fd98e10-7944-11ed-abb2-0a564ca58c2a" } ] }
Dockerfile
FROM rustlang/rust:nightly-bullseye AS builder WORKDIR app COPY . . RUN cargo build --bin hello -Z sparse-registry FROM debian:11.5 COPY --from=builder ./app/target/debug/hello . CMD ["./hello"]
请问我哪里配置出现了问题?
解决方案
1. Docker镜像缺少健康检查依赖
你使用的debian:11.5基础镜像默认未安装curl,导致健康检查命令执行失败,容器会被标记为不健康,进而影响服务可用性。
修改Dockerfile,在最终阶段安装curl:
FROM rustlang/rust:nightly-bullseye AS builder WORKDIR app COPY . . RUN cargo build --bin hello -Z sparse-registry FROM debian:11.5 # 安装curl并清理APT缓存以减小镜像体积 RUN apt-get update && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/* COPY --from=builder ./app/target/debug/hello . CMD ["./hello"]
2. Fargate任务未分配公网IP
在awsvpc网络模式下,部署在公有子网的Fargate任务默认不会分配公网IP,导致外部无法通过公网地址访问服务。
解决:
- 创建ECS服务时,在网络配置中勾选「启用自动分配公网IP」;
- 若通过CLI或CloudFormation部署,需设置
assignPublicIp: ENABLED。
3. 端口映射配置冗余(Fargate模式)
Fargate的awsvpc网络模式中,hostPort配置会被忽略(每个任务拥有独立网络栈),建议修改端口映射,移除hostPort字段,或保持与containerPort一致(不影响功能,但更规范)。
额外验证步骤
- 登录同VPC内的EC2实例,尝试访问任务的内网IP+端口(如
http://<任务内网IP>:3015/health),确认容器内服务是否正常运行; - 查看ECS任务日志,确认
run_health_check函数是否成功启动warp服务,有无panic信息; - 确认安全组入站规则允许3014、3015端口的TCP流量(你已确认,但再次核对避免遗漏)。
内容的提问来源于stack exchange,提问作者J. Doe
相关产品推荐
相关产品推荐

