Skip to content
This repository was archived by the owner on Sep 18, 2020. It is now read-only.

update-agent: wait for pods to terminate before rebooting#137

Merged
dghubble merged 4 commits into
coreos:masterfrom
hankjacobs:wait-for-pod-deletion
Oct 17, 2017
Merged

update-agent: wait for pods to terminate before rebooting#137
dghubble merged 4 commits into
coreos:masterfrom
hankjacobs:wait-for-pod-deletion

Conversation

@hankjacobs

Copy link
Copy Markdown
Contributor

This change makes the agent wait until all pods are terminated before rebooting. Prior to this change, the agent would reboot immediately after the pod delete request was made and, consequently, pods were not given the time needed to terminate gracefully.

@coreosbot

Copy link
Copy Markdown

Can one of the admins verify this patch?

@hankjacobs

Copy link
Copy Markdown
Contributor Author

Just a friendly ping on this

@dghubble dghubble left a comment

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.

Minor tweaks related to polling interval, timeout, and not terminating on transient errors.

Comment thread pkg/agent/agent.go Outdated

// waitForPodDeletion waits for a pod to be deleted
func (k *Klocksmith) waitForPodDeletion(pod v1.Pod) error {
return wait.PollInfinite(time.Second, func() (bool, 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.

Let's not poll every second. We already have issues filed about CLUO having too aggressive call rates. Other code in agent polls every 10 seconds.

const reapInterval = 10 * time.Second
...
wait.PollUntil(reapInterval, func() (bool, error) {...})

Forever is a long time to wait as well. We should probably set a sane maximum time we're willing to wait for a troublesome pod before proceeding with the shutdown anyway (e.g. 10 min). Otherwise one app developer with a misbehaving app and an unreasonable grace period will stall Container Linux updates for the cluster.

const (
    reapInterval = 10 * time.Second
    reapTimeout = 10 * time.Minute
)
...
wait.Poll(reapInterval, reapTimeout, func() (bool, error) {...})

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.

Any opposition to making the reap timeout configurable? 10 minutes seems reasonable for most use cases but there are some that require a much longer timeout. Specifically, some of our pods running in our cluster can take up to 24 hours to terminate gracefully in certain scenarios.

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'm not greatly in favor of new flags, unless really needed. Apps that require 24 hours to terminate gracefully, on clusters designed around the idea that individual nodes can go down and workloads move flexibly?

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.

If you've been using the original PR on clusters (and we mentioned that on any transient call error, the wait would exit), it sounds like your apps are already subject to ungraceful shutdown?

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.

We haven't been using it on our clusters for the very reason this PR was opened. The fact that pods don't terminate gracefully before reboot showed up in our testing prior to rolling this out.

Yep, it's most definitely not ideal that these jobs can take so long (most of the time they don't) and it's something that is being worked on but we have to work around it for the time being.

24 hours is most definitely an outlier but I'd argue that 11 minutes isn't. 15 minutes still sounds pretty reasonable to me depending on what's running in the cluster. The point I'm trying to make is that the reap timeout is heavily dependent on the type of workload that runs on your cluster. Because it could be so variable, I'd say it's worth making into a flag.

@hankjacobs hankjacobs Oct 9, 2017

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.

Ah sorry, misunderstood that last question. We’ve used this PR on our test cluster running a similar workload but with not nearly as long of running.

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.

Hm, I'm not excited to add config flags (flags cascade into higher-level components), but I can see a --grace-period flag on update-agent (mirroring the --grace-period flag of kubectl drain) being warranted here. It does also set expectations that there has to be some period of time beyond which we give up on waiting (i.e. its best-effort, we can't guarantee we'll satisfy every pod grace period).

Comment thread pkg/agent/agent.go Outdated
return false, nil
case errors.IsNotFound(err):
return true, nil
default:

@dghubble dghubble Oct 9, 2017

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.

Handling this more naively could improve logs and retry. Compared with logging at the top level where the wait return error is just a loose approximation of what happened.

_, err := k.kc.CoreV1().Pods(pod.Namespace).Get(pod.Name, v1meta.GetOptions{})
if errors.IsNotFound(err) {
    glog.Infof("deleted pod %s", pod.Name)   // success log
	return true, nil
}
if err != nil {
  glog.Errorf("failed to get pod %v", err)    // some API call error, likely transient
}
return false, nil

So then success looks like:

Waiting for pod POD to terminate
failed to get pod: transient error
failed to get pod: transient error
deleted pod POD

And "failure" gets logged (by the caller) as skipping the wait.

Waiting for pod POD to terminate
failed to get pod: transient error
failed to get pod: transient error
Skipping wait on pod POD: timeout error

Comment thread pkg/agent/agent.go Outdated
for _, pod := range pods {
glog.Infof("Waiting for pod %q to terminate", pod.Name)
if err := k.waitForPodDeletion(pod); err != nil {
glog.Errorf("failed waiting for pod %q to terminate: %v", pod.Name, err)

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.

In practice, many of these log lines will be about transient errors to make API calls and we shouldn't stop the wait for them (e.g. "failed waiting for pod xx to terminate: can't reach 10.3.0.1"). Other uses of wait in agent allow the anon function to log errors and return nil to continue trying.

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.

On timeout, we can say we're skipping waiting for the pod to terminate.

@hankjacobs

Copy link
Copy Markdown
Contributor Author

@dghubble feedback implemented. thanks!

@FaKod

FaKod commented Oct 11, 2017

Copy link
Copy Markdown

Just a comment on this. I am using Rook (a Ceph based cluster filesystem) and failed with locksmith and this PR to reboot updated nodes.
This PR doesn't help because Rooks Pods (OSDs) are killed before the Pods of f.e. ElasticSearch are killed. So the ES Pods cannot be terminated at all by K8s and the agent waits forever.

@hankjacobs

Copy link
Copy Markdown
Contributor Author

@FaKod If graceful termination of a pod is dependent on the order in which other pods are terminated, this PR won't help much. That being said, the agent shouldn't hang with the changes I pushed ~6 hours ago. The agent should timeout after 10 minutes of waiting for a pod to terminate. Did you try with that version? If so, could you send me the agent logs? If not, could you try with that version? My testing proved successful in our test cluster.

@FaKod

FaKod commented Oct 11, 2017

Copy link
Copy Markdown

@hankjacobs In case of rook: Although pods are terminated eventually, the mounts are still valid on the node. That prevents a node from restarting. See rook/rook#1018

This may be a specific issue of Rook. What would help is to call "drain" before and wait for the non DaemonSet Pods to terminate.

@hankjacobs

hankjacobs commented Oct 11, 2017

Copy link
Copy Markdown
Contributor Author

This is effectively a "drain" as implemented by kubectl (albeit a rather simplified version -- doesn't use the eviction APIs). The hanging you saw was an issue with my original implementation. That should be fixed with the current version of this PR.

@hankjacobs

hankjacobs commented Oct 11, 2017

Copy link
Copy Markdown
Contributor Author

Additionally, DaemonSet pods are skipped. To me, this points to something with your setup. This is outside the scope of this PR but I'd double check that you're running rook as a DS.

@FaKod

FaKod commented Oct 11, 2017

Copy link
Copy Markdown

Right, its a ReplicaSet (created by an Operator) - sorry for the confusion. So I'll issue a feature request to exclude some Resources from deletion by labels or so.

@dghubble dghubble left a comment

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.

Minor nits. Can another maintainer agree on the flag addition? @sdemos

Otherwise, I'll approve after these fixes.

Comment thread pkg/agent/agent.go Outdated
nc v1core.NodeInterface
ue *updateengine.Client
lc *login1.Conn
ri time.Duration

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.

remove, this isn't customized anywhere

Comment thread pkg/agent/agent.go Outdated
ue *updateengine.Client
lc *login1.Conn
ri time.Duration
rt time.Duration

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.

Can you name this reapTimeout or gracePeriod? No need to emulate these (poor imo) existing field names

Comment thread pkg/agent/agent.go Outdated
rt time.Duration
}

const reapInterval = 10 * time.Second

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.

Let's have this be defaultInterval or something more general to replace the other occurrences of 10 second intervals

Comment thread pkg/agent/agent.go Outdated
}

return &Klocksmith{node, kc, nc, ue, lc}, nil
return &Klocksmith{node, kc, nc, ue, lc, reapInterval, reapTimeout}, 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.

No need to configure with the constant

@sdemos

sdemos commented Oct 16, 2017

Copy link
Copy Markdown
Contributor

adding the --grace-period flag seems reasonable to me.

Hank Jacobs added 4 commits October 17, 2017 13:08
This change makes the agent wait until all pods are terminated before
rebooting. Prior to this change, the agent would reboot immediately after
the pod delete request was made and, consequently, pods were not
given the time needed to terminate gracefully.
@hankjacobs hankjacobs force-pushed the wait-for-pod-deletion branch from 8f1b849 to 8015b10 Compare October 17, 2017 20:08
@hankjacobs

Copy link
Copy Markdown
Contributor Author

nits picked and an image to go with it: quay.io/dollarshaveclub/container-linux-update-operator:8015b109a973f0e8497ae894b90f66b6e47e5455-dirty

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants