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

Http header routing pattern #1177

Open
gjreasoner opened this issue Oct 27, 2024 · 6 comments · May be fixed by #1183
Open

Http header routing pattern #1177

gjreasoner opened this issue Oct 27, 2024 · 6 comments · May be fixed by #1183

Comments

@gjreasoner
Copy link

Proposal

Would like to use a HTTP header in the HTTP request to determine which service to route to using http header based routing pattern.

Use-Case

Imagine hundreds of customers each with 100s of different domains pointed to their own HttpScaledObject.

Rather than maintaining the 100 domains on HTTPScaledObject, you send a custom header X-Customer-Id: customer-id-1 and register the hosts as customer-id-1, customer-id-2.

This means your upstream can add/update/delete host names without needing to update HTTPScaledObjects or extra k8s ingress/services.

Is this a feature you are interested in implementing yourself?

Yes

Anything else?

Can see this being implemented like

  • An env variable on the interceptor, KEDA_HTTP_ADDTL_ROUTING_HEADER=X-Customer-Id when blank or missing from the request, it still uses the HTTP Host header

This gives you an easy way to opt into the feature and have fallback/main site domains.

apiVersion: http.keda.sh/v1alpha1
kind: HTTPScaledObject
spec:
  hosts:
  - my-main-site.example.com
  - customer-id-1
  replicas:
    max: 1
    min: 0

While your remaining domains might look like

- custom-domain-1.com
- ...
- custom-domain-99.com
- *.svc.domain.com
@wozniakjan
Copy link
Member

I really like the idea of the feature, I think it's essential that http-add-on supports routing based on headers.

I would like to propose an alternative path on how it is implemented. Instead of ENV variable static for the entire traffic passing through the interceptor, it would be imho better to follow the design decisions from Gateway API. Mixing URLs and header values in spec.hosts might lead to confusing UX.

Currently the HTTPScaledObject allows routing based on hosts and pathPrefixes, e.g.

apiVersion: http.keda.sh/v1alpha1
kind: HTTPScaledObject
spec:
  hosts:
  - my.domain.com
  - my2.domain.com
  pathPrefixes:
  - /root
  - /new-feature

Adding headers to the spec would allow very fine-grained configurability resulting in overall increased satisfaction

apiVersion: http.keda.sh/v1alpha1
kind: HTTPScaledObject
spec:
  hosts:
  - my.domain.com
  - my2.domain.com
  pathPrefixes:
  - /root
  - /new-feature
  headers:
  - name: x-header-test
    value: abc
  - name: x-header-test2
    value: def

@erich23
Copy link

erich23 commented Dec 11, 2024

hey @gjreasoner I'm here because I'm seeing a similar issue (#851 to be exact) and I think this fix can address it. If I can pass in custom headers, I can rewrite the L7 path on the ingress and route based on custom headers rather than the path.

when do you think you can have this PR merged by? im happy to help

@gjreasoner
Copy link
Author

Hey @erich23 it's definitely top of mind, I have a work around (this patch on the interceptor image) in place that's keeping it from a being an absolute rush on my side. I'd still like to get back to the proposed solution hopefully in the week or so with the holiday slowdown. Feel free to take a stab at it if you'd like, I'll update here if I start work on it 👍🏻

@erich23
Copy link

erich23 commented Dec 17, 2024

hey @gjreasoner and @wozniakjan, here's my implementation of the proposed solution: #1222. Can you guys give this a review? I'm going to try and get test cases to pass now but please let me know if anything is missing

@kahirokunn
Copy link
Contributor

kahirokunn commented Dec 20, 2024

In KEDA's HTTPScaledObject, when two objects are configured as follows, which one will handle requests to my.domain.com?

apiVersion: http.keda.sh/v1alpha1
kind: HTTPScaledObject
metadata:
  name: canary
spec:
  hosts:
    - my.domain.com
    - my2.domain.com
  pathPrefixes:
    - /root
    - /new-feature
---
apiVersion: http.keda.sh/v1alpha1
kind: HTTPScaledObject
metadata:
  name: primary
spec:
  hosts:
    - my.domain.com
    - my2.domain.com
  pathPrefixes:
    - /root
    - /new-feature
  headers:
    - name: X-Flagger-Traffic-Target-To
      value: primary

In this setup, both HTTPScaledObject resources are configured to handle requests to my.domain.com with the paths /root and /new-feature. The primary object includes an additional header match condition: X-Flagger-Traffic-Target-To: primary.

The Kubernetes Gateway API's HTTPRoute defines matching precedence rules that could be beneficial to adopt here. According to the HTTPRoute documentation, the matching precedence is determined by the specificity of the match conditions:

  1. Exact path matches: Routes with exact path matches take the highest precedence.
  2. Longest prefix matches: Among prefix matches, the longest prefix has higher precedence.
  3. Header matches: Routes with more header matches have higher precedence.
  4. Query parameter matches: Routes with more query parameter matches have higher precedence.

Applying these principles to HTTPScaledObject would mean that the primary object, with its additional header match condition, should take precedence over the canary object for requests to my.domain.com with the specified paths.

Implementing such matching precedence in HTTPScaledObject would enable more granular traffic management, similar to systems like Knative. It would allow for the simultaneous management of canary, primary, and all past revisions within a single KEDA interceptor.

By clarifying and adopting these matching precedence rules, we can leverage header matches to achieve sophisticated routing and scaling scenarios, enhancing the flexibility and control over traffic management in KEDA deployments.

This approach would facilitate configurations like the one depicted below, enabling efficient management of multiple traffic versions and revisions:

Desired Configuration

@erich23
Copy link

erich23 commented Dec 21, 2024

@kahirokunn I'm not familiar with Gateway API's implementation of HTTPRoute, but based on what you're describing my PR is almost fulling these requirements.

Since it returns a longest prefix match that fulfills

  • Exact path matches: Routes with exact path matches take the highest precedence.
  • Longest prefix matches: Among prefix matches, the longest prefix has higher precedence.

But we can tweak the PR to rank HTTPScaledObjects by # of header matches and go with the most frequent one. This would also mean that, when none of headers match any HTTPScaledObjects which all have headers, it will just randomly choose one. I personally would prefer just failing unless you have HTTPScaledObject without any headers specified as its makes everything even more customizable / intentional. not familiar with what HTTPRoute does. i think all this sorting also comes with performance trade-offs so that's something we want to consider as well..

please feel free to review the current implementation 😊 I plan to chip away at this PR more tomorrow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: To Triage
Development

Successfully merging a pull request may close this issue.

4 participants