Skip to content

Commit

Permalink
Improve algorithm configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
machadovilaca committed Apr 3, 2021
1 parent f25eb21 commit b4357a5
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 51 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/develop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
on:
push:
branches:
- develop

name: Publish Docker image
jobs:
push_to_registries:
name: Push Docker image
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: ${{ runner.os }}-buildx-

- name: Get current time
uses: 1466587594/get-current-time@v2
id: current-time
with:
format: YYYYMMDDHHmmss

- name: Build and Push
uses: docker/build-push-action@v2
with:
push: true
tags: ghcr.io/${{ github.repository_owner }}/aida-schedulder:${{ steps.current-time.outputs.formattedTime }}
21 changes: 15 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
FROM golang:1.16-alpine
FROM golang:1.16-alpine as builder

WORKDIR /code
WORKDIR /workspace

COPY . .
RUN go build && mv aida-scheduler /aida-scheduler
COPY go.mod go.sum ./
RUN go mod download

COPY utils/ utils/
COPY main.go main.go
COPY scheduler/ scheduler/
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build


FROM gcr.io/distroless/static:nonroot

WORKDIR /
RUN rm -rf /code
COPY --from=builder /workspace/aida-scheduler .
USER nonroot:nonroot

CMD /aida-scheduler
ENTRYPOINT ["/aida-scheduler"]
67 changes: 43 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,43 @@

