ECS Service ConnectでgRPCを使おうとして躓いたところ

# ざっとしたまとめ
 - ECS Service ConnectはClientService側のサービスを先に作成しないといけない
 - host名の grpc:// は必要なかった
 - listenしているportは本当にあってる?

# 検証構成図
ECS ServiceをPrivateサブネットに配置  
LambdaからALBを経由してServiceAをたたき、ServiceAはServiceBをたたくという構成  
![20240414_構成図.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1952885/fac721f1-6007-f6d7-8e7c-8137e9e75dfb.png)

# 各躓きポイント
## ClientService側を先に作成する必要がある
ECS Service Connectでは
ClientService側を先に立てないとClient側のサービスがClientService側のサービスを検出することができないらしい
[参考記事](https://repost.aws/questions/QUczl6EqoRRemvw4b6KL_kDw/ecs-service-connect-could-not-resolve-host)
なので、AWSコンソールで操作する場合には作成順を気をつける必要があるし、CloudFormationを使用する場合にはDependsOnとかでClientService側のサービスを立てるように明示的にしなければならない
↓参考にして頂ければと思います
```
AWSTemplateFormatVersion: "2010-09-09"
Description: "ECS Service Connect Test Services"
Parameters:
  ProjectName:
    Type: String
    Default: "EcsGrpcTest"
  EcsTaskCpuUnit:
    Type: String
    Default: "256"
  EcsTaskMemory:
    Type: String
    Default: "512"
  Port:
    Type: Number
    Default: 50051

Resources:
# ------------------------------------------------------------#
# Namespace
# ------------------------------------------------------------#
  NameSpace:
    Type: AWS::ServiceDiscovery::HttpNamespace
    Properties:
      Name: "local"
# ------------------------------------------------------------#
# ECS Cluster
# ------------------------------------------------------------#
  ECSCluster:
    Type: AWS::ECS::Cluster
    DependsOn: NameSpace
    Properties:
      ClusterName: !Sub "${ProjectName}-ECSCluster"
      ServiceConnectDefaults:
        Namespace: "local"

# ------------------------------------------------------------#
# ECS TaskDefinitionB
# ------------------------------------------------------------#
  TaskDefinitionB:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub "${ProjectName}-EcsTask-B"
      Cpu: !Ref EcsTaskCpuUnit
      Memory: !Ref EcsTaskMemory
      ExecutionRoleArn: !ImportValue ECSTaskExecutionRoleArn
      TaskRoleArn: !ImportValue ECSTaskRoleArn
      NetworkMode: "awsvpc"
      RequiresCompatibilities:
        - "FARGATE"
      #ContainerDefinitions
      ContainerDefinitions:
        - Name: !Sub "${ProjectName}-EcsContainer-B"
          Image: "{{resolve:ssm:/grpc_test/Service/B:4}}"
          LogConfiguration:
            LogDriver: "awslogs"
            Options:
              awslogs-group: !ImportValue ECSLogGroupResourceNameB
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: !Ref ProjectName
          MemoryReservation: 128
          Essential: true
          PortMappings:
            - HostPort: !Ref Port
              Protocol: "tcp"
              AppProtocol: "grpc"
              ContainerPort: !Ref Port
              Name: "service_b"
          Environment:
            - Name: AlbDnsName
              Value: !ImportValue AlbDnsName
            - Name: port
              Value: !Ref Port
            # - Name: GRPC_VERBOSITY
            #   Value: "debug"
# ------------------------------------------------------------#
# ECS ServiceB
# ------------------------------------------------------------#
  ECSServiceB:
    Type: AWS::ECS::Service
    DependsOn: ECSCluster
    Properties: 
      ServiceName: !Sub "${ProjectName}-EcsService-B"
      Cluster: !Sub "${ProjectName}-ECSCluster"
      TaskDefinition: !Ref TaskDefinitionB
      LaunchType: "FARGATE"
      EnableExecuteCommand: true
      DesiredCount: 1
      NetworkConfiguration:
        AwsvpcConfiguration:
            AssignPublicIp: "DISABLED"
            SecurityGroups:
              - !ImportValue ServiceBEcsSecurityGroupResourceId
            Subnets:
              - !ImportValue PrivateSubnet1Id
              - !ImportValue PrivateSubnet2Id
      ServiceConnectConfiguration:
        Enabled: true
        Namespace: "local"
        LogConfiguration:
          LogDriver: "awslogs"
          Options:
            awslogs-group: !ImportValue ECSLogGroupResourceNameB
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: !Ref ProjectName
        Services:
          - DiscoveryName: "service_b"
            ClientAliases:
              - DnsName: "service_b.local"
                Port: !Ref Port
            PortName: "service_b"
# ------------------------------------------------------------#
# ECS TaskDefinitionA
# ------------------------------------------------------------#
  TaskDefinitionA:
    Type: AWS::ECS::TaskDefinition
    DependsOn: TaskDefinitionB
    Properties:
      Family: !Sub "${ProjectName}-EcsTask-A"
      Cpu: !Ref EcsTaskCpuUnit
      Memory: !Ref EcsTaskMemory
      ExecutionRoleArn: !ImportValue ECSTaskExecutionRoleArn
      TaskRoleArn: !ImportValue ECSTaskRoleArn
      NetworkMode: "awsvpc"
      RequiresCompatibilities:
        - "FARGATE"
      #ContainerDefinitions
      ContainerDefinitions:
        - Name: !Sub "${ProjectName}-EcsContainer-A"
          Image: "{{resolve:ssm:/grpc_test/Service/A:9}}"
          LogConfiguration:
            LogDriver: "awslogs"
            Options:
              awslogs-group: !ImportValue ECSLogGroupResourceNameA
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: !Ref ProjectName
          MemoryReservation: 128
          Essential: true
          PortMappings:
            - HostPort: !Ref Port
              Protocol: "tcp"
              ContainerPort: !Ref Port
              # Name: "service_a"
          Environment:
            - Name: AlbDnsName
              Value: !ImportValue AlbDnsName
            - Name: port
              Value: !Ref Port
            # - Name: GRPC_VERBOSITY
            #   Value: "debug"
# ------------------------------------------------------------#
# ECS ServiceA
# ------------------------------------------------------------#
  ECSServiceA:
    Type: AWS::ECS::Service
    DependsOn: ECSServiceB
    Properties: 
      ServiceName: !Sub "${ProjectName}-EcsService-A"
      Cluster: !Sub "${ProjectName}-ECSCluster"
      TaskDefinition: !Ref TaskDefinitionA
      LaunchType: "FARGATE"
      EnableExecuteCommand: true
      DesiredCount: 1
      LoadBalancers:
        -
          TargetGroupArn: !ImportValue TargetGroupArn
          ContainerPort: !Ref Port
          ContainerName: !Sub "${ProjectName}-EcsContainer-A"
      NetworkConfiguration:
        AwsvpcConfiguration:
            AssignPublicIp: "DISABLED"
            SecurityGroups:
              - !ImportValue ServiceAEcsSecurityGroupResourceId
            Subnets:
              - !ImportValue PrivateSubnet1Id
              - !ImportValue PrivateSubnet2Id
      ServiceConnectConfiguration:
        Enabled: true
        Namespace: "local"
        LogConfiguration:
          LogDriver: "awslogs"
          Options:
            awslogs-group: !ImportValue ECSLogGroupResourceNameA
            awslogs-region: !Ref AWS::Region
            awslogs-stream-prefix: !Ref ProjectName
```

## host名の grpc:// は必要なかった
ECS Service Connectで接続先を確認すると以下のようにgrpc://service_b.local:50051と書かれていた
![20240414_SerciceConnect.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1952885/55ac8edf-b8d8-d8b6-c234-eded7ea14dc6.png)
なので、Client側であるServiceAのコードを以下のようにしていた
```
class Greeter(serviceA_pb2_grpc.GreeterServicer):
    def SayHello(self, request, content):
        with grpc.insecure_channel("grpc://service_b.local:50051") as channel:
            stub = serviceB_pb2_grpc.GreeterStub(channel)
            response = stub.SayHello(serviceB_pb2.HelloRequest(name="serviceA"))
        return serviceA_pb2.HelloReply(message=response.message)
```
しかし、これで実行するとDNS系のエラーが出て「そんな接続先は見つかりませんでした」と怒られてしまう
色々と試したら、grpc://の部分は必要ありません。
```
class Greeter(serviceA_pb2_grpc.GreeterServicer):
    def SayHello(self, request, content):
        print("revice request", flush=True)
        with grpc.insecure_channel("service_b.local:50051") as channel:
            stub = serviceB_pb2_grpc.GreeterStub(channel)
            response = stub.SayHello(serviceB_pb2.HelloRequest(name="serviceA"))
        return serviceA_pb2.HelloReply(message=response.message)
```
## listenしているportは本当にあってる?
なんだかんだでこれが1番時間を要してしょうもないミスでした。
ECSで試す前にローカルのDockerを使用していたのでportが被らないように
ServiceA -> 50051 port
ServiceB -> 50052 port
でやっていたので、そのままECR登録してしまいサービスを立ててました。
logとかで何番portでlisternしているかを確認したほうがいいですね。。。
```
def serve():
    port = "50051"
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    serviceA_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port(f"[::]:{port}")
    _configure_health_server(server)
    server.start()
    print("Server started, listening on " + port, flush=True)
    server.wait_for_termination()

if __name__ == "__main__":
    serve()
```