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

Refactor node draining racing avoid condition #130

Merged
merged 1 commit into from
Jun 25, 2021

Conversation

pliurh
Copy link
Collaborator

@pliurh pliurh commented May 8, 2021

Utilize k8s leader election mechanism to prevent annotating nodes
at the same time.

}, 3*time.Second, 3, true, ctx.Done())

done := make(chan bool)
go dn.getDrainLock(ctx, done)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we run dn.getDrainLock in foreground and not use done chan?
I assume leaderelection.RunOrDie is a loop w/o timeout, is it true?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The leaderelection.RunOrDie runs forever unless the context is canceled. I didn't run it in the foreground, because I want it keeps leading until the node finishes draining. So the rest nodes can only start the election after the prior one finishes draining if there is no reboot required.

@@ -18,6 +18,14 @@ import (
"time"

"github.com/golang/glog"
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: could you separate local imports from external imports

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was updated by some plugin of my editor automatically. I'll change it back.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

}

// start the leader election
leaderelection.RunOrDie(ctx, leaderelection.LeaderElectionConfig{
Copy link
Collaborator

@adrianchiris adrianchiris May 9, 2021

Choose a reason for hiding this comment

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

looking at leaderelection docs:

Package leaderelection implements leader election of a set of endpoints. It uses an annotation in the endpoints object to store the record of the election state. This implementation does not guarantee that only one client is acting as a leader (a.k.a. fencing).

is it not an issue ? i think what we are doing here is considered fencing
how will the system behave when there is only one endpoint trying to take the lead on LeaseLock ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Instead of using endpoint, I use Lease API for leader election here. I don't think that statement applies. As all the clients race for the same Lease object. So I don't think there could be more than one acting as leader.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK thanks for explaining

time.Sleep(1 * time.Second)
if dn.drainable {
glog.V(2).Info("drainNode(): no other node is draining")
err = dn.annotateNode(dn.name, annoDraining)
Copy link
Collaborator

Choose a reason for hiding this comment

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

using this mechanism, do we still need to annotate the node with annotDraining ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That is the trick. The leader election mechanism requires the leader to keep updating the Lease object. But in our case, the node may reboot itself, then lose leadership. So I use a 2 layers lock here. The node can only start draining with 2 conditions: 1) it becomes the leader 2) no other node is draining which is indicated by the annotation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see, thanks for clarifying

Copy link
Collaborator

Choose a reason for hiding this comment

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

@pliurh mind mentioning this in the commit message ? so its clear in the commit message how this mechanism is used to control node draining

RetryPeriod: 1 * time.Second,
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
glog.V(2).Info("drainNode(): started leading")
Copy link
Member

Choose a reason for hiding this comment

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

I see in your log messages you've drainNode() - should it not be getDrainLock()?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

glog.V(2).Info("drainNode(): started leading")
for {
time.Sleep(1 * time.Second)
if dn.drainable {
Copy link
Member

Choose a reason for hiding this comment

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

Outside of any concern for this PR because this pattern was here before this PR - but have you folks ever seen nodes getting stuck on this condition? It could happen if a node reboots and doesn't startup and daemonset is unable to update its draining status.
Then line 847 is blocked indefinitely.

Copy link
Collaborator Author

@pliurh pliurh May 14, 2021

Choose a reason for hiding this comment

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

That is intentional. We don't want a configuration mistake to break more nodes. If users encounter such a problem, they'd better do some troubleshooting to find out why the node cannot come back.

@pliurh pliurh force-pushed the leader_election branch 2 times, most recently from 55f619a to fb30f8c Compare May 17, 2021 03:12
Copy link
Collaborator

@adrianchiris adrianchiris left a comment

Choose a reason for hiding this comment

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

I would prefer the package updates be performed in a separate commit in the PR (easier to review this way). but would not block on it.

@SchSeba i see below you were requested as reviewer. once you approve this can be merged IMO

Utilize k8s leader election mechanism to prevent annotating nodes
at the same time.

The leader election mechanism requires the leader to keep updating
the Lease object. But in our case, the node may reboot itself, then
lose leadership. So I use a 2 layers lock here. The node can only
start draining with 2 conditions: 1) it becomes the leader 2) no
other node is draining which is indicated by the annotation.
@pliurh pliurh merged commit 9bf123f into k8snetworkplumbingwg:master Jun 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants