You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@apisix.apache.org by Zhang Chao <zc...@gmail.com> on 2020/10/12 08:29:58 UTC
Re: Proposal: inptroduce traffic split plugin
That’d be better than implementing a plugin to support it.
Chao Zhang
zchao1995@gmail.com
> On Sep 28, 2020, at 1:34 PM, wei jin <kv...@apache.org> wrote:
>
> I like this proposal very much and think it will be the final form.
> This function can be divided into two parts:
> 1.label && selector;
> 2. A grayscale upgrade configuration;
> These two things can be done simultaneously.
>
> 1. Implement label && selector to replace the current upstream_id binding
> relationship;
> 2. The configuration method of grayscale upgrade can be realized by a
> plug-in version first.
> After waiting for the label function to be implemented, the gray scale
> capability is migrated to the native implementation in the apisix object.
>
> The plug-in configuration is similar to this
>
> ```json
> "plugins":{
> "routex": {
> "rules": [
> {
> "match": [
> {
> "vars": [
> ["arg_user_id",">=",13],
> ["arg_user_id","<=",133]
> ]
> },
> {
> "vars": [
> ["arg_user_id","==",222]
> ]
> }
> ],
> "upstreams": [
> {
> "upstream_id": 3,
> "weight": 20
> },
> {
> "upstream_id": 5,
> "weight": 80
> }
> ]
> }
> ]
> }
> }
> ```
>
> YuanSheng Wang <me...@apache.org> 于2020年9月26日周六 下午5:25写道:
>
>> wow, this is an interesting feature.
>>
>> It is very different from the current way. I need time to understand it.
>> ^_^
>>
>>
>> On Fri, Sep 25, 2020 at 10:08 AM Zhang Chao <zc...@gmail.com> wrote:
>>
>>>
>>> ### 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
>>> }
>>> ]
>>> }
>>> ```
>>>
>>> The Route already contains an upstream, after introducing the traffic
>>> split plugin, this upstream will be treated as the default upstream and
>>> will be used when requests can not hit the route rules.
>>>
>>> ### 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
>>> * TrafficSplit:
>>>
>> https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md
>>>
>>>
>>> Chao Zhang
>>> zchao1995@gmail.com
>>>
>>>
>>>
>>>
>>
>> --
>>
>> *MembPhis*
>> My GitHub: https://github.com/membphis
>> Apache APISIX: https://github.com/apache/incubator-apisix
>>