Search code examples
amazon-web-servicesamazon-ec2amazon-route53amazon-elb

AWS Route 53 - Domain name route to different ports of an Application load balancer


We are implementing a micro-services architecture in AWS. We have several EC2 instances which has the micro-services deployed on different ports. We also have an internet facing Application Load Balancer, which routes to different services based on the port.

eg: 
xxxx-xx.xx.elb.amazonaws.com:8080/ go to microservice 1 
xxxx-xx.xx.elb.amazonaws.com:8090/ go to microservice 2

We need to have a domain name instead of the ELB, the port should not be exposed through the domain name as well. Almost all the resources I found regarding route 53, use alias which does the following:

xx.xxxx.co.id -> xxxx-xx.xx.elb.amazonaws.com or
xx.xxxx.co.id -> 111.111.111.11 (static ip)

1) Do we need separate domains for each micro service?

2) How to use alias to point domains to a specific port of the ELB?

3) Is it possible to use this setup if the domains are from another provider other than AWS.


Solution

  • Important Update

    Since this answer was originally written, Application Load Balancer introduced the capability for ALB to route requests to a specific target group based on the Host header of the incoming request.

    The incoming host header can now be used to route requests to specific instances and ports.

    Additionally, ALB introduced SNI support, allowing you to associate multiple TLS (SSL) certificates with a single balancer, and the correct certificate will be automatically selected based on the SNI presented by the client when TLS is negotiated. Multi-domain and wildcard certs from Amazon Certificate Manager also work with ALB.

    Based on these factors, no separate ports or different listeners are needed -- simply assign hostnames and/or path prefixes for each service, and map those patterns to the appropriate target group of instances.

    The original answer is no longer accurate, but is included below.


    1.) Do we need separate domains for each micro service?

    No, this won't help you. ALB does not interpret the hostname attached to the incoming request.

    Separate hostnames in the same domain won't directly accomplish your objective, either.

    2.) How to use alias to point domains to a specific port of the ELB?

    Domains do not point to ports. Hostnames do not point to ports. DNS is only used for address resolution. This is true everywhere on the Internet.

    3.) Is it possible to use this setup if the domains are from another provider other than AWS.

    This is not a limitation of AWS. DNS simply does not work this way.

    A service endpoint is unaware of the DNS records that point to it. The DNS entry itself is strictly used for discovering an IP address that can be used to access the endpoint. After that, the endpoint does not actually know anything about the DNS, and there is no way to tell the browser, via DNS, to use a different port.

    For HTTP, the implicit port is 80. For HTTPS, it is 443. Unless a port is provided in the URL, these are the only usable ports.

    However, in HTTP and HTTPS, each request is accompanied by a Host: header, sent by the web browser with each request. This is the hostname in the address bar.

    To differentiate between requests for different hostnames arriving at a device (such as ELB/ALB), the device at the endpoint must interpret the incoming host header and route the request to an back-end system providing that service.

    ALB does not currently support this capability.

    ALB does, however, support choosing endpoints based on a path prefix. So microservices.example.com/api/foo could route to one set of services, while microservices.example.com/api/bar could route to another.

    But ALB does not directly support routing by host header.


    In my infrastructure, we use a combination of ELB or ALB, but the instances behind the load balancer are not the applications. Instead, they are instances that run HAProxy load balancer software, and route the requests to the backend.

    A brief example of the important configuration elements looks like this:

    frontend main
      use_backend svc1 if { hdr(Host) -i foo.example.com }
      use_backend svc2 if { hdr(Host) -i bar.example.com }
    
    backend svc1
      server foo-a 192.168.2.24:8080
      server foo-b 192.168.12.18:8080
    
    backend svc2
      ....
    

    The ELB terminates the SSL and selects a proxy at random and the proxy checks the Host: header and selects a backend (a group of 1 or more instances) to which the request will be routed. It is a thin layer between the ELB and the application, which handles the request routing by examining the host header or any other characteristic of the request.

    This is one solution, but is a somewhat advanced configuration, depending on your expertise.


    If you are looking for an out-of-the-box, serverless, AWS-centric solution, then the answer is actually found in CloudFront. Yes, it's a CDN, but it has several other applications, including as a reverse proxy.

    • For each service, choose a hostname from your domain to assign to that service, foo.api.example.com or bar.api.example.com.

    • For each service, create a CloudFront distribution.

    • Configure the Alternate Domain Name of each distribution to use that service's assigned hostname.

    • Set the Origin Domain Name to the ELB hostname.

    • Set the Origin HTTP Port to the service's specific port on the ALB, e.g. 8090.

    • Configure the default Cache Behavior to forward any headers you need. If you don't need the caching capability of CloudFront, choose Forward All Headers. Also enable forwarding of Query Strings and Cookies if needed.

    • In Route 53, create foo.api.example.com as an Alias to that specific CloudFront distribution's hostname, e.g. dxxxexample.cloudfront.net.

    Your problem is solved.

    You see what I did there?

    For each hostname you configure, a dedicated CloudFront distribution receives the request on the standard ports (80/443) and -- based on which distribution the host header matches -- CloudFront routes the requests to the same ELB/ALB hostname but a custom port number.