mirror of https://github.com/docker/docs.git
Add support for soft affinities
Add documentation for soft affinity trim ~ if the expression is not regex Add example for !=~ Signed-off-by: sivaram mothiki <sivaram@opdemand.com>
This commit is contained in:
parent
24b46bb002
commit
204216f173
|
@ -185,6 +185,23 @@ For example,
|
|||
* `constraint:node!=/foo\[bar\]/` will match all nodes, except `foo[bar]`. You can see the use of escape characters here.
|
||||
* `constraint:node==/(?i)node1/` will match node `node1` case-insensitive. So 'NoDe1' or 'NODE1' will also match.
|
||||
|
||||
#### Soft Affinities
|
||||
|
||||
By default, affinities are hard enforced. If an affinity is not met, the container won't be scheduled.
|
||||
With soft affinites the scheduler will try to meet the affinity. If it is not met, the scheduler will discard the filter and schedule the container according to the scheduler strategy.
|
||||
|
||||
soft affinites are expressed with a **~** in the expression
|
||||
Example ,
|
||||
```
|
||||
$ docker run -d --name redis1 -e affinity:image==~redis redis
|
||||
```
|
||||
If none of the nodes in the cluster has image redis, the scheduler will discard the affinity and schedules according to the strategy.
|
||||
|
||||
```
|
||||
$ docker run -d --name redis5 -e affinity:container!=~redis* redis
|
||||
```
|
||||
The affinity filter is about scheduling a new redis5 container to a different node that doesn't have a container with the name that satisfies redis*. If each node in the cluster has a redis* container the scheduler with discard the affinity rule and schedules according to the strategy.
|
||||
|
||||
## Port Filter
|
||||
|
||||
With this filter, `ports` are considered as unique resources.
|
||||
|
|
|
@ -48,6 +48,9 @@ func (f *AffinityFilter) Filter(config *dockerclient.ContainerConfig, nodes []cl
|
|||
}
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
if affinity.IsSoft() {
|
||||
return nodes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find a node that satisfies %s%s%s", affinity.key, OPERATORS[affinity.operator], affinity.value)
|
||||
}
|
||||
nodes = candidates
|
||||
|
|
|
@ -193,6 +193,38 @@ func TestAffinityFilter(t *testing.T) {
|
|||
}, nodes)
|
||||
assert.Error(t, err)
|
||||
|
||||
//Tests for Soft affinity
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~image-0:tag3"},
|
||||
}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~image-1:tag3"},
|
||||
}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 3)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~image-*"},
|
||||
}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image!=~image-*"},
|
||||
}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0], nodes[2])
|
||||
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image==~/image-\\d*/"},
|
||||
}, nodes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 2)
|
||||
|
||||
// Not support = any more
|
||||
result, err = f.Filter(&dockerclient.ContainerConfig{
|
||||
Env: []string{"affinity:image=image-0:tag3"},
|
||||
|
@ -206,4 +238,5 @@ func TestAffinityFilter(t *testing.T) {
|
|||
}, nodes)
|
||||
assert.Error(t, err)
|
||||
assert.Len(t, result, 0)
|
||||
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ func parseExprs(key string, env []string) ([]expr, error) {
|
|||
// allow leading = in case of using ==
|
||||
// allow * for globbing
|
||||
// allow regexp
|
||||
matched, err := regexp.MatchString(`^(?i)[=!\/]?[a-z0-9:\-_\.\*/\(\)\?\+\[\]\\\^\$]+$`, parts[1])
|
||||
matched, err := regexp.MatchString(`^(?i)[=!\/]?(~)?[a-z0-9:\-_\.\*/\(\)\?\+\[\]\\\^\$]+$`, parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -74,17 +74,24 @@ func parseExprs(key string, env []string) ([]expr, error) {
|
|||
|
||||
func (e *expr) Match(whats ...string) bool {
|
||||
var (
|
||||
pattern string
|
||||
match bool
|
||||
err error
|
||||
pattern string
|
||||
match bool
|
||||
err error
|
||||
startIndex int
|
||||
)
|
||||
|
||||
if e.value[0] == '/' && e.value[len(e.value)-1] == '/' {
|
||||
if e.IsSoft() {
|
||||
startIndex = 1
|
||||
} else {
|
||||
startIndex = 0
|
||||
}
|
||||
|
||||
if e.value[startIndex] == '/' && e.value[len(e.value)-1] == '/' {
|
||||
// regexp
|
||||
pattern = e.value[1 : len(e.value)-1]
|
||||
pattern = e.value[startIndex+1 : len(e.value)-1]
|
||||
} else {
|
||||
// simple match, create the regex for globbing (ex: ub*t* -> ^ub.*t.*$) and match.
|
||||
pattern = "^" + strings.Replace(e.value, "*", ".*", -1) + "$"
|
||||
pattern = "^" + strings.Replace(strings.Trim(e.value, "~"), "*", ".*", -1) + "$"
|
||||
}
|
||||
|
||||
for _, what := range whats {
|
||||
|
@ -104,3 +111,11 @@ func (e *expr) Match(whats ...string) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *expr) IsSoft() bool {
|
||||
if e.value[0] == '~' {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue