tokers opened a new issue #2303:
URL: https://github.com/apache/apisix/issues/2303


   ### Background
   
   I observed the Pull Request https://github.com/apache/apisix/pull/2279 adds 
labels for `upstream` object, which inspires me that if we attach labels for 
each node in `upstream`,  then we can implement fancy traffic split feature.
   
   ### Design
   
   Traffic split, which means to setup several route rules and schedule 
requests by respecting these rules to specified upstream (or a subset nodes in 
a single upstream). With this feature, we can support the [Canary 
Release](https://martinfowler.com/bliki/CanaryRelease.html#:~:text=Canary%20release%20is%20a%20technique,making%20it%20available%20to%20everybody.),
 [Blue Green 
Release](https://docs.cloudfoundry.org/devguide/deploy-apps/blue-green.html#:~:text=Blue%2Dgreen%20deployment%20is%20a,live%20and%20Green%20is%20idle.)
 for backend applications when they are releasing, it's useful to reduce the 
downtime when something fault happens.
   
   But it's not a good idea to introduce these concepts into APISIX directly, 
since features like Blue Green Release is closer to business, not the 
infrastructure, so what we can do is introducing the concept traffic split , to 
abstract all the business logics into the traffic split rules (route rules), 
and it can be implemented as a plugin.
   
   Both the Istio and [SMI](https://github.com/servicemeshinterface/smi-spec) 
support traffic split by `VirtualService` and `TrafficSplit` CRD respectively, 
although the latter is not perfect (at lease for now), we still can be aware of 
the similarity between them.
   
   In general, we need two parts to define a traffic split rule, match and 
route, the `match` defines the conditions that needed to judge wether a request 
is eligible to apply the `route`, for instance, a `match` might be "the method 
of HTTP request must be `GET`, and the parameter `id` must be equal to `1006`. 
From APISIX's point of view, match conditions can be scoped to HTTP, TLS, so 
conditions can vary, like URI, method, headers, SNI and even other APISIX 
instance-scoped variables (hostname, env and etc). 
   
   The APISIX Route already defines a fantastic match mechanism which is 
similar with the traffic split. But that not means we don't need to embed the 
match part in traffic split plugin, instead, we can reuse the match mechanism 
in Route as it's own match mechanism to further split requests that hit the 
same Route. See https://github.com/api7/lua-resty-radixtree#new for more 
details about Route match.
   
   The route, decides the ultimate upstream that a request will go, either 
specifying the upstream name or with a label selector to filter an eligible 
subset from that upstream. What's more, multiple upstreams can be specified, 
each with a non-negative weight to support probabilistic selection. For now, 
node in APISIX Upstream doesn't contain labels attribute,  so we can implement 
it by the metadata map in node indirectly.
   
   ```json
   # route requests to the nodes that has label release=canary in fake_upstream,
   {
       "route": [
           {
               "upstream_id": "1",
               "labels": {
                   "release": "canary"
               }
           }
       ]
   }
   
   # route 75% requests to upstream 2 and other 25% to upstream 3.
   {
       "route": [
           {
               "upstream_id": "2",
               "weight": 75
           },
           {
               "upstream_id": "3",
               "weight": 25
           }
       ]
   }
   ```
   
   ### Examples
   
   #### Weighted Blue Green Release
   
   Say we have two upstreams `app-blue` (id: 1) and `app-green` (id: 2), which 
represent the old and new releases for `app` respectively. Now we want to route 
10% requests which UA is Android to `app-green`.
   
   ```json
   [
       {
           "match": [
               {
                   "vars": [
                       [ "http_user_agent", "~~", "Android"]
                   ]
               }
           ],
           "route": [
               {
                   "upstream_id": 2,
                   "weight": 10
               }
           ]
       },
       {
           "route": [
               {
                   "upstream_id": 1,
                   "weight": 90
               }
           ]
       }
   ]
   ```
   
   ### Canary Release in a single upstream
   
   Say we updated a node in upstream `app` (id: 3), and this node have a unique 
label `release=canary`, other nodes in `app` has label `release=stable`, now we 
want to route requests which parameter `user_id` is between [13, 133] to this 
node.
   
   ```json
   [
       {
           "match": [
               {
                   "vars": [
                       [ "arg_user_id", ">=", 13],
                       [ "arg_user_id", "<=", 133]
                   ]
               }
           ],
           "route": [
               {
                   "upstream_id": 3,
                   "labels": "release=canary"
               }
           ]
       },
       {
           "route": [
               {
                   "upstream_id": 3,
                   "labels": "release=stable"
               }
           ]
       }
   ]
   ```
   
   ### References
   
   * VirtualService: 
https://istio.io/latest/docs/reference/config/networking/virtual-service/#RouteDestination
   * TrafficSplit: 
https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to