AWS AppMeshのサンプルを試してみます

##はじめに

2018/11/29に発表になったAWS App Meshのサンプルを試してみます。
https://aws.amazon.com/jp/blogs/news/reinvent2018-aws-app-mesh/

サンプルはこちらにあります。
https://github.com/awslabs/aws-app-mesh-examples/tree/master/examples

App Meshは、AWS上のマイクロサービスアプリケーション間の通信を簡単に監視、制御できるサービスメッシュを提供するサービスとなります。サービスメッシュのコントロールプレーン実装としては、OSSのIstioが有名です。また、App Meshではコンテナ間のプロキシとしてOSSとして開発されているEnvoyが使われています。

##サンプルアプリケーションの概要
Gatewayに対してHTTPリクエストを送ると、バックエンドのアプリケーションから任意のカラーをJSONで返します。
返されるカラーは、White/Black/Blue/Redになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/00fcb14f-4626-d1cd-90a6-63008807a81d.png)

```json
$ curl -s http://colorgateway.default.svc.cluster.local:9080/color
{"color":"white", "stats": {"white":1}}
```

構成としては、物理レイヤーと論理レイヤーの２つに分けて考えた方が理解しやすいと思います。
ここでは、物理レイヤーをVPCやECSクラスタとしています。論理レイヤーはサービスメッシュを構成するコンポーネントとなります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/69a339d7-5a3c-5996-c8d3-84793546e6d9.png)

サンプルを手順通り作成すると以下のような物理構成が作成されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/e9a6411e-7ec2-9443-6223-195540578a6b.png)

論理構成は以下になります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/fa3146bf-3220-6452-b147-9273beb03b07.png)

サービスメッシュを構成するコンポーネントの簡単な説明をします。
Mesh
複数のマイクロサービスをまとめたグループ
https://docs.aws.amazon.com/ja_jp/app-mesh/latest/userguide/meshes.html

Virtual Node
Amazon ECSサービスやKubernetesデプロイメントなどの特定のタスクグループへの論理的なポインタ
https://docs.aws.amazon.com/ja_jp/app-mesh/latest/userguide/virtual_nodes.html

Virtual Router
Virtual Node間のトラフィックをルーティングする仮想ルータ
https://docs.aws.amazon.com/ja_jp/app-mesh/latest/userguide/virtual_routers.html

Route
Virtual Routerに関連付けられたルーティング情報
https://docs.aws.amazon.com/ja_jp/app-mesh/latest/userguide/routes.html

##(そもそも)Envoyとサービスメッシュについて
EnvoyはLyftが開発したプロキシソフトウェアです。
https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy
機能が多彩で以下の様なことが出来ます。

- L3/L4 filter architecture
- HTTP L7 filter architecture
- First class HTTP/2 support
- HTTP L7 routing
- gRPC support
- MongoDB L7 support
- DynamoDB L7 support
- Service discovery and dynamic configuration
- Health checking
- Advanced load balancing

単にEnvoyをリバプロとして使うのであれば、Envoy単体で運用も可能な場合があるが、マイクロサービス化するとEnvoyがサイドカーコンテナとして多数起動することになり、ルーティング情報などの設定情報を集中的に管理する機能が必要になってきます。
前述した、Istioが代表的なコントロールプレーンのツールです。AppMeshは、現時点(2018/12/28)ではPublic Previewのたトラフィックルーティングのみ使用可能です。

##手順

###前提
最新のaws-cliをインストールします。今回は1.16.67を使用します。

```
$ aws --version
aws-cli/1.16.67 Python/3.5.4 Darwin/16.7.0 botocore/1.12.57
```

###事前
サンプルアプリケーションをダウンロードします。

```
$ git clone https://github.com/awslabs/aws-app-mesh-examples.git
Cloning into 'aws-app-mesh-examples'...
remote: Enumerating objects: 98, done.
remote: Counting objects: 100% (98/98), done.
remote: Compressing objects: 100% (57/57), done.
remote: Total 321 (delta 30), reused 85 (delta 29), pack-reused 223
Receiving objects: 100% (321/321), 83.70 KiB | 49.00 KiB/s, done.
Resolving deltas: 100% (125/125), done
```

作成する環境を定義します。

現時点(2018/12/28)でApp Meshが使えるリージョンは、Oregon,Virginia,East,Ohio,Irelandになります。
今回はオレゴンリージョンを使用します。

```
export AWS_PROFILE="default"
export AWS_REGION="us-west-2"
export AWS_DEFAULT_REGION="$AWS_REGION"
```
<>内は、環境にあった値に変更をお願いします。
インスタンス数は、2台とかだとawsvpcモードでENI数が足りなくてタスクが起動できなくなります。
SERVICES_DOMAINで定義したドメイン名でサービスディスカバリや名前解決ができます。

```
export ENVIRONMENT_NAME="AppMeshSample"
export MESH_NAME="default"
export KEY_PAIR_NAME=<key-pair to access ec2 instances where apps are running>
export ENVOY_IMAGE="111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.8.0.2-beta"
export CLUSTER_SIZE=5
export SERVICES_DOMAIN="default.svc.cluster.local"
```

###VPC作成
VPCを作成します。スクリプトを実行するとCloudFormationが実行されて以下の環境が作成されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/7688b2f8-eb56-e77e-cf03-91b1e5d97659.png)

```
$ cd aws-app-mesh-examples/examples/
$ ./infrastructure/vpc.sh create-stack
+++ dirname ./infrastructure/vpc.sh
++ cd ./infrastructure
++ pwd
+ DIR=/aws-app-mesh-examples/examples/infrastructure
+ aws --profile default --region us-west-2 cloudformation deploy --stack-name AppMeshSample-vpc --capabilities CAPABILITY_IAM --template-file /aws-app-mesh-examples/examples/infrastructure/vpc.yaml --parameter-overrides EnvironmentName=AppMeshSample

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - AppMeshSample-vpc
```

###メッシュのセットアップ

スクリプトのなかでaws-cliが実行されMeshのみ作成されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/1af56821-b8dd-3402-27e6-c3b6d50ca6fb.png)

```
$ ./infrastructure/mesh.sh create-mesh
+ '[' '!' -z default ']'
+ PROFILE_OPT='--profile default'
+ aws --profile default appmesh create-mesh --mesh-name default
{
    "mesh": {
        "meshName": "default",
        "status": {
            "status": "ACTIVE"
        },
        "metadata": {
            "version": 1,
            "lastUpdatedAt": 1545887155.41,
            "arn": "arn:aws:appmesh:us-west-2:*****:mesh/default",
            "createdAt": 1545887155.41,
            "uid": "c2324ff1-7308-4be0-8a9b-c74f30ee6c62"
        }
    }
}
```

###ECSかEKSのクラスタを作成する
今回はECSクラスタを作成します。CloudFormationが実行されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/c6be789c-2f49-2a3b-1d8e-6b9ab1d65111.png)


```
$ ./infrastructure/ecs-cluster.sh create-stack
+++ dirname ./infrastructure/ecs-cluster.sh
++ cd ./infrastructure
++ pwd
+ DIR=/aws-app-mesh-examples/examples/infrastructure
+ aws --profile default --region us-west-2 cloudformation deploy --stack-name AppMeshSample-ecs-cluster --capabilities CAPABILITY_IAM --template-file /aws-app-mesh-examples/examples/infrastructure/ecs-cluster.yaml --parameter-overrides EnvironmentName=AppMeshSample KeyName=oregon-key ECSServicesDomain=default.svc.cluster.local ClusterSize=5

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - AppMeshSample-ecs-cluster
```

###アプリケーションのデプロイ
サンプルに含まれているgatewayとcolortellerというアプリケーションをデプロイします。
最初にECRのレポジトリを手動で2つ作成します。

```
aws ecr create-repository --repository-name "color-gateway" --region us-west-2
aws ecr create-repository --repository-name "color-teller" --region us-west-2
```

環境変数に作成したリポジトリのURIを設定する。

```
export COLOR_GATEWAY_IMAGE=<image location for colorapp's gateway>
export COLOR_TELLER_IMAGE=<image location for colorapp's teller>
```

以下に、サンプルアプリケーション用のDockerfileがあります

```
./apps/colorapp/src/gateway/Dockerfile
./apps/colorapp/src/colorteller/Dockerfile
```

デプロイ用のスクリプトが用意されてるのでこちらでECRにPushします。

GatewayアプリケーションをECRにPush

```
$ cd ./apps/colorapp/src/gateway/
$ ./deploy.sh 
+ '[' -z ******.dkr.ecr.us-west-2.amazonaws.com/color-gateway ']'
+ docker build -t ******.dkr.ecr.us-west-2.amazonaws.com/color-gateway .
Sending build context to Docker daemon  32.26kB
Step 1/11 : FROM golang:1.10 AS builder
 ---> a3e70d5ea200
Step 2/11 : ADD https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64 /usr/bin/dep
Downloading [==================================================>]  8.499MB/8.499MB
(略)
da67ad63a8b3: Pushed 
latest: digest: sha256:634f329a49fd15dc1cad2b20cc694794a3340530e6041056b6b0b9923e3448d5 size: 528
```

ColorTellerアプリケーションをECRにPush

```
$ cd ../
$ cd colorteller/
$ ./deploy.sh 
+ '[' -z ******.dkr.ecr.us-west-2.amazonaws.com/color-teller ']'
+ docker build -t ******.dkr.ecr.us-west-2.amazonaws.com/color-teller .
Sending build context to Docker daemon  8.192kB
Step 1/11 : FROM golang:1.10 AS builder
 ---> a3e70d5ea200
Step 2/11 : ADD https://github.com/golang/dep/releases/download/v0.4.1/dep-linux-amd64 /usr/bin/dep
Downloading [==================================================>]  8.499MB/8.499MB
(略)
8c7a1b9b5ec0: Pushed 
latest: digest: sha256:c4b21221d55272295ddce74eb891451d34714d7c09ad826deba3b1eb3db14fad size: 528
```

ここまでで以下のような環境が作成されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/6f70d9cf-cae0-a0df-eced-772891465dbc.png)

###サービスメッシュのセットアップ
Virtual Node,Virtual Router,Routeを作成します。aws-cliが実行されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/c0319872-012c-31c5-6cdd-7cf904354907.png)

```
$ cd ../../
$ pwd
/aws-app-mesh-examples/examples/apps/colorapp
$ ./servicemesh/deploy.sh 
[MESH] [Thu Dec 27 15:53:37 JST 2018] : Creating virtual nodes
[MESH] [Thu Dec 27 15:53:37 JST 2018] : ======================
[MESH] [Thu Dec 27 15:53:37 JST 2018] : cli_input={
    "spec": {
        "listeners": [
(略)
[MESH] [Thu Dec 27 15:54:09 JST 2018] : --> a529b027-6176-4324-9e56-423e6ada8c94
[MESH] [Thu Dec 27 15:54:09 JST 2018] : --> a529b027-6176-4324-9e56-423e6ada8c94
```

###ECSにアプリケーションをデプロイ

CloudFormationが実行されて、ECRからコンテナイメージがECSクラスタにデプロイされます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/78184/c722bff4-ef08-0990-ce0d-f2af7853da7d.png)


```
$ ./ecs/ecs-colorapp.sh 
+++ dirname ./ecs/ecs-colorapp.sh
++ cd ./ecs
++ pwd
+ DIR=/aws-app-mesh-examples/examples/apps/colorapp/ecs
+ aws --profile default --region us-west-2 cloudformation deploy --stack-name AppMeshSample-ecs-colorapp --capabilities CAPABILITY_IAM --template-file /aws-app-mesh-examples/examples/apps/colorapp/ecs/ecs-colorapp.yaml --parameter-overrides EnvironmentName=AppMeshSample EnvoyImage=111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.8.0.2-beta ECSServicesDomain=default.svc.cluster.local AppMeshMeshName=default ColorGatewayImage=******.dkr.ecr.us-west-2.amazonaws.com/color-gateway ColorTellerImage=******.dkr.ecr.us-west-2.amazonaws.com/color-teller

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - AppMeshSample-ecs-colorapp
```

###動作確認
踏み台サーバを準備するかSession ManagerでECSクラスタ用のEC2インスタンスにログインする。

Gatewayに対してcurlしてみる。

```json
$ curl -s http://colorgateway.default.svc.cluster.local:9080/color
{"color":"white", "stats": {"white":1}}
```

Whiteが返される。これはルーティング設定(Route)でvirtualNodeがcolorteller-vnのみ設定してあり、colorteller-vnは「White」を返すコンテナになります。


```json
$ cat servicemesh/config/routes/colorteller-route.json 
{
    "routeName": "colorteller-route",
    "spec": {
        "httpRoute": {
            "action": {
                "weightedTargets": [
                    {
                        "virtualNode": "colorteller-vn",
                        "weight": 1
                    }
                ]
            },
            "match": {
                "prefix": "/"
            }
        }
    },
    "virtualRouterName": "colorteller-vr"
}
```

Blueを返すように変更してみます。まずは、Virtual Nodeを確認します。

