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

Add lifecycle hooks and readiness checks for smoother autoscaling #670

Merged
merged 4 commits into from
Apr 20, 2018

Conversation

tcnghia
Copy link
Contributor

@tcnghia tcnghia commented Apr 16, 2018

Fixes #429

Proposed Changes

(1) Adding readinessProbes and PreStop hooks and
(2) Gracefully shutting down queue HTTP server.

I tested this change on a cluster having 24 nodes, with 500 clients gradually ramping up in 100 seconds. That generated about 170K requests.

Before: 4K 503 errors out of 170K requests.
After: 0 error out of 170K requests.

@tcnghia tcnghia requested a review from josephburnett April 16, 2018 23:10
@google-prow-robot google-prow-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 16, 2018
@tcnghia tcnghia requested a review from mattmoor April 16, 2018 23:12
@tcnghia
Copy link
Contributor Author

tcnghia commented Apr 17, 2018

/retest

Copy link
Member

@mattmoor mattmoor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. I am super excited to see some of this instability put to rest :)

mutex sync.RWMutex
}

// isAlive() returns true iff a PreStop hook has not been called.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is clearer: // isAlive() returns true until a PreStop hook has been called.

func (h *healthServer) kill() {
h.mutex.Lock()
h.alive = false
defer h.mutex.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Generally when defer is used it's right after the Lock(). If you leave it here, you may as well drop the defer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// the same time the pod is marked for removal.
func (h *healthServer) quitHandler(w http.ResponseWriter, r *http.Request) {
h.kill()
time.Sleep(quitSleepSecs * time.Second)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. You never write back a response?
  2. Can you explain the ordering for the kill / delay? Why does this work? Why do you hold the connection open for 20 seconds after you start responding to health checks with non-200?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

defer h.mutex.Unlock()
}

// healthHandler is used for readinessProbe of queue-proxy.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readiness vs. liveness?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// Add a SIGTERM handler to gracefully shutdown the servers during
// pod termination.
var sigTermChan = make(chan os.Signal)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sigTermChan := make(...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// Queue proxy admin port and paths provide health check and
// lifecycle hooks.
requestQueueAdminPortName string = "queueadm-port"
requestQueueAdminPort = 8022
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this public and use this constant from //cmd/ela-queue/main.go? Same for 8012 above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -59,13 +87,41 @@ func MakeElaPodSpec(u *v1alpha1.Revision, queueSidecarImage string) *corev1.PodS
},
},
Ports: []corev1.ContainerPort{
// TOOD: HTTPS connections from the Cloud LB require
// TODO: HTTPS connections from the Cloud LB require
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we no longer use nginx, so perhaps this comment should simply be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

elaContainer.Lifecycle = &corev1.Lifecycle{}
}
if elaContainer.Lifecycle.PreStop == nil {
elaContainer.Lifecycle.PreStop = &corev1.Handler{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what this is doing in a detailed comment?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your other comments are great, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if elaContainer.Lifecycle == nil {
elaContainer.Lifecycle = &corev1.Lifecycle{}
}
if elaContainer.Lifecycle.PreStop == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and above, we should simply disallow user-provided lifecycle hooks.

cc @evankanderson

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if p.Handler.HTTPGet == nil {
return false
}
return p.Handler.HTTPGet.Path != ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also check that it's on the expected port?

I'm wondering if we should simply disallow users from specifying port in the readiness check, since the port will be implied by our runtime contract.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should disallow setting the port here. I'm putting TODOs to follow up with webhook changes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open issues for each please :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattmoor mattmoor self-assigned this Apr 17, 2018
@mattmoor
Copy link
Member

@tcnghia can you also see what this does to this issue? #348

@tcnghia
Copy link
Contributor Author

tcnghia commented Apr 17, 2018

@mattmoor the change didn't help #348, and I do suspect that it is Istio since the endpoint we curl is also the health check endpoint.

(1) Adding readinessProbes and PreStop hooks, and
(2) Gracefully shutting down queue HTTP server.
@tcnghia
Copy link
Contributor Author

tcnghia commented Apr 17, 2018

/retest

if p.Handler.HTTPGet == nil {
return false
}
return p.Handler.HTTPGet.Path != ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open issues for each please :)

Name: RequestQueuePortName,
ContainerPort: int32(RequestQueuePort),
},
// Provides health checks and life cycle hooks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lifecycle

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@mattmoor
Copy link
Member

/lgtm

@google-prow-robot google-prow-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 18, 2018
@mattmoor
Copy link
Member

/hold for comments

@mattmoor
Copy link
Member

/hold

@google-prow-robot google-prow-robot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Apr 18, 2018
@google-prow-robot google-prow-robot removed the lgtm Indicates that a PR is ready to be merged. label Apr 18, 2018
@mattmoor
Copy link
Member

/lgtm

@google-prow-robot google-prow-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 18, 2018
@tcnghia
Copy link
Contributor Author

tcnghia commented Apr 18, 2018

@josephburnett can you please give feedback/approval? thanks

@tcnghia
Copy link
Contributor Author

tcnghia commented Apr 18, 2018

We have an issue to make sure sidecar injection works properly or else Pod creation would fail (#379). Such would insulate us in case our changes here (to Istio injector configuration) break sidecar injection.

@mattmoor
Copy link
Member

/approve
/lgtm

@google-prow-robot google-prow-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 18, 2018
@mattmoor
Copy link
Member

/hold

@mattmoor
Copy link
Member

Sorry I thought that was asking me :)

@google-prow-robot google-prow-robot removed the lgtm Indicates that a PR is ready to be merged. label Apr 19, 2018
@mattmoor
Copy link
Member

I'm still good with this, deferring to @josephburnett for final approval.

Thanks for the improvements to availability :)

io.WriteString(w, "alive: false")
}

// Sets up /health and /quitquitquit endpoints.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that any IP on the Internet can send a bunch of requests to /quitquitquit and bring down a revision?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is not exposed like :8012. I believe only a local prober can access it.

@@ -143,15 +154,92 @@ func statReporter() {
}
}

func isProbe(r *http.Request) bool {
// Since K8s 1.8, prober requests have
// User-Agent = "kube-probe/{major-version}.{minor-version}".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this User-Agent be spoofed? Can any IP on the Internet send a bunch of requests to with this User-Agent and overwhelm pods without causing autoscaling to kick in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a valid concern. I'll open an issue to find out if we can avoid.

Copy link
Contributor

@josephburnett josephburnett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few questions. Looks good!

@mattmoor
Copy link
Member

/approve
/lgtm

@google-prow-robot google-prow-robot added the lgtm Indicates that a PR is ready to be merged. label Apr 20, 2018
@google-prow-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mattmoor, tcnghia

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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tcnghia tcnghia removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Apr 20, 2018
@tcnghia
Copy link
Contributor Author

tcnghia commented Apr 20, 2018

/retest

@google-prow-robot google-prow-robot merged commit 50a596f into knative:master Apr 20, 2018
markusthoemmes referenced this pull request in openshift/knative-serving Apr 3, 2019
* Reduce autoscaling-related 503s by:
(1) Adding readinessProbes and PreStop hooks, and
(2) Gracefully shutting down queue HTTP server.

* Address PR feedback.

* Exclude K8s prober requests from autoscaling consideration.

* Clarifying why we added flag.Parse().
nak3 pushed a commit to nak3/serving that referenced this pull request Mar 24, 2021
Fixes knative/pkg#2026

The actual issue is that the test context expires between individual
stages run by the upgrade framework. This fix passes an external logger
that survives the stages.
markusthoemmes pushed a commit to markusthoemmes/knative-serving that referenced this pull request Apr 7, 2021
Fixes knative/pkg#2026

The actual issue is that the test context expires between individual
stages run by the upgrade framework. This fix passes an external logger
that survives the stages.
nak3 pushed a commit to nak3/serving that referenced this pull request Apr 15, 2021
* Fix race condition with Prober logger in upgrade tests (knative#670)

Fixes knative/pkg#2026

The actual issue is that the test context expires between individual
stages run by the upgrade framework. This fix passes an external logger
that survives the stages.

* Only use exec probe at startup time (knative#10741)

* Only use exec probe at startup time

Now that StartupProbe is available, we can avoid using spawning the exec
probe other than at startup time. For requests after startup this
directly uses the same endpoint as the exec probe in the QP as the
target of a HTTP readiness probe.

Following on from this I think we may want to rework quite a bit of how
our readiness probe stuff works (e.g. it'd be nice to keep the probes on
the user container so failures are on the right object, and we currently
ignore probes ~entirely after startup if periodSeconds>0), but this is a
minimal change that should be entirely backwards-compatible and saves
quite a few cpu cycles.

* Use ProgressDeadline as failure timeout for startup probe

- Also just drop exec probe entirely for periodSeconds > 1 since these
  can just use the readiness probe now. (Easier than figuring out how to
  do square ProgressDeadline with a custom period).

* See if flag is what's making upgrades unhappy

* reorganize comments

* Default PeriodSeconds of the readiness probe to 1 if unset (knative#10992)

Co-authored-by: Martin Gencur <[email protected]>
Co-authored-by: Julian Friedman <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants