-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert scale_from_zero_test.go to Mako benchmark (#5985)
* add init files * more changes * convert scale-from-zero to mako benchmark * revert unnecessary changes * address codereview feedbacks
- Loading branch information
1 parent
2a7cb04
commit 78995f5
Showing
12 changed files
with
561 additions
and
257 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright 2019 The Knative 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 | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# 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. | ||
|
||
# Configuration file for the cluster that runs this benchmark continuously. | ||
|
||
GKECluster: | ||
location: "us-west1" | ||
nodeCount: 3 | ||
nodeType: "n1-standard-4" | ||
addons: "HorizontalPodAutoscaling,HttpLoadBalancing" |
1 change: 1 addition & 0 deletions
1
test/performance/benchmarks/scale-from-zero/continuous/kodata/HEAD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../../../.git/HEAD |
1 change: 1 addition & 0 deletions
1
test/performance/benchmarks/scale-from-zero/continuous/kodata/dev.config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../dev.config |
1 change: 1 addition & 0 deletions
1
test/performance/benchmarks/scale-from-zero/continuous/kodata/prod.config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../prod.config |
1 change: 1 addition & 0 deletions
1
test/performance/benchmarks/scale-from-zero/continuous/kodata/refs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../../../.git/refs/ |
220 changes: 220 additions & 0 deletions
220
test/performance/benchmarks/scale-from-zero/continuous/main.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
/* | ||
Copyright 2019 The Knative 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 | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
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. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"github.com/google/mako/go/quickstore" | ||
"k8s.io/apimachinery/pkg/labels" | ||
"knative.dev/pkg/test/mako" | ||
"knative.dev/serving/pkg/apis/serving" | ||
"knative.dev/serving/test/performance" | ||
"log" | ||
"strconv" | ||
"sync" | ||
"testing" | ||
"time" | ||
|
||
"golang.org/x/sync/errgroup" | ||
|
||
"knative.dev/pkg/injection/sharedmain" | ||
pkgTest "knative.dev/pkg/test" | ||
"knative.dev/serving/pkg/apis/autoscaling" | ||
ktest "knative.dev/serving/pkg/testing/v1alpha1" | ||
"knative.dev/serving/test" | ||
v1a1test "knative.dev/serving/test/v1alpha1" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/resource" | ||
) | ||
|
||
var ( | ||
parallelCount = flag.Int("parallel", 0, "The count of ksvcs we want to run scale-from-zero in parallel") | ||
) | ||
|
||
const ( | ||
testNamespace = "default" | ||
serviceName = "perftest-scalefromzero" | ||
helloWorldExpectedOutput = "Hello World!" | ||
helloWorldImage = "helloworld" | ||
waitToServe = 10 * time.Minute | ||
) | ||
|
||
func clientsFromConfig() (*test.Clients, error) { | ||
cfg, err := sharedmain.GetConfig("", "") | ||
if err != nil { | ||
return nil, fmt.Errorf("error building kubeconfig: %v", err) | ||
} | ||
return test.NewClientsFromConfig(cfg, testNamespace) | ||
} | ||
|
||
func createServices(clients *test.Clients, count int) ([]*v1a1test.ResourceObjects, func(), error) { | ||
testNames := make([]*test.ResourceNames, count) | ||
|
||
// Initialize our service names. | ||
for i := 0; i < count; i++ { | ||
testNames[i] = &test.ResourceNames{ | ||
Service: test.AppendRandomString(fmt.Sprintf("%s-%02d", serviceName, i)), | ||
// The crd.go helpers will convert to the actual image path. | ||
Image: helloWorldImage, | ||
} | ||
} | ||
|
||
cleanupNames := func() { | ||
for i := 0; i < count; i++ { | ||
test.TearDown(clients, *testNames[i]) | ||
} | ||
} | ||
|
||
objs := make([]*v1a1test.ResourceObjects, count) | ||
begin := time.Now() | ||
sos := []ktest.ServiceOption{ | ||
// We set a small resource alloc so that we can pack more pods into the cluster. | ||
ktest.WithResourceRequirements(corev1.ResourceRequirements{ | ||
Limits: corev1.ResourceList{ | ||
corev1.ResourceCPU: resource.MustParse("50m"), | ||
corev1.ResourceMemory: resource.MustParse("50Mi"), | ||
}, | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceCPU: resource.MustParse("10m"), | ||
corev1.ResourceMemory: resource.MustParse("20Mi"), | ||
}, | ||
}), | ||
ktest.WithConfigAnnotations(map[string]string{ | ||
autoscaling.WindowAnnotationKey: "7s", | ||
}), | ||
} | ||
g := errgroup.Group{} | ||
for i := 0; i < count; i++ { | ||
ndx := i | ||
g.Go(func() error { | ||
var err error | ||
if objs[ndx], _, err = v1a1test.CreateRunLatestServiceReady(&testing.T{}, clients, testNames[ndx], false, sos...); err != nil { | ||
return fmt.Errorf("%02d: failed to create Ready service: %v", ndx, err) | ||
} | ||
return nil | ||
}) | ||
} | ||
if err := g.Wait(); err != nil { | ||
return nil, nil, err | ||
} | ||
log.Printf("Created all the services in %v", time.Since(begin)) | ||
return objs, cleanupNames, nil | ||
} | ||
|
||
func parallelScaleFromZero(ctx context.Context, clients *test.Clients, objs []*v1a1test.ResourceObjects, count int, q *quickstore.Quickstore) { | ||
// Get the key for saving latency and error metrics in the benchmark. | ||
lk := "l" + strconv.Itoa(count) | ||
ek := "e" + strconv.Itoa(count) | ||
var wg sync.WaitGroup | ||
wg.Add(count) | ||
for i := 0; i < count; i++ { | ||
ndx := i | ||
go func() { | ||
defer wg.Done() | ||
dur, err := runScaleFromZero(ctx, clients, ndx, objs[ndx]) | ||
log.Printf("%02d: duration: %v, err: %v", ndx, dur, err) | ||
if err == nil { | ||
q.AddSamplePoint(mako.XTime(time.Now()), map[string]float64{ | ||
lk: dur.Seconds(), | ||
}) | ||
} else { | ||
// Add 1 to the error metric whenever there is an error. | ||
q.AddSamplePoint(mako.XTime(time.Now()), map[string]float64{ | ||
ek: 1, | ||
}) | ||
// By reporting errors like this, the error strings show up on | ||
// the details page for each Mako run. | ||
q.AddError(mako.XTime(time.Now()), err.Error()) | ||
} | ||
}() | ||
} | ||
wg.Wait() | ||
} | ||
|
||
func runScaleFromZero(ctx context.Context, clients *test.Clients, idx int, ro *v1a1test.ResourceObjects) (time.Duration, error) { | ||
log.Printf("%02d: waiting for deployment to scale to zero.", idx) | ||
selector := labels.SelectorFromSet(labels.Set{ | ||
serving.ServiceLabelKey: ro.Service.Name, | ||
}) | ||
|
||
if err := performance.WaitForScaleToZero(ctx, testNamespace, selector, 2*time.Minute); err != nil { | ||
m := fmt.Sprintf("%02d: failed waiting for deployment to scale to zero: %v", idx, err) | ||
log.Println(m) | ||
return 0, errors.New(m) | ||
} | ||
|
||
start := time.Now() | ||
log.Printf("%02d: waiting for endpoint to serve request", idx) | ||
url := ro.Route.Status.URL.URL() | ||
if _, err := pkgTest.WaitForEndpointStateWithTimeout( | ||
clients.KubeClient, | ||
log.Printf, | ||
url, | ||
pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.MatchesBody(helloWorldExpectedOutput)), | ||
"HelloWorldServesText", | ||
test.ServingFlags.ResolvableDomain, waitToServe, | ||
); err != nil { | ||
m := fmt.Sprintf("%02d: the endpoint for Route %q at %q didn't serve the expected text %q: %v", idx, ro.Route.Name, url, helloWorldExpectedOutput, err) | ||
log.Println(m) | ||
return 0, errors.New(m) | ||
} | ||
|
||
log.Printf("%02d: request completed", idx) | ||
|
||
return time.Since(start), nil | ||
} | ||
|
||
func testScaleFromZero(clients *test.Clients, count int) { | ||
parallelTag := fmt.Sprintf("parallel=%d", count) | ||
mc, err := mako.Setup(context.Background(), parallelTag) | ||
if err != nil { | ||
log.Fatalf("failed to setup mako: %v", err) | ||
} | ||
q, qclose, ctx := mc.Quickstore, mc.ShutDownFunc, mc.Context | ||
defer qclose(ctx) | ||
// Wrap fatalf in a helper or our sidecar will live forever. | ||
fatalf := func(f string, args ...interface{}) { | ||
qclose(ctx) | ||
log.Fatalf(f, args...) | ||
} | ||
|
||
// Create the services once. | ||
objs, cleanup, err := createServices(clients, count) | ||
if err != nil { | ||
fatalf("Failed to create services: %v", err) | ||
} | ||
defer cleanup() | ||
|
||
parallelScaleFromZero(ctx, clients, objs, count, q) | ||
if err := mc.StoreAndHandleResult(); err != nil { | ||
fatalf("Failed to store and handle benchmarking result: %v", err) | ||
} | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
clients, err := clientsFromConfig() | ||
if err != nil { | ||
log.Fatalf("Failed to setup clients: %v", err) | ||
} | ||
|
||
testScaleFromZero(clients, *parallelCount) | ||
} |
Oops, something went wrong.