gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/benchmarks/base/usage_test.go (about) 1 // Copyright 2023 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package usage_test 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 "testing" 22 "time" 23 24 "github.com/docker/docker/api/types" 25 "gvisor.dev/gvisor/pkg/test/dockerutil" 26 "gvisor.dev/gvisor/test/benchmarks/base" 27 "gvisor.dev/gvisor/test/benchmarks/harness" 28 "gvisor.dev/gvisor/test/benchmarks/tools" 29 "gvisor.dev/gvisor/test/metricsviz" 30 ) 31 32 // BenchmarkSizeEmpty creates N alpine containers and reads memory usage using `docker stats`. 33 func BenchmarkSizeEmpty(b *testing.B) { 34 ctx := context.Background() 35 machine, err := harness.GetMachine() 36 if err != nil { 37 b.Fatalf("failed to get machine: %v", err) 38 } 39 defer machine.CleanUp() 40 41 var sumMemoryUsage uint64 42 43 // Make N containers. 44 for i := 0; i < b.N; i++ { 45 container := machine.GetContainer(ctx, b) 46 defer container.CleanUp(ctx) 47 if err := container.Spawn(ctx, dockerutil.RunOpts{ 48 Image: "benchmarks/alpine", 49 }, "sh", "-c", "echo Hello && sleep 1000"); err != nil { 50 b.Fatalf("failed to run container: %v", err) 51 } 52 if i == 0 { 53 defer metricsviz.FromContainerLogs(ctx, b, container) 54 } 55 if _, err := container.WaitForOutputSubmatch(ctx, "Hello", 5*time.Second); err != nil { 56 b.Fatalf("failed to read container output: %v", err) 57 } 58 59 stats, err := container.Stats(ctx) 60 if err != nil { 61 b.Fatalf("failed to get container stats: %v", err) 62 } 63 if err := validateStats(stats); err != nil { 64 b.Fatalf("failed to validate container stats: %v", err) 65 } 66 sumMemoryUsage += stats.Stats.MemoryStats.Usage 67 } 68 reportMemoryUsage(b, sumMemoryUsage) 69 } 70 71 // BenchmarkSizeNginx starts N containers running Nginx, checks that they're 72 // serving, and checks memory usage from `docker stats`. 73 func BenchmarkSizeNginx(b *testing.B) { 74 ctx := context.Background() 75 machine, err := harness.GetMachine() 76 if err != nil { 77 b.Fatalf("failed to get machine with: %v", err) 78 } 79 defer machine.CleanUp() 80 81 // Make N Nginx containers. 82 runOpts := dockerutil.RunOpts{ 83 Image: "benchmarks/nginx", 84 } 85 const port = 80 86 var sumMemoryUsage uint64 87 for i := 0; i < b.N; i++ { 88 server, err := base.StartServer(ctx, b, 89 base.ServerArgs{ 90 Machine: machine, 91 Port: port, 92 RunOpts: runOpts, 93 Cmd: []string{"nginx", "-c", "/etc/nginx/nginx_gofer.conf"}, 94 }) 95 if err != nil { 96 b.Fatalf("failed to start server: %v", err) 97 } 98 defer server.CleanUp(ctx) 99 if i == 0 { 100 defer metricsviz.FromContainerLogs(ctx, b, server) 101 } 102 stats, err := server.Stats(ctx) 103 if err != nil { 104 b.Fatalf("failed to get container stats: %v", err) 105 } 106 if err := validateStats(stats); err != nil { 107 b.Fatalf("failed to validate container stats: %v", err) 108 } 109 sumMemoryUsage += stats.Stats.MemoryStats.Usage 110 } 111 reportMemoryUsage(b, sumMemoryUsage) 112 } 113 114 // BenchmarkSizeNode starts N containers running a Node app, checks that 115 // they're serving, and checks memory used based on /proc/meminfo. 116 func BenchmarkSizeNode(b *testing.B) { 117 ctx := context.Background() 118 machine, err := harness.GetMachine() 119 if err != nil { 120 b.Fatalf("failed to get machine with: %v", err) 121 } 122 defer machine.CleanUp() 123 124 // Make a redis instance for Node to connect. 125 redis := base.RedisInstance(ctx, b, machine) 126 defer redis.CleanUp(ctx) 127 128 // Create N Node servers. 129 runOpts := dockerutil.RunOpts{ 130 Image: "benchmarks/node", 131 WorkDir: "/usr/src/app", 132 Links: []string{redis.MakeLink("redis")}, 133 } 134 nodeCmd := []string{"node", "index.js", "redis"} 135 const port = 8080 136 var sumMemoryUsage uint64 137 for i := 0; i < b.N; i++ { 138 server, err := base.StartServer(ctx, b, 139 base.ServerArgs{ 140 Machine: machine, 141 Port: port, 142 RunOpts: runOpts, 143 Cmd: nodeCmd, 144 }) 145 if err != nil { 146 b.Fatalf("failed to start server: %v", err) 147 } 148 defer server.CleanUp(ctx) 149 if i == 0 { 150 defer metricsviz.FromContainerLogs(ctx, b, server) 151 } 152 stats, err := server.Stats(ctx) 153 if err != nil { 154 b.Fatalf("failed to get container stats: %v", err) 155 } 156 if err := validateStats(stats); err != nil { 157 b.Fatalf("failed to validate container stats: %v", err) 158 } 159 sumMemoryUsage += stats.Stats.MemoryStats.Usage 160 } 161 reportMemoryUsage(b, sumMemoryUsage) 162 } 163 164 func validateStats(stats *types.StatsJSON) error { 165 // The runc empty container is on the order of multiple kB, so if this is smaller than that, 166 // there is probably something wrong. 167 var memoryAtLeast uint64 = 1000 168 if stats.MemoryStats.Usage < memoryAtLeast { 169 return fmt.Errorf("reported memory usage below sanity check minimum: %d: reported: %d", memoryAtLeast, stats.MemoryStats.Usage) 170 } 171 return nil 172 } 173 174 func reportMemoryUsage(b *testing.B, sumMemoryUsage uint64) { 175 averageUsage := float64(sumMemoryUsage) / float64(b.N) 176 tools.ReportCustomMetric(b, averageUsage, "average_container_memory_usage", "bytes") 177 } 178 179 // TestMain is the main method for this package. 180 func TestMain(m *testing.M) { 181 harness.Init() 182 os.Exit(m.Run()) 183 }