From 5e2f66189c1031c0f4abc08a462aabe9675a5ebf Mon Sep 17 00:00:00 2001 From: Etienne Perot Date: Fri, 4 Nov 2022 13:48:49 -0700 Subject: [PATCH] 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 --- test/benchmarks/base/base.go | 19 +++---------------- test/benchmarks/base/size_test.go | 4 ++-- test/benchmarks/base/startup_test.go | 12 +++--------- test/benchmarks/database/redis_test.go | 15 +++------------ test/benchmarks/harness/util.go | 10 +++++----- test/benchmarks/network/network.go | 17 +++-------------- test/benchmarks/network/node_test.go | 15 +++------------ test/benchmarks/network/ruby_test.go | 21 ++++----------------- test/benchmarks/tools/ab.go | 5 ++--- test/benchmarks/tools/hey.go | 5 ++--- test/benchmarks/tools/redis.go | 7 +++---- 11 files changed, 33 insertions(+), 97 deletions(-) diff --git a/test/benchmarks/base/base.go b/test/benchmarks/base/base.go index 32cfa618b..c3cd20b09 100644 --- a/test/benchmarks/base/base.go +++ b/test/benchmarks/base/base.go @@ -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 } diff --git a/test/benchmarks/base/size_test.go b/test/benchmarks/base/size_test.go index 452926e5f..39cb44c00 100644 --- a/test/benchmarks/base/size_test.go +++ b/test/benchmarks/base/size_test.go @@ -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{ diff --git a/test/benchmarks/base/startup_test.go b/test/benchmarks/base/startup_test.go index 197241622..2076d6c11 100644 --- a/test/benchmarks/base/startup_test.go +++ b/test/benchmarks/base/startup_test.go @@ -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 diff --git a/test/benchmarks/database/redis_test.go b/test/benchmarks/database/redis_test.go index 7497f69b3..7365436d7 100644 --- a/test/benchmarks/database/redis_test.go +++ b/test/benchmarks/database/redis_test.go @@ -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 { diff --git a/test/benchmarks/harness/util.go b/test/benchmarks/harness/util.go index f7e569751..528a3a01d 100644 --- a/test/benchmarks/harness/util.go +++ b/test/benchmarks/harness/util.go @@ -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 } diff --git a/test/benchmarks/network/network.go b/test/benchmarks/network/network.go index d61002cea..70f53d7e7 100644 --- a/test/benchmarks/network/network.go +++ b/test/benchmarks/network/network.go @@ -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) } diff --git a/test/benchmarks/network/node_test.go b/test/benchmarks/network/node_test.go index ba3157694..1a2291f07 100644 --- a/test/benchmarks/network/node_test.go +++ b/test/benchmarks/network/node_test.go @@ -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) diff --git a/test/benchmarks/network/ruby_test.go b/test/benchmarks/network/ruby_test.go index 017fbb6f8..3a801b762 100644 --- a/test/benchmarks/network/ruby_test.go +++ b/test/benchmarks/network/ruby_test.go @@ -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) diff --git a/test/benchmarks/tools/ab.go b/test/benchmarks/tools/ab.go index d9abf0763..36b365449 100644 --- a/test/benchmarks/tools/ab.go +++ b/test/benchmarks/tools/ab.go @@ -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} diff --git a/test/benchmarks/tools/hey.go b/test/benchmarks/tools/hey.go index de908feeb..5f95368ac 100644 --- a/test/benchmarks/tools/hey.go +++ b/test/benchmarks/tools/hey.go @@ -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), } } diff --git a/test/benchmarks/tools/redis.go b/test/benchmarks/tools/redis.go index 12fdbc7cc..15b695eb0 100644 --- a/test/benchmarks/tools/redis.go +++ b/test/benchmarks/tools/redis.go @@ -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), }