alice

package module
v0.0.0-...-2383540 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 17, 2017 License: MIT Imports: 18 Imported by: 0

README

# Alice

Alice is an autoscaler used at Notonthehighstreet. It is designed to scale up and down server and application instances based on daily demand, and is designed to be flexible and easy to extend.

Alice is written in Go and runs as a standalone background process in our production environment. Using metrics from a monitoring provider, it can make decisions on how to scale an inventory of resources. An inventory can be any set of resources that provide the capacity to deal with current demand - for example, the servers in a server farm, or the number of docker containers running on an instance of your application.

To support different monitoring providers and resources types, we have to write a monitor or inventory plugin to talk to the different backend APIs.

The currently supported backends are:

  • Monitors: Datadog, Stats directly from Mesos
  • Inventories: Marathon applications, AWS EC2 instances (via autoscaling groups)

It's relatively easy to write plugins for additional backends as required.

In addition to this, Alice is also easy to integrate with Slack and Fluentd.

Build

We use glide for vendoring dependencies, so once you have cloned this repository you will want to install it. This is usually fairly straightfoward:

OSX: brew install glide

Ubuntu: sudo add-apt-repository ppa:masterminds/glide && sudo apt-get update &&sudo apt-get install glide

Once this is installed you can install all the dependencies (into ./vendor by default):

glide install

To build Alice, just go build.

Configuration

When Alice starts up it reads ./config/config.yaml which should be relative to the executable's working directory. Copying and editing the config/config.yaml.dist file is a good place to start.

The main body of the configuration is under the managers section of the config file. Alice can manage multiple resource inventories at a time. A manager is a grouping of an inventory to be managed, a monitor from which to collect metrics, and a strategy which determines what scaling should be done on the inventory taking the current metrics into account.

A simple manager configuration might look like this:

managers:
  # Unique name of this manager
  my_web_application:
    monitor:
      # A datadog plugin example
      name: datadog
      api_key: xxxxxx
      app_key: xxxxxx
      time_period: 5m  # Most recent data point must be within this time
      metrics:
        active_users:
          query: avg:application.users.active{*}

    inventory:
      # A marathon application plugin example
      name: marathon
      settle_down_period: 3m
      url: http://marathon.example.com:8080
      app: www_example_com  # Application ID in marathon
      minimum_instances: 1
      maximum_instances: 10

    strategy:
       # A ratio strategy plugin example
      name: ratio
      ratios:
        active_users:
          metric: 100  # Keep one application instance for every 100 active users
          inventory: 1

How to test the software

The tests for Alice can be run using go test like this: go test -race -cover $(go list ./... | grep -v /vendor/)

Alternatively you can run make test

Getting help

If you have questions, concerns, bug reports, etc, please file an issue in this repository's Issue Tracker.

Getting involved

If you'd like to get involved and make Alice better, we'd be happy to accept your help!

Please read CONTRIBUTING for more information on how to contribute to Alice.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterInventory

func RegisterInventory(name string, factory inventoryFactoryFunc)

RegisterInventory allows a new inventory type to be registered with a string name. This name is used to match configuration to the correct NewFooInventory function that can read it.

func RegisterMonitor

func RegisterMonitor(name string, factory monitorFactoryFunc)

RegisterMonitor allows a new monitor type to be registered with a string name. This name is used to match configuration to the correct NewFooMonitor function that can read it.

func RegisterStrategy

func RegisterStrategy(name string, factory strategyFactoryFunc)

RegisterStrategy allows a new strategy type to be registered with a string name. This name is used to match configuration to the correct NewFooStrategy function that can read it.

Types

type AWSInventory

type AWSInventory struct {
	Config         *viper.Viper
	AutoscalingSvc autoscalingiface.AutoScalingAPI
	EC2metadataSvc EC2MetadataAPI
	// contains filtered or unexported fields
}

AWSInventory is an inventory of AWS EC2 instances in an autoscaling group

func (*AWSInventory) Decrease

func (a *AWSInventory) Decrease() error

Decrease (scale down) the number of resources in the inventory

func (*AWSInventory) GroupName

func (a *AWSInventory) GroupName() string

GroupName returns the autoscaling group for this inventory

func (*AWSInventory) Increase

func (a *AWSInventory) Increase() error

Increase (scale up) the number of resources in the inventory

func (*AWSInventory) RefreshMetadata

func (a *AWSInventory) RefreshMetadata()

RefreshMetadata pulls updated metadata

func (*AWSInventory) Scale

func (a *AWSInventory) Scale(amount int) error

Scale attempts to increase the number of instances by the amount specified

func (*AWSInventory) Status

func (a *AWSInventory) Status() (Status, error)

Status returns OK if the inventory is ready to be scaled, UPDATING if an update is in progress, or FAILED

func (*AWSInventory) Total

func (a *AWSInventory) Total() (int, error)

Total returns the current total number of resources

type AWSMetadata

type AWSMetadata struct {
	// contains filtered or unexported fields
}

AWSMetadata provides region and instance id metadata

type DatadogMonitor

type DatadogMonitor struct {
	Client DatadogMonitorClient
	// contains filtered or unexported fields
}

DatadogMonitor is a monitor that can pull metrics from Datadog

func (*DatadogMonitor) GetUpdatedMetrics

func (d *DatadogMonitor) GetUpdatedMetrics(names []string) (*[]MetricUpdate, error)

GetUpdatedMetrics returns MetricUpdates for each of the metrics requested

type DatadogMonitorClient

type DatadogMonitorClient interface {
	Validate() (bool, error)
	QueryMetrics(int64, int64, string) ([]datadog.Series, error)
}

DatadogMonitorClient is an intenface allowing mocks of go-datadog-api

type EC2MetadataAPI

type EC2MetadataAPI interface {
	GetMetadata(string) (string, error)
}

EC2MetadataAPI is an interface allowing mocks of the AWS client

type FakeInventory

type FakeInventory struct {
	// contains filtered or unexported fields
}

FakeInventory doesn't control a real inventory, instead it stores a count of dummy objects

func (*FakeInventory) Decrease

func (f *FakeInventory) Decrease() error

Decrease (scale down) the number of resources in the inventory

func (*FakeInventory) Increase

func (f *FakeInventory) Increase() error

Increase (scale up) the number of resources in the inventory

func (*FakeInventory) Status

func (f *FakeInventory) Status() (Status, error)

Status returns OK if the inventory is ready to be scaled, UPDATING if an update is in progress, or FAILED

func (*FakeInventory) Total

func (f *FakeInventory) Total() (int, error)

Total returns the current total number of resources

type FakeMonitor

type FakeMonitor struct {
	// contains filtered or unexported fields
}

FakeMonitor is a dummy monitor that will generate numbers for any metric requested. Values returned for metrics are between 1 and 100 and follow a sine-wave pattern which is useful for testing demand that gradually changes with time.

func (*FakeMonitor) GetUpdatedMetrics

func (f *FakeMonitor) GetUpdatedMetrics(names []string) (*[]MetricUpdate, error)

GetUpdatedMetrics returns MetricUpdates for each of the metrics requested

type Inventory

type Inventory interface {
	Total() (int, error)
	Increase() error
	Decrease() error
	Status() (Status, error)
}

Inventory represents the generic inventory interface. An inventory can manage any type of resource (server instances, application instances, sheep etc). As long as it can return a total, be scaled up and down, and let us know if the inventory is healthy, then it will work..

func NewAWSInventory

func NewAWSInventory(config *viper.Viper, log *logrus.Entry) (Inventory, error)

NewAWSInventory creates a new AWSInventory

func NewFakeInventory

func NewFakeInventory(config *viper.Viper, log *logrus.Entry) (Inventory, error)

NewFakeInventory creates a new Inventory

func NewInventory

func NewInventory(config *viper.Viper, log *logrus.Entry) (Inventory, error)

NewInventory will take a generic block of configuration and read look for a 'name' key, and immediately pass the block of config to the factory function that has been registered with that name.

func NewMarathonInventory

func NewMarathonInventory(config *viper.Viper, log *logrus.Entry) (Inventory, error)

NewMarathonInventory creates a new Inventory

type Manager

type Manager struct {
	Inventory Inventory
	Logger    *logrus.Entry
	Strategy  Strategy
	Config    *viper.Viper
}

Manager ties together the inventory and the strategy. It will evaluate the strategy and will execute scaling actions on the inventory based on the recommendation it received.

func New

func New(config *viper.Viper, log *logrus.Entry) (Manager, error)

New creates a new Manager

func (*Manager) Run

func (m *Manager) Run() error

Run requests a recommendation from the strategy, and if not running in dry-run mode will attempt to scale up the inventory.

type MarathonInventory

type MarathonInventory struct {
	Client MarathonInventoryClient
	Config *viper.Viper
	// contains filtered or unexported fields
}

MarathonInventory is an inventory of instances running as a marathon application in Marathon

func (*MarathonInventory) Decrease

func (m *MarathonInventory) Decrease() error

Decrease (scale down) the number of resources in the inventory

func (*MarathonInventory) GetApplication

func (m *MarathonInventory) GetApplication() (*marathon.Application, error)

GetApplication returns the marathon.Application for the current application being managed

func (*MarathonInventory) Increase

func (m *MarathonInventory) Increase() error

Increase (scale up) the number of resources in the inventory

func (*MarathonInventory) Scale

func (m *MarathonInventory) Scale(amount int) error

Scale attempts to increase the number of instances by the amount specified

func (*MarathonInventory) Status

func (m *MarathonInventory) Status() (Status, error)

Status returns OK if the inventory is ready to be scaled, UPDATING if an update is in progress, or FAILED

func (*MarathonInventory) Total

func (m *MarathonInventory) Total() (int, error)

Total returns the current total number of resources

type MarathonInventoryClient

type MarathonInventoryClient interface {
	ApplicationBy(name string, opts *marathon.GetAppOpts) (*marathon.Application, error)
	ScaleApplicationInstances(name string, instances int, force bool) (*marathon.DeploymentID, error)
}

MarathonInventoryClient is an intenface allowing mocks of go-marathon

type MesosClient

type MesosClient interface {
	GetStateFromLeader() (*megos.State, error)
	DetermineLeader() (*megos.Pid, error)
}

MesosClient allows mocks of the megos client

type MesosMonitor

type MesosMonitor struct {
	Client MesosClient
	// contains filtered or unexported fields
}

MesosMonitor can pull metrics directly from the Mesos Stats API endpoint

func (*MesosMonitor) GetUpdatedMetrics

func (m *MesosMonitor) GetUpdatedMetrics(names []string) (*[]MetricUpdate, error)

GetUpdatedMetrics returns MetricUpdates for each of the metrics requested

func (*MesosMonitor) Stats

func (m *MesosMonitor) Stats() (*MesosMonitorStats, error)

Stats calculates interesting metrics about the Mesos cluster and the slaves

type MesosMonitorStats

type MesosMonitorStats struct {
	Metrics map[string]float64
}

MesosMonitorStats holds a set of calculated metrics

type MetricUpdate

type MetricUpdate struct {
	Name           string
	CurrentReading float64
}

MetricUpdate stores the name of the metric requested and its current reading.

type Monitor

type Monitor interface {
	GetUpdatedMetrics([]string) (*[]MetricUpdate, error)
}

Monitor represents the generic Monitor interface. Monitors are a source of information that feed in to strategies. Given a list of metrics (often in dot-notation for a lot of providers, eg sys.mem.free), a monitor must provide a current reading for each one.

func NewDatadogMonitor

func NewDatadogMonitor(config *viper.Viper, log *logrus.Entry) (Monitor, error)

NewDatadogMonitor returns a new DatadogMonitor

func NewFakeMonitor

func NewFakeMonitor(config *viper.Viper, log *logrus.Entry) (Monitor, error)

NewFakeMonitor returns a new Monitor

func NewMesosMonitor

func NewMesosMonitor(config *viper.Viper, log *logrus.Entry) (Monitor, error)

NewMesosMonitor creates a new Monitor

func NewMonitor

func NewMonitor(config *viper.Viper, log *logrus.Entry) (Monitor, error)

NewMonitor will take a generic block of configuration and read look for a 'name' key, and immediately pass the block of config to the factory function that has been registered with that name.

type RatioStrategy

type RatioStrategy struct {
	Config    *viper.Viper
	Inventory Inventory
	Monitor   Monitor
	// contains filtered or unexported fields
}

RatioStrategy tries to keep the resources in an inventory at a set ratio to a current metric reading

func (*RatioStrategy) Evaluate

func (r *RatioStrategy) Evaluate() (*Recommendation, error)

Evaluate will pull data from the associated Monitor and return a scaling recommendation

type Recommendation

type Recommendation int

Recommendation is the return type representing the action the strategy recommends the Manager take

const (
	// SCALEDOWN - we have too much inventory, decrease it
	SCALEDOWN Recommendation = iota - 1
	// HOLD - the inventory is just right
	HOLD
	// SCALEUP - we have too little inventory, increase it
	SCALEUP
)

type Status

type Status int

Status represents the various statuses that can be returned by an inventory's Status() function.

const (
	// OK - means the inventory is ready to be scaled and we can call Increase/Decrease.
	OK Status = iota
	// UPDATING - means the inventory is still making changes after the last scaling action and we should wait.
	UPDATING
	// FAILED - means something is wrong, and we should definitely not trust the state of the inventory or change anything
	FAILED
)

type Strategy

type Strategy interface {
	Evaluate() (*Recommendation, error)
}

Strategy represents the generic Strategy interface. A strategy contains the logic required to take some information and come to a decision about whether an inventory of resources needs to be increased or decreased. It doesn't make any changes, and only provides a recommendation to a Manager.

func NewRatioStrategy

func NewRatioStrategy(config *viper.Viper, inv Inventory, mon Monitor, log *logrus.Entry) (Strategy, error)

NewRatioStrategy creates a new Strategy

func NewStrategy

func NewStrategy(config *viper.Viper, inv Inventory, mon Monitor, log *logrus.Entry) (Strategy, error)

NewStrategy will take a generic block of configuration and read look for a 'name' key, and immediately pass the block of config to the factory function that has been registered with that name.

func NewThresholdStrategy

func NewThresholdStrategy(config *viper.Viper, inv Inventory, mon Monitor, log *logrus.Entry) (Strategy, error)

NewThresholdStrategy creates a new Strategy

type ThresholdStrategy

type ThresholdStrategy struct {
	Config *viper.Viper
	// <metric name>: [<lower threshold>, <upper threshold>]
	Inventory Inventory
	Monitor   Monitor
	// contains filtered or unexported fields
}

ThresholdStrategy aims to keep the value in the middle but will always recommend scaling up if any metric is above it's upper threshold

func (*ThresholdStrategy) Evaluate

func (p *ThresholdStrategy) Evaluate() (*Recommendation, error)

Evaluate will pull data from the associated Monitor and return a scaling recommendation

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL