aws appmesh 在ecs中部署和配置appmesh服务网格
appmesh配置ecs
参考资料
- https://docs.amazonaws.cn/app-mesh/latest/userguide/getting-started-ecs.html
需要操作:
- 授权ecs任务中部署的envoy代理读取虚拟节点配置
- 更新ecs任务定义使用envoy代理
- 使用xray跟踪envoy请求
注意点:
-
envoy容器需要凭证和appmesh服务进行通信(ec2类型可以使用元数据或任务角色,fargate只能使用任务角色)
-
创建任务定义集成appmesh,启用appmesh,和之前创建的mesh关联,确保使用awsvpc网络模式
-
任务定义中容器必须等待envoy代理引导和启动之后才能启动,设置dependon参数
appmesh 示例存储库,https://github.com/aws/aws-app-mesh-examples
中国区存储库的使用方式:
- 修改资源为aws-cn
- 修改终端节点为amazonaws.com.cn
- 绕过80/8080/443端口
本次部署的架构类似之前的ec2部署appmesh,详情参考这篇文章
创建ecs应用
配置ecs任务定义
我们使用fargate部署测试服务,方便起见我们使用amazonlinux镜像(最好是手动安装工具之后打包成image),并开启ecs exec功能
yum install procps-ng -y
yum install bind-utils -y
yum install nginx -y
yum install telnet -y
docker commit c6d40d5fb469 xxxxxx.dkr.ecr.cn-north-1.amazonaws.com.cn/amazonlinux
docker push xxxxxx.dkr.ecr.cn-north-1.amazonaws.com.cn/amazonlinux:latest
https://docs.amazonaws.cn/en_us/AmazonECS/latest/developerguide/ecs-exec.html
注意:不能为现有任务打开 ECS Exec,只能为新任务打开 ECS Exec,会话的空闲超时时间为20分钟
需要为ecs task role配置一定权限
为了在ecs中使用appmesh,需要满足以下条件
- 确保ecs 任务定义中已经配置了appmesh的容器
- 确保使用了服务发现,实际上就是通过dns找到该服务,在eks里可以直接使用coredns,在ec2上需要手动配置dns解析(cloudmap),ecs可以通过开启服务发现自动注册cloudmap

ecs服务配置服务发现的过程
当创建服务时,涉及到负载均衡器,服务发现,自动扩缩,创建删除网卡,cw告警日志和ecs exec时,都需要使用ecs服务角色,信任的对象为ecs.amazonaws.com服务
https://docs.aws.amazon.com/cli/latest/reference/ecs/create-service.html
控制服务发现的是--service-registries参数,有以下注意事项
https://docs.amazonaws.cn/AmazonECS/latest/developerguide/service-discovery.html
- 服务发现的每个服务限制任务数量在1000以内
- 仅支持向私有dns命名空间注册服务
- 需要配置vpc的dns解析
- 自动创建的cloudmap资源需要手动删除
注意:以下cli操作只是为了了解整个服务发现创建的流程,实际上如果使用控制台,会自动创建cloudmap的ns和service,不需要手动配置
使用命令行创建cloudmap,这里注意使用的命令是servicediscovery,创建名为ecs-mesh的名称空间,默认还会创建一个route53的私有托管域
aws servicediscovery create-private-dns-namespace \
--name ecs-mesh.local \
--vpc vpc-086d798b56f59e2ae
之后创建的ecs服务如果指定命名空间,会自动将服务名称注册到该托管域中

创建出来的私有托管域,注意,该托管域无法直接进行编辑,只能通过cloudmap进行设置

创建servicea,这个namespace-id是cloudmap的namespaceid
aws servicediscovery create-service \
--name servicebv1 \
--namespace-id ns-ngtw4luqvwo5pgnw \
--dns-config "NamespaceId=ns-ngtw4luqvwo5pgnw,RoutingPolicy=WEIGHTED
,DnsRecords=[{Type=A,TTL=60}]"
编写服务发现的配置文件,这里注意需要手动创建一个名为servicea,文件中的arn无法在控制台上直接找到,只能通过service的id手动拼接

// serviceA-config.json
{
"serviceRegistries": [
{
"registryArn": "arn:aws-cn:servicediscovery:cn-north-1:xxxxxx:service/srv-sqvht7gsx7ikq7um",
"containerName": "simpleweb"
}
]
}
创建服务,在创建服务的时候指定服务发现配置
aws ecs create-service \
--cluster workfargate \
--service-name serviceA \
--task-definition testservice \
--desired-count 1 \
--network-configuration "awsvpcConfiguration={subnets=[subnet-027025e9d9760acdd ],securityGroups=[sg-096df1a0cb9a6d7e9],assignPublicIp=ENABLED}" \
--platform-version LATEST \
--launch-type FARGATE \
--cli-input-json file://serviceA-config.json
在cloudmap控制台查看服务已经注册成功

