本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
AWS App Mesh 和 Amazon 入门 EC2
本主题可帮助您 AWS App Mesh 使用在 Amazon 上运行的实际服务EC2。本教程介绍了多种 App Mesh 资源类型的基本功能。
场景
为了说明如何配合使用 App Mesh,假定您有一个具有以下功能的应用程序:
-
由名为
serviceA
和serviceB
的两项服务组成。 -
这两项服务均注册到名为
apps.local
的命名空间。 -
ServiceA
与 HTTP /2、端口 8serviceB
0 进行通信。 -
您已部署
serviceB
的版本 2,并采用名称serviceBv2
在apps.local
命名空间中注册了该服务。
您的要求如下:
-
您想将 75% 的流量从发送
serviceA
到serviceB
,将 25% 的流量发送到serviceBv2
第一个。通过仅向发送25%的流量serviceBv2
,您就可以在发送来自的100%的流量之前验证它是否没有错误serviceA
。 -
您希望能够轻松调整流量权重,以便一旦证明
serviceBv2
可靠便可将 100% 的流量发送到该服务。将所有流量发送到serviceBv2
后,您希望弃用serviceB
。 -
您不想为了满足之前的要求而更改实际服务的任何现有应用程序代码或服务发现注册。
为了满足您的要求,您决定创建包含虚拟服务、虚拟节点、虚拟路由器和路由的 App Mesh 服务网格。实施您的网格后,更新使用 Envoy 代理的服务。更新后,您的服务将通过 Envoy 代理相互通信,而不是直接相互通信。
先决条件
App Mesh 支持在DNS AWS Cloud Map、或两者中注册的 Linux 服务。要使用本入门指南,我们建议您注册三项现有服务DNS。即使服务不存在,您也可以创建服务网格及其资源,但在部署实际服务之前,您无法使用网格。
如果您尚未运行服务,则可以启动 Amazon EC2 实例并向其部署应用程序。有关更多信息,请参阅亚马逊EC2用户指南中的教程:Amazon EC2 Linux 实例入门。剩余步骤假定实际服务命名为 serviceA
、serviceB
和 serviceBv2
,并且可以在名为 apps.local
的命名空间中发现所有服务。
步骤 1:创建网格和虚拟服务
服务网格是一种用于驻留在其内的服务之间的网络流量的逻辑边界。有关更多信息,请参阅 服务网格。虚拟服务是实际服务的抽象。有关更多信息,请参阅 虚拟服务。
创建以下 资源:
-
名为
apps
的网格,因为此场景中的所有服务均注册到apps.local
命名空间。 -
名为
serviceb.apps.local
的虚拟服务,因为虚拟服务表示可以使用该名称发现的服务,并且您不希望更改代码以引用其他名称。稍后的步骤中将添加名为servicea.apps.local
的虚拟服务。
您可以使用或 1.18.116 AWS Management Console 或更高 AWS CLI 版本或 2.0.38 或更高版本来完成以下步骤。如果使用 AWS CLI,请使用aws --version
命令检查已安装的 AWS CLI 版本。如果您没有安装版本 1.18.116 或更高版本或者没有安装版本 2.0.38 或更高版本,则必须安装或更新 AWS CLI。选择要使用的工具所对应的选项卡。
步骤 2:创建虚拟节点
虚拟节点充当实际服务的逻辑指针。有关更多信息,请参阅 虚拟节点。
创建名为 serviceB
的虚拟节点,因为某个虚拟节点表示名为 serviceB
的实际服务。可使用主机名 serviceb.apps.local
,通过 DNS
发现虚拟节点所表示的实际服务。或者,也可以使用 AWS Cloud Map发现实际服务。虚拟节点在端口 80 上使用 HTTP /2 协议监听流量。此外,还支持其他协议和运行状况检查。您将在后面的步骤中为 serviceA
和 serviceBv2
创建虚拟节点。
步骤 3:创建虚拟路由器和路由
虚拟路由器路由网格中一个或多个虚拟服务的流量。有关更多信息,请参阅虚拟路由器 和路由。
创建以下 资源:
-
名为
serviceB
的虚拟路由器,因为serviceB.apps.local
虚拟服务不会启动与任何其他服务的出站通信。请记住,您之前创建的虚拟服务是实际serviceb.apps.local
服务的抽象。虚拟服务将流量发送到虚拟路由器。虚拟路由器在端口 80 上使用 HTTP /2 协议监听流量。此外,还支持其他协议。 -
名为
serviceB
的路由。它将 100% 的流量路由到serviceB
虚拟节点。添加serviceBv2
虚拟节点后,在稍后的步骤中出现权重。虽然本指南中未作介绍,但您可以为路由添加额外的筛选条件,并添加重试策略,从而使 Envoy 代理在遇到通信问题时会多次尝试将流量发送到虚拟节点。
步骤 4:审核并创建
根据之前的说明审核设置。
步骤 5:创建其他资源
要完成此场景,您需要执行以下操作:
-
创建名为
serviceBv2
的虚拟节点,以及名为serviceA
的虚拟节点。两个虚拟节点都通过 HTTP /2 端口 80 监听请求。对于serviceA
虚拟节点,将后端配置为serviceb.apps.local
。来自serviceA
虚拟节点的所有出站流量都将发送到名为serviceb.apps.local
的虚拟服务。虽然本指南中未作介绍,但您还可以为虚拟节点指定用于写入访问日志的文件路径。 -
另外创建一个名为的虚拟服务
servicea.apps.local
,该服务将所有流量直接发送到serviceA
虚拟节点。 -
更新在上一步中创建的
serviceB
路由,以将 75% 的流量发送到serviceB
虚拟节点,将 25% 的流量发送到serviceBv2
虚拟节点。随着时间的推移,您可以继续修改权重,直到serviceBv2
收到 100% 的流量。将所有流量发送到serviceBv2
后,您可以关闭并弃用serviceB
虚拟节点和实际服务。在更改权重时,不需要对代码进行任何修改,因为serviceb.apps.local
虚拟服务名称和实际服务名称没有改变。回想一下,serviceb.apps.local
虚拟服务将流量发送到虚拟路由器,该路由器将流量路由到虚拟节点。虚拟节点的服务发现名称可以随时更改。
网格摘要
在创建服务网格之前,您具有三个名为 servicea.apps.local
、serviceb.apps.local
和 servicebv2.apps.local
的实际服务。除实际服务之外,现在您还具有一个服务网格,其中包含用以表示实际服务的以下资源:
-
两个虚拟服务。代理通过虚拟路由器将所有流量从
servicea.apps.local
虚拟服务发送到serviceb.apps.local
虚拟服务。 -
三个名为
serviceA
、serviceB
和serviceBv2
的虚拟节点。Envoy 代理使用为虚拟节点配置的服务发现信息来查找实际服务的 IP 地址。 -
一个虚拟路由器,其路由指示 Envoy 代理将 75% 的入站流量路由到
serviceB
虚拟节点,将 25% 的流量路由到serviceBv2
虚拟节点。
步骤 6:更新服务
创建网格后,您需要完成以下任务:
-
授予随每项服务部署的 Envoy 代理读取一个或多个虚拟节点配置的权限。有关如何向代理授权的更多信息,请参阅 Envoy Proxy 授权。
-
要更新现有服务,请完成以下步骤。
将 Amazon EC2 实例配置为虚拟节点成员
-
创建IAM角色。
-
使用以下内容创建名为
ec2-trust-relationship.json
的文件。{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
-
使用以下命令创建IAM角色。
aws iam create-role --role-name
mesh-virtual-node-service-b
--assume-role-policy-document file://ec2-trust-relationship.json
-
-
为角色附加IAM策略,使其能够从 Amazon 读取数据,ECR并且仅允许其读取特定 App Mesh 虚拟节点的配置。
-
使用以下内容创建名为
virtual-node-policy.json
的文件。apps
是您在 步骤 1:创建网格和虚拟服务 中创建的网格的名称,serviceB
是您在 步骤 2:创建虚拟节点 中创建的虚拟节点的名称。Replace(替换)111122223333
使用您的账户 ID 和us-west-2
使用您创建网格的区域。{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "appmesh:StreamAggregatedResources", "Resource": [ "arn:aws:appmesh:
us-west-2
:111122223333
:mesh/apps
/virtualNode/serviceB
" ] } ] } -
使用以下命令创建策略。
aws iam create-policy --policy-name
virtual-node-policy
--policy-document file://virtual-node-policy.json -
将您在上一步中创建的策略附加到角色,以便角色仅从 App Mesh 中读取
serviceB
虚拟节点的配置。aws iam attach-role-policy --policy-arn arn:aws:iam::
111122223333
:policy/virtual-node-policy --role-namemesh-virtual-node-service-b
-
将
AmazonEC2ContainerRegistryReadOnly
托管策略附加到该角色,使其可以从 Amazon 提取 Envoy 容器镜像ECR。aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly --role-name
mesh-virtual-node-service-b
-
-
通过连接到您的实例SSH。
-
根据您的操作系统文档, AWS CLI 在您的实例上安装 Docker 和。
-
向你希望 Docker 客户端提取镜像的区域中的 Envoy Amazon ECR 存储库进行身份验证。
-
除
me-south-1
、ap-east-1
、ap-southeast-3
、eu-south-1
、il-central-1
和af-south-1
以外的所有区域。你可以更换us-west-2
包括除me-south-1
、、、ap-east-1
ap-southeast-3
eu-south-1
il-central-1
、和之外的任何受支持区域af-south-1
。$
aws ecr get-login-password \ --region
us-west-2
\ | docker login \ --username AWS \ --password-stdin 840364872350.dkr.ecr.us-west-2
.amazonaws.com -
me-south-1
区域$
aws ecr get-login-password \ --region me-south-1 \ | docker login \ --username AWS \ --password-stdin 772975370895.dkr.ecr.me-south-1.amazonaws.com
-
ap-east-1
区域$
aws ecr get-login-password \ --region ap-east-1 \ | docker login \ --username AWS \ --password-stdin 856666278305.dkr.ecr.ap-east-1.amazonaws.com
-
-
运行下列命令之一,在您的实例上启动 Envoy 容器,具体取决于要从中拉取映像的区域。这些区域有:
apps
以及serviceB
值是在场景中定义的网格和虚拟节点名称。此信息告知代理要从 App Mesh 中读取的虚拟节点配置。要完成该场景,您还需要为托管由serviceBv2
和serviceA
虚拟节点表示的服务的 Amazon EC2 实例完成这些步骤。对于您自己的应用程序,请将这些值替换为您自己的值。-
除
me-south-1
、ap-east-1
、ap-southeast-3
、eu-south-1
、il-central-1
和af-south-1
以外的所有区域。你可以更换Region-code
包含除me-south-1
、、、ap-east-1
ap-southeast-3
eu-south-1
il-central-1
、和区域之外的任何受支持af-south-1
区域。您可以将
替换为介于1337
0
和2147483647
之间的任何值。sudo docker run --detach --env APPMESH_RESOURCE_ARN=
mesh/
\ -uapps
/virtualNode/serviceB
1337
--network host 840364872350.dkr.ecr.region-code
.amazonaws.com/aws-appmesh-envoy:v1.29.6.1-prod -
me-south-1
区域。您可以将
替换为介于1337
0
和2147483647
之间的任何值。sudo docker run --detach --env APPMESH_RESOURCE_ARN=
mesh/
\ -uapps
/virtualNode/serviceB
1337
--network host 772975370895.dkr.ecr.me-south-1.amazonaws.com/aws-appmesh-envoy:v1.29.6.1-prod -
ap-east-1
区域。您可以将
替换为介于1337
0
和2147483647
之间的任何值。sudo docker run --detach --env APPMESH_RESOURCE_ARN=
mesh/
\ -uapps
/virtualNode/serviceB
1337
--network host 856666278305.dkr.ecr.ap-east-1.amazonaws.com/aws-appmesh-envoy:v1.29.6.1-prod
注意
该
APPMESH_RESOURCE_ARN
属性需要版本1.15.0
或更高版本的 Envoy 映像。有关更多信息,请参阅 Envoy 镜像。重要
仅支持将版本 v1.9.0.0-prod 或更高版本与 App Mesh 一起使用。
-
在下面选择
Show more
。使用以下内容在实例上创建名为envoy-networking.sh
的文件。Replace(替换)8000
使用您的应用程序代码用于传入流量的端口。您可以更改APPMESH_IGNORE_UID
的值,但该值必须与您在上一步中指定的值相同;例如1337
。如有必要,您可以向APPMESH_EGRESS_IGNORED_IP
添加其他地址。请不要修改任何其他行。#!/bin/bash -e # # Start of configurable options # #APPMESH_START_ENABLED="0" APPMESH_IGNORE_UID="
1337
" APPMESH_APP_PORTS="8000
" APPMESH_ENVOY_EGRESS_PORT="15001" APPMESH_ENVOY_INGRESS_PORT="15000" APPMESH_EGRESS_IGNORED_IP="169.254.169.254,169.254.170.2" # Enable routing on the application start. [ -z "$APPMESH_START_ENABLED" ] && APPMESH_START_ENABLED="0" # Enable IPv6. [ -z "$APPMESH_ENABLE_IPV6" ] && APPMESH_ENABLE_IPV6="0" # Egress traffic from the processess owned by the following UID/GID will be ignored. if [ -z "$APPMESH_IGNORE_UID" ] && [ -z "$APPMESH_IGNORE_GID" ]; then echo "Variables APPMESH_IGNORE_UID and/or APPMESH_IGNORE_GID must be set." echo "Envoy must run under those IDs to be able to properly route it's egress traffic." exit 1 fi # Port numbers Application and Envoy are listening on. if [ -z "$APPMESH_ENVOY_EGRESS_PORT" ]; then echo "APPMESH_ENVOY_EGRESS_PORT must be defined to forward traffic from the application to the proxy." exit 1 fi # If an app port was specified, then we also need to enforce the proxies ingress port so we know where to forward traffic. if [ ! -z "$APPMESH_APP_PORTS" ] && [ -z "$APPMESH_ENVOY_INGRESS_PORT" ]; then echo "APPMESH_ENVOY_INGRESS_PORT must be defined to forward traffic from the APPMESH_APP_PORTS to the proxy." exit 1 fi # Comma separated list of ports for which egress traffic will be ignored, we always refuse to route SSH traffic. if [ -z "$APPMESH_EGRESS_IGNORED_PORTS" ]; then APPMESH_EGRESS_IGNORED_PORTS="22" else APPMESH_EGRESS_IGNORED_PORTS="$APPMESH_EGRESS_IGNORED_PORTS,22" fi # # End of configurable options # function initialize() { echo "=== Initializing ===" if [ ! -z "$APPMESH_APP_PORTS" ]; then iptables -t nat -N APPMESH_INGRESS if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then ip6tables -t nat -N APPMESH_INGRESS fi fi iptables -t nat -N APPMESH_EGRESS if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then ip6tables -t nat -N APPMESH_EGRESS fi } function enable_egress_routing() { # Stuff to ignore [ ! -z "$APPMESH_IGNORE_UID" ] && \ iptables -t nat -A APPMESH_EGRESS \ -m owner --uid-owner $APPMESH_IGNORE_UID \ -j RETURN [ ! -z "$APPMESH_IGNORE_GID" ] && \ iptables -t nat -A APPMESH_EGRESS \ -m owner --gid-owner $APPMESH_IGNORE_GID \ -j RETURN [ ! -z "$APPMESH_EGRESS_IGNORED_PORTS" ] && \ for IGNORED_PORT in $(echo "$APPMESH_EGRESS_IGNORED_PORTS" | tr "," "\n"); do iptables -t nat -A APPMESH_EGRESS \ -p tcp \ -m multiport --dports "$IGNORED_PORT" \ -j RETURN done if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then # Stuff to ignore ipv6 [ ! -z "$APPMESH_IGNORE_UID" ] && \ ip6tables -t nat -A APPMESH_EGRESS \ -m owner --uid-owner $APPMESH_IGNORE_UID \ -j RETURN [ ! -z "$APPMESH_IGNORE_GID" ] && \ ip6tables -t nat -A APPMESH_EGRESS \ -m owner --gid-owner $APPMESH_IGNORE_GID \ -j RETURN [ ! -z "$APPMESH_EGRESS_IGNORED_PORTS" ] && \ for IGNORED_PORT in $(echo "$APPMESH_EGRESS_IGNORED_PORTS" | tr "," "\n"); do ip6tables -t nat -A APPMESH_EGRESS \ -p tcp \ -m multiport --dports "$IGNORED_PORT" \ -j RETURN done fi # The list can contain both IPv4 and IPv6 addresses. We will loop over this list # to add every IPv4 address into `iptables` and every IPv6 address into `ip6tables`. [ ! -z "$APPMESH_EGRESS_IGNORED_IP" ] && \ for IP_ADDR in $(echo "$APPMESH_EGRESS_IGNORED_IP" | tr "," "\n"); do if [[ $IP_ADDR =~ .*:.* ]] then [ "$APPMESH_ENABLE_IPV6" == "1" ] && \ ip6tables -t nat -A APPMESH_EGRESS \ -p tcp \ -d "$IP_ADDR" \ -j RETURN else iptables -t nat -A APPMESH_EGRESS \ -p tcp \ -d "$IP_ADDR" \ -j RETURN fi done # Redirect everything that is not ignored iptables -t nat -A APPMESH_EGRESS \ -p tcp \ -j REDIRECT --to $APPMESH_ENVOY_EGRESS_PORT # Apply APPMESH_EGRESS chain to non local traffic iptables -t nat -A OUTPUT \ -p tcp \ -m addrtype ! --dst-type LOCAL \ -j APPMESH_EGRESS if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then # Redirect everything that is not ignored ipv6 ip6tables -t nat -A APPMESH_EGRESS \ -p tcp \ -j REDIRECT --to $APPMESH_ENVOY_EGRESS_PORT # Apply APPMESH_EGRESS chain to non local traffic ipv6 ip6tables -t nat -A OUTPUT \ -p tcp \ -m addrtype ! --dst-type LOCAL \ -j APPMESH_EGRESS fi } function enable_ingress_redirect_routing() { # Route everything arriving at the application port to Envoy iptables -t nat -A APPMESH_INGRESS \ -p tcp \ -m multiport --dports "$APPMESH_APP_PORTS" \ -j REDIRECT --to-port "$APPMESH_ENVOY_INGRESS_PORT" # Apply AppMesh ingress chain to everything non-local iptables -t nat -A PREROUTING \ -p tcp \ -m addrtype ! --src-type LOCAL \ -j APPMESH_INGRESS if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then # Route everything arriving at the application port to Envoy ipv6 ip6tables -t nat -A APPMESH_INGRESS \ -p tcp \ -m multiport --dports "$APPMESH_APP_PORTS" \ -j REDIRECT --to-port "$APPMESH_ENVOY_INGRESS_PORT" # Apply AppMesh ingress chain to everything non-local ipv6 ip6tables -t nat -A PREROUTING \ -p tcp \ -m addrtype ! --src-type LOCAL \ -j APPMESH_INGRESS fi } function enable_routing() { echo "=== Enabling routing ===" enable_egress_routing if [ ! -z "$APPMESH_APP_PORTS" ]; then enable_ingress_redirect_routing fi } function disable_routing() { echo "=== Disabling routing ===" iptables -t nat -F APPMESH_INGRESS iptables -t nat -F APPMESH_EGRESS if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then ip6tables -t nat -F APPMESH_INGRESS ip6tables -t nat -F APPMESH_EGRESS fi } function dump_status() { echo "=== iptables FORWARD table ===" iptables -L -v -n echo "=== iptables NAT table ===" iptables -t nat -L -v -n if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then echo "=== ip6tables FORWARD table ===" ip6tables -L -v -n echo "=== ip6tables NAT table ===" ip6tables -t nat -L -v -n fi } function clean_up() { disable_routing ruleNum=$(iptables -L PREROUTING -t nat --line-numbers | grep APPMESH_INGRESS | cut -d " " -f 1) iptables -t nat -D PREROUTING $ruleNum ruleNum=$(iptables -L OUTPUT -t nat --line-numbers | grep APPMESH_EGRESS | cut -d " " -f 1) iptables -t nat -D OUTPUT $ruleNum iptables -t nat -X APPMESH_INGRESS iptables -t nat -X APPMESH_EGRESS if [ "$APPMESH_ENABLE_IPV6" == "1" ]; then ruleNum=$(ip6tables -L PREROUTING -t nat --line-numbers | grep APPMESH_INGRESS | cut -d " " -f 1) ip6tables -t nat -D PREROUTING $ruleNum ruleNum=$(ip6tables -L OUTPUT -t nat --line-numbers | grep APPMESH_EGRESS | cut -d " " -f 1) ip6tables -t nat -D OUTPUT $ruleNum ip6tables -t nat -X APPMESH_INGRESS ip6tables -t nat -X APPMESH_EGRESS fi } function main_loop() { echo "=== Entering main loop ===" while read -p '> ' cmd; do case "$cmd" in "quit") clean_up break ;; "status") dump_status ;; "enable") enable_routing ;; "disable") disable_routing ;; *) echo "Available commands: quit, status, enable, disable" ;; esac done } function print_config() { echo "=== Input configuration ===" env | grep APPMESH_ || true } print_config initialize if [ "$APPMESH_START_ENABLED" == "1" ]; then enable_routing fi main_loop-
要配置
iptables
规则以将应用程序流量路由到 Envoy 代理,请运行您在上一步中创建的脚本。sudo ./envoy-networking.sh
-
启动虚拟节点应用程序代码。
注意
有关 App Mesh