[![Test](https://github.com/aida-dos/aida-scheduler/actions/workflows/test.yml/badge.svg?branch=develop)](https://github.com/aida-dos/aida-scheduler/actions/workflows/test.yml)

IoT and other data-intensive systems produce enormous amounts of data to be processed. In this project, we explore the
Edge Computing concept as additional computational power to the Cloud nodes, allowing data process computations to occur
closer to their source.

Using Kubernetes/KubeEdge to manage the data processing workloads, the aida-scheduler aims towards substantial
improvements in scalability levels, reducing the request latency and network usage, by scheduling those workloads in
Edge Nodes based on the geographical location of the data and resource availability.

## Usage

> :warning: First, make sure you have deployed or are running the aida-controller in the cluster by following the instructions in
the [project repository](https://github.com/aida-dos/aida-controller). This controller is responsible for the management and reconciliation of the AIDA EdgeDeployments, which are the
the resource type of our workloads.

## Deployment
### Configuration - Environment variables

`ALGORITHM` defines the method used to select target nodes for workload placement available algorithms are:
* `metricslocation`(default): where nodes are filtered based on if they have enough available resources, and
the target node is selected based on the deployment required/preferred locations,
* `geographiclocation`: where the target node is selected based on the deployment required/preferred locations
* `random`: where any edge node is a good fit for the given workloads


### Deployment

```shell
kubectl apply -f examples/release_scheduler_crd.yaml
```

### Build custom docker image
#### Build custom docker image
```shell
docker build -t aida-scheduler .
```

## Development
### Development

### Run the aida-scheduler
#### Run the aida-scheduler

To develop aida-scheduler in a Kubernetes cluster we use [ksync](https://github.com/ksync/ksync)
to sync files between our local system, and the cluster.
Expand All @@ -46,9 +65,27 @@ to sync files between our local system, and the cluster.
```shell
kubectl exec -it $(kubectl get pod -n kube-system | grep aida-scheduler | awk '{print $1}') -- sh
cd /code
go run main.go
ALGORITHM=geographiclocation go run main.go
```

#### Lint
```shell
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.39.0
golangci-lint ./...
```

#### Testing and Coverage
```shell
go test --coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```

#### Format

```shell
go fmt ./...
```

### Manage nodes

The aida-scheduler only manages Edge nodes, because the main purpose is to allow application workload to be deployed
Expand All @@ -64,7 +101,7 @@ labels.
```shell
kubectl label node node0 --overwrite node-role.kubernetes.io/edge-
```

### Deploy workloads

Apply any of the workload [examples](examples)
Expand All @@ -83,21 +120,3 @@ Apply any of the workload [examples](examples)
```shell
kubectl apply -f examples/workload_preferred_location.yaml
```

### Lint
```shell
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.39.0
golangci-lint ./...
```

### Testing and Coverage
```shell
go test --coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```

### Format

```shell
go fmt ./...
```
3 changes: 3 additions & 0 deletions examples/release_scheduler_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ spec:
containers:
- name: aida-scheduler
image: ghcr.io/aida-dos/aida-scheduler/aida-scheduler:latest
env:
- name: ALGORITHM
value: metricslocation
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
Expand Down
15 changes: 13 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package main

import "aida-scheduler/scheduler"
import (
"aida-scheduler/scheduler"
"k8s.io/klog/v2"
"os"
)

func main() {
scheduler.Run()
algorithm := os.Getenv("ALGORITHM")

if algorithm == "" {
algorithm = "metricslocation"
}

err := scheduler.Run(algorithm)
klog.Errorln(err)
}
1 change: 1 addition & 0 deletions scheduler/algorithms/algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import (

// Algorithm interface that exposes GetNode method
type Algorithm interface {
GetName() string
GetNode(pod *v1.Pod) (*nodes.Node, error)
}
4 changes: 4 additions & 0 deletions scheduler/algorithms/geographiclocation/geographiclocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func New(nodes nodes.INodes) algorithms.Algorithm {
}
}

func (geo *geographiclocation) GetName() string {
return "geographiclocation"
}

// GetNode select the best node matching the given constraints labels
// It returns error if there are no nodes available and if no node matches an existing 'requiredLocation' label
func (geo *geographiclocation) GetNode(pod *v1.Pod) (*nodes.Node, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,9 @@ func TestParseLocations(t *testing.T) {
assert.Equal(t, 1, len(geoStruct.countries))
assert.Equal(t, 1, len(geoStruct.continents))
}

func TestGetName(t *testing.T) {
geoStruct := newTestGeo(nil, nil)
name := geoStruct.GetName()
assert.Equal(t, "geographiclocation", name)
}
4 changes: 4 additions & 0 deletions scheduler/algorithms/metricslocation/metricslocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ func New(nodes nodes.INodes) algorithms.Algorithm {
}
}

func (geo *metricslocation) GetName() string {
return "metricslocation"
}

// GetNode select the best node matching the given constraints labels
// It returns error if there are no nodes available and if no node matches an existing 'requiredLocation' label
func (geo *metricslocation) GetNode(pod *v1.Pod) (*nodes.Node, error) {
Expand Down
6 changes: 6 additions & 0 deletions scheduler/algorithms/metricslocation/metricslocation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,9 @@ func TestNotEnoughMemoryResources(t *testing.T) {
_, err := geoStruct.GetNode(pod)
assert.Error(t, err)
}

func TestGetName(t *testing.T) {
geoStruct := newTestGeo(nil, nil)
name := geoStruct.GetName()
assert.Equal(t, "metricslocation", name)
}
4 changes: 4 additions & 0 deletions scheduler/algorithms/random/random.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ func New(inodes nodes.INodes) algorithms.Algorithm {
}
}

func (r random) GetName() string {
return "random"
}

func (r random) GetNode(*v1.Pod) (*nodes.Node, error) {
klog.Infoln("getting cached nodes")
return getRandomNode(r.inodes)
Expand Down
6 changes: 6 additions & 0 deletions scheduler/algorithms/random/random_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ func TestGetNode(t *testing.T) {
node, _ := getRandomNode(newTestRandomWithNode())
assert.Equal(t, "Node0", node.Name)
}

func TestGetName(t *testing.T) {
randomStruct := newTestRandom()
name := randomStruct.GetName()
assert.Equal(t, "random", name)
}
59 changes: 40 additions & 19 deletions scheduler/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"aida-scheduler/scheduler/nodes"
"aida-scheduler/utils"
"context"
"errors"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
Expand All @@ -16,16 +17,14 @@ import (
"os"
)

const algorithmName = "geographiclocation"

// const algorithmName = "random"
var availableAlgorithms = []string{"metricslocation", "geographiclocation", "random"}

func bind(clientset *kubernetes.Clientset, algorithm algorithms.Algorithm, pod *v1.Pod) {
node, err := algorithm.GetNode(pod)

if err != nil {
klog.Errorln(err)
utils.EmitEvent(algorithmName, clientset, pod, "", err)
utils.EmitEvent(algorithm.GetName(), clientset, pod, "", err)
return
}

Expand All @@ -47,7 +46,7 @@ func bind(clientset *kubernetes.Clientset, algorithm algorithms.Algorithm, pod *
metaV1.CreateOptions{},
)

utils.EmitEvent(algorithmName, clientset, pod, node.Name, err)
utils.EmitEvent(algorithm.GetName(), clientset, pod, node.Name, err)
}

func watch(clientset *kubernetes.Clientset, algorithm algorithms.Algorithm) {
Expand Down Expand Up @@ -75,9 +74,40 @@ func watch(clientset *kubernetes.Clientset, algorithm algorithms.Algorithm) {
}
}

func algorithmExists(algorithmName string) bool {
for _, algorithm := range availableAlgorithms {
if algorithm == algorithmName {
return true
}
}

return false
}

func initAlgorithm(algorithmName string, nodesStruct nodes.INodes) algorithms.Algorithm {
var algorithm algorithms.Algorithm

switch algorithmName {
case "random":
algorithm = random.New(nodesStruct)
case "geographiclocation":
algorithm = geographiclocation.New(nodesStruct)
case "metricslocation":
algorithm = metricslocation.New(nodesStruct)
default:
algorithm = random.New(nodesStruct)
}

return algorithm
}

// Run init the scheduler service
func Run() {
klog.Infof("starting %s aida-scheduler...\n", algorithmName)
func Run(algorithmName string) error {
if !algorithmExists(algorithmName) {
return errors.New("selected algorithm does not exist")
}

klog.Infof("starting aida-scheduler - %s algorithm...\n", algorithmName)

config, err := rest.InClusterConfig()
if err != nil {
Expand All @@ -94,18 +124,9 @@ func Run() {
}

nodesStruct := nodes.New(clientset)
var algorithm algorithms.Algorithm

switch algorithmName {
case "random":
algorithm = random.New(nodesStruct)
case "geographiclocation":
algorithm = geographiclocation.New(nodesStruct)
case "metricslocation":
algorithm = metricslocation.New(nodesStruct)
default:
algorithm = random.New(nodesStruct)
}
algorithm := initAlgorithm(algorithmName, nodesStruct)

watch(clientset, algorithm)

return nil
}

0 comments on commit b4357a5

Please sign in to comment.