TerraformでAWS AppMeshを使ってECS Fargateのサービスを統合する(その2 バックエンドサービス通信編)

# はじめに
AWS AppMesh記事第2弾。
[前回の記事](https://qiita.com/neruneruo/items/b4aa8a8f4a7460b06608)の続き。

今回は、前回記事のフロント側サービス(サービスA)にも AppMesh を適用して、バックエンド通信も含めて統合することを目指す。

前回に引き続き、本記事を書いている 2021/1/2 時点で AWS AppMesh はまだまだ発展途上なサービスであり、情報が変わる可能性があることはご了承いただきたい。

# バックエンドサービス通信編で目指す構成
前回の構成を以下のように変更する。

- 前回の構成

![figure1_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/6f26bd00-6352-5019-4c22-0f0ba1a0f4dc.png)

- 今回の構成

![figure2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/e25b7e76-c449-bf50-4775-32b45bb6f354.png)

サービスAは後続のサービスBに HTTP リクエストを転送する機能を持ったプロキシである前提だ。

今回は、サービスBは特に触らず、サービスAについて変更を行っていく。

# AppMesh のサービスAに関連した項目の設定
基本的にサービスBに行った変更とは大きく変わらない。
ECS に関連した変更は前回と同じであるため、今回は割愛する。
※サービスAはロードバランサ配下にいるため、その設定は削除しない。

AppMesh に関連したリソースを以下のように変更する。
今回も、```aws_appmesh_virtual_service``` と ```aws_appmesh_virtual_node``` を作成する。
メッシュそのものは、サービスA/Bで共通であるため今回は作成しない。

```HCL
resource "aws_appmesh_virtual_service" "service_a" {
  name      = "${aws_service_discovery_service.service_a.name}.${aws_service_discovery_private_dns_namespace.internal.name}"
  mesh_name = aws_appmesh_mesh.trial.id

  spec {
    provider {
      virtual_node {
        virtual_node_name = aws_appmesh_virtual_node.service_a.name
      }
    }
  }
}

resource "aws_appmesh_virtual_node" "service_a" {
  name      = local.service_a_appmesh_virtualnode_name
  mesh_name = aws_appmesh_mesh.trial.id

  spec {
    listener {
      port_mapping {
        port     = 80
        protocol = "http"
      }
      health_check {
        protocol            = "http"
        path                = "/healthcheck"
        healthy_threshold   = 2
        unhealthy_threshold = 2
        timeout_millis      = 2000
        interval_millis     = 5000
      }
    }

    backend {
      virtual_service {
        virtual_service_name = aws_appmesh_virtual_service.service_b.name
      }
    }

    service_discovery {
      aws_cloud_map {
        namespace_name = [サービスディスカバリの名前空間]
        service_name   = [サービスAのサービス名]
      }
    }

    logging {
      access_log {
        file {
          path = "/dev/stdout"
        }
      }
    }
  }
}
```

ポイントは

```HCL
    backend {
      virtual_service {
        virtual_service_name = aws_appmesh_virtual_service.service_b.name
      }
    }
```

の部分で、この設定によりバックエンド通信をプロキシ経由に変更する。

# 動かしてみる
これで、NLB のパブリックドメインに対して curl でトラフィックを入れてみると、サービスB側の情報が返却されるだろう。

その上で、サービスAの CloudWatch Logs を確認すると、以下の2つのアクセスログが出力されていることが分かる。

```
[2021-01-02T11:20:33.732Z] "GET / HTTP/1.1" 200 - 0 413 3 3 "-" "curl/7.61.1" "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" "service-b.appmesh-trial.internal" "xxx.xxx.xxx.xxx:80"
[2021-01-02T11:20:33.730Z] "GET / HTTP/1.1" 200 - 0 413 5 5 "-" "curl/7.61.1" "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" "appmesh-trial-svc-a-nlb-230cfbfddd423b72.elb.ap-northeast-1.amazonaws.com" "127.0.0.1:80"
```

また、サービスB側のログにも以下が出力されている。

```
[2021-01-02T11:20:33.734Z] "GET / HTTP/1.1" 200 - 0 413 0 0 "-" "curl/7.61.1" "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" "service-b.appmesh-trial.internal" "127.0.0.1:80"
```

マスクしているが、```xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``` には同じ値が入っていて、トラフィックを一意に識別することができる。

また、X-Ray のサービスマップは以下のように変わる。

![キャプチャ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/5cb730a1-d730-b776-2efa-4950b8719574.png)

分かりにくいが、下がサービスA、上がサービスBだ。
サービスBにも Client から線が延びているのは、ヘルスチェックのトラフィックが計上されているためだ。
エンドユーザトランザクションはサービスA側からしか入れていない。

このように簡単にサービスマップを後付けで作ることができるのも AppMesh のポイントだろう。