Skip to content

Service Discovery Design #599

@kohidave

Description

@kohidave

I'm really interested in integrating service discovery, by default for our apps. This design doesn't include App Mesh integration, but I think that'd be additive.

What is service discovery?

Service discovery allows two services to communicate using an internal (within the same VPC) DNS.

An example would be, if you have two apps, backend and frontend, the frontend app might have a public loadbancer, but the backend might be isolated in a private subnet.

The frontend would be able to communicate with the backend service using a VPC-local DNS like backend.local. The backend would also be able to contact the frontend through a VPC-local DNS in the same way. This has the benefit of the data not transiting the internet and the inter-service communicate doesn't rely on the LB url.

What services to use?

  • AWS CloudMap
  • ECS's ServiceDiscovery (same thing, but handles the registering of tasks into CloudMap)

What do we have to do?

It's actually really easy.

  1. In the environment, create a ServiceDiscovery Namespace (this is the DNS suffix). I'm thinking .local.
  ServiceDiscoveryNamespace:
    Type: AWS::ServiceDiscovery::PrivateDnsNamespace
    Properties:
        Name: local
        Vpc: !Ref VPC
  1. Export the Namespace ID

  2. In the app stack, create a discovery service (this is the actual DNS for this app):

  DiscoveryService:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      Description: Discovery Service for the ECS CLI V2 services
      DnsConfig:
        RoutingPolicy: MULTIVALUE
        DnsRecords:
          - TTL: 60
            Type: A
          - TTL: 60
            Type: SRV
      HealthCheckCustomConfig:
        FailureThreshold: 1
      Name:  !Ref AppName
      NamespaceId:
               Fn::ImportValue:
               !Sub '${ProjectName}-${EnvName}-NamespaceID'
  1. Update the service to use ServiceRegistries
      ServiceRegistries:
        - RegistryArn: !GetAtt DiscoveryService.Arn
          Port: !Ref ContainerPort
  1. This will set up everything, but one tricky thing is our security groups don't permit inter-app communication right now (since each app gets its own security group and each security group only allows traffic from the ALB and itself. So, what we could do is move the security group up to the env, and have each app use the same security group. This is definitely the trickiest part.

  2. Pass in dnsSearchDomains to the Task Definition. When we create the .local namespace it'd be rad if customers didn't have to include it in their actual http requests. A feature called Search Domains (which we can set in our task definitions) lets us add automatic suffixes if a request is made without a fully qualified domain. This means if we add a .local search domain, if the customer's container makes a request to backend - it'll be resolved to backend.local.

Example

Calling a backend app from the frontend app. The backend is running on port 8080:

resp, err := http.Get("http://backend:8080/")

Metadata

Metadata

Assignees

Labels

WIPPull requests that are being modified now.type/designIssues that are design proposals.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions