Make all network benchmarks use container links.

This CL uses container links rather than going over the host network stack.

This avoids relying on that network stack, making the benchmarks more reliable,
and simplifies benchmark setup code.

#codehealth

PiperOrigin-RevId: 486221868
This commit is contained in:
Etienne Perot
2022-11-04 13:51:45 -07:00
committed by gVisor bot
parent 8400a3d40d
commit 5e2f66189c
11 changed files with 33 additions and 97 deletions
+3 -16
View File
@@ -17,7 +17,6 @@ package base
import (
"context"
"net"
"testing"
"time"
@@ -48,15 +47,8 @@ func StartServers(ctx context.Context, b *testing.B, args ServerArgs) []*dockeru
b.Fatalf("failed to spawn node instance: %v", err)
}
// Get the container IP.
servingIP, err := server.FindIP(ctx, false)
if err != nil {
CleanUpContainers(ctx, servers)
b.Fatalf("failed to get ip from server: %v", err)
}
// Wait until the server is up.
if err := harness.WaitUntilServing(ctx, args.Machine, servingIP, args.Port); err != nil {
if err := harness.WaitUntilContainerServing(ctx, args.Machine, server, args.Port); err != nil {
CleanUpContainers(ctx, servers)
b.Fatalf("failed to wait for serving")
}
@@ -74,7 +66,7 @@ func CleanUpContainers(ctx context.Context, containers []*dockerutil.Container)
}
// RedisInstance returns a Redis container and its reachable IP.
func RedisInstance(ctx context.Context, b *testing.B, machine harness.Machine) (*dockerutil.Container, net.IP) {
func RedisInstance(ctx context.Context, b *testing.B, machine harness.Machine) *dockerutil.Container {
b.Helper()
// Spawn a redis instance for the app to use.
redis := machine.GetNativeContainer(ctx, b)
@@ -89,10 +81,5 @@ func RedisInstance(ctx context.Context, b *testing.B, machine harness.Machine) (
redis.CleanUp(ctx)
b.Fatalf("failed to start redis server: %v %s", err, out)
}
redisIP, err := redis.FindIP(ctx, false)
if err != nil {
redis.CleanUp(ctx)
b.Fatalf("failed to get IP from redis instance: %v", err)
}
return redis, redisIP
return redis
}
+2 -2
View File
@@ -132,7 +132,7 @@ func BenchmarkSizeNode(b *testing.B) {
// Make a redis instance for Node to connect.
ctx := context.Background()
redis, redisIP := base.RedisInstance(ctx, b, machine)
redis := base.RedisInstance(ctx, b, machine)
defer redis.CleanUp(ctx)
// DropCaches after redis is created.
@@ -152,7 +152,7 @@ func BenchmarkSizeNode(b *testing.B) {
WorkDir: "/usr/src/app",
Links: []string{redis.MakeLink("redis")},
}
nodeCmd := []string{"node", "index.js", redisIP.String()}
nodeCmd := []string{"node", "index.js", "redis"}
const port = 8080
servers := base.StartServers(ctx, b,
base.ServerArgs{
+3 -9
View File
@@ -85,7 +85,7 @@ func BenchmarkStartupNode(b *testing.B) {
defer machine.CleanUp()
ctx := context.Background()
redis, redisIP := base.RedisInstance(ctx, b, machine)
redis := base.RedisInstance(ctx, b, machine)
defer redis.CleanUp(ctx)
runOpts := dockerutil.RunOpts{
Image: "benchmarks/node",
@@ -93,7 +93,7 @@ func BenchmarkStartupNode(b *testing.B) {
Links: []string{redis.MakeLink("redis")},
}
cmd := []string{"node", "index.js", redisIP.String()}
cmd := []string{"node", "index.js", "redis"}
runServerWorkload(ctx, b,
base.ServerArgs{
Machine: machine,
@@ -122,15 +122,9 @@ func runServerWorkload(ctx context.Context, b *testing.B, args base.ServerArgs)
return fmt.Errorf("failed to spawn node instance: %v", err)
}
harness.DebugLog(b, "Finding Container IP")
servingIP, err := server.FindIP(ctx, false)
if err != nil {
return fmt.Errorf("failed to get ip from server: %v", err)
}
// Wait until the Client sees the server as up.
harness.DebugLog(b, "Waiting for container to start.")
if err := harness.WaitUntilServing(ctx, args.Machine, servingIP, args.Port); err != nil {
if err := harness.WaitUntilContainerServing(ctx, args.Machine, server, args.Port); err != nil {
return fmt.Errorf("failed to wait for serving: %v", err)
}
return nil
+3 -12
View File
@@ -81,17 +81,7 @@ func BenchmarkRedis(b *testing.B) {
b.Fatalf("failed to start redis server: %v %s", err, out)
}
ip, err := serverMachine.IPAddress()
if err != nil {
b.Fatalf("failed to get IP from server: %v", err)
}
serverPort, err := server.FindPort(ctx, port)
if err != nil {
b.Fatalf("failed to get IP from server: %v", err)
}
if err = harness.WaitUntilServing(ctx, clientMachine, ip, serverPort); err != nil {
if err = harness.WaitUntilContainerServing(ctx, clientMachine, server, port); err != nil {
b.Fatalf("failed to start redis with: %v", err)
}
for _, operation := range operations {
@@ -120,7 +110,8 @@ func BenchmarkRedis(b *testing.B) {
out, err = client.Run(ctx, dockerutil.RunOpts{
Image: "benchmarks/redis",
}, redis.MakeCmd(ip, serverPort, b.N /*requests*/)...)
Links: []string{server.MakeLink("redis")},
}, redis.MakeCmd("redis", port, b.N /*requests*/)...)
}
if err != nil {
+5 -5
View File
@@ -17,7 +17,6 @@ package harness
import (
"context"
"fmt"
"net"
"strings"
"testing"
@@ -29,16 +28,17 @@ import (
//TODO(gvisor.dev/issue/3535): move to own package or move methods to harness struct.
// WaitUntilServing grabs a container from `machine` and waits for a server at
// IP:port.
func WaitUntilServing(ctx context.Context, machine Machine, server net.IP, port int) error {
// WaitUntilContainerServing grabs a container from `machine` and waits for a server on
// the given container and port.
func WaitUntilContainerServing(ctx context.Context, machine Machine, container *dockerutil.Container, port int) error {
var logger testutil.DefaultLogger = "util"
netcat := machine.GetNativeContainer(ctx, logger)
defer netcat.CleanUp(ctx)
cmd := fmt.Sprintf("while ! wget -q --spider http://%s:%d; do true; done", server, port)
cmd := fmt.Sprintf("while ! wget -q --spider http://%s:%d; do true; done", "server", port)
_, err := netcat.Run(ctx, dockerutil.RunOpts{
Image: "benchmarks/util",
Links: []string{container.MakeLink("server")},
}, "sh", "-c", cmd)
return err
}
+3 -14
View File
@@ -52,26 +52,15 @@ func runStaticServer(b *testing.B, serverOpts dockerutil.RunOpts, serverCmd []st
b.Fatalf("failed to start server: %v", err)
}
// Get its IP.
ip, err := serverMachine.IPAddress()
if err != nil {
b.Fatalf("failed to find server ip: %v", err)
}
// Get the published port.
servingPort, err := server.FindPort(ctx, port)
if err != nil {
b.Fatalf("failed to find server port %d: %v", port, err)
}
// Make sure the server is serving.
harness.WaitUntilServing(ctx, clientMachine, ip, servingPort)
harness.WaitUntilContainerServing(ctx, clientMachine, server, port)
// Run the client.
b.ResetTimer()
out, err := client.Run(ctx, dockerutil.RunOpts{
Image: "benchmarks/hey",
}, hey.MakeCmd(ip, servingPort)...)
Links: []string{server.MakeLink("server")},
}, hey.MakeCmd("server", port)...)
if err != nil {
b.Fatalf("run failed with: %v", err)
}
+3 -12
View File
@@ -102,26 +102,17 @@ func runNode(b *testing.B, hey *tools.Hey) {
}
defer nodeApp.CleanUp(ctx)
servingIP, err := serverMachine.IPAddress()
if err != nil {
b.Fatalf("failed to get ip from server: %v", err)
}
servingPort, err := nodeApp.FindPort(ctx, port)
if err != nil {
b.Fatalf("failed to port from node instance: %v", err)
}
// Wait until the Client sees the server as up.
harness.WaitUntilServing(ctx, clientMachine, servingIP, servingPort)
harness.WaitUntilContainerServing(ctx, clientMachine, nodeApp, port)
heyCmd := hey.MakeCmd(servingIP, servingPort)
heyCmd := hey.MakeCmd("node", port)
// the client should run on Native.
b.ResetTimer()
client := clientMachine.GetNativeContainer(ctx, b)
out, err := client.Run(ctx, dockerutil.RunOpts{
Image: "benchmarks/hey",
Links: []string{nodeApp.MakeLink("node")},
}, heyCmd...)
if err != nil {
b.Fatalf("hey container failed: %v logs: %s", err, out)
+4 -17
View File
@@ -80,10 +80,6 @@ func runRuby(b *testing.B, hey *tools.Hey) {
if out, err := redis.WaitForOutput(ctx, "Ready to accept connections", 3*time.Second); err != nil {
b.Fatalf("failed to start redis server: %v %s", err, out)
}
redisIP, err := redis.FindIP(ctx, false)
if err != nil {
b.Fatalf("failed to get IP from redis instance: %v", err)
}
// Ruby runs on port 9292.
const port = 9292
@@ -100,7 +96,7 @@ func runRuby(b *testing.B, hey *tools.Hey) {
"WEB_CONCURRENCY=20",
"WEB_MAX_THREADS=20",
"RACK_ENV=production",
fmt.Sprintf("HOST=%s", redisIP),
"HOST=redis",
},
User: "nobody",
}, "sh", "-c", "/usr/bin/puma"); err != nil {
@@ -108,21 +104,11 @@ func runRuby(b *testing.B, hey *tools.Hey) {
}
defer rubyApp.CleanUp(ctx)
servingIP, err := serverMachine.IPAddress()
if err != nil {
b.Fatalf("failed to get ip from server: %v", err)
}
servingPort, err := rubyApp.FindPort(ctx, port)
if err != nil {
b.Fatalf("failed to port from node instance: %v", err)
}
// Wait until the Client sees the server as up.
if err := harness.WaitUntilServing(ctx, clientMachine, servingIP, servingPort); err != nil {
if err := harness.WaitUntilContainerServing(ctx, clientMachine, rubyApp, port); err != nil {
b.Fatalf("failed to wait until serving: %v", err)
}
heyCmd := hey.MakeCmd(servingIP, servingPort)
heyCmd := hey.MakeCmd("ruby", port)
// the client should run on Native.
b.ResetTimer()
@@ -130,6 +116,7 @@ func runRuby(b *testing.B, hey *tools.Hey) {
defer client.CleanUp(ctx)
out, err := client.Run(ctx, dockerutil.RunOpts{
Image: "benchmarks/hey",
Links: []string{rubyApp.MakeLink("ruby")},
}, heyCmd...)
if err != nil {
b.Fatalf("hey container failed: %v logs: %s", err, out)
+2 -3
View File
@@ -16,7 +16,6 @@ package tools
import (
"fmt"
"net"
"regexp"
"strconv"
"testing"
@@ -31,8 +30,8 @@ type ApacheBench struct {
}
// MakeCmd makes an ApacheBench command.
func (a *ApacheBench) MakeCmd(ip net.IP, port int) []string {
path := fmt.Sprintf("http://%s:%d/%s", ip, port, a.Doc)
func (a *ApacheBench) MakeCmd(host string, port int) []string {
path := fmt.Sprintf("http://%s:%d/%s", host, port, a.Doc)
// See apachebench (ab) for flags.
cmd := fmt.Sprintf("ab -n %d -c %d %s", a.Requests, a.Concurrency, path)
return []string{"sh", "-c", cmd}
+2 -3
View File
@@ -16,7 +16,6 @@ package tools
import (
"fmt"
"net"
"regexp"
"strconv"
"testing"
@@ -30,7 +29,7 @@ type Hey struct {
}
// MakeCmd returns a 'hey' command.
func (h *Hey) MakeCmd(ip net.IP, port int) []string {
func (h *Hey) MakeCmd(host string, port int) []string {
c := h.Concurrency
if c > h.Requests {
c = h.Requests
@@ -39,7 +38,7 @@ func (h *Hey) MakeCmd(ip net.IP, port int) []string {
"hey",
"-n", fmt.Sprintf("%d", h.Requests),
"-c", fmt.Sprintf("%d", c),
fmt.Sprintf("http://%s:%d/%s", ip.String(), port, h.Doc),
fmt.Sprintf("http://%s:%d/%s", host, port, h.Doc),
}
}
+3 -4
View File
@@ -16,7 +16,6 @@ package tools
import (
"fmt"
"net"
"regexp"
"strconv"
"testing"
@@ -28,7 +27,7 @@ type Redis struct {
}
// MakeCmd returns a redis-benchmark client command.
func (r *Redis) MakeCmd(ip net.IP, port, requests int) []string {
func (r *Redis) MakeCmd(host string, port, requests int) []string {
// There is no -t PING_BULK for redis-benchmark, so adjust the command in that case.
// Note that "ping" will run both PING_INLINE and PING_BULK.
if r.Operation == "PING_BULK" {
@@ -36,7 +35,7 @@ func (r *Redis) MakeCmd(ip net.IP, port, requests int) []string {
"redis-benchmark",
"--csv",
"-t", "ping",
"-h", ip.String(),
"-h", host,
"-p", fmt.Sprintf("%d", port),
"-n", fmt.Sprintf("%d", requests),
}
@@ -47,7 +46,7 @@ func (r *Redis) MakeCmd(ip net.IP, port, requests int) []string {
"redis-benchmark",
"--csv",
"-t", r.Operation,
"-h", ip.String(),
"-h", host,
"-p", fmt.Sprintf("%d", port),
"-n", fmt.Sprintf("%d", requests),
}