LoginSignup
0
0

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

Posted at

ざっとしたまとめ

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

検証構成図

ECS ServiceをPrivateサブネットに配置
LambdaからALBを経由してServiceAをたたき、ServiceAはServiceBをたたくという構成
20240414_構成図.PNG

各躓きポイント

ClientService側を先に作成する必要がある

ECS Service Connectでは
ClientService側を先に立てないとClient側のサービスがClientService側のサービスを検出することができないらしい
参考記事
なので、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
なので、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()
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0