-
Notifications
You must be signed in to change notification settings - Fork 8
ADR-012: Unified Networking and HTTP/2 Support #20
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| # ADR: Unified Networking and HTTP/2 Support | ||
|
|
||
| ## Context | ||
|
|
||
| **Problem**: RHDH frontend startup time grows significantly with larger plugin counts (40+ plugins). The number of frontend assets (JavaScript bundles, CSS) increases with each plugin, and the Backstage New Frontend System (NFS) further increases asset count. With HTTP/1.1, browsers are limited to ~6 concurrent connections per domain, causing sequential loading and slower page render times. | ||
|
|
||
| HTTP/2 provides significant performance benefits through: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also clarify that we are talking about HTTP/2 on the public-facing connection segment (i.e, from client to proxy, as highlighted in the POC), not full HTTP/2 end-to-end requiring HTTP/2 also between the proxy/router and the application container (separate concern).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it is clear from the problem statement, the problem we are solving is "frontend startup time". Propose your addition if needed. |
||
| - **Multiplexing**: Multiple requests over a single TCP connection | ||
| - **Header compression**: Reduced overhead with HPACK | ||
| - **No head-of-line blocking**: Independent streams at HTTP level | ||
|
|
||
| **Platform constraints:** | ||
|
|
||
| **OpenShift:** | ||
| - HTTP/2 requires cluster-admin to enable at IngressController level (cluster-wide) | ||
| - Custom certificate required (default wildcard certificate blocks HTTP/2 due to connection coalescing) | ||
| - No per-route control | ||
|
|
||
| **Vanilla Kubernetes:** | ||
| - **Traefik**: HTTP/2 enabled by default with TLS | ||
| - **Caddy**: HTTP/2 enabled by default with TLS | ||
| - **NGINX Ingress Controller**: HTTP/2 disabled by default, requires cluster-admin to enable via ConfigMap (cluster-wide) | ||
| - **HAProxy**: Depends on configuration | ||
|
|
||
| On both platforms, RHDH users on shared clusters cannot enable HTTP/2 without cluster-admin cooperation. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I think this requires further clarification. Users can force-use HTTP/2 even without cluster-admin cooperation. With curl, you can skip the protocol negotiation (ALPN) and talk directly via HTTP/2, and this works, even on OCP without the cluster-admin annotation: The problem is that this doesn't work with browsers, which strictly require a successful ALPN negotiation during the TLS handshake to use HTTP/2 over HTTPS. So they are switching to HTTP/1.1. I think it is worth mentioning ALPN here as this is central to the browser behaviour. Claude review suggests that this distinction matters throughout the ADR:
Making this distinction explicit would help reviewers evaluate alternatives more clearly. For example, it immediately clarifies why "just enable HTTP/2 on the router" isn't as simple as it sounds, and why the sidecar needs to handle TLS.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "The problem is that this doesn't work with browsers" - exactly, so what do you propose? To add it as an alternative and reject because it does not work in browser? |
||
|
|
||
| ## Decision | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gazarenkov FYI (also shared during the standup call today), I raised the concerns here in the SOS call yesterday, and we agreed on the following next steps:
So I think we may no longer need this ADR for now, as we can "just" test and document the setup that was shared in the POC, so we can claim support for it, without having to update the install methods for now. I'll update the Epics under that Feature to reflect that.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think ADR did its work - making it clear that initial requirement about HTTP/2 by default does not seem to be realistic and so we can fallback to the simplest possible HTTP/2 solution to try to workaround the frontend performance problem. |
||
|
|
||
| Introduce unified networking configuration for both OpenShift (Route) and vanilla Kubernetes (Ingress), with HTTP/2 proxy support: | ||
|
|
||
| - **OpenShift**: Provide optional sidecar proxy that handles TLS termination and HTTP/2, allowing users to enable HTTP/2 without cluster-admin privileges | ||
| - **Vanilla Kubernetes**: Add Ingress configuration; sidecar provides HTTP/2 without cluster-admin dependency (similar benefit as OpenShift) | ||
|
|
||
| **Implementation approach**: | ||
|
|
||
| 1. **Add optional HTTP/2 proxy sidecar container** to Backstage deployment: | ||
| - Lightweight reverse proxy (e.g., NGINX, Envoy, Caddy) configured for HTTP/2 and TLS termination | ||
| - Proxies requests to Backstage on localhost:7007 | ||
| - Mounts TLS certificates from Secret | ||
| - Can add caching headers for static assets (e.g., `/api/scalprum/*/static/*.js`) — reduces repeat downloads on subsequent page loads — see [POC](https://github.com/karthikjeeyar/rhdh-http2) | ||
|
|
||
| 2. **Use passthrough termination** (when `http2Proxy` enabled): | ||
|
|
||
| **OpenShift Route:** | ||
| ```yaml | ||
| apiVersion: route.openshift.io/v1 | ||
| kind: Route | ||
| spec: | ||
| tls: | ||
| termination: passthrough | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There might be understated consequences to switching from edge to passthrough termination. Could be stated under the consequences section?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's looks like truth :) so what must we do with this information? |
||
| port: | ||
| targetPort: https | ||
| ``` | ||
|
|
||
| **Kubernetes Ingress** (if http2Proxy used): Configure Ingress controller for SSL passthrough to let the sidecar handle HTTP/2 termination. | ||
|
|
||
| 3. **Certificate provisioning options**: | ||
| - OpenShift service serving certificates (annotation-based, auto-rotated, but causes browser warnings — internal CA) | ||
| - cert-manager with Let's Encrypt (public CA, no browser warnings) | ||
| - User-provided certificates via Secret | ||
|
|
||
| 4. **Expose via `spec.network`**: | ||
|
|
||
| **OpenShift (Route):** | ||
| ```yaml | ||
| spec: | ||
| network: | ||
| route: | ||
| enabled: true | ||
| host: my-backstage.example.com | ||
| tls: | ||
| externalCertificateSecretName: my-tls | ||
| http2Proxy: | ||
| enabled: true # required for HTTP/2 on OpenShift without cluster-admin | ||
| ``` | ||
|
|
||
| **Vanilla Kubernetes (Ingress):** | ||
| ```yaml | ||
| spec: | ||
| network: | ||
| ingress: | ||
| enabled: true | ||
| host: my-backstage.example.com | ||
| className: <ingress-class> # optional | ||
| tls: | ||
| secretName: my-tls | ||
| http2Proxy: | ||
| enabled: true # enables HTTP/2 without cluster-admin (NGINX Ingress also requires cluster-admin to enable HTTP/2) | ||
| ``` | ||
|
|
||
| - `spec.network.route` — moved from `spec.application.route` (deprecated, supported with warning) | ||
| - `spec.network.ingress` — new, for vanilla Kubernetes deployments | ||
| - `spec.network.http2Proxy.enabled` — adds sidecar; switches Route to passthrough / configures Ingress for SSL passthrough | ||
| - Certificate reuse: when `http2Proxy.enabled`, proxy uses `route.tls` or `ingress.tls` certificate; defaults to service serving certificates if not specified | ||
|
|
||
| **Note:** The `http2Proxy` sidecar provides HTTP/2 support without cluster-admin cooperation on both OpenShift and Kubernetes (where NGINX Ingress Controller also requires cluster-admin to enable HTTP/2). | ||
|
|
||
| 5. **Default behavior considerations**: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be labeled explicitly as an "Open Question"? |
||
|
|
||
| Enabling `http2Proxy` by default is possible but has UX trade-offs: | ||
| - **Without user-provided certificate**: Uses OpenShift service serving certificates (internal CA) — HTTP/2 works but browsers show certificate warning | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, for clarity, HTTP/2 won't work until the user explicitly bypasses the certificate error in their browser, as ALPN negotiation happens during the TLS handshake itself. |
||
| - **With user-provided certificate**: No warnings, full HTTP/2 benefits | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should also clarify that the certificate should be trusted by browsers and match the RHDH hostname. Otherwise, they could provide a self-signed cert or one for the wrong hostname and would still get the same previous hard error in the browser, I guess. |
||
|
|
||
| **Options:** | ||
| - `http2Proxy.enabled: false` by default — users opt-in, no surprises | ||
| - `http2Proxy.enabled: true` by default — HTTP/2 out of the box, but certificate warning unless user provides `route.tls.externalCertificateSecretName` | ||
|
|
||
| If decided to default to `enabled: true`: provide clear documentation that users should supply their own certificate to avoid browser warnings. The performance benefit may justify the default, and the warning serves as a signal to configure properly. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The JIRA requirement is clear and this was my understanding as well:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, that's the point. |
||
|
|
||
| ## Alternatives Considered | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks there is another possible alternative: Gateway API (GA in OCP and available in vanilla K8s). It seems to rely on Envoy on OCP and h2 works automatically via ALPN without any cluster-admin annotation. However, I've quickly checked on a ROSA 4.21 cluster that while the CRDs are installed, no GatewayClass is deployed by default; so a cluster-admin would still need to set up the Gateway infrastructure first. On vanilla K8s, Gateway API is a standard with multiple implementations but also requires installation. Worth mentioning as a forward-looking alternative with these caveats..
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought there was an earlier revision of the ADR mentioning "Node.js native HTTP/2", or did I hallucinate? :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can mention but I do not think it is about proxy so I did not yhink about it |
||
|
|
||
| ### Alternative 1: Document cluster-admin HTTP/2 enablement | ||
| - **Approach**: Document how cluster-admins can enable HTTP/2 and require custom certificates per-route | ||
| - **Rejected because**: Users on shared clusters have no control; requires coordination with cluster-admin for each deployment; doesn't solve the core user autonomy problem | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is true, but might also be the cleanest solution when cluster-admin cooperation is available: no sidecar, no TLS shift, no extra container..
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might but does not fit initial requirements (no admin privileges :) ) |
||
|
|
||
| ### Alternative 2: Standalone HTTP/2 proxy Deployment | ||
| - **Approach**: Deploy NGINX/Envoy as separate Deployment + Service; Route/Ingress points to proxy, proxy routes to Backstage Service | ||
| - **Rejected because**: Single point of failure; extra network hop adds latency; doesn't scale automatically with Backstage replicas; more complex Service topology to manage | ||
|
|
||
| ## Consequences | ||
|
|
||
| ### Positive | ||
| ✅ Sidecar approach enables HTTP/2 without cluster-admin privileges (bypasses Ingress controller limitations) | ||
| ✅ Faster frontend page loads (multiplexing, reduced connections) | ||
| ✅ Works on any OpenShift/Kubernetes cluster | ||
| ✅ Lightweight proxy sidecars have minimal resource footprint | ||
| ✅ Works correctly with multi-replica deployments (sidecar per pod) | ||
|
|
||
| ### Negative | ||
| ❌ HTTP/2 not available by default — requires user to provide certificate to avoid browser warnings | ||
| ❌ Additional container per pod (slight resource overhead) | ||
| ❌ Users must manage TLS certificates (unless accepting browser warnings with service serving certs) | ||
| ❌ More complex deployment architecture to understand/debug | ||
| ❌ Proxy configuration must be maintained by operator | ||
|
|
||
| ## References | ||
|
|
||
| **Related issues:** | ||
| - [RHDHSUPP-320](https://redhat.atlassian.net/browse/RHDHSUPP-320) | ||
| - [RHDHPLAN-1598](https://redhat.atlassian.net/browse/RHDHPLAN-1598) | ||
| - [POC: rhdh-http2](https://github.com/karthikjeeyar/rhdh-http2) | ||
|
|
||
| **Documentation:** | ||
| - [FAQ: Does OpenShift Router support HTTP/2](https://docs.google.com/document/d/1S6E_sSmtxHknoVNBzW36q5Q-SHssbNFGWSWL3cwNEeo/edit?tab=t.0#heading=h.renxd4iwgobg) | ||
| - [Red Hat Blog: gRPC or HTTP/2 Ingress Connectivity in OpenShift](https://www.redhat.com/en/blog/grpc-or-http/2-ingress-connectivity-in-openshift) | ||
|
gazarenkov marked this conversation as resolved.
|
||
| - [OpenShift Docs: Configuring Routes](https://docs.redhat.com/en/documentation/openshift_container_platform/4.21/html/networking/configuring-routes) | ||
| - [NGINX Ingress Controller: ConfigMap HTTP/2 Setting](https://docs.nginx.com/nginx-ingress-controller/configuration/global-configuration/configmap-resource/#listeners) | ||
| - [HTTP/2 connection coalescing explanation](https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/) | ||
|
gazarenkov marked this conversation as resolved.
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this could be misleading as it reads like the limit is due to HTTP/1.1. The ~6 concurrent connection limit is a browser-imposed limit, not an HTTP/1.1 protocol constraint. Browsers apply this limit regardless of HTTP version, and the limit is set per domain. With HTTP/2, the connection limit is still there, except that browsers can typically send multiple requests to a same domain over one connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is misleading?
In a context of proposing HTTP/2 as a workaround we want to bypass the "limitation" of HTTP/1.1 and browser.
Explaining how exactly browser works is out of the scope of this ADR and it is clear IMO that we do not try to bypass browser limitation, we try to use potential of HTTP/2 protocol, no?