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


はじめに

AWS AppMesh記事第2弾。
前回の記事の続き。

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

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

バックエンドサービス通信編で目指す構成

前回の構成を以下のように変更する。

  • 前回の構成

  • 今回の構成

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

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

AppMesh のサービスAに関連した項目の設定

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

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

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"
        }
      }
    }
  }
}

ポイントは

    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 のサービスマップは以下のように変わる。

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

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