Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support action redirecting to another service for traffic manager #519

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions eBPF_Supermarket/TrafficManager/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,5 @@ test:
kubectl get nodes
kubectl get pods -owide -A
kubectl get services

sudo go test -c acceptance/performance_test.go
sudo ./acceptance.test
sudo make build
sudo go test -v ./...
2 changes: 1 addition & 1 deletion eBPF_Supermarket/TrafficManager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ minikube start --kubernetes-version=1.26.6 --force

# Install eBPF development tools
apt update -y
apt install -y llvm clang
apt install -y llvm clang make gcc
apt install -y libbfd-dev libcap-dev libelf-dev
git clone --recurse-submodules https://github.com/libbpf/bpftool.git
make install -C bpftool/src/
Expand Down
54 changes: 54 additions & 0 deletions eBPF_Supermarket/TrafficManager/acceptance/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2023 The LMP Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// author: Woa <[email protected]>

package acceptance

import (
"fmt"
"log"
"net/http"
"strings"
)

type Router struct {
Route map[string]map[string]http.HandlerFunc
}

func (r *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if f, ok := r.Route[request.Method][request.URL.Path]; ok {
f(writer, request)
}
}

func (r *Router) HandleFunc(method, path string, f http.HandlerFunc) {
method = strings.ToUpper(method)
if r.Route == nil {
r.Route = make(map[string]map[string]http.HandlerFunc)
}
if r.Route[method] == nil {
r.Route[method] = make(map[string]http.HandlerFunc)
}
r.Route[method][path] = f
}

func startServer(portStr string) {
route := Router{}
route.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, portStr)
})

log.Fatal(http.ListenAndServe("localhost:"+portStr, &route))
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type SiegeResponse struct {
func siegeService(siegePodName string, service *v1.Service) (*SiegeResponse, error) {
log.Println("Start Sieging")
// kubectl exec siege -- siege -c 5 -r 20000 http://sisyphe-sfs.default.svc.cluster.local
out, err := exec.Command("kubectl", "exec", siegePodName, "--", "siege", "-c", "20", "-r", "40000", "http://"+service.Spec.ClusterIPs[0]).Output()
out, err := exec.Command("kubectl", "exec", siegePodName, "--", "siege", "-c", "20", "-r", "20000", "http://"+service.Spec.ClusterIPs[0]).Output()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -92,7 +92,7 @@ func TestServicePerformance(t *testing.T) {
}

// fmt.Println(service.Spec.ClusterIP, strconv.Itoa(int(service.Spec.Ports[0].Port)))
programs.InsertServiceItem(service.Spec.ClusterIP, strconv.Itoa(int(service.Spec.Ports[0].Port)), len(pods.Items))
programs.InsertServiceItem(service.Spec.ClusterIP, strconv.Itoa(int(service.Spec.Ports[0].Port)), len(pods.Items), bpf.RandomAction)
for i := 0; i < len(pods.Items); i++ {
// fmt.Println(strconv.Itoa(int(pods.Items[i].Spec.Containers[0].Ports[0].ContainerPort)))
programs.AutoInsertBackend(service.Spec.ClusterIP, strconv.Itoa(int(service.Spec.Ports[0].Port)), pods.Items[i].Status.PodIP, strconv.Itoa(int(pods.Items[i].Spec.Containers[0].Ports[0].ContainerPort)), i+1, float64(1/float64(len(pods.Items))))
Expand Down
132 changes: 132 additions & 0 deletions eBPF_Supermarket/TrafficManager/acceptance/redirect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2023 The LMP Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// author: Woa <[email protected]>

package acceptance

import (
"fmt"
"io"
"net/http"
"testing"

"lmp/eTrafficManager/bpf"
)

func TestRedirect(t *testing.T) {
var serverPortList = []string{
"8001",
"8002",
"8003",
"8004",
"8005",
}

var repeatNum int64 = 125000
for _, s := range serverPortList {
go startServer(s)
}

progs, err := bpf.LoadProgram()
if err != nil {
fmt.Println("[ERROR] Loading program failed:", err)
return
}
s := bpf.Service{
IP: "1.1.1.1",
Port: "80",
Possibility: 0.75,
}
b := []bpf.Backend{
{
IP: "127.0.0.1",
Port: "8001",
Possibility: 0.25,
}, {
IP: "127.0.0.1",
Port: "8002",
Possibility: 0.75,
},
}
progs.AutoInsertService(s, b, bpf.WeightedAction, nil)

s2 := bpf.Service{
IP: "127.0.0.1",
Port: "8003",
}
b2 := []bpf.Backend{
{
IP: "127.0.0.1",
Port: "8004",
Possibility: 0.125,
},
{
IP: "127.0.0.1",
Port: "8005",
Possibility: 0.125,
},
}

progs.AutoInsertService(s2, b2, bpf.RedirectAction, []bpf.Service{s})

err = progs.Attach()
if err != nil {
fmt.Println("[ERROR] Attaching failed:", err)
}

countBucket := make(map[string]int)

for i := 0; i < int(repeatNum); i++ {
client := &http.Client{
Transport: &http.Transport{
// We must set DisableKeepAlives, otherwise all requests will be in one HTTP connection
DisableKeepAlives: true,
},
}

url := "http://" + "127.0.0.1:8003"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}

resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}

// fmt.Println("Response:", string(body))
countBucket[string(body)] += 1
}

for i := 0; i < len(serverPortList); i++ {
actualNumber := float64(countBucket[serverPortList[i]])
fmt.Printf("For port: %s, got actualNumber: %d.\n", serverPortList[i], int64(actualNumber))
}

fmt.Println("[INFO] Test is done...")
progs.AutoDeleteService(s, nil)
progs.AutoDeleteService(s2, []bpf.Service{s})
progs.Close()
}
86 changes: 26 additions & 60 deletions eBPF_Supermarket/TrafficManager/acceptance/weight_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,27 @@ package acceptance
import (
"fmt"
"io"
"log"
"math"
"net/http"
"strings"
"testing"

"lmp/eTrafficManager/bpf"
)

type testCase struct {
weights []float64
repeatNum int64
}

const (
targetIP = "1.1.1.1"
targetPort = "80"
)

var serverPortList = []string{
"8001",
"8002",
"8003",
}
func TestWeight(t *testing.T) {
const (
targetIP = "1.1.1.1"
targetPort = "80"
)

var serverPortList = []string{
"7001",
"7002",
"7003",
}

var repeatNum int64 = 10000
var repeatNum int64 = 10000

func TestWeight(t *testing.T) {
go startServer(serverPortList[0])
go startServer(serverPortList[1])
go startServer(serverPortList[2])
Expand All @@ -56,7 +49,10 @@ func TestWeight(t *testing.T) {
// _ = c1.Start()
// }()

for _, tc := range []testCase{
for _, tc := range []struct {
weights []float64
repeatNum int64
}{
{
weights: []float64{
1,
Expand Down Expand Up @@ -99,17 +95,17 @@ func TestWeight(t *testing.T) {
},
{
weights: []float64{
0.33333,
0.33333,
0.33333,
0.25,
0.25,
0.5,
},
repeatNum: repeatNum,
},
{
weights: []float64{
0.2,
0.3,
0.5,
0.125,
0.25,
0.625,
},
repeatNum: repeatNum,
},
Expand All @@ -120,7 +116,7 @@ func TestWeight(t *testing.T) {
return
}

progs.InsertServiceItem(targetIP, targetPort, len(serverPortList))
progs.InsertServiceItem(targetIP, targetPort, len(serverPortList), bpf.WeightedAction)
for i := 0; i < len(serverPortList); i++ {
progs.AutoInsertBackend(targetIP, targetPort, "127.0.0.1", serverPortList[i], i+1, tc.weights[i])
}
Expand Down Expand Up @@ -167,10 +163,10 @@ func TestWeight(t *testing.T) {
for i := 0; i < len(serverPortList); i++ {
expectNumber := tc.weights[i] * float64(tc.repeatNum)
actualNumber := float64(countBucket[serverPortList[i]])
if math.Abs(actualNumber-expectNumber)/expectNumber > 0.05 && false {
if math.Abs(actualNumber-expectNumber)/expectNumber > 0.05 {
t.Errorf("For port: %s, expectNumber: %f, but actualNumber: %d, rate: %f. Maybe retesting will fix this", serverPortList[i], expectNumber, int64(actualNumber), math.Abs(actualNumber-expectNumber)/expectNumber)
} else {
fmt.Printf("For port: %s, expectNumber: %f, but actualNumber: %d, rate: %f.\n", serverPortList[i], expectNumber, int64(actualNumber), math.Abs(actualNumber-expectNumber)/expectNumber)
fmt.Printf("For port: %s, expectNumber: %f, got actualNumber: %d, rate: %f.\n", serverPortList[i], expectNumber, int64(actualNumber), math.Abs(actualNumber-expectNumber)/expectNumber)
}
}

Expand All @@ -179,37 +175,7 @@ func TestWeight(t *testing.T) {
IP: targetIP,
Port: targetPort,
}
progs.AutoDeleteService(s)
progs.AutoDeleteService(s, nil)
progs.Close()
}
}

type Router struct {
Route map[string]map[string]http.HandlerFunc
}

func (r *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if f, ok := r.Route[request.Method][request.URL.Path]; ok {
f(writer, request)
}
}

func (r *Router) HandleFunc(method, path string, f http.HandlerFunc) {
method = strings.ToUpper(method)
if r.Route == nil {
r.Route = make(map[string]map[string]http.HandlerFunc)
}
if r.Route[method] == nil {
r.Route[method] = make(map[string]http.HandlerFunc)
}
r.Route[method][path] = f
}

func startServer(portStr string) {
route := Router{}
route.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, portStr)
})

log.Fatal(http.ListenAndServe("localhost:"+portStr, &route))
}
3 changes: 1 addition & 2 deletions eBPF_Supermarket/TrafficManager/bpf/bpf_connect_bpf.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified eBPF_Supermarket/TrafficManager/bpf/bpf_connect_bpf.o
Binary file not shown.
Loading