From 006f5e71188c213f717b6254ec323ac0195e0180 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Fri, 14 Nov 2014 15:49:45 -0800 Subject: [PATCH] Initial scheduler implementation. The scheduler is composed of filters (to apply constraints) and a placement strategy (random, bin packing, ...). Signed-off-by: Andrea Luzzardi --- scheduler/filter/filter.go | 24 +++++++++++++ scheduler/scheduler.go | 62 ++++++++++++++++++++++++++++++++++ scheduler/strategy/random.go | 19 +++++++++++ scheduler/strategy/strategy.go | 12 +++++++ 4 files changed, 117 insertions(+) create mode 100644 scheduler/filter/filter.go create mode 100644 scheduler/scheduler.go create mode 100644 scheduler/strategy/random.go create mode 100644 scheduler/strategy/strategy.go diff --git a/scheduler/filter/filter.go b/scheduler/filter/filter.go new file mode 100644 index 0000000000..5c3a8aa02c --- /dev/null +++ b/scheduler/filter/filter.go @@ -0,0 +1,24 @@ +package filter + +import ( + "github.com/docker/libcluster" + "github.com/samalba/dockerclient" +) + +type Filter interface { + // Return a subset of nodes that were accepted by the filtering policy. + Filter(*dockerclient.ContainerConfig, []*libcluster.Node) ([]*libcluster.Node, error) +} + +// Apply a set of filters in batch. +func ApplyFilters(filters []Filter, config *dockerclient.ContainerConfig, nodes []*libcluster.Node) ([]*libcluster.Node, error) { + var err error + + for _, filter := range filters { + nodes, err = filter.Filter(config, nodes) + if err != nil { + return nil, err + } + } + return nodes, nil +} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go new file mode 100644 index 0000000000..93f39f89b9 --- /dev/null +++ b/scheduler/scheduler.go @@ -0,0 +1,62 @@ +package scheduler + +import ( + "sync" + + "github.com/docker/libcluster" + "github.com/docker/libcluster/scheduler/filter" + "github.com/docker/libcluster/scheduler/strategy" + "github.com/samalba/dockerclient" +) + +type Scheduler struct { + sync.Mutex + + cluster *libcluster.Cluster + strategy strategy.PlacementStrategy + filters []filter.Filter +} + +func NewScheduler(cluster *libcluster.Cluster) *Scheduler { + return &Scheduler{ + cluster: cluster, + strategy: &strategy.RandomPlacementStrategy{}, + filters: []filter.Filter{}, + } +} + +// Find a nice home for our container. +func (s *Scheduler) selectNodeForContainer(config *dockerclient.ContainerConfig) (*libcluster.Node, error) { + candidates := []*libcluster.Node{} + for _, node := range s.cluster.Nodes() { + candidates = append(candidates, node) + } + + accepted, err := filter.ApplyFilters(s.filters, config, candidates) + if err != nil { + return nil, err + } + + return s.strategy.PlaceContainer(config, accepted) +} + +// Schedule a brand new container into the cluster. +func (s *Scheduler) CreateContainer(config *dockerclient.ContainerConfig, name string) (*libcluster.Container, error) { + s.Lock() + defer s.Unlock() + + node, err := s.selectNodeForContainer(config) + if err != nil { + return nil, err + } + return node.Create(config, name, true) +} + +// Remove a container from the cluster. Containers should always be destroyed +// through the scheduler to guarantee atomicity. +func (s *Scheduler) RemoveContainer(container *libcluster.Container, force bool) error { + s.Lock() + defer s.Unlock() + + return container.Node().Remove(container, force) +} diff --git a/scheduler/strategy/random.go b/scheduler/strategy/random.go new file mode 100644 index 0000000000..3df79e1b6e --- /dev/null +++ b/scheduler/strategy/random.go @@ -0,0 +1,19 @@ +package strategy + +import ( + "errors" + + "github.com/docker/libcluster" + "github.com/samalba/dockerclient" +) + +// Randomly place the container into the cluster. +type RandomPlacementStrategy struct { +} + +func (p *RandomPlacementStrategy) PlaceContainer(config *dockerclient.ContainerConfig, nodes []*libcluster.Node) (*libcluster.Node, error) { + for _, node := range nodes { + return node, nil + } + return nil, errors.New("No nodes running in the cluster") +} diff --git a/scheduler/strategy/strategy.go b/scheduler/strategy/strategy.go new file mode 100644 index 0000000000..5356403671 --- /dev/null +++ b/scheduler/strategy/strategy.go @@ -0,0 +1,12 @@ +package strategy + +import ( + "github.com/docker/libcluster" + "github.com/samalba/dockerclient" +) + +type PlacementStrategy interface { + // Given a container configuration and a set of nodes, select the target + // node where the container should be scheduled. + PlaceContainer(config *dockerclient.ContainerConfig, nodes []*libcluster.Node) (*libcluster.Node, error) +}