配置ecs服务
创建前端服务serviceA
创建cloudmap解析
aws servicediscovery create-service \
--name servicebv1 \
--namespace-id ns-ngtw4luqvwo5pgnw \
--dns-config "NamespaceId=ns-ngtw4luqvwo5pgnw,RoutingPolicy=WEIGHTED
,DnsRecords=[{Type=A,TTL=60}]"
指定服务发现配置
// serviceA-config.json
{
"serviceRegistries": [
{
"registryArn": "arn:aws-cn:servicediscovery:cn-north-1:xxxxxx:service/srv-sqvht7gsx7ikq7um",
"containerName": "simpleweb"
}
]
}
创建前端服务serviceA,在创建服务的时候指定服务发现配置
打包testcurl镜像
FROM public.ecr.aws/docker/library/alpine:latest
RUN apk add --update curl && rm -rf /var/cache/apk/*
COPY ./entrypoint.sh .
RUN chmod +x entrypoint.sh
CMD ./entrypoint.sh
//./entrypoint.sh
while [ true ]; do
sleep 3
curl -v serviceb.ecs-mesh-local
done
serviceA使用testcurl镜像
aws ecs create-service \
--cluster workfargate \
--service-name serviceA \
--task-definition mesh-serviceA:8 \
--desired-count 1 \
--network-configuration "awsvpcConfiguration={subnets=[subnet-027025e9d9760acdd ],securityGroups=[sg-096df1a0cb9a6d7e9],assignPublicIp=ENABLED}" \
--platform-version LATEST \
--launch-type FARGATE \
--cli-input-json file://serviceA-config.json

创建后端服务serviceBv1
同理,创建cloudmap解析
aws servicediscovery create-service \
--name servicebv1 \
--namespace-id ns-ngtw4luqvwo5pgnw \
--dns-config "NamespaceId=ns-ngtw4luqvwo5pgnw,RoutingPolicy=WEIGHTED
,DnsRecords=[{Type=A,TTL=60}]"
指定服务发现配置
// serviceBv1-config.json
{
"serviceRegistries": [
{
"registryArn": "arn:aws-cn:servicediscovery:cn-north-1:xxxxxx:service/srv-rzhhkc5iwcmkkla5",
"containerName": "simpleweb"
}
]
}
创建后端服务serviceBv1,后端服务使用zhaojiew/simpleweb镜像,通过环境变量MY_PORT设置监听端口
aws ecs create-service \
--cluster workfargate \
--service-name serviceBv1 \
--task-definition mesh-serviceBv1:3 \
--desired-count 1 \
--network-configuration "awsvpcConfiguration={subnets=[subnet-027025e9d9760acdd ],securityGroups=[sg-096df1a0cb9a6d7e9],assignPublicIp=ENABLED}" \
--platform-version LATEST \
--launch-type FARGATE \
--cli-input-json file://serviceBv1-config.json
创建后端服务serviceBv2
同理,创建cloudmap解析
aws servicediscovery create-service \
--name servicebv2 \
--namespace-id ns-ngtw4luqvwo5pgnw \
--dns-config "NamespaceId=ns-ngtw4luqvwo5pgnw,RoutingPolicy=WEIGHTED
,DnsRecords=[{Type=A,TTL=60}]"
指定服务发现配置
// serviceBv2-config.json
{
"serviceRegistries": [
{
"registryArn": "arn:aws-cn:servicediscovery:cn-north-1:xxxxxx:service/srv-ddh5y5tnqsmc3z5d",
"containerName": "simpleweb"
}
]
}
创建后端服务serviceBv2
aws ecs create-service \
--cluster workfargate \
--service-name serviceBv2 \
--task-definition mesh-serviceBv2:2 \
--desired-count 1 \
--network-configuration "awsvpcConfiguration={subnets=[subnet-027025e9d9760acdd ],securityGroups=[sg-096df1a0cb9a6d7e9],assignPublicIp=ENABLED}" \
--platform-version LATEST \
--launch-type FARGATE \
--cli-input-json file://serviceBv2-config.json
现在我们有3个服务,并且将80端口暴露

解析dns名称
# nslookup servicebv1.ecs-mesh.local
Server: 172.31.0.2
Address: 172.31.0.2#53
Non-authoritative answer:
Name: servicebv1.ecs-mesh.local
Address: 172.31.24.182
尝试访问
$ curl servicebv2.ecs-mesh.local:8090
success
创建和配置appmesh
https://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/create-task-definition-classic.html
通过上面的步骤我们已经将基本的服务框架启动了。
接下来我们需要将服务接入到网格中,在控制台上对ecs任务定义进行更新,开启集成appmesh

配置报错提示,按照提示进行配置

这里有个坑,问题在于创建mesh中的virtualnode的时候,dns名称必须进行设置,狗则

之后注入可以成功,并提示以下信息

之后我们需要分别更新每个服务的任务定义,启动新的任务,这里需要注意的是由于我们使用了同一个任务定义,每次都需要手动指定mesh中的virtualnode,原理可以参考ec2那篇的逻辑
代理注入之后实例无法在通过exec链接,推测可能是代理对请求进行了处理。。。。
修改之前的serviceBv1和v2的任务定义,使用nginx进行测试
在ec2上尝试请求后端v1,可见envoy已经接管请求
$ curl servicebv1.ecs-mesh.local:8090
HTTP/1.1 200 OK
date: Tue, 28 Feb 2023 14:28:58 GMT
server: envoy
content-length: 7
content-type: text/html; charset=UTF-8
x-envoy-upstream-service-time: 1
查看访问日志,意料之中解析不到dns名称,我们手动在cloudmap中创建serviceb的解析,并指向任意一个版本的后端,和ec2环境类似,这个解析并不走dns而只是由envoy进行转发操作

手动注册serviceb

之后成功访问到服务

由于制定了5050的权重因此两个版本的后端都能收到请求

修改为100:0,查看其中一个服务已经不在接受新的请求,说明我们的配置是生效的

这之后我们不需要再改动任何ecs应用,仅仅通过appmesh就能完成流控等复杂配置
实际上我们这样的客户端进行操作仍旧很难操作,可以直接使用ec2代替,对于mesh来说,底层应用是无所谓的
这个应该是目前所有文章中最难的测试之一,希望大家能有所收获

浙公网安备 33010602011771号