'Use Envoy for Blue/Green Deployment

I am trying to understand if it would be possible to use Envoy proxy to route traffic between services based on the custom logic.

In the example, I found: https://www.tetrate.io/blog/envoy-101-configuring-envoy-as-a-gateway/ Envoy has a defined config file to route the request to the right instance after performing certain filtering.

Would it be possible to write a custom javascript/python logic for the routing?

E.g. take param from the query string and lookup database mapping to make a decision where to route the request.



Solution 1:[1]

You can introduce custom logic with Lua filter, but not directly with pure Javascript or Python. Also, instead of Lua filter, maybe Wasm filter can suit your needs. I have not tested it and it's experimental, so I don't know if it works in your case.

Anyway, the following example is a base config to showcase what you can do with Lua filter (envoy.filters.http.lua section). It:

  • parses the path (something like /something?param=1&other=xxx)
  • retrieves the param field
  • adds it in a header (X-App) used in the route matching (param=1 will redirect traffic to cluster first and param=2 will redirect traffic to cluster second; other values will redirect traffic to cluster first by default)

Of course, here I have used a Lua filter to allow you to add some other custom logic.

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 8080
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: backend
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/"
                  headers:
                  - name: "X-App"
                    string_match: # with envoy < 1.22.0, use exact_match: "1" instead
                      exact: "1"
                route:
                  cluster: first
              - match:
                  prefix: "/"
                  headers:
                  - name: "X-App"
                    string_match: # with envoy < 1.22.0, use exact_match: "2" instead
                      exact: "2"
                route:
                  cluster: second
              - match: # default, if no headers
                  prefix: "/"
                route:
                  cluster: first
          http_filters:
          - name: envoy.filters.http.lua
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
              inline_code: |
                function envoy_on_request(request_handle)
                  path = request_handle:headers():get(":path")
                  param_value = string.match(path, '/.*[?&]param=([^&]+)')

                  if param_value then
                    request_handle:headers():add("X-App", param_value)
                  end
                end
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: first
    connect_timeout: 5s
    type: LOGICAL_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: first
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: first
                port_value: 5000
  - name: second
    connect_timeout: 5s
    type: LOGICAL_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: second
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: second
                port_value: 5000

You can also do some canary traffic routing with a random number (here 50/50):

function envoy_on_request(request_handle)
  math.randomseed(os.clock())
  -- math.random() returns a number in [0;1)
  if math.random() < 0.5 then
    request_handle:headers():add("x-app", "1")
  else
    request_handle:headers():add("x-app", "2")
  end
end

But note that Lua is very limited when used in Envoy filter (I'm not sure but it looks like you can't install external packages/modules), and you probably won't be able to query a database to make your routing decision. You should take a look at Wasm filter, it may work for this.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1