From 8dbb9aeeb15f177fe3c9c9f78075080b6ee4a087 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Tue, 18 Nov 2014 15:54:46 -0800 Subject: [PATCH 1/4] Removed debug log when refreshing containers after creation Signed-off-by: Andrea Luzzardi --- cluster/node.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cluster/node.go b/cluster/node.go index 78897032f2..fd6cedb4eb 100644 --- a/cluster/node.go +++ b/cluster/node.go @@ -206,7 +206,6 @@ func (n *Node) Create(config *dockerclient.ContainerConfig, name string, pullIma // Register the container immediately while waiting for a state refresh. // Force a state refresh to pick up the newly created container. - log.Debug("Updating containers after create") n.updateContainers() return n.containers[id], nil From 41071a88a7bfb1507cb8e72fd0a9afa4a06b0a52 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Tue, 18 Nov 2014 16:03:36 -0800 Subject: [PATCH 2/4] Port Filtering. Consider only nodes that have the requested port available, and abort the container creation if none is found. Signed-off-by: Andrea Luzzardi --- manage.go | 2 +- scheduler/filter/port.go | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 scheduler/filter/port.go diff --git a/manage.go b/manage.go index f3fbc8ec6e..ee4767fd47 100644 --- a/manage.go +++ b/manage.go @@ -57,7 +57,7 @@ func manage(c *cli.Context) { } } - s := scheduler.NewScheduler(cluster, &strategy.BinPackingPlacementStrategy{}, []filter.Filter{}) + s := scheduler.NewScheduler(cluster, &strategy.BinPackingPlacementStrategy{}, []filter.Filter{&filter.PortFilter{}}) log.Fatal(api.ListenAndServe(cluster, s, c.String("addr"))) } diff --git a/scheduler/filter/port.go b/scheduler/filter/port.go new file mode 100644 index 0000000000..9bd2a04f6c --- /dev/null +++ b/scheduler/filter/port.go @@ -0,0 +1,55 @@ +package filter + +import ( + "fmt" + + "github.com/docker/swarm/cluster" + "github.com/samalba/dockerclient" +) + +// PortFilter guarantees that, when scheduling a container binding a public +// port, only nodes that have not already allocated that same port will be +// considered. +type PortFilter struct { +} + +func (p *PortFilter) Filter(config *dockerclient.ContainerConfig, nodes []*cluster.Node) ([]*cluster.Node, error) { + for _, port := range config.HostConfig.PortBindings { + for _, binding := range port { + candidates := []*cluster.Node{} + for _, node := range nodes { + if !p.portAlreadyInUse(node, binding) { + candidates = append(candidates, node) + } + } + if len(candidates) == 0 { + return nil, fmt.Errorf("unable to find a node with port %s available", binding.HostPort) + } + nodes = candidates + } + } + return nodes, nil +} + +func (p *PortFilter) portAlreadyInUse(node *cluster.Node, requested dockerclient.PortBinding) bool { + for _, c := range node.Containers() { + for _, port := range c.Info.NetworkSettings.Ports { + for _, binding := range port { + if binding.HostPort == requested.HostPort { + // Another container on the same host is binding on the same + // port/protocol. Verify if they are requesting the same + // binding IP, or if the other container is already binding on + // every interface. + if requested.HostIp == binding.HostIp || bindsAllInterfaces(requested) || bindsAllInterfaces(binding) { + return true + } + } + } + } + } + return false +} + +func bindsAllInterfaces(binding dockerclient.PortBinding) bool { + return binding.HostIp == "0.0.0.0" || binding.HostIp == "" +} From a40ebef79e29267433a540cd2112942364acb36a Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Tue, 18 Nov 2014 16:32:44 -0800 Subject: [PATCH 3/4] Rename "graphdriver" to "storagedriver". Signed-off-by: Andrea Luzzardi --- cluster/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster/node.go b/cluster/node.go index fd6cedb4eb..bef5d35963 100644 --- a/cluster/node.go +++ b/cluster/node.go @@ -98,7 +98,7 @@ func (n *Node) updateSpecs() error { n.Cpus = info.NCPU n.Memory = info.MemTotal n.Labels = map[string]string{ - "graphdriver": info.Driver, + "storagedriver": info.Driver, "executiondriver": info.ExecutionDriver, "kernelversion": info.KernelVersion, "operatingsystem": info.OperatingSystem, From 4726477dbe1dd5ac3a7eee6430aee2f54f516b50 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Tue, 18 Nov 2014 17:00:27 -0800 Subject: [PATCH 4/4] Attribute filter: Schedule containers only on nodes that match certain attributes. Signed-off-by: Andrea Luzzardi --- manage.go | 2 +- scheduler/filter/attributes.go | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 scheduler/filter/attributes.go diff --git a/manage.go b/manage.go index ee4767fd47..eb15ee4555 100644 --- a/manage.go +++ b/manage.go @@ -57,7 +57,7 @@ func manage(c *cli.Context) { } } - s := scheduler.NewScheduler(cluster, &strategy.BinPackingPlacementStrategy{}, []filter.Filter{&filter.PortFilter{}}) + s := scheduler.NewScheduler(cluster, &strategy.BinPackingPlacementStrategy{}, []filter.Filter{&filter.AttributeFilter{}, &filter.PortFilter{}}) log.Fatal(api.ListenAndServe(cluster, s, c.String("addr"))) } diff --git a/scheduler/filter/attributes.go b/scheduler/filter/attributes.go new file mode 100644 index 0000000000..248834345e --- /dev/null +++ b/scheduler/filter/attributes.go @@ -0,0 +1,45 @@ +package filter + +import ( + "fmt" + "strings" + + "github.com/docker/swarm/cluster" + "github.com/samalba/dockerclient" +) + +// AttributeFilter selects only nodes that match certain attributes. Attributes +// include storagedriver, executiondriver and so on. +type AttributeFilter struct { +} + +func (f *AttributeFilter) extractConstraints(env []string) map[string]string { + constraints := make(map[string]string) + for _, e := range env { + if strings.HasPrefix(e, "constraint:") { + constraint := strings.TrimPrefix(e, "constraint:") + parts := strings.SplitN(constraint, "=", 2) + constraints[strings.ToLower(parts[0])] = strings.ToLower(parts[1]) + } + } + return constraints +} + +func (f *AttributeFilter) Filter(config *dockerclient.ContainerConfig, nodes []*cluster.Node) ([]*cluster.Node, error) { + constraints := f.extractConstraints(config.Env) + for k, v := range constraints { + candidates := []*cluster.Node{} + for _, node := range nodes { + if label, ok := node.Labels[k]; ok { + if strings.Contains(strings.ToLower(label), v) { + candidates = append(candidates, node) + } + } + } + if len(candidates) == 0 { + return nil, fmt.Errorf("unable to find a node that satisfies %s == %s", k, v) + } + nodes = candidates + } + return nodes, nil +}