KEP-4006: Transition from SPDY to Websockets
KEP-4006: Transition from SPDY to WebSockets
- Release Signoff Checklist
- Summary
- Motivation
- Proposal
- Design Details
- Background: Streaming Protocol Basics
- Background:
RemoteCommandSubprotocol - Background: API Server and Kubelet
UpgradeAwareProxy - Proposal:
kubectlWebSocket Executor and Fallback Executor - Proposal: New
RemoteCommandSub-Protocol Version -v5.channel.k8s.io - Proposal: API Server RemoteCommand
StreamTranslatorProxy - Background:
PortForwardSubprotocol - Proposal: New
PortForwardTunneling Subprotocol Version -v2.portforward.k8s.io - Proposal: API Server PortForward – Stream Tunnel Proxy
- Proposal: Synthetic RBAC CREATE Authorization Check
- Proposal: Transitioning the API Server-to-Kubelet Connection
- Test Plan
- Graduation Criteria
- Upgrade / Downgrade Strategy
- Version Skew Strategy
- Production Readiness Review Questionnaire
- Implementation History
- Drawbacks
- Alternatives
- Infrastructure Needed (Optional)
Release Signoff Checklist
Items marked with (R) are required prior to targeting to a milestone / release.
- (R) Enhancement issue in release milestone, which links to KEP dir in kubernetes/enhancements (not the initial KEP PR)
- (R) KEP approvers have approved the KEP status as
implementable - (R) Design details are appropriately documented
- (R) Test plan is in place, giving consideration to SIG Architecture and SIG Testing input (including test refactors)
- e2e Tests for all Beta API Operations (endpoints)
- (R) Ensure GA e2e tests meet requirements for Conformance Tests
- (R) Minimum Two Week Window for GA e2e tests to prove flake free
- (R) Graduation criteria is in place
- (R) all GA Endpoints must be hit by Conformance Tests
- (R) Production readiness review completed
- (R) Production readiness review approved
- “Implementation History” section is up-to-date for milestone
- User-facing documentation has been created in kubernetes/website , for publication to kubernetes.io
- Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes
Summary
Some Kubernetes clients need to communicate with the API Server using a bi-directional
streaming protocol, instead of the standard HTTP request/response mechanism. A streaming
protocol provides the ability to read and write arbitrary data messages between the
client and server, instead of providing a single response to a client request.
For example, the commands kubectl exec, kubectl attach, and kubectl port-forward
both benefit from a bi-directional streaming protocol (kubectl cp is build on top
of kubectl exec primitives so it utilizes streaming as well). Currently,
the bi-directional streaming solution for these kubectl commands is SPDY/3.1. For
the communication leg between kubectl and the API Server, this enhancement transitions
the bi-directional streaming protocol to WebSockets from SPDY/3.1.
Motivation
The SPDY streaming protocol has been deprecated since 2015, and by now many proxies, gateways, and load-balancers do not support SPDY. Our effort to modernize the streaming protocol between Kubernetes clients and the API Server using WebSockets is necessary to enable the aforementioned intermediaries. WebSockets is a currently supported standardized protocol (https://www.rfc-editor.org/rfc/rfc6455 ) that guarantees compatibility and interoperability with the different components and programming languages. Finally, WebSockets is preferrable to HTTP/2.0 because the updated HTTP standard does not support streaming well. The decision to forego HTTP/2.0 is discussed at greater length in the Alternatives Section .
Goals
Transition the bi-directional streaming protocol from SPDY/3.1 to WebSockets for
kubectl exec,kubectl attach,kubectl cp, andkubectl port-forwardfor the communication leg betweenkubectland the API Server.Extend the WebSockets communication leg from the API Server to Kubelet. After this extension, WebSockets streaming will occur between
kubectland Kubelet (proxied through the API Server).
Non-Goals
We will not make any changes to current WebSocket based browser/javascript clients.
We will not transition the streaming protocol for the communication leg on the Node between the Kubelet and the container runtime. This leg will continue to stream the SPDY protocol.
Proposal
Currently, the bi-directional streaming protocols (either SPDY or WebSockets) are
initiated from clients, proxied by the API Server and Kubelet, and terminated at
the Container Runtime (e.g. containerd or CRI-O). This enhancement proposes to 1)
modify kubectl to request a WebSocket based streaming connection, and to 2) modify
the current API Server proxy to translate or tunnel the kubectl WebSockets data stream to
a SPDY upstream connection. In this way, the cluster components upstream from the
API Server will not initially need to be changed. We intend to extend the communication
path for WebSockets streaming from kubectl to Kubelet once the the initial leg
is proven to work (i.e. that it goes GA).
User Stories (Optional)
The functionality of this KEP will allow kubectl users to leverage L7 proxies and
gateways that support WebSockets but not SPDY. Usually, the setup for these intermediaries
is specific to a cloud provider or cluster operator. For example, to use the
Anthos Connect Gateway to communicate with (some) Google clusters, users must
run gcloud specific commands which update the kubeconfig file to point to the
gateway. Afterwards, users can run streaming commands such as kubectl exec ...,
and the commands will transparently use the now supported WebSockets protocol.
Notes/Constraints/Caveats (Optional)
N/A
Risks and Mitigations
- Risk: Security
A possible security vulnerability might occur when a potential upgraded connection is redirected to other API endpoints.
Mitigation: Upgraded connections are disallowed from redirecting.
Risk: Performance
When transitioning from the SPDY streaming protocol to WebSockets, there may be a performance degradation. In order to implement the WebSocket streaming functionality that SPDY already implements, it is necessary for additional headers to be prepended to the WebSocket data package.
- Mitigation: Performance testing to ensure the WebSockets implementation is not noticeably slower than the current SPDY streaming implementation.
Design Details
Current SPDY Streaming Architectural Diagram

Background: Streaming Protocol Basics
kubectl bi-directional streaming connections are created by upgrading an
initial HTTP/1.1 request. By adding two headers (Connection: Upgrade, Upgrade: SPDY/3.1),
the request can initiate the streaming upgrade. And when the response returns status
101 Switching Protocols signalling success, the connection can then be kept open
for subsequent streaming. An example of an upgraded HTTP Request/Response for kubectl exec
could look like:
HTTP Request
GET /api/v1/…/pods/nginx/exec?command=<CMD>... HTTP/1.1
Connection: Upgrade
Upgrade: SPDY/3.1
X-Stream-Protocol-Version: v4.channel.k8s.io
X-Stream-Protocol-Version: v3.channel.k8s.io
X-Stream-Protocol-Version: v2.channel.k8s.io
X-Stream-Protocol-Version: v1.channel.k8s.io
HTTP Response
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: SPDY/3.1
X-Stream-Protocol-Version: v4.channel.k8s.io
If the upgrade is successful, one of the requested subprotocol versions is chosen
and returned in the response. In this instance, the chosen version of the subprotocol
is: v4.channel.k8s.io.
Background: RemoteCommand Subprotocol

Once the connection is upgraded to a bi-directional streaming connection, the
client and server can exchange data messages. These messages are interpreted with
agreed upon standards which are called subprotocols. The three kubectl commands
(exec, attach, and cp) communicate using the RemoteCommand subprotocol. Basically,
this subprotocol provides command line functionality from the client to a running
container in the cluster. By multiplexing stdin, stdout, stderr, and tty
resizing over a streaming connection, this subprotocol supports clients executing
and interacting with commands executed on a container in the cluster. An example of
kubectl exec running the date command on an nginx pod/container is:
$ kubectl exec nginx -- date
Tue May 16 03:34:04 PM PDT 2023
The RemoteCommand Subprotocol has iterated through four different versions, where the
transmitted data has changed. The second version of the subprotocol (v2.channel.k8s.io)
included stdin, stdout, and stderr, and the third version (v3.channel.k8s.io)
added support for terminal resizing with the tty stream. The fourth and most
current version, v4.channel.k8s.io added a structured error stream, which includes
the process exit code from the container. All of these streams are multiplexed over
a single connnection by prepending a stream identififer byte to the data message.
For example, a stdout data message sent over the connection will have the stdout
file descriptor (1) prepended to the data message.
Background: API Server and Kubelet UpgradeAwareProxy
In order to route the data streamed between the client and the container, both the
API Server and Kubelet must proxy these data messages. Both the API Server and the
Kubelet provide this functionality with the UpgradeAwareProxy, which is a reverse
proxy that knows how to deal with the connection upgrade handshake.
Proposal: kubectl WebSocket Executor and Fallback Executor
This enhancement proposes adding a WebSocketExecutor to kubectl, implementing
the WebSocket client using a new subprotocol version (v5.channel.k8s.io).
Additionally, we propose creating a FallbackExecutor to address client/server version
skew. The FallbackExecutor first attempts to upgrade the connection with the
WebSocketExecutor, then falls back to the legacy SPDYExecutor, if the upgrade is
unsuccessful. Note that this mechanism can require two request/response trips instead
of one. While the fallback mechanism may require an extra request/response if the
initial upgrade is not successful, we believe this possible extra roundtrip is
justified for the following reasons:
- The upgrade handshake is implemented in low-level SPDY and WebSocket libraries, and it is not easily exposed by these libraries. If it is even possible to modify the upgrade handshake, the added complexity would not be worth the effort.
- The streaming is already IO heavy, so another roundtrip will not substantially affect the perceived performance.
- As releases increment, the probablity of a WebSocket enabled
kubectlcommunicating with an older non-WebSocket enabled API Server decreases.
Proposal: New RemoteCommand Sub-Protocol Version - v5.channel.k8s.io
The latest RemoteCommand version does not address an important protocol feature–a
stream CLOSE signal. In order to communicate to another endpoint that the current
stream of sending data is complete, a CLOSE signal is necessary. This problem
currently arises when sending data over the STDIN stream, and it is more fully described
in the following issue: exec over web sockets protocol is flawed
.
A new RemoteCommand version (v5.channel.k8s.io) adds this CLOSE signal.
Proposal: API Server RemoteCommand StreamTranslatorProxy

Currently, the API Server role within client/container streaming is to proxy the
data stream using the UpgradeAwareProxy. This enhancement proposes to modify the
SPDY data stream between kubectl and the API Server by conditionally adding a
StreamTranslatorProxy at the API Server. If the request is for a WebSocket upgrade
with the protocol request for RemoteCommand: v5.channel.k8s.io , the handler will
delegate to the StreamTranslatorProxy instead of the UpgradeAwareProxy. This
translation proxy terminates the WebSocket connection, and it de-multiplexes the
various streams in order to pass the data on to a SPDY connection, which continues
upstream (to Kubelet and eventually the container runtime).
Background: PortForward Subprotocol
The following steps articulate the difference betweeen a request for an upgraded streaming connection, and the following subrequests which are made over the upgraded connection.
kubectl port-forwardmakes a request to the server upgrading to a SPDY streaming connection.- An arbitrary number of subsequent (and possibly concurrent) subrequests can be made over
this previously established connection. Example:
curl http://localhost:8080/index.html. - Each of these subrequests creates two streams over the connection (a uni-directional error stream and a bi-directional data stream) between the client and the container runtime.
- The resources associated with the subrequest are reclaimed once the subrequest is completed.
The PortForward subprotocol is used to implement kubectl port-forward, and it differs
from the RemoteCommand subprotocol in how the multiple streams within the single upgraded
connection are created. The RemoteCommand subprotocol statically creates streams (e.g
STDIN, STDOUT, etc.) when the connection is created. But the PortForward subprotocol
dynamically creates two streams (a bi-directional data stream and a unidirectional error
stream) for each subsequent portforward subrequest. These streams are removed when the subrequest
is complete. The connection, however, continues to exist until it is manually stopped
(with a signal). For example, the following portforward command creates the upgraded
connection, but without any streams, listening on the local port 8080:
$ kubectl port-forward nginx 8080:80
Once the upgraded, streaming connection is created, portforward subrequests are handled by dynamically creating a data and error stream in another goroutine, forwarding the data to the remote port on the target. In this example, the HTTP subrequest is forwarded over the data stream from the local port 8080 to the nginx container listening on port 80:
$ curl http://localhost:8080/index.html
The nginx HTTP response is returned over the same data stream. Once the subrequest is complete, both streams are closed and removed.
Proposal: New PortForward Tunneling Subprotocol Version - v2.portforward.k8s.io
We propose a new PortForward version v2.portforward.k8s.io, which identifies upgrade
requests which require tunneling. PortForward tunneling transparently encodes and
decodes SPDY framed messages into (and out of) the payload of a WebSocket message.
This tunneling is implemented on the client by substituting a WebSocket connection
(which implements the net.Conn interface) into the constructor of a SPDY client.
The SPDY client reads and writes its messages into and out of this connection. These
SPDY messages are then encoded or decoded into and out of the WebSocket message payload.
Proposal: API Server PortForward – Stream Tunnel Proxy
At the API Server, tunneling is implemented by sending different parameters into the
UpgradeAwareProxy. If the new subprotocol version v2.portforward.k8s.io is requested,
the UpgradeAwareProxy is called with a new tunnelingResponseWriter. This ResponseWriter
contains a tunneling WebSocket connection, which is returned when the connection is
hijacked. And this tunneling WebSocket connection encodes and decodes SPDY messages
as the downstream connection within the dual concurrent io.Copy proxying goroutines.
The upstream connection is the same SPDY connection to the container (through the
Kubelet and CRI).
Proposal: Synthetic RBAC CREATE Authorization Check
The transition to WebSockets requires changing the initial streaming upgrade request
from a POST to a GET, as the WebSocket protocol specification
(RFC 6455
) mandates that the
opening handshake must be an HTTP GET request. This has an unintended security
consequence: RBAC policies that only grant the get verb, such as a typical read-only
“viewer” role, now unexpectedly allow users to run kubectl exec, attach, and port-forward.
To close this privilege escalation vector and restore the principle of least privilege,
this proposal introduces a secondary, synthetic authorization check performed within the
API Server. When a WebSocket upgrade request is received for the pods/exec, pods/attach,
or pods/portforward subresources, the handler will perform an additional check to ensure
the user also has the create verb permission for that specific subresource. This new
authorization check will be controlled by a feature gate,
AuthorizePodWebsocketUpgradeCreatePermission, which will be enabled by default to
ensure clusters are secure while allowing operators to temporarily disable it as
they update their RBAC policies.
Proposal: Transitioning the API Server-to-Kubelet Connection
The long-term goal of this KEP is to replace SPDY with WebSockets for the entire
communication path from the client to the Kubelet. The second phase of this
transition, extending WebSocket support to the Kubelet for exec, attach, cp,
and port-forward, is controlled by the ExtendWebSocketsToKubelet feature gate.
This feature gate depends on the NodeDeclaredFeatures feature gate.
When both feature gates are enabled, the Kubelet advertises its streaming protocol
capabilities by adding ExtendWebSocketsToKubelet to the declaredFeatures field in the
Node.Status object.
The API server’s handlers for exec, attach, and port-forward are modified to check for
this feature on the target node.
- If the
ExtendWebSocketsToKubeletgate is enabled and the node’sdeclaredFeaturesfield containsExtendWebSocketsToKubelet, the API server acts as a simple pass-through proxy, forwarding the client WebSocket connection directly to the Kubelet without any protocol translation or tunneling. This is crucial for distributing the load of translation/tunneling from the API server to the Node, thereby offloading the proxying work from the API server. - If the Kubelet does not advertise WebSocket support (or either feature gate is disabled),
the API server falls back to the original behavior: it accepts the client’s WebSocket
connection and either translates it (
exec/attach) or tunnels it (port-forward) to an upstream SPDY connection to the Kubelet.
On the Kubelet side, the HTTP server is updated to handle incoming WebSocket upgrade requests.
- For
execandattach, it uses aStreamTranslatorProxyto terminate the WebSocket connection and translate thev5.channel.k8s.iosubprotocol into a SPDY stream for the final communication leg to the container runtime. - For
port-forward, it uses aStreamTunnelingProxyto terminate the WebSocket connection, decode the SPDY frames from the WebSocket message payloads, and forward them over a standard SPDY connection to the container runtime.
In both cases, legacy SPDY requests from older API servers are still handled correctly.
Test Plan
[X] I/we understand the owners of the involved components may require updates to existing tests to make this code solid enough prior to committing the changes necessary to implement this enhancement.
Prerequisite testing updates
Unit tests
The following packages (including current test coverage) will be modified to implement this SDPY to WebSockets migration.
k8s.io/kubernetes/staging/src/k8s.io/client-go/tools/portforward:2024-05-27-86.3%k8s.io/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand:2023-05-31-57.3%k8s.io/kubernetes/staging/src/k8s.io/client-go/transport:2023-05-31-57.7%k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream:2023-05-31-76.7%k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/proxy:2023-05-31-59.1%k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy:2024-05-27-81.5%k8s.io/kubernetes/staging/src/k8s.io/kubectl/pkg/cmd/attach:2023-06-05-43.4%k8s.io/kubernetes/staging/src/k8s.io/kubectl/pkg/cmd/cp:2023-06-05-66.3%k8s.io/kubernetes/staging/src/k8s.io/kubectl/pkg/cmd/exec:2023-06-05-70.0%k8s.io/kubernetes/staging/src/k8s.io/kubectl/pkg/cmd/portforward:2024-05-27-74.7%
An important set of tests for this migration will be loopback tests, which exercise the WebSocket client and the StreamTranslator proxy. These tests create two test servers: a proxy server handling the stream translation, and a fake SPDY server which sends received data from one stream (e.g. stdin) back down another stream (e.g. stdout). These tests send random data from the WebSocket client to the StreamTranslator proxy, which then sends the data to the test SPDY server.
WebSocket client <-> Proxy Server (StreamTranslator) <-> SPDY Server
Once the data is received back at the WebSocket client on the separate stream, it is compared to the data that was sent to ensure the data is the same. These loopback tests have been implemented in a proof-of-concept PR, validating the various streams sent over the WebSocket connection by the client through the StreamTranslator proxy.
Integration tests
PortForward: https://github.com/kubernetes/kubernetes/blob/master/test/integration/apiserver/portforward/portforward_test.go
e2e tests
While there are already numerous current e2e tests for kubectl exec, cp, attach, and port-forward,
we will enhance these tests with the permutations of the feature flags for kubectl
and the API Server. We will add e2e test coverage for flags and arguments that are
not already covered for these commands.
Graduation Criteria
Alpha
v1.29 RemoteCommand Subprotocol (exec, cp, and attach)
- Implement the alpha version of the
RemoteCommandsubprotocol, and surface the newkubectl exec,kubectl cp, andkubectl attachbehind akubectlenvironment variable which is OFF by default. WebSocketExecutorandFallbackExecutorcompleted and functional behind thekubectlenvironment variable KUBECTL_REMOTE_COMMAND_WEBSOCKETS which is OFF by default.StreamTranslatorProxysuccessfully integrated into theUpgradeAwareProxybehind an API Server feature flag which is off by default.- Initial
exec,cp, andattachunit tests completed and enabled. - Existing
exec,cp, andattachintegration tests continue to work. - Existing
exec,cp, andattache2e tests continue to work.
v1.30 PortForward Subprotocol (port-forward)
- Implement the alpha version of the
PortForwardsubprotocol, and surface the newkubectl port-forwardbehind thekubectlenvironment variable KUBECTL_PORT_FORWARD_WEBSOCKETS which is OFF by default. - FallbackDialer is completed and functional behind the
kubectlenvironment variable KUBECTL_PORT_FORWARD which is OFF by default. The FallbackDialer executes legacy SPDYport-forwardif the server does not support the new WebSockets functionality. - PortForward
StreamTunnelingProxysuccessfully added and integrated, living behind the API Server feature flagPortForwardWebsocketswhich is OFF by default.
Beta
v1.30 RemoteCommand Subprotocol (exec, cp, and attach)
kubectlenvironment variable KUBECTL_REMOTE_COMMAND_WEBSOCKETS is ON by default.- API Server feature flag
TranslateStreamCloseWebsocketRequestsis ON by default. - Additional
exec,cp, andattachunit tests completed and enabled. - Additional
exec,cp, andattachintegration tests completed and enabled. - Additional
exec,cp, andattache2e tests completed and enabled.
v1.31 PortForward Subprotocol (port-forward)
kubectl port-forwardis behind thekubectlenvironment variable KUBECTL_PORT_FORWARD_WEBSOCKETS which is ON by default.- FallbackDialer is completed and functional behind the
kubectlenvironment variable KUBECTL_PORT_FORWARD which is ON by default. The FallbackDialer executes legacy SPDYport-forwardif the server does not support the new WebSockets functionality. - PortForward
StreamTunnelingProxysuccessfully added and integrated, living behind the API Server feature flagPortForwardWebsocketswhich is ON by default. - Additional
port-forwardunit tests completed and enabled. - Additional
port-forwardintegration tests completed and enabled. - Additional
port-forwarde2e tests completed and enabled.
v1.35 Synthetic RBAC CREATE Authorization Check
- Force synthetic RBAC
CREATEauthorization check for WebSocket upgrades on the following subresources:pods/exec,pods/attach, andpods/portforward. This additional check will be gated by the API ServerAuthorizePodWebsocketUpgradeCreatePermissionfeature flag, which defaults to TRUE.
v1.36 Extend WebSockets to Kubelet
ExtendWebSocketsToKubeletfeature gate is ON by default (Beta), controlling the extension for bothRemoteCommand(exec/attach) andPortForward.- API Server uses the Kubelet’s
declaredFeaturesfield in theNode.Statusto determine if it can proxy WebSocket requests directly to the Kubelet for all streaming commands. - Kubelet handles incoming WebSocket requests for
exec/attach(translation) andport-forward(tunneling), converting them to SPDY for the container runtime. - Unit and integration tests for the new API Server and Kubelet logic are completed and enabled.
GA
kubectlenvironment variables and API Server feature gates are locked to on by default.- Deprecate
kubectlenvironment variables and API Server feature gates for future removal. - Add WebSocket support for HTTPS proxies.
- Conformance tests for
RemoteCommandcompleted and enabled. - Conformance tests for
RemoteCommandhave been stable and non-flaky for two weeks. - Conformance tests for
PortForwardcompleted and enabled. - Conformance tests for
PortForwardhave been stable and non-flaky for two weeks. - Achieve stable (GA) status for the extension of the WebSockets communication leg from the API Server to Kubelet.
Upgrade / Downgrade Strategy
Upgrade requires both the kubectl environment variable and API Server feature flags to be enabled. Downgrade requires one of the kubectl environment variable or API Server feature flags to be disabled.
Version Skew Strategy
This feature needs to take into account the following version skew scenarios:
RemoteCommand Subprotocol
- A newer WebSockets enabled
kubectlcommunicating with an older API Server that does not support the newerStreamTranslatorproxy.
In this case, the initial upgrade request for WebSockets/RemoteCommand will
fail, because the WebSockets upgrade request v5.channel.k8s.io will be proxied
to the current container runtime which only supports up to version v4.channel.k8s.io.
The FallbackExecutor will follow up with a subsequent legacy upgrade request for
SDPY/RemoteCommand. The streaming functionality in this case will work exactly
as it has for the last several years.
- A legacy non-WebSockets enabled
kubectlcommunicating with a newer API Server that supports the newerStreamTranslatorproxy.
The legacy kubectl will successfully request an upgrade for SPDY/RemoteCommand - V4,
just as it has for the last several years.
PortForward Subprotocol
- A newer WebSockets enabled
kubectlcommunicating with an older API Server that does not support the newer PortForwardStreamTunnelingproxy.
In this case, the initial upgrade request for PortForward WebSockets will
fail, because the WebSockets upgrade request v2.portforward.k8s.io will be proxied
to the current container runtime which only supports version v1.portforward.k8s.io.
Upon receiving this upgrade failure, the portforward client will fallback to the
legacy SPDY v1.portforward.k8s.io. In this fallback case, the PortForward streaming
functionality in this case will work exactly as it has for the last several years.
- A legacy non-WebSockets enabled
kubectlcommunicating with a newer API Server that supports the newer PortForwardStreamTunnelingproxy.
The kubectl port-forward will successfully request an upgrade for legacy
SPDY/PortForward - V1, just as it has for the last several years.
Version Skew within the Control Plane and Nodes
The phased rollout of this feature creates three distinct API server behaviors and three Kubelet behaviors. The system is designed to handle version skew gracefully across all combinations.
API Server Versions:
- Legacy SPDY-Only (prior to k8s 1.29): Does not support WebSocket streaming.
- Phase 1 (WS @ API Server) (k8s 1.29+, enabled by default since 1.30 for RemoteCommand and 1.31 for PortForward): Supports WebSocket streaming from the client but always translates/tunnels this to an upstream SPDY connection for the Kubelet.
- Phase 2 (WS @ Kubelet) (k8s 1.36+, beta): Supports WebSocket streaming and can proxy it directly to a capable Kubelet, falling back to Phase 1 behavior if the Kubelet does not support WebSockets.
Kubelet Versions:
- SPDY-Only (prior to k8s 1.36): The legacy Kubelet which only accepts SPDY streams.
- WS-Capable during development/rollout (k8s 1.36+, beta): The newer Kubelet which accepts both SPDY and WebSocket streams, advertising its capabilities during the period of time there are supported Kubelet versions which do not have the feature or which can disable the feature.
- WS-Capable after all supported versions have this feature (stable): The newer Kubelet which accepts both SPDY and WebSocket streams, no longer needing to advertise its capabilities because all supported kubelet versions have the feature locked enabled.
Interaction Scenarios:
Any
kubectlvs.Legacy SPDY-Only API Server:kubectl’s initial WebSocket upgrade request is rejected, and it automatically falls back to using SPDY. Streaming works via legacy SPDY.Phase 1 API Servervs.SPDY-OnlyorWS-Capable Kubelet: The API server accepts the client’s WebSocket stream, performs the translation/tunneling itself, and sends a SPDY stream to the Kubelet. Since both Kubelet versions accept SPDY, streaming works.Phase 2 API Servervs.SPDY-Only Kubelet: The API server checks the Kubelet’sdeclaredFeaturesfield, does not findExtendWebSocketsToKubelet, and gracefully falls back to the Phase 1 behavior (translating/tunneling locally). Streaming works via API server fallback.Phase 2 API Servervs.WS-Capable Kubelet: The API server checks thedeclaredFeaturesfield, findsExtendWebSocketsToKubelet, and acts as a pass-through proxy for the WebSocket stream directly to the Kubelet. Streaming works via the optimal end-to-end WebSocket path.
This design ensures that clusters can be upgraded one component at a time without breaking streaming functionality.
Production Readiness Review Questionnaire
Feature Enablement and Rollback
How can this feature be enabled / disabled in a live cluster?
- Feature gates (also fill in values in
kep.yaml)
The feature is controlled by a combination of client-side environment variables and server-side feature gates.
Client-side (kubectl):
KUBECTL_REMOTE_COMMAND_WEBSOCKETS=true: Enables WebSocket usage forexec,attach, andcpcommands.KUBECTL_PORT_FORWARD_WEBSOCKETS=true: Enables WebSocket usage for theport-forwardcommand.
Server-side (Feature Gates):
TranslateStreamCloseWebsocketRequests: (Component:kube-apiserver) Enables the API server to handle the WebSocket-basedv5.channel.k8s.iosubprotocol for remote commands.PortForwardWebsockets: (Component:kube-apiserver) Enables the API server to handle WebSocket-based tunneling for port forwarding.AuthorizePodWebsocketUpgradeCreatePermission: (Component:kube-apiserver) Enforces a syntheticCREATEauthorization check on WebSocket upgrade requests to maintain least privilege.ExtendWebSocketsToKubelet: (Components:kube-apiserver,kubelet) Enables the end-to-end WebSocket communication path to the Kubelet, allowing the API server to proxy streams directly instead of translating/tunneling them. (Note: Depends on theNodeDeclaredFeaturesgate).
Does enabling the feature change any default behavior?
For each of the two streaming subprotocols: RemoteCommand (such as /exec and
/attach APIs) and PortForward (for /portforward), enabling the respective
feature gate on the API Server will allow the streaming mechanism to be WebSockets
instead of SPDY for communication between kubectl and the API Server. Additionally,
the kubectl client must also have the KUBECTL_REMOTE_COMMAND_WEBSOCKETS environment
variable set to ON for exec, cp, and attach commands. While the
KUBECTL_PORT_FORWARD_WEBSOCKETS environment variable must be set to ON for
port-forward command. These modifications, however, will be transparent to the
user unless the kubectl/API Server communication is communicating through an
intermediary such as a proxy (which is the whole reason for the feature). The API Server
feature flag AuthorizePodWebsocketUpgradeCreatePermission forces a synthetic, secondary
RBAC check for the CREATE verb permission on WebSocket upgrade requests. When this
feature gate is TRUE, the additional permission check will apply to endpoints
pods/exec, pods/attach, and pods/portforward. Enabling the ExtendWebSocketsToKubelet
feature gate on the API Server will change the default behavior for exec, attach, and port-forward
by attempting to proxy WebSocket requests directly to Kubelets that advertise
support for it, thus offloading protocol translation and tunneling from the API Server to the Kubelet.
Can the feature be disabled once it has been enabled (i.e. can we roll back the enablement)?
The features can be disabled for a single user by setting the kubectl environment
variable associated with the feature to OFF. Or the features can be turned off
for all kubectl users communicating with a cluster by turning off the feature flags
for the API Server. A cluster operator can temporarily disable the more stringent permissions for
subresources pods/exec, pods/attach, and pods/portforward by setting the
AuthorizePodWebsocketUpgradeCreatePermission feature flag to FALSE. Disabling the
ExtendWebSocketsToKubelet feature gate in the API Server causes a reversion
to the previous behavior where translating and tunneling occur in the API Server,
even if the Kubelet supports this functionality. Additionally, disabling the
ExtentWebSocketsToKublet in the Kubelet ensures new code implementing the
translation and tunneling in the Kubelet does not run.
What happens if we reenable the feature if it was previously rolled back?
The feature does not depend on state, and can be disabled/enabled at will.
Are there any tests for feature enablement/disablement?
- There will be unit tests for the
kubectlenvironment variable KUBECTL_REMOTE_COMMAND_WEBSOCKETS. - There are unit tests for the
kubectlenvironment variable KUBECTL_PORT_FORWARD_WEBSOCKETS. - There will be unit tests in the API Server which exercise the feature gate within
the
UpgradeAwareProxy, which conditionally delegates to theStreamTranslatorproxy (depending on the feature gate and the upgrade parameters). - There are unit tests in the API Server which exercise the feature gate within
the
UpgradeAwareProxy, which conditionally delegates to theStreamTunnelingproxy for the PortForward subprotocol. - There will be unit tests in the API Server to verify the feature gate
forcing more stringent RBAC checks for
pods/exec,pods/attach, andpods/portforward. - There will be unit tests in the API Server and Kubelet to verify the feature gate
ExtendWebSocketsToKubelet.
Rollout, Upgrade and Rollback Planning
How can a rollout or rollback fail? Can it impact already running workloads?
This feature does not impact already running workloads, as it only affects the
initiation of new kubectl exec/attach/port-forward commands.
The design is resilient to most rollout and rollback failures due to its discovery and fallback mechanisms. However, failures can still occur:
Partial API Server Rollout: During a rolling update, a
kubectlclient may connect to an API server that does not yet support WebSockets. In this case, the client’s connection upgrade will be rejected, and it will automatically fall back to using the legacy SPDY protocol for that request. This is seamless to the user.Partial Node Rollout (Kubelet Extension): When
ExtendWebSocketsToKubeletis enabled, a rolling update of nodes can lead to a mix of capable and incapable Kubelets. This is handled gracefully by the API server, which checks theNode.Status.declaredFeaturesfor each request. It will use the WebSocket-to-Kubelet path for updated nodes and revert to the SPDY translation path for older nodes.Misconfiguration: A rollout could fail if the
ExtendWebSocketsToKubeletgate is enabled on a Kubelet, but a network policy between the API server and the Kubelet blocks the WebSocket protocol’s port and upgrade headers. This would cause streaming commands to those specific nodes to fail. This would also fail in the case of legacy SPDY streaming, so it would not be a change.Implementation Bugs: As with any new feature, a bug in the WebSocket implementation in the API server or Kubelet could cause failures for streaming commands that use the new code paths. A rollback of the specific feature gate (
ExtendWebSocketsToKubelet) would be the primary mitigation strategy.
What specific metrics should inform a rollback?
The most straightforward signal indicating a problem for the feature is a high rate
of failures for kubectl exec, cp, attach, and port-forward commands.
For the Kubelet extension specifically, a key rollback signal would be a
significant increase in streaming connection failures or latency that is
isolated to nodes that have the ExtendWebSocketsToKubelet feature enabled,
while nodes that still use SPDY connections from the API server continue to
function normally. This can be monitored via the metrics proposed in the
Service Level Indicators
section.
Were upgrade and rollback tested? Was the upgrade->downgrade->upgrade path tested?
This feature is stateless (it does not persist any new API objects or fields), so a formal stateful upgrade->downgrade->upgrade test path is not applicable.
However, the resilience of the system during rolling upgrades and rollbacks has been validated through comprehensive version skew testing. These tests cover all permutations of client, API server, and Kubelet versions to ensure that the fallback and feature-discovery mechanisms work as intended. For example:
- A newer
kubectlclient correctly falls back to SPDY when communicating with an older API server that does not support WebSockets. - A newer API server correctly reverts to translating/tunneling SPDY when communicating with an older Kubelet that does not support the WebSocket extension.
This ensures that streaming functionality is maintained across the cluster during the entire upgrade or rollback process.
Is the rollout accompanied by any deprecations and/or removals of features, APIs, fields of API types, flags, etc.?
No.
Monitoring Requirements
How can an operator determine if the feature is in use by workloads?
An operator can detect if the WebSocket functionality is enabled by checking either the
num_ws_remote_command_v5_total[success]metric forRemoteCommandor thenum_ws_port_forward_v2_total[success]metric forPortForward.To determine if the Kubelet extension is active for a specific node, an operator can inspect the
status.declaredFeaturesfield of the Node object for the presence ofExtendWebSocketsToKubelet. Additionally, proposed new metrics will differentiate between API server connections that are proxied directly to the Kubelet versus those that are translated to SPDY.
How can someone using this feature know that it is working for their instance?
To confirm this feature is working for a given instance, both its configuration and runtime behavior can be observed:
Configuration Verification:
- API Server Feature Gates: Inspect the API server’s startup flags (
--feature-gates) to confirm that relevant feature gates (e.g.,TranslateStreamCloseWebsocketRequests,PortForwardWebsockets,ExtendWebSocketsToKubelet) are enabled. - Kubelet Feature Gates: Confirm the
kubeletis started with--feature-gates=ExtendWebSocketsToKubelet=true(or withoutExtendWebSocketsToKubelet=falseif it is enabled by default). - Node Declared Features: For the Kubelet extension, verify that individual nodes are advertising support:
kubectl get node <node-name> -o jsonpath='{.status.declaredFeatures}'should includeExtendWebSocketsToKubelet.
- API Server Feature Gates: Inspect the API server’s startup flags (
Runtime Verification:
- Client-side (
kubectl -v=7logs):- For RemoteCommand (
exec,attach,cp), look for...websocket.go:137] The subprotocol is v5.channel.k8s.io. - For PortForward, look for
...websocket-dialer.go:91] negotiated protocol: v2.portforward.k8s.io.
- For RemoteCommand (
- Server-side (API Server and Kubelet logs):
- API Server logs: When
ExtendWebSocketsToKubeletis active, API server logs will indicate it is proxying the WebSocket connection directly to the Kubelet, rather than translating it. - Kubelet logs: Kubelet logs on the target node will show it receiving and processing a WebSocket connection for the streaming request.
- API Server logs: When
- Client-side (
What are the reasonable SLOs (Service Level Objectives) for the enhancement?
At a high level, we are aiming for an SLO which is the same as the current SPDY
streaming SLO. Turning on WebSockets streaming between the kubectl client and
the API Server should not noticeably degrade streaming performance compared to
the current SPDY implementation. But since we do not have numbers for our current
SPDY streaming implementation, these SLO’s are necessarily educated estimates.
99.9% of initial HTTP connections eventually succeed in upgrading to a streaming connection (i.e.
101 Switching Protocolsas the HTTP response).99.9% of streaming connections complete without writing to the error channel. Each of the streaming subprotocols,
RemoteCommandandPortForward, create a separate error channel to communicate problems while streaming. But a normally successful streaming command should NOT need to use these channels.
What are the SLIs (Service Level Indicators) an operator can use to determine the health of the service?
- Metrics
Metric name:
num_ws_remote_command_v5_totalwithtypedimension containing enum valuessuccessandfailure. Counts the total number of times the API Server witnessed a WebSocket/V5 RemoteCommand connection upgrade attempt (eithersuccessorfailure).Components exposing the metric: API Server
Metric name:
num_ws_port_forward_v2_totalwithtypedimension containing enum valuessuccessandfailure. Counts the total number of times the API Server witnessed a WebSocket/V2 PortForward connection upgrade attempt (eithersuccessorfailure).Components exposing the metric: API Server
Metric name:
apiserver_streaming_active_connections(Gauge)- Help: “Gauge of active streaming connections, to distinguish between connections proxied directly to the Kubelet vs. those that require protocol translation or tunneling at the API server.”
- Dimensions:
subresource(exec,attach,portforward),proxy_type(proxied,translated_or_tunneled) - Component exposing the metric:
kube-apiserver
Metric name:
kubelet_streaming_websocket_requests_total(Counter)- Help: “Counter of WebSocket streaming upgrade requests handled by the Kubelet.”
- Dimensions:
subresource(exec,attach,portforward),result(success,failure) - Component exposing the metric:
kubelet
Are there any missing metrics that would be useful to have to improve observability of this feature?
Yes. To properly observe the Kubelet extension, the following metrics are needed:
- An API server metric (e.g.,
apiserver_streaming_connections) is needed to differentiate between WebSocket connections that are proxied directly to the Kubelet versus those that are translated to SPDY. This is critical for understanding whether the feature is active and for debugging rollout. - A Kubelet metric (e.g.,
kubelet_streaming_websocket_requests_total) is needed to monitor the rate and success of incoming WebSocket requests directly on the node. This provides visibility into the Kubelet’s performance as a streaming server, which is currently not available.
Dependencies
- Gorilla/WebSockets library: The new WebSockets streaming functionality imports the Gorilla/WebSockets library.
- NodeDeclaredFeatures (KEP-5328
): The
ExtendWebSocketsToKubeletfeature gate depends on theNodeDeclaredFeaturesfeature gate.
Does this feature depend on any specific services running in the cluster?
No.
Scalability
Will enabling / using this feature result in any new API calls?
The proposed design envisions a fallback mechanism when a new kubectl communicates
with an older API Server. The client will initially request an upgrade to WebSockets,
but it will fallback to the legacy SPDY if it is not supported. In this version
skew scenario where the client implements the new functionality but the server does
not, there is an extra request/response. Since bi-directional streaming already is
very IO intensive, this extra request/response should not be significant. Additionally,
as releases are incremented, the probability of the version skew will continually
decrease.
The newer WebSockets streaming mechanism will also include heartbeat messages, which will require network IO. But this heartbeat mechanism should contain no more messages than the current SPDY heartbeat mechanism.
Will enabling / using this feature result in introducing new API types?
No.
Will enabling / using this feature result in any new calls to the cloud provider?
No.
Will enabling / using this feature result in increasing size or count of the existing API objects?
No.
Will enabling / using this feature result in increasing time taken by any operations covered by existing SLIs/SLOs?
No.
Will enabling / using this feature result in non-negligible increase of resource usage (CPU, RAM, disk, IO, …) in any components?
Enabling the WebSocket transition to the API server does not in itself increase resource usage.
However, enabling the ExtendWebSocketsToKubelet feature will shift the
resource load (CPU and memory) of protocol translation/tunneling from the
central API servers to the individual Kubelets on the nodes. This is the explicit
goal of this part of the enhancement. While it does increase resource usage on
each Kubelet handling a WebSocket stream, it is part of a deliberate strategy
to distribute the load and improve the overall scalability of the control plane.
Can enabling / using this feature result in resource exhaustion of some node resources (PIDs, sockets, inodes, etc.)?
kubectl exec, cp, attach, and port-forward commands spawn container runtime processes,
so there is the danger of node resource exhaustion. This feature, however, does not
change the current legacy mechanism for how these container runtime processes execute or
communicate, except for the communication leg between kubectl and the API Server.
There should be no more risk of node resource exhaustion than already exists.
Troubleshooting
How does this feature react if the API server and/or etcd is unavailable?
- This feature fails when the API server is unavailable. The WebSocket streaming is proxied through the API server. If the API server is unavailable, streaming functionality does not work (this applies to the legacy SPDY streaming as well, so there is no detectable difference in this scenario).
What are other known failure modes?
Failure Mode: Proxy or Gateway that does not support SPDY or WebSockets. SPDY had been deprecated for since 2015, and not all proxies support WebSockets. If the intermediary does not support either streaming protocol it will not be possible to run the
kubectlstreaming commands.- Detection: The
kubectlstreaming command will return a connection error. The error returned to the client will be from the proxy or gateway; not from the API Server (since the communication never reaches the API Server). - Mitigations: None
- Diagnostics: N/A
- Testing: N/A
- Detection: The
Failure Mode: Overloaded API Server from too many concurrent streaming commands.
- Detection: The streaming commands will show increased latency and timeout errors. The streaming commands could also hang after initiation on the client.
- Mitigations: The mitigations for this phenomenon are described in detail in the Risks and Mitigations section. But a simple way for a user to mitigate this problem would be to reduce the number of concurrent streaming command from clients.
- Diagnostics: API Server
/healthzand/metricsmonitoring. - Testing: Manual stress tests of
kubectlstreaming commands through shell scripts which initiate numerous concurrent streaming commmands.
Failure Mode: Increased network latency between the client and the API Server causes problems with streaming commands.
- Detection: The client streaming commands will lag, timeout, and possibly block.
- Mitigations: Mitigating this problem would require fixing the problematic network connection.
- Diagnostics: Adding extra logging from the
kubectlstreaming commands will show the timings for individual streaming request/responses. For example:$ kubectl exec -v=7 ...will produce logging which shows response times, like:... Response Status: 101 Switching Protocols in 20 millisecondsAlso, API Server/healthzand/metricsmonitoring will produces output demonstrating increased response times and increased timeouts. - Testing: Manual stress tests which simulate a bad network connection by randomly dropping streamed packets.
Failure Mode: API Server that does not support the new WebSockets functionality or the the feature flags are not enabled. If
kubectlsupports new WebSockets functionality, but the API Server does not, we automatically fall back to legacy SPDY streaming.- Detection: The fallback functionality is automatic. But if the user wanted
to see if this fallback was occurring, the user could add the
-v=7flag to thekubectlcommand. The output logging would display the initial WebSockets connection upgrade attempt and failure, and the subsequent SPDY completion of the command. - Mitigations: The mitigation (fallback to legacy SPDY) is automatic.
- Diagnostics: We have suggested metrics to measure the number of fallbacks.
the metrics
num_ws_remote_command_v5_total[failure]as well asnum_ws_port_forward_v2_total[failure]will measure the number of fallbacks. - Testing: We have implemented tests for both the
FallbackWebSocketExecutor(forRemoteCommand), and theFallbackDialer(forPortForward).
- Detection: The fallback functionality is automatic. But if the user wanted
to see if this fallback was occurring, the user could add the
Failure Mode: Kubelet advertises WebSocket support but fails to handle streams.
- Detection: The
kubectlcommand will fail with a generic streaming error. API server logs will show a successful WebSocket upgrade request being proxied to the Kubelet, but theapiserver_streaming_connectionsmetric withproxy_type="proxied"will show failures or short-lived connections. Kubelet logs on the target node will show errors in the WebSocket handling or translation/tunneling logic. - Mitigations: An operator can disable the
ExtendWebSocketsToKubeletfeature gate on the failing node(s) and restart the kubelet service. This will cause the node to stop advertising the feature, and the API server will revert to translating streams for that node, restoring functionality while the issue is investigated. - Diagnostics: Kubelet logs (with increased verbosity if necessary) on the failing node will be the primary source for debugging. They will contain errors related to WebSocket handshake, subprotocol translation, or SPDY forwarding to the container runtime.
- Testing: Unit and integration tests for the Kubelet’s WebSocket server and proxying logic cover the expected behavior. e2e tests will be added to validate the end-to-end flow with the feature gate enabled.
- Detection: The
What steps should be taken if SLOs are not being met to determine the problem?
- Step 1: Turn off the
kubectlfeature gate, and check the SLO afterwards.
For kubectl exec, kubectl cp, and kubectl attach:
$ unset KUBECTL_REMOTE_COMMAND_WEBSOCKETS
# Run simple exec command with higher verbosity to see relevant logging.
# Look for successful connection upgrade with response 101 Switching Protocols.
# Example: $ kubectl exec -v=7 <TARGET> -- date
#
$ kubectl exec -v=7 nginx -- date
...
I0206 02:22:40.500200 3666799 round_trippers.go:463] POST https://127.0.0.1:37243/api/v1/namespaces/default/pods/nginx/exec?command=date&container=nginx&stderr=true&stdout=true
...
I0206 02:22:40.521184 3666799 round_trippers.go:574] Response Status: 101 Switching Protocols in 20 milliseconds
...
Tue Feb 6 02:22:40 UTC 2024
For kubectl port-forward:
$ unset KUBECTL_PORT_FORWARD_WEBSOCKETS
# Run simple port-forward command against webserving pod/deployment/service
# with higher verbosity to see relevant logging. Look for successful connection
# upgrade with response 101 Switching Protocols.
# Example: $ kubectl port-forward -v=7 <TARGET> <LOCAL_PORT>:<REMOTE_PORT>
#
$ kubectl port-forward -v=7 nginx 8080:80
...
I0206 02:28:04.352912 3669354 round_trippers.go:463] POST https://127.0.0.1:37243/api/v1/namespaces/default/pods/nginx/portforward
...
I0206 02:28:04.372175 3669354 round_trippers.go:574] Response Status: 101 Switching Protocols in 19 milliseconds
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
I0206 02:28:04.372540 3669354 portforward.go:309] Before listener.Accept()...
I0206 02:28:04.372586 3669354 portforward.go:309] Before listener.Accept()...
...
# In another terminal, print out index.html served from TARGET after request forwarded.
# Example: $ curl http://localhost:<LOCAL_PORT>/index.html
#
$ curl http://localhost:8080/index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
- Step 2: For a more thorough possible fix, restart the API server with the cluster feature gate turned off. Check the SLO after restart.
While there are many ways to start a Kubernetes cluster, I will detail how to modify
the cluster feature flags for this WebSockets functionality using kubeadm or kind
and cluster configuration files.
Example:
$ kind create cluster --config cluster-config.yaml --name <NAME> --image <IMAGE>
or
$ kubeadm init --config cluster-config.yaml
where the cluster-config.yaml looks like:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
# Enables/Disables WebSockets for RemoteCommand (e.g. exec, cp, attach)
"TranslateStreamCloseWebsocketRequests": false
# Enables/Disables WebSockets for PortForwar (e.g. port-forward)
"PortForwardWebsockets": true
...
Step 3: If investigation points to an issue with the Kubelet extension (e.g., failures are isolated to nodes with the feature enabled), disable the
ExtendWebSocketsToKubeletfeature gate on the API servers. This will cause the API server to revert to performing the protocol translation for all nodes, effectively pausing the extension of WebSocket communication to the Kubelet. The method for configuring API server feature gates varies by provider; for example, on a managed provider like GKE, this would be done via a cluster update command. The goal is to pass the following flag to the API server:--feature-gates=ExtendWebSocketsToKubelet=false
Implementation History
- First Kubernetes release with initial version of KEP available: v1.29
- RemoteCommand over WebSockets shipped as alpha: v1.29
- RemoteCommand over WebSockets shipped as beta: v1.30
- First Kubernetes release where PortForward over WebSockets described in KEP: v1.30
- PortForward over WebSockets shipped as alpha: v1.30
- PortForward over WebSockets shipped as beta: v1.31
- WebSocket HTTPS Proxy functionality shipped: v1.33
- Synthetic RBAC
CREATEauthz check for WebSocket upgrade requests: v1.35 - Extend WebSocket communication to the Kubelet for RemoteCommand and PortForward shipped as beta: v1.36
Drawbacks
The main motivation for taking the risk to change the streaming protocol from SPDY to WebSockets is to support proxies or gateways in between Kubernetes clients and the API Server. If we do not believe it is worth it to support these intermediaries with a modern bi-directional streaming protocol, then we should re-consider this effort.
Alternatives
The only currently supported bi-directional streaming protocol is WebSockets.
When HTTP/2.0 was initially proposed, many believed it would provide streaming functionality;
this belief appears to have been misplaced. Ironically, HTTP/2.0 is based on SPDY.
But the upgraded HTTP/2.0 standard did not surface streaming functionality.
For example, HTTP/2.0 specifically does not support Upgrade requests to
create a streamable connection. In the golang standard library, HTTP/2.0 requests
with the Upgrade header return an error code.
Extend WebSocket Streaming to the Kubelet: Try WebSockets and Fallback to SPDY
Instead of the Kubelet advertising its capabilities, the API server could try to
use WebSockets and fallback to SPDY, similar to how kubectl interacts with the
API server.
Pros:
- No Feature Declaration: The Kubelet would not need to advertise its capabilities, simplifying the Kubelet’s implementation.
- Familiar Pattern: This approach reuses the “try and fallback” logic
already used by
kubectl.
Cons:
- Extra Network Call: A failed WebSocket connection attempt would be required before falling back to SPDY, adding latency to every streaming command for older Kubelets.
- Implementation Simplicity: The current API server code is already designed to check for features and proxy accordingly. Adding a new “try and fallback” dialing mechanism would be more complex than reusing the existing logic.
Report Protocol Support Per-Subresource
Instead of the single ExtendWebSocketsToKubelet feature gate for all streaming
subresources, we could report protocol support on a per-subresource basis (e.g.,
for exec/attach and port-forward individually).
Pros:
- Granular Control: Allows for a phased rollout and targeted testing of each subresource’s migration to WebSockets.
- Isolation: An issue with one subresource’s WebSocket implementation would not impact the others.
Cons:
- Increased Complexity: The Kubelet would need to report a more complex data structure, and the API server logic to check for it would be more involved.
- Complex Configuration: This could lead to a more complex configuration for administrators.
- Cognitive Overhead: It increases the mental model for developers and users to understand which subresource uses which protocol.
Use metadata.annotations
Instead of using the status.declaredFeatures field, the Kubelet’s WebSocket
capabilities could be advertised using an annotation on the Node object.
Pros:
- Simpler Implementation: Uses the existing, well-understood annotation mechanism.
- No
NodeDeclaredFeaturesDependency: Avoids a formal dependency on theNodeDeclaredFeaturesfeature gate. - Flexibility: Key-value nature allows for more expressive feature declarations.
Cons:
- Not Standardized:
NodeDeclaredFeaturesis the purpose-built, standard way to declare node features (see KEP-5328 for more reasoning behind using a unified mechanism). - Less Secure: The RBAC permissions for modifying annotations are typically broader than for the more protected status subresource.
- No Centralized Validation: Prone to errors due to the lack of a centralized validation mechanism for feature names.
- Poor Integration: Other components, like the scheduler, would need custom logic to interpret the annotation.
- Not Standardized:
Infrastructure Needed (Optional)
N/A