Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Add Listeners to Virtual Routers #94

Closed
dastbe opened this issue Feb 28, 2019 · 1 comment
Closed

[FEATURE] Add Listeners to Virtual Routers #94

dastbe opened this issue Feb 28, 2019 · 1 comment
Labels

Comments

@dastbe
Copy link
Contributor

dastbe commented Feb 28, 2019

What we're changing

To provide a safer and more understandable experience when using Virtual Routers, we're proposing the addition of Virtual Router Listeners to the Virtual Router Spec. Instead of using target Virtual Nodes in a route to generate listeners in Envoy configuration, customers will define listener ports on the Virtual Router itself.

With this new feature, you can now abstract the port clients talk to from the port Envoy sends traffic to on the destinations. This operates similar to load balancers like ALB and NLB, where you define a distinct listener and then bind targets to them.

In the CLI, this will look like

$ aws appmesh create-virtual-router \
    --mesh-name example-mesh \
    --cli-input-json file://virtualrouter.json
    
// virtualrouter.json:
{
    "virtualRouterName": "example-router"
    "spec": {
        listeners: [
            {
                port: 80,
                protocol: http
            }
        ]
    }
}

Which would configure a Virtual Router that listens on port 80 and applies to all HTTP routes for matching. When an HTTP route matches, Envoy would forward traffic to an endpoint in the target Virtual Node on the Virtual Node's listener port.

Let's relate this back to the Color Teller example. If we made this modification

$ aws appmesh update-virtual-router \
    --mesh-name colorapp \
    --cli-input-json file://colorteller-virtualrouter.json
    
// colorteller-virtualrouter.json:
{
    "virtualRouterName": "colorteller-vr"
    "spec": {
        listeners: [
            {
                port: 80,
                protocol: http
            }
        ]
    }
}

we can now have clients talk directly on port 80 to the colorteller router, abstracting away the actual destination port (9080) from clients.

As part of our plan to revise our API ahead of GA (#92 ), we would like to make this a required field on all Virtual Routers created via the new API. For backwards compatibility, calls using the preview API will continue to work and generate equivalent Envoy configuration, and all existing Virtual Routers will continue to work as before.

Why This is Important

We believe that the existing experience leads to several sharp edges in the API that can and will cause customer confusion, especially as their routes grow in number and complexity. I will explain the two most important scenarios below.

Blackholed Routes

Let's suppose you define the following Route

aws appmesh describe-route --mesh-name test-mesh --virtual-router-name test-router --route-name test-route
{
    "route": {
        "status": {
            "status": "ACTIVE"
        },
        "meshName": "test-mesh",
        "virtualRouterName": "test-router",
        "routeName": "test-route",
        "spec": {
            "httpRoute": {
                "action": {
                    "weightedTargets": [
                        {
                            "virtualNode": "vn-1", // Listens on port 8080
                            "weight": 1
                        },
                        {
                            "virtualNode": "vn-2", // Listens on port 8081
                            "weight": 1
                        }
                    ]
                },
                "match": {
                    "prefix": "/"
                }
            }
        }
}

Because the two target Virtual Nodes have different listener ports, App Mesh will fail to generate a route in the Envoy configuration (See bug #93 for more details). This is because the listener ports generated in the Envoy configuration come from the target Virtual Nodes, and there is no obvious choice as to which Virtual Node listener port to use.

With listeners on the Virtual Router, Virtual Node listener ports are abstracted away from clients and this route will be safely materialized.

Non-Obvious Routing behavior

Let's suppose you define the following two routes

aws appmesh describe-route --mesh-name test-mesh --virtual-router-name test-router --route-name test-route-1
{
    "route": {
        "status": {
            "status": "ACTIVE"
        },
        "meshName": "test-mesh",
        "virtualRouterName": "test-router",
        "routeName": "test-route-1",
        "spec": {
            "httpRoute": {
                "action": {
                    "weightedTargets": [
                        {
                            "virtualNode": "vn-1", // Listens on port 8080
                            "weight": 1
                        }
                    ]
                },
                "match": {
                    "prefix": "/"
                }
            }
        }
}

aws appmesh describe-route --mesh-name test-mesh --virtual-router-name test-router --route-name test-route-2
{
    "route": {
        "status": {
            "status": "ACTIVE"
        },
        "meshName": "test-mesh",
        "virtualRouterName": "test-router",
        "routeName": "test-route-2",
        "spec": {
            "httpRoute": {
                "action": {
                    "weightedTargets": [
                        {
                            "virtualNode": "vn-2", // Listens on port 8081
                            "weight": 1
                        }
                    ]
                },
                "match": {
                    "prefix": "/internal"
                }
            }
        }
}

At first glance, you would expect that all traffic to "/internal" would go to targets defined by vn-2, and all other traffic would go to vn-1. However, where traffic goes is additionally dependent on the port defined.

  1. When requests are sent to /internal on port 8080, requests go to vn-1
  2. When requests are sent to /internal on port 8081, requests go to vn-2
  3. When requests are sent to / on port 8080, requests go to vn-1
  4. When requests are sent to / on port 8081, requests are blackholed

With listeners on the Virtual Router, the target Virtual Node listener ports are no longer used for initial route matching in Envoy. Traffic to the router would always route requests matching prefix "/internal" to vn-2, and all other traffic would flow to vn-1.

In the future, support for multiple listeners on a Virtual Router and port matching rules on routes could be added to re-enable now disallowed routing logic. Importantly, customers would be explicitly configuring these routes rather than receiving them implicitly.

@bcelenza
Copy link
Contributor

bcelenza commented Mar 7, 2019

These changes are now available in the following SDK versions:

CLI: 1.16.120
Java: 1.11.514
Java (V2): 2.5.5
CPP: 1.7.65
Go: 1.17.13
PHP: 3.89.0
Python: 1.9.110
JavaScript: 2.417.0
Ruby: 2.11.236
.NET: 3.3.471.0

@bcelenza bcelenza closed this as completed Mar 7, 2019
Y0Username pushed a commit to Y0Username/aws-app-mesh-examples that referenced this issue Mar 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants