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

Added support for maximum replicas per node #2758

Merged
merged 1 commit into from
Oct 17, 2018

Conversation

olljanat
Copy link
Contributor

@olljanat olljanat commented Sep 29, 2018

- What I did
Added support for maximum replicas per node

First step to be able solve moby/moby#26259

- How I did it
Added maxreplicas settings under type Placement and --replicas-max-per-node to cli.

- How to test it
Create test service on one node environment using command:

swarmctl service create --image nginx --replicas 2 --replicas-max-per-node 1 --name web

@codecov
Copy link

codecov bot commented Sep 30, 2018

Codecov Report

❗ No coverage uploaded for pull request base (master@7d5d33b). Click here to learn what that means.
The diff coverage is 100%.

@@            Coverage Diff            @@
##             master    #2758   +/-   ##
=========================================
  Coverage          ?   61.85%           
=========================================
  Files             ?      134           
  Lines             ?    21874           
  Branches          ?        0           
=========================================
  Hits              ?    13531           
  Misses            ?     6884           
  Partials          ?     1459

@olljanat olljanat force-pushed the replicas-max-per-node branch 3 times, most recently from 17745e1 to d49d639 Compare September 30, 2018 09:09
@olljanat olljanat changed the title WIP: Added support for maximum replicas per node Added support for maximum replicas per node Sep 30, 2018
@olljanat
Copy link
Contributor Author

Ready for review.

Note that I needed switch to Protoc 3.6.1 because it was failing to error:

protoc --decode google.protobuf.FileDescriptorSet /usr/local/include/google/protobuf/descriptor.proto
/usr/local/include/google/protobuf/descriptor.proto: File does not reside within any path specified using --proto_path (or -I).  You must specify a --proto_path which encompasses this file.  Note that the proto_path must be an exact prefix of the .proto file names -- protoc is too dumb to figure out when two paths (e.g. absolute and relative) are equivalent (it's harder than you think).
2018/09/27 15:41:47 exit status 1
make: *** [protos] Error 1
Exited with code 2

Same way than example #2757 does

Copy link
Collaborator

@dperny dperny left a comment

Choose a reason for hiding this comment

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

The feature looks fine to me.

The code mostly looks good, but has some small issues.

Here's a big question for you, though, that we'll need an answer to before we proceed: If the user is trying to do a rolling update, and they have the update set to Start First, but the maximum number of tasks is already present on every node, then the update will be unable to proceed. The user will have to first scale the service down, then do the rolling update. Is this expected and acceptable behavior?

api/types.proto Outdated
@@ -862,6 +862,9 @@ message Placement {
// This field is used in the platform filter for scheduling. If empty,
// then the platform filter is off, meaning there are no scheduling restrictions.
repeated Platform platforms = 3;

// Max replicas specifies limit for maximum number of replicas running on one node.
uint64 maxreplicas = 4;
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: calling this max_replicas should get it to compile to go as MaxReplicas

Additionally, the comment should then be of the form

// MaxReplicas specifies the limit . . . 


// Check returns true if there is free slots for task in a given node.
func (f *MaxReplicasFilter) Check(n *NodeInfo) bool {
if uint64(n.ActiveTasksCountByService[f.t.ServiceID]) < f.t.Spec.Placement.Maxreplicas {
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this concurrency safe? if so, can you explain why it's concurrency safe in a comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far I understand swarmkit code then scheduler will process only one task at time?
and code logic on https://github.com/docker/swarmkit/blob/master/manager/scheduler/nodeinfo.go#L114 updates ActiveTasksCountByService counter immediately when task is scheduled to host without waiting that it actually starts. That should make it concurrency safe.

But to be honest I'm not sure how to describe it nicely.


assert.Len(t, t1Assignments, 2)
assert.Equal(t, 2, t1Assignments["id1"])
assert.Equal(t, 2, t1Assignments["id2"])
Copy link
Collaborator

Choose a reason for hiding this comment

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

need to add a check to the test to see that the Explain step works correctly.

@cpuguy83
Copy link
Member

cpuguy83 commented Oct 1, 2018

@dperny My first thought, this should be taken literally with a separate option to override.

But I see the dilemma here in that allowing this (more than N-max on a node) to happen may be very bad in some cases, and having a separate option is cumbersome and confusing... and at what level is this option set at considering that you can potentially be updating more than one replica on a node in parallel.

@olljanat
Copy link
Contributor Author

olljanat commented Oct 1, 2018

Good point. I think that correct logic would be that if start first setting is enabled then:
max replicas => max replicas value + update parallelism value

I can try look about it.

Btw. I also setup voting about it to original issue: moby/moby#26259 (comment)

@olljanat
Copy link
Contributor Author

olljanat commented Oct 3, 2018

Here's a big question for you, though, that we'll need an answer to before we proceed: If the user is trying to do a rolling update, and they have the update set to Start First, but the maximum number of tasks is already present on every node, then the update will be unable to proceed. The user will have to first scale the service down, then do the rolling update. Is this expected and acceptable behavior?

Actually more I think this more sure I came that it is excepted and acceptable behavior as it would very hard to define good logic when scheduler is allowed to exceed that limit. So I would say that it just need to be explained on documentation that user need to make sure that there is always room of update tasks on that combination. Simplest solution is actually have one more nodes than is needed to run application (example 3 nodes with settings --replicas 2 are --replicas-max-per-node 1 )

@dperny I also made changes you requested.

@olljanat olljanat mentioned this pull request Oct 4, 2018
@olljanat olljanat force-pushed the replicas-max-per-node branch 2 times, most recently from f3919d1 to 6bc6b12 Compare October 4, 2018 21:10
@olljanat
Copy link
Contributor Author

olljanat commented Oct 5, 2018

@wk8 I can see that you have been reviewing many PRs here so can you take a look also this one?

@dperny
Copy link
Collaborator

dperny commented Oct 8, 2018

LGTM

@anshulpundir and @wk8, can one of y'all sign off and merge?

Copy link
Contributor

@wk8 wk8 left a comment

Choose a reason for hiding this comment

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

Minor nitpicks :)

// SetTask returns true when max replicas per node filter > 0 for a given task.
func (f *MaxReplicasFilter) SetTask(t *api.Task) bool {
if t.Spec.Placement != nil {
if t.Spec.Placement.MaxReplicas > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for the 2 ifs, just use &&?

return true
}

return false
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe more readable as return uint64(n.ActiveTasksCountByService[f.t.ServiceID]) < f.t.Spec.Placement.MaxReplicas ?

manager/scheduler/scheduler_test.go Show resolved Hide resolved
@olljanat olljanat force-pushed the replicas-max-per-node branch 2 times, most recently from f58495f to 1c93a4b Compare October 14, 2018 17:22
@olljanat olljanat force-pushed the replicas-max-per-node branch from 1c93a4b to 6553bbe Compare October 14, 2018 17:42
@olljanat
Copy link
Contributor Author

@wk8 Changes which you requested are now in-place :)

Copy link
Contributor

@wk8 wk8 left a comment

Choose a reason for hiding this comment

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

Thank you! :)

Copy link
Member

@cpuguy83 cpuguy83 left a comment

Choose a reason for hiding this comment

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

LGTM

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.

5 participants