mirror of https://github.com/docker/docs.git
Filter for containerslots-label
Signed-off-by: michael.freund <michael.freund@shopgate.com>
This commit is contained in:
parent
e8cb44fe81
commit
0ccc958b50
|
@ -25,6 +25,7 @@ Each filter has a name that identifies it. The node filters are:
|
|||
|
||||
* `constraint`
|
||||
* `health`
|
||||
* `containerslots`
|
||||
|
||||
The container configuration filters are:
|
||||
|
||||
|
@ -48,6 +49,8 @@ $ swarm manage --filter=health --filter=dependency
|
|||
|
||||
When creating a container or building an image, you use a `constraint` or
|
||||
`health` filter to select a subset of nodes to consider for scheduling.
|
||||
If there are nodes in the swarm that have a label with key as `containerslots`
|
||||
and a number-value, Swarm will not launch more containers than the given number.
|
||||
|
||||
### Use a constraint filter
|
||||
|
||||
|
@ -175,6 +178,16 @@ The node `health` filter prevents the scheduler form running containers
|
|||
on unhealthy nodes. A node is considered unhealthy if the node is down or it
|
||||
can't communicate with the cluster store.
|
||||
|
||||
### Use the containerslots filter
|
||||
|
||||
You may give your Docker nodes the containerslots label
|
||||
```bash
|
||||
$ docker daemon --label containerslots=3
|
||||
```
|
||||
Swarm will prevent running more than three containers at this node, if
|
||||
all nodes are "full", an error is thrown. If the value is not castable
|
||||
to an integer number or is not present, there will be no limit.
|
||||
|
||||
## Container filters
|
||||
|
||||
When creating a container, you can use three types of container filters:
|
||||
|
|
|
@ -30,6 +30,7 @@ func init() {
|
|||
filters = []Filter{
|
||||
&HealthFilter{},
|
||||
&PortFilter{},
|
||||
&SlotsFilter{},
|
||||
&DependencyFilter{},
|
||||
&AffinityFilter{},
|
||||
&ConstraintFilter{},
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package filter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoNodeWithFreeSlotsAvailable is exported
|
||||
ErrNoNodeWithFreeSlotsAvailable = errors.New("No node with enough open slots available in the cluster")
|
||||
)
|
||||
|
||||
//SlotsFilter only schedules containers with open slots.
|
||||
type SlotsFilter struct {
|
||||
}
|
||||
|
||||
// Name returns the name of the filter
|
||||
func (f *SlotsFilter) Name() string {
|
||||
return "containerslots"
|
||||
}
|
||||
|
||||
// Filter is exported
|
||||
func (f *SlotsFilter) Filter(_ *cluster.ContainerConfig, nodes []*node.Node, _ bool) ([]*node.Node, error) {
|
||||
result := []*node.Node{}
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
if slotsString, ok := node.Labels["containerslots"]; ok {
|
||||
slots, err := strconv.Atoi(slotsString) //if err => cannot cast to int, so ignore the label
|
||||
if err != nil || len(node.Containers) < slots {
|
||||
result = append(result, node)
|
||||
}
|
||||
} else {
|
||||
//no limit if label is missing
|
||||
result = append(result, node)
|
||||
}
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, ErrNoNodeWithFreeSlotsAvailable
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetFilters returns just the info that this node failed, because there where no free slots
|
||||
func (f *SlotsFilter) GetFilters(config *cluster.ContainerConfig) ([]string, error) {
|
||||
return []string{"free slots"}, nil
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
package filter
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/swarm/cluster"
|
||||
"github.com/docker/swarm/scheduler/node"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var labelsWithSlots = make(map[string]string)
|
||||
var labelsWithoutSlots = make(map[string]string)
|
||||
var labelsWithStringSlot = make(map[string]string)
|
||||
|
||||
func testFixturesAllFreeNode() []*node.Node {
|
||||
return []*node.Node{
|
||||
{
|
||||
ID: "node-0-id",
|
||||
Name: "node-0-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "node-1-id",
|
||||
Name: "node-1-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testFixturesPartlyFreeNode() []*node.Node {
|
||||
return []*node.Node{
|
||||
{
|
||||
ID: "node-0-id",
|
||||
Name: "node-0-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "node-1-id",
|
||||
Name: "node-1-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testFixturesAllNoLabelNode() []*node.Node {
|
||||
return []*node.Node{
|
||||
{
|
||||
ID: "node-0-id",
|
||||
Name: "node-0-name",
|
||||
Labels: labelsWithoutSlots,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
ID: "node-1-id",
|
||||
Name: "node-1-name",
|
||||
Labels: labelsWithoutSlots,
|
||||
Containers: []*cluster.Container{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testFixturesNoFreeNode() []*node.Node {
|
||||
return []*node.Node{
|
||||
{
|
||||
ID: "node-0-id",
|
||||
Name: "node-0-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
ID: "node-1-id",
|
||||
Name: "node-1-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testFixturesNoFreeNodeButStringLabel() []*node.Node {
|
||||
return []*node.Node{
|
||||
{
|
||||
ID: "node-0-id",
|
||||
Name: "node-0-name",
|
||||
Labels: labelsWithSlots,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
ID: "node-1-id",
|
||||
Name: "node-1-name",
|
||||
Labels: labelsWithStringSlot,
|
||||
Containers: []*cluster.Container{
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
{Container: types.Container{}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlotsFilter(t *testing.T) {
|
||||
|
||||
labelsWithSlots["containerslots"] = "3"
|
||||
labelsWithStringSlot["containerslots"] = "foo"
|
||||
|
||||
var (
|
||||
f = SlotsFilter{}
|
||||
nodesAllFree = testFixturesAllFreeNode()
|
||||
nodesPartlyFree = testFixturesPartlyFreeNode()
|
||||
nodesAllNoLabel = testFixturesAllNoLabelNode()
|
||||
nodesNoFree = testFixturesNoFreeNode()
|
||||
nodesNoFreeButStringLabel = testFixturesNoFreeNodeButStringLabel()
|
||||
result []*node.Node
|
||||
err error
|
||||
)
|
||||
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodesAllFree, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodesAllFree)
|
||||
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodesPartlyFree, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodesPartlyFree[1])
|
||||
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodesAllNoLabel, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, result, nodesAllNoLabel)
|
||||
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodesNoFree, true)
|
||||
assert.Equal(t, err, ErrNoNodeWithFreeSlotsAvailable)
|
||||
assert.Nil(t, result)
|
||||
|
||||
result, err = f.Filter(&cluster.ContainerConfig{}, nodesNoFreeButStringLabel, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodesNoFreeButStringLabel[1])
|
||||
}
|
|
@ -18,7 +18,7 @@ function teardown() {
|
|||
run docker_swarm build --build-arg="constraint:node==node-9" $TESTDATA/build
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${lines[1]}" == *"Unable to find a node that satisfies the following conditions"* ]]
|
||||
[[ "${lines[2]}" == *"[node==node-9]"* ]]
|
||||
[[ "${lines[3]}" == *"[node==node-9]"* ]]
|
||||
|
||||
run docker_swarm images -q
|
||||
[ "$status" -eq 0 ]
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function teardown() {
|
||||
swarm_manage_cleanup
|
||||
stop_docker
|
||||
}
|
||||
|
||||
@test "containerslots filter" {
|
||||
start_docker_with_busybox 2 --label containerslots=2
|
||||
swarm_manage
|
||||
|
||||
# Use busybox to save image pulling time for integration test.
|
||||
# Running the first 4 containers, it should be fine.
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# When trying to start the 5th one, it should be error finding a node with free slots.
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "${lines[0]}" == *"Unable to find a node that satisfies the following conditions"* ]]
|
||||
[[ "${lines[1]}" == *"free slots"* ]]
|
||||
|
||||
# And the number of running containers should be still 4.
|
||||
run docker_swarm ps
|
||||
[ "${#lines[@]}" -eq 5 ]
|
||||
}
|
||||
|
||||
@test "containerslots without existing label" {
|
||||
start_docker_with_busybox 2
|
||||
swarm_manage
|
||||
|
||||
# Use busybox to save image pulling time for integration test.
|
||||
# Running more than 5 containers, it should be fine.
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# And the number of running containers should be 5.
|
||||
run docker_swarm ps
|
||||
[ "${#lines[@]}" -eq 6 ]
|
||||
}
|
||||
|
||||
@test "containerslots with invalid label" {
|
||||
start_docker_with_busybox 2 --label containerslots="foo"
|
||||
swarm_manage
|
||||
|
||||
# Use busybox to save image pulling time for integration test.
|
||||
# Running more than 5 containers, it should be fine.
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
run docker_swarm run -d -t busybox sh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# And the number of running containers should be 5.
|
||||
run docker_swarm ps
|
||||
[ "${#lines[@]}" -eq 6 ]
|
||||
}
|
Loading…
Reference in New Issue