-
Notifications
You must be signed in to change notification settings - Fork 38.7k
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
spdy: add optional periodic Pings on the connection #94170
Conversation
/assign @smarterclayton |
/assign @caesarxuchao |
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.
// actually closed. | ||
} else { | ||
// Count successful pings, for testing. | ||
atomic.AddInt64(&c.pingsSent, 1) |
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.
Can we avoid this testing-only code? Dose the SPDY library provide any testing support that exposes if a ping frame is received?
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.
Unfortunately, the library handles incoming pings internally without any external hooks I can find.
This unexported field is the least intrusive way I could verify the behavior. Using sync/atomic
, performance overhead should be negligible.
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.
+1, if you want to test this, add a member variable of type function for calling Ping and let the test swap that out. It's not performance, it's just a code smell.
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.
fair enough, added a swap-able function field in 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.
How's the user going to enable the ping?
@caesarxuchao this PR doesn't expose it to k8s users.
My motivation is for https://github.com/gravitational/teleport/ which uses this library to intercept kubectl exec
connections for audit purposes (recording session traffic). For kube-apiserver
, pings are usually unnecessary since it's generally not fronted by a strict load balancer killing idle connections.
If you'd like, I can plumb a flag up to kube-apiserver
. But it adds unnecessary complexity, if nobody is asking to use it there.
// actually closed. | ||
} else { | ||
// Count successful pings, for testing. | ||
atomic.AddInt64(&c.pingsSent, 1) |
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.
Unfortunately, the library handles incoming pings internally without any external hooks I can find.
This unexported field is the least intrusive way I could verify the behavior. Using sync/atomic
, performance overhead should be negligible.
staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go
Outdated
Show resolved
Hide resolved
I think I'm OK with adding this and I might even be OK with turning it on, as long as we do that early in the release cycle. But please don't change the function signatures. :) |
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.
PTAL
staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go
Outdated
Show resolved
Hide resolved
staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go
Outdated
Show resolved
Hide resolved
// actually closed. | ||
} else { | ||
// Count successful pings, for testing. | ||
atomic.AddInt64(&c.pingsSent, 1) |
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.
fair enough, added a swap-able function field in connection
I think I'm OK with this now, @caesarxuchao can you do a final review? |
/retest |
@caesarxuchao could you take another look please? |
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.
Two nits. Otherwise lgtm.
Proxier func(*http.Request) (*url.URL, error) | ||
// ConnPingPeriod is a period for sending SPDY Pings on the connection. | ||
// Optional. | ||
ConnPingPeriod time.Duration |
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.
nit: why not just naming it "PingPeriod"?
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.
renamed, here an in upgrade.go
go conn.Serve(c.newSpdyStream) | ||
if pingPeriod > 0 { |
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.
Also check if c.ping
is nil?
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.
done
When an SPDY connection goes over an intermediate box (proxy or load balancer, e.g. AWS ELB), it can get interrupted due to idleness (default in ELB is 60s). For example, this happens with `kubectl exec` sessions are left open without any activity for a while. TCP-level keep-alives are not sufficient for all intermediate boxes, they may pay attention to application-layer traffic only. SPDY pings make the connection appear active, letting it survive a period of idleness. Note: this commit adds support for pings in `k8s.io/apimachinery/pkg/util/httpstream/spdy`, but doesn't enable it anywhere in the calling code. There is no behavior change for existing callers.
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.
Thanks @caesarxuchao, nits fixed
go conn.Serve(c.newSpdyStream) | ||
if pingPeriod > 0 { |
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.
done
Proxier func(*http.Request) (*url.URL, error) | ||
// ConnPingPeriod is a period for sending SPDY Pings on the connection. | ||
// Optional. | ||
ConnPingPeriod time.Duration |
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.
renamed, here an in upgrade.go
} | ||
|
||
// newConnection returns a new connection wrapping conn. newStreamHandler | ||
// will be invoked when the server receives a newly created stream from the | ||
// client. | ||
func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { | ||
c := &connection{conn: conn, newStreamHandler: newStreamHandler} | ||
func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler, pingPeriod time.Duration, pingFn func() (time.Duration, error)) httpstream.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.
nit: given that this is a private function, and it looks like pingFn
is always spdyConn.Ping
. Would it be better to omit the pingFn
argument and invoke c.conn.Ping
directly in c.sendPings
?
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.
Oh I overlook the test..
Ping @caesarxuchao |
/retest |
/milestone v1.20 |
@awly: You must be a member of the kubernetes/milestone-maintainers GitHub team to set the milestone. If you believe you should be able to issue the /milestone command, please contact your and have them propose you as an additional delegate for this responsibility. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
/priority backlog |
@caesarxuchao this PR is still missing your approval, i think |
ping @caesarxuchao |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: awly, lavalamp The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
What type of PR is this?
/kind cleanup
What this PR does / why we need it:
When an SPDY connection goes over an intermediate box (proxy or load
balancer, e.g. AWS ELB), it can get interrupted due to idleness (default
in ELB is 60s). For example, this happens with
kubectl exec
sessionsare left open without any activity for a while.
TCP-level keep-alives are not sufficient for all intermediate boxes,
they may pay attention to application-layer traffic only. SPDY pings
make the connection appear active, letting it survive a period of
idleness.
Note: this commit adds support for pings in
k8s.io/apimachinery/pkg/util/httpstream/spdy
, but doesn't enable itanywhere in the calling code. There is no behavior change for existing
callers.
Which issue(s) this PR fixes:
N/A
Special notes for your reviewer:
This will be used in https://github.com/gravitational/teleport/, which acts as a man-in-the-middle auth proxy in for k8s API. When teleport is fronted by AWS ELB (same probably applies to other load balancers),
kubectl exec
sessions get disconnected after 60s of idleness. This PR addresses that.Does this PR introduce a user-facing change?:
Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:
N/A