```json
$ aws appmesh list-virtual-nodes --mesh-name default
{
    "virtualNodes": [
        {
            "arn": "arn:aws:appmesh:us-east-1:******:mesh/default/virtualNode/colorteller-vn",
            "meshName": "default",
            "virtualNodeName": "colorteller-vn"
        },
        {
            "arn": "arn:aws:appmesh:us-east-1:******:mesh/default/virtualNode/colorteller-red-vn",
            "meshName": "default",
            "virtualNodeName": "colorteller-red-vn"
        },
        {
            "arn": "arn:aws:appmesh:us-east-1:******:mesh/default/virtualNode/colorteller-blue-vn",
            "meshName": "default",
            "virtualNodeName": "colorteller-blue-vn"
        },
        {
            "arn": "arn:aws:appmesh:us-east-1:******:mesh/default/virtualNode/colorteller-black-vn",
            "meshName": "default",
            "virtualNodeName": "colorteller-black-vn"
        },
        {
            "arn": "arn:aws:appmesh:us-east-1:******:mesh/default/virtualNode/colorgateway-vn",
            "meshName": "default",
            "virtualNodeName": "colorgateway-vn"
        }
    ]
}
```

Blueですが、virtualNodeNameがcolorteller-blue-vnになりますので、colorteller-route.jsonを編集します。

```json
$ emacs servicemesh/config/routes/colorteller-route.json 
{
    "routeName": "colorteller-route",
    "spec": {
        "httpRoute": {
            "action": {
                "weightedTargets": [
                    {
                        "virtualNode": "colorteller-blue-vn",
                        "weight": 1
                    }
                ]
            },
            "match": {
                "prefix": "/"
            }
        }
    },
    "virtualRouterName": "colorteller-vr"
}
```

更新します。

```
$ aws appmesh update-route --mesh-name default --cli-input-json file://servicemesh/config/routes/colorteller-route.json 
```

virtualNodeがcolorteller-blue-vnとなっていることを確認する。

```json
$ aws appmesh describe-route --mesh-name default  --route-name colorteller-route --virtual-router-name colorteller-vr
{
    "route": {
        "meshName": "default",
        "spec": {
            "httpRoute": {
                "action": {
                    "weightedTargets": [
                        {
                            "weight": 1,
                            "virtualNode": "colorteller-blue-vn"
```

curlコマンドで確認すると、blueが返ってきます。

```json
sh-4.2$ curl -s http://colorgateway.default.svc.cluster.local:9080/color
{"color":"blue", "stats": {"blue":0,"white":1}}sh-4.2$
```

トラフィックの30%をBlackのマイクロサービス、70％をRedのマイクロサービスといったようにルーティングすることも可能です。

##落ち穂拾い
GatewayのEnvoyコンテナにログインして設定情報を確認してみます。
GatewayのEnvoyコンテナが起動しているEC2インスタンスにログイン via SSM or 踏み台経由してSSH

コンテナIDを確認

```
sh-4.2$ sudo su -
Last login: Fri Dec 28 05:10:11 UTC 2018 on pts/6
[root@ip-10-0-126-201 ~]# docker ps
CONTAINER ID        IMAGE                                                                          COMMAND                  CREATED             STATUS              PORTS               NAMES
13591540c2b5        111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.8.0.2-beta   "/bin/sh -c /usr/bin…"   24 hours ago        Up 24 hours                             ecs-AppMeshSample-ecs-colorapp-ColorGatewayTaskDefinition-1P2K347XVMJFW-1-envoy-fec5ebb5a1fbec900a00
```

コンテナにログイン

```
root@ip-10-0-126-201 ~]#
[root@ip-10-0-126-201 ~]# docker exec -it 13591540c2b5 /bin/bash
bash-4.2$
```

Envoyの設定情報確認
Virtual Nodeがcolorgateway-vn|colorteller-blue-vnになっていることが確認できます。

```json
bash-4.2$ curl localhost:9901/config_dump
{
 "configs": [
  {
   "@type": "type.googleapis.com/envoy.admin.v2alpha.BootstrapConfigDump",

(略)
  {
   "@type": "type.googleapis.com/envoy.admin.v2alpha.RoutesConfigDump",
   "dynamic_route_configs": [
    {
     "version_info": "61077530",
     "route_config": {
      "name": "rds|egress|default|colorgateway-vn|http|9080|61077530",
      "virtual_hosts": [
       {
        "name": "rds|egress|default|colorgateway-vn|http|9080|colorteller.default.svc.cluster.local|61077530",
        "domains": [
         "colorteller.default.svc.cluster.local",
         "colorteller.default.svc.cluster.local:9080"
        ],
        "routes": [
         {
          "match": {
           "prefix": "/"
          },
          "route": {
           "weighted_clusters": {
            "clusters": [
             {
              "name": "cds|egress|default|colorgateway-vn|colorteller-blue-vn|http|9080|59827320",
              "weight": 1
             }
            ],
            "total_weight": 1
           }
          }
         }
        ]
       }
      ]
     },
     "last_updated": "2018-12-28T04:19:28.852Z"
    },
```

##お約束
投稿内容は私個人の意見であり、所属企業・部門見解を代表するものではありません。