Skip to content

Keep the deployment when scale to zero.#1320

Closed
akyyy wants to merge 8 commits intoknative:masterfrom
akyyy:keepDeploymentWhenScaleToZero
Closed

Keep the deployment when scale to zero.#1320
akyyy wants to merge 8 commits intoknative:masterfrom
akyyy:keepDeploymentWhenScaleToZero

Conversation

@akyyy
Copy link
Copy Markdown
Contributor

@akyyy akyyy commented Jun 21, 2018

Fixes #1250

Proposed Changes

  • Instead of deleting the revision service and the deployment while scaling to 0, we need to keep the deployment and just set its replicas to 0. This way, the deployment can have 1 pod in terminating state while another pod is spinning up. So we don't have to wait for the pod to be deleted.
  • To be able to update the deployment, we need to remove the route label from the revision. More details are here The revision labels should be a static set #1293

@google-prow-robot google-prow-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jun 21, 2018
@akyyy akyyy requested a review from mattmoor June 21, 2018 21:47
@akyyy
Copy link
Copy Markdown
Contributor Author

akyyy commented Jun 21, 2018

This pr contains a subset of changes (keeping the deployment and service) in my other pr #1251.

@google-prow-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: akyyy
To fully approve this pull request, please assign additional approvers.
We suggest the following additional approver: mattmoor

Assign the PR to them by writing /assign @mattmoor in a comment when ready.

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details 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

Comment thread pkg/controller/revision/revision.go Outdated

// teardownK8SResources deletes autoscaler resources, but keeps the revision service and deployment.
// It is used when the revision serving state is Reserve.
func (c *Controller) teardownK8SResources(ctx context.Context, rev *v1alpha1.Revision) error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A clearer name would be better here. scaleRevisionResourcesToZero or something. Otherwise it is unclear what this does vs delete does unless you read the func comments.

Comment thread pkg/controller/revision/revision.go Outdated

func (c *Controller) deleteAutoscalerResources(ctx context.Context, rev *v1alpha1.Revision) error {
logger := logging.FromContext(ctx)
if err := c.deleteAutoscalerDeployment(ctx, rev); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not set the replica size to 0 for this one as well? (Keep in mind that if we do that, updating controller code will no longer update any existing revision's autoscaler).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For revision deployment, the reason to keep it is to unblock new pod creation during existing pod terminating. For autoscaler deployment, I don't see a reason to keep it?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Keep in mind that if we do that, updating controller code will no longer update any existing revision's autoscaler

@mdemirhan Curious to know why you think changes couldn't be reconciled to update existing autoscaler deployments?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

+1 to setting the autoscaler deployment to 0 as well. I hope that will allow the autoscaler to come back up more quickly and therefore respond more quickly to large traffic spikes when scaled to zero. It would also help avoid a similar 30-second deadzone in scaling from 1-N (e.g. the deployment is being deleted but passes the controller reconciliation loop by existing).

@dprotaso, I think that he means we won't update the single-tenant autoscaler binary if we just scale to zero instead of creating a deployment anew from the latest autoscaler container passed into the controller through commandline args. That's true. But we are in the process of migrating to an always-on multiple-revision autoscaler. So that issue won't be around for long.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mdemirhan, @josephburnett You're right. I should keep autoscaler deployment as well. I'll work on that. Thanks!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dprotaso I was looking at the current implementation for reconcileAutoscalerDeployment - if there is an existing deployment, it will skip reconcilliation. So, if we don't delete the deployment but just update the replica count of the existing deployment, the autoscaler will never be updated. Currently though, if something scales to zero, autoscaler will get latest bits once activation happens next time.

That being said, I like deterministic behavior and I think current behavior is worse because in case there is an issue with an updated autoscaler, you will see the negative effects of it sometime in the future when N->0->1 happens.

We need a better way to upgrade Knative - ko apply -f config/ is too unpredictable for upgrades. I will file an Issue on this issue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@mdemirhan IMO if we update our sidecars (because of a controller update), we should update deployments during the normal reconciliation. The same if Operators push other config changes (in fact, the sidecars should probably move to ConfigMaps with updates picked up via configmap.Watcher).

My attempt at updating deployments during reconciliation was thwarted by its defaulter, but I'd like to reach a point where we reconcile updates around our controllers for all resources we manage.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd also like any Deployment reconciliation we do to follow the pattern of checkAndUpdateDeployment that's commented as much as possible (though it's implementation may remain heavily commented).

Comment thread pkg/controller/revision/revision.go Outdated
// TODO(mattmoor): Compare the deployments and update if it has changed
// out from under us.
logger.Infof("Found existing deployment %q, updating", deploymentName)
_, err = dc.Update(deployment)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Old code used to skip updating the deployment if it already exist. However; this one will keep updating the deployment objects every 30 seconds. Seems like a regression.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nice find! Fixed.

@knative-metrics-robot
Copy link
Copy Markdown

The following is the coverage report on pkg/. Say /test pull-knative-serving-go-coverage to run the coverage report again

File Old Coverage New Coverage Delta
pkg/controller/route/route.go 78.5% 79.2% 0.7
pkg/controller/revision/revision.go 78.4% 76.5% -1.8

*TestCoverage feature is being tested, do not rely on any info here yet

return err
// TODO(mattmoor): Compare the deployments and update if it has changed
// out from under us. So far the deployment could only be updated for replicas field.
if *existingDeployment.Spec.Replicas == *desiredDeployment.Spec.Replicas {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Something happened to my comments. I think I accidentally deleted them. Here it goes again:

This code will reset the replica count that is set by autoscaler every 30 seconds and that seems wrong. If a deployment exist, we should just return.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah, this seems strange to me too. I think the controller should be overwriting everything except the replicas count. We leave that to the Activator and the Autoscaler. And the update is punted for now by the TODO(mattmoor) above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am not sure, but I think the reason for not updating deployments every 30 seconds today is because updating deployments even without changes might cause restart of the pods (I don't think that is the case unless the pod spec changes, but I am not 100% sure). @mattmoor is that the reason that deployments are never reconciled today?

// And the deployment is no longer ready, so update that
rev.Status.MarkInactive()
logger.Infof("Updating status with the following conditions %+v", rev.Status.Conditions)
if _, err := c.updateStatus(rev); err != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you rebase you won't need the updateStatus call anymore due to the PR #1321

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This whole function will be gone after #1334

// And the deployment is no longer ready, so update that
rev.Status.MarkInactive()
logger.Infof("Updating status with the following conditions %+v", rev.Status.Conditions)
if _, err := c.updateStatus(rev); err != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Likewise see: #1321

Comment thread pkg/controller/revision/revision.go Outdated

func (c *Controller) deleteAutoscalerResources(ctx context.Context, rev *v1alpha1.Revision) error {
logger := logging.FromContext(ctx)
if err := c.deleteAutoscalerDeployment(ctx, rev); err != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Keep in mind that if we do that, updating controller code will no longer update any existing revision's autoscaler

@mdemirhan Curious to know why you think changes couldn't be reconciled to update existing autoscaler deployments?

if err := c.resolver.Resolve(desiredDeployment); err != nil {
logger.Error("Error resolving deployment", zap.Error(err))
rev.Status.MarkContainerMissing(err.Error())
if _, updateErr := c.updateStatus(rev); updateErr != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Likewise see: #1321

var (
elaPodReplicaCount = int32(1)
elaPodMaxUnavailable = intstr.IntOrString{Type: intstr.Int, IntVal: 1}
elaPodMaxUnavailable = intstr.IntOrString{Type: intstr.Int, IntVal: 0}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just curious to know what your motivation was to change this to 0?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I had the same question.

return err
}
if *deployment.Spec.Replicas == 0 {
logger.Infof("Deployment %s is scaled to 0 already.", deploymentName)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

%q

return err
}

logger.Infof("Successfully scaled deployment %s to 0.", deploymentName)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

%q

Labels: map[string]string{
"testLabel1": "foo",
"testLabel2": "bar",
serving.RouteLabelKey: "test-route",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is the constant serving.RouteLabelKey used anymore?

@google-prow-robot
Copy link
Copy Markdown

@akyyy: The following tests failed, say /retest to rerun them all:

Test name Commit Details Rerun command
pull-knative-serving-go-coverage 313f770 link /test pull-knative-serving-go-coverage
pull-knative-serving-unit-tests 313f770 link /test pull-knative-serving-unit-tests
pull-knative-serving-integration-tests 313f770 link /test pull-knative-serving-integration-tests
pull-knative-serving-build-tests 313f770 link /test pull-knative-serving-build-tests

Full PR test history. Your PR dashboard.

Details

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. I understand the commands that are listed here.

@mattmoor
Copy link
Copy Markdown
Member

I believe this is subsumed by @mdemirhan's new PR.

@mattmoor mattmoor closed this Jun 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

8 participants