[lxc-devel] [lxd/master] Refactor lxd-benchmark
albertodonato on Github
lxc-bot at linuxcontainers.org
Tue Sep 5 15:01:23 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 503 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170905/d8868148/attachment.bin>
-------------- next part --------------
From 2dfef81b8a04a2a8aa61ac4565757c04d77d5453 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 12:02:45 +0200
Subject: [PATCH 1/7] benchmark: extract logic to separate package
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 327 +++++++++++++++++++++++++++++++++++++++++++++
test/lxd-benchmark/main.go | 324 +-------------------------------------------
2 files changed, 330 insertions(+), 321 deletions(-)
create mode 100644 benchmark/benchmark.go
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
new file mode 100644
index 000000000..e8fe5e8f7
--- /dev/null
+++ b/benchmark/benchmark.go
@@ -0,0 +1,327 @@
+package benchmark
+
+import (
+ "fmt"
+ "io/ioutil"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/lxc/lxd/client"
+ "github.com/lxc/lxd/lxc/config"
+ "github.com/lxc/lxd/shared/api"
+ "github.com/lxc/lxd/shared/version"
+)
+
+func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, freeze bool) error {
+ batch := parallel
+ if batch < 1 {
+ // Detect the number of parallel actions
+ cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+ if err != nil {
+ return err
+ }
+
+ batch = len(cpus)
+ }
+
+ batches := count / batch
+ remainder := count % batch
+
+ // Print the test header
+ st, _, err := c.GetServer()
+ if err != nil {
+ return err
+ }
+
+ privilegedStr := "unprivileged"
+ if privileged {
+ privilegedStr = "privileged"
+ }
+
+ mode := "normal startup"
+ if freeze {
+ mode = "start and freeze"
+ }
+
+ fmt.Printf("Test environment:\n")
+ fmt.Printf(" Server backend: %s\n", st.Environment.Server)
+ fmt.Printf(" Server version: %s\n", st.Environment.ServerVersion)
+ fmt.Printf(" Kernel: %s\n", st.Environment.Kernel)
+ fmt.Printf(" Kernel architecture: %s\n", st.Environment.KernelArchitecture)
+ fmt.Printf(" Kernel version: %s\n", st.Environment.KernelVersion)
+ fmt.Printf(" Storage backend: %s\n", st.Environment.Storage)
+ fmt.Printf(" Storage version: %s\n", st.Environment.StorageVersion)
+ fmt.Printf(" Container backend: %s\n", st.Environment.Driver)
+ fmt.Printf(" Container version: %s\n", st.Environment.DriverVersion)
+ fmt.Printf("\n")
+ fmt.Printf("Test variables:\n")
+ fmt.Printf(" Container count: %d\n", count)
+ fmt.Printf(" Container mode: %s\n", privilegedStr)
+ fmt.Printf(" Startup mode: %s\n", mode)
+ fmt.Printf(" Image: %s\n", image)
+ fmt.Printf(" Batches: %d\n", batches)
+ fmt.Printf(" Batch size: %d\n", batch)
+ fmt.Printf(" Remainder: %d\n", remainder)
+ fmt.Printf("\n")
+
+ // Pre-load the image
+ var fingerprint string
+ if strings.Contains(image, ":") {
+ var remote string
+
+ defaultConfig := config.DefaultConfig
+ defaultConfig.UserAgent = version.UserAgent
+
+ remote, fingerprint, err = defaultConfig.ParseRemote(image)
+ if err != nil {
+ return err
+ }
+
+ d, err := defaultConfig.GetImageServer(remote)
+ if err != nil {
+ return err
+ }
+
+ if fingerprint == "" {
+ fingerprint = "default"
+ }
+
+ alias, _, err := d.GetImageAlias(fingerprint)
+ if err == nil {
+ fingerprint = alias.Target
+ }
+
+ _, _, err = c.GetImage(fingerprint)
+ if err != nil {
+ logf("Importing image into local store: %s", fingerprint)
+ image, _, err := d.GetImage(fingerprint)
+ if err != nil {
+ logf(fmt.Sprintf("Failed to import image: %s", err))
+ return err
+ }
+
+ op, err := c.CopyImage(d, *image, nil)
+ if err != nil {
+ logf(fmt.Sprintf("Failed to import image: %s", err))
+ return err
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf(fmt.Sprintf("Failed to import image: %s", err))
+ return err
+ }
+ } else {
+ logf("Found image in local store: %s", fingerprint)
+ }
+ } else {
+ fingerprint = image
+ logf("Found image in local store: %s", fingerprint)
+ }
+
+ // Start the containers
+ spawnedCount := 0
+ nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d"
+ wgBatch := sync.WaitGroup{}
+ nextStat := batch
+
+ startContainer := func(name string) {
+ defer wgBatch.Done()
+
+ // Configure
+ config := map[string]string{}
+ if privileged {
+ config["security.privileged"] = "true"
+ }
+ config["user.lxd-benchmark"] = "true"
+
+ // Create
+ req := api.ContainersPost{
+ Name: name,
+ Source: api.ContainerSource{
+ Type: "image",
+ Fingerprint: fingerprint,
+ },
+ }
+ req.Config = config
+
+ op, err := c.CreateContainer(req)
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ // Start
+ op, err = c.UpdateContainerState(name, api.ContainerStatePut{Action: "start", Timeout: -1}, "")
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ // Freeze
+ if freeze {
+ op, err := c.UpdateContainerState(name, api.ContainerStatePut{Action: "freeze", Timeout: -1}, "")
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+ }
+ }
+
+ logf("Starting the test")
+ timeStart := time.Now()
+
+ for i := 0; i < batches; i++ {
+ for j := 0; j < batch; j++ {
+ spawnedCount = spawnedCount + 1
+ name := fmt.Sprintf(nameFormat, spawnedCount)
+
+ wgBatch.Add(1)
+ go startContainer(name)
+ }
+ wgBatch.Wait()
+
+ if spawnedCount >= nextStat {
+ interval := time.Since(timeStart).Seconds()
+ logf("Started %d containers in %.3fs (%.3f/s)", spawnedCount, interval, float64(spawnedCount)/interval)
+ nextStat = nextStat * 2
+ }
+ }
+
+ for k := 0; k < remainder; k++ {
+ spawnedCount = spawnedCount + 1
+ name := fmt.Sprintf(nameFormat, spawnedCount)
+
+ wgBatch.Add(1)
+ go startContainer(name)
+ }
+ wgBatch.Wait()
+
+ logf("Test completed in %.3fs", time.Since(timeStart).Seconds())
+
+ return nil
+}
+
+func DeleteContainers(c lxd.ContainerServer, parallel int) error {
+ batch := parallel
+ if batch < 1 {
+ // Detect the number of parallel actions
+ cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+ if err != nil {
+ return err
+ }
+
+ batch = len(cpus)
+ }
+
+ // List all the containers
+ allContainers, err := c.GetContainers()
+ if err != nil {
+ return err
+ }
+
+ containers := []api.Container{}
+ for _, container := range allContainers {
+ if container.Config["user.lxd-benchmark"] != "true" {
+ continue
+ }
+
+ containers = append(containers, container)
+ }
+
+ // Delete them all
+ count := len(containers)
+ logf("%d containers to delete", count)
+
+ batches := count / batch
+
+ deletedCount := 0
+ wgBatch := sync.WaitGroup{}
+ nextStat := batch
+
+ deleteContainer := func(ct api.Container) {
+ defer wgBatch.Done()
+
+ // Stop
+ if ct.IsActive() {
+ op, err := c.UpdateContainerState(ct.Name, api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "")
+ if err != nil {
+ logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err))
+ return
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err))
+ return
+ }
+ }
+
+ // Delete
+ op, err := c.DeleteContainer(ct.Name)
+ if err != nil {
+ logf("Failed to delete container: %s", ct.Name)
+ return
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf("Failed to delete container: %s", ct.Name)
+ return
+ }
+ }
+
+ logf("Starting the cleanup")
+ timeStart := time.Now()
+
+ for i := 0; i < batches; i++ {
+ for j := 0; j < batch; j++ {
+ wgBatch.Add(1)
+ go deleteContainer(containers[deletedCount])
+
+ deletedCount = deletedCount + 1
+ }
+ wgBatch.Wait()
+
+ if deletedCount >= nextStat {
+ interval := time.Since(timeStart).Seconds()
+ logf("Deleted %d containers in %.3fs (%.3f/s)", deletedCount, interval, float64(deletedCount)/interval)
+ nextStat = nextStat * 2
+ }
+ }
+
+ for k := deletedCount; k < count; k++ {
+ wgBatch.Add(1)
+ go deleteContainer(containers[deletedCount])
+
+ deletedCount = deletedCount + 1
+ }
+ wgBatch.Wait()
+
+ logf("Cleanup completed")
+
+ return nil
+}
+
+func logf(format string, args ...interface{}) {
+ fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...)
+}
diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go
index 9c8eb3c11..55de62154 100644
--- a/test/lxd-benchmark/main.go
+++ b/test/lxd-benchmark/main.go
@@ -2,16 +2,11 @@ package main
import (
"fmt"
- "io/ioutil"
"os"
- "strings"
- "sync"
- "time"
+ "github.com/lxc/lxd/benchmark"
"github.com/lxc/lxd/client"
- "github.com/lxc/lxd/lxc/config"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/version"
)
@@ -68,323 +63,10 @@ func run(args []string) error {
switch os.Args[1] {
case "spawn":
- return spawnContainers(c, *argCount, *argImage, *argPrivileged)
+ return benchmark.SpawnContainers(c, *argCount, *argParallel, *argImage, *argPrivileged, *argFreeze)
case "delete":
- return deleteContainers(c)
+ return benchmark.DeleteContainers(c, *argParallel)
}
return nil
}
-
-func logf(format string, args ...interface{}) {
- fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...)
-}
-
-func spawnContainers(c lxd.ContainerServer, count int, image string, privileged bool) error {
- batch := *argParallel
- if batch < 1 {
- // Detect the number of parallel actions
- cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
- if err != nil {
- return err
- }
-
- batch = len(cpus)
- }
-
- batches := count / batch
- remainder := count % batch
-
- // Print the test header
- st, _, err := c.GetServer()
- if err != nil {
- return err
- }
-
- privilegedStr := "unprivileged"
- if privileged {
- privilegedStr = "privileged"
- }
-
- mode := "normal startup"
- if *argFreeze {
- mode = "start and freeze"
- }
-
- fmt.Printf("Test environment:\n")
- fmt.Printf(" Server backend: %s\n", st.Environment.Server)
- fmt.Printf(" Server version: %s\n", st.Environment.ServerVersion)
- fmt.Printf(" Kernel: %s\n", st.Environment.Kernel)
- fmt.Printf(" Kernel architecture: %s\n", st.Environment.KernelArchitecture)
- fmt.Printf(" Kernel version: %s\n", st.Environment.KernelVersion)
- fmt.Printf(" Storage backend: %s\n", st.Environment.Storage)
- fmt.Printf(" Storage version: %s\n", st.Environment.StorageVersion)
- fmt.Printf(" Container backend: %s\n", st.Environment.Driver)
- fmt.Printf(" Container version: %s\n", st.Environment.DriverVersion)
- fmt.Printf("\n")
- fmt.Printf("Test variables:\n")
- fmt.Printf(" Container count: %d\n", count)
- fmt.Printf(" Container mode: %s\n", privilegedStr)
- fmt.Printf(" Startup mode: %s\n", mode)
- fmt.Printf(" Image: %s\n", image)
- fmt.Printf(" Batches: %d\n", batches)
- fmt.Printf(" Batch size: %d\n", batch)
- fmt.Printf(" Remainder: %d\n", remainder)
- fmt.Printf("\n")
-
- // Pre-load the image
- var fingerprint string
- if strings.Contains(image, ":") {
- var remote string
-
- defaultConfig := config.DefaultConfig
- defaultConfig.UserAgent = version.UserAgent
-
- remote, fingerprint, err = defaultConfig.ParseRemote(image)
- if err != nil {
- return err
- }
-
- d, err := defaultConfig.GetImageServer(remote)
- if err != nil {
- return err
- }
-
- if fingerprint == "" {
- fingerprint = "default"
- }
-
- alias, _, err := d.GetImageAlias(fingerprint)
- if err == nil {
- fingerprint = alias.Target
- }
-
- _, _, err = c.GetImage(fingerprint)
- if err != nil {
- logf("Importing image into local store: %s", fingerprint)
- image, _, err := d.GetImage(fingerprint)
- if err != nil {
- logf(fmt.Sprintf("Failed to import image: %s", err))
- return err
- }
-
- op, err := c.CopyImage(d, *image, nil)
- if err != nil {
- logf(fmt.Sprintf("Failed to import image: %s", err))
- return err
- }
-
- err = op.Wait()
- if err != nil {
- logf(fmt.Sprintf("Failed to import image: %s", err))
- return err
- }
- } else {
- logf("Found image in local store: %s", fingerprint)
- }
- } else {
- fingerprint = image
- logf("Found image in local store: %s", fingerprint)
- }
-
- // Start the containers
- spawnedCount := 0
- nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d"
- wgBatch := sync.WaitGroup{}
- nextStat := batch
-
- startContainer := func(name string) {
- defer wgBatch.Done()
-
- // Configure
- config := map[string]string{}
- if privileged {
- config["security.privileged"] = "true"
- }
- config["user.lxd-benchmark"] = "true"
-
- // Create
- req := api.ContainersPost{
- Name: name,
- Source: api.ContainerSource{
- Type: "image",
- Fingerprint: fingerprint,
- },
- }
- req.Config = config
-
- op, err := c.CreateContainer(req)
- if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
- return
- }
-
- err = op.Wait()
- if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
- return
- }
-
- // Start
- op, err = c.UpdateContainerState(name, api.ContainerStatePut{Action: "start", Timeout: -1}, "")
- if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
- return
- }
-
- err = op.Wait()
- if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
- return
- }
-
- // Freeze
- if *argFreeze {
- op, err := c.UpdateContainerState(name, api.ContainerStatePut{Action: "freeze", Timeout: -1}, "")
- if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
- return
- }
-
- err = op.Wait()
- if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
- return
- }
- }
- }
-
- logf("Starting the test")
- timeStart := time.Now()
-
- for i := 0; i < batches; i++ {
- for j := 0; j < batch; j++ {
- spawnedCount = spawnedCount + 1
- name := fmt.Sprintf(nameFormat, spawnedCount)
-
- wgBatch.Add(1)
- go startContainer(name)
- }
- wgBatch.Wait()
-
- if spawnedCount >= nextStat {
- interval := time.Since(timeStart).Seconds()
- logf("Started %d containers in %.3fs (%.3f/s)", spawnedCount, interval, float64(spawnedCount)/interval)
- nextStat = nextStat * 2
- }
- }
-
- for k := 0; k < remainder; k++ {
- spawnedCount = spawnedCount + 1
- name := fmt.Sprintf(nameFormat, spawnedCount)
-
- wgBatch.Add(1)
- go startContainer(name)
- }
- wgBatch.Wait()
-
- logf("Test completed in %.3fs", time.Since(timeStart).Seconds())
-
- return nil
-}
-
-func deleteContainers(c lxd.ContainerServer) error {
- batch := *argParallel
- if batch < 1 {
- // Detect the number of parallel actions
- cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
- if err != nil {
- return err
- }
-
- batch = len(cpus)
- }
-
- // List all the containers
- allContainers, err := c.GetContainers()
- if err != nil {
- return err
- }
-
- containers := []api.Container{}
- for _, container := range allContainers {
- if container.Config["user.lxd-benchmark"] != "true" {
- continue
- }
-
- containers = append(containers, container)
- }
-
- // Delete them all
- count := len(containers)
- logf("%d containers to delete", count)
-
- batches := count / batch
-
- deletedCount := 0
- wgBatch := sync.WaitGroup{}
- nextStat := batch
-
- deleteContainer := func(ct api.Container) {
- defer wgBatch.Done()
-
- // Stop
- if ct.IsActive() {
- op, err := c.UpdateContainerState(ct.Name, api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "")
- if err != nil {
- logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err))
- return
- }
-
- err = op.Wait()
- if err != nil {
- logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err))
- return
- }
- }
-
- // Delete
- op, err := c.DeleteContainer(ct.Name)
- if err != nil {
- logf("Failed to delete container: %s", ct.Name)
- return
- }
-
- err = op.Wait()
- if err != nil {
- logf("Failed to delete container: %s", ct.Name)
- return
- }
- }
-
- logf("Starting the cleanup")
- timeStart := time.Now()
-
- for i := 0; i < batches; i++ {
- for j := 0; j < batch; j++ {
- wgBatch.Add(1)
- go deleteContainer(containers[deletedCount])
-
- deletedCount = deletedCount + 1
- }
- wgBatch.Wait()
-
- if deletedCount >= nextStat {
- interval := time.Since(timeStart).Seconds()
- logf("Deleted %d containers in %.3fs (%.3f/s)", deletedCount, interval, float64(deletedCount)/interval)
- nextStat = nextStat * 2
- }
- }
-
- for k := deletedCount; k < count; k++ {
- wgBatch.Add(1)
- go deleteContainer(containers[deletedCount])
-
- deletedCount = deletedCount + 1
- }
- wgBatch.Wait()
-
- logf("Cleanup completed")
-
- return nil
-}
From 6b459fc01bfea1e68bd9d6b0de6f5e22fc38b8a1 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 12:24:36 +0200
Subject: [PATCH 2/7] benchmark: extract PrintServerInfo function
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 41 +++++++++++++++++++++++------------------
test/lxd-benchmark/main.go | 2 ++
2 files changed, 25 insertions(+), 18 deletions(-)
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index e8fe5e8f7..26e2a60a1 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -13,6 +13,28 @@ import (
"github.com/lxc/lxd/shared/version"
)
+// PrintServerInfo prints out information about the server.
+func PrintServerInfo(c lxd.ContainerServer) error {
+ server, _, err := c.GetServer()
+ if err != nil {
+ return err
+ }
+ env := server.Environment
+ fmt.Printf("Test environment:\n")
+ fmt.Printf(" Server backend: %s\n", env.Server)
+ fmt.Printf(" Server version: %s\n", env.ServerVersion)
+ fmt.Printf(" Kernel: %s\n", env.Kernel)
+ fmt.Printf(" Kernel architecture: %s\n", env.KernelArchitecture)
+ fmt.Printf(" Kernel version: %s\n", env.KernelVersion)
+ fmt.Printf(" Storage backend: %s\n", env.Storage)
+ fmt.Printf(" Storage version: %s\n", env.StorageVersion)
+ fmt.Printf(" Container backend: %s\n", env.Driver)
+ fmt.Printf(" Container version: %s\n", env.DriverVersion)
+ fmt.Printf("\n")
+ return nil
+}
+
+// SpawnContainers launches a set of containers.
func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, freeze bool) error {
batch := parallel
if batch < 1 {
@@ -29,32 +51,14 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
remainder := count % batch
// Print the test header
- st, _, err := c.GetServer()
- if err != nil {
- return err
- }
-
privilegedStr := "unprivileged"
if privileged {
privilegedStr = "privileged"
}
-
mode := "normal startup"
if freeze {
mode = "start and freeze"
}
-
- fmt.Printf("Test environment:\n")
- fmt.Printf(" Server backend: %s\n", st.Environment.Server)
- fmt.Printf(" Server version: %s\n", st.Environment.ServerVersion)
- fmt.Printf(" Kernel: %s\n", st.Environment.Kernel)
- fmt.Printf(" Kernel architecture: %s\n", st.Environment.KernelArchitecture)
- fmt.Printf(" Kernel version: %s\n", st.Environment.KernelVersion)
- fmt.Printf(" Storage backend: %s\n", st.Environment.Storage)
- fmt.Printf(" Storage version: %s\n", st.Environment.StorageVersion)
- fmt.Printf(" Container backend: %s\n", st.Environment.Driver)
- fmt.Printf(" Container version: %s\n", st.Environment.DriverVersion)
- fmt.Printf("\n")
fmt.Printf("Test variables:\n")
fmt.Printf(" Container count: %d\n", count)
fmt.Printf(" Container mode: %s\n", privilegedStr)
@@ -69,6 +73,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
var fingerprint string
if strings.Contains(image, ":") {
var remote string
+ var err error
defaultConfig := config.DefaultConfig
defaultConfig.UserAgent = version.UserAgent
diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go
index 55de62154..f2a2a6f2b 100644
--- a/test/lxd-benchmark/main.go
+++ b/test/lxd-benchmark/main.go
@@ -61,6 +61,8 @@ func run(args []string) error {
return err
}
+ benchmark.PrintServerInfo(c)
+
switch os.Args[1] {
case "spawn":
return benchmark.SpawnContainers(c, *argCount, *argParallel, *argImage, *argPrivileged, *argFreeze)
From 31e612a9620a56b9273cafa3661e0f452ee7f917 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 12:40:01 +0200
Subject: [PATCH 3/7] benchmark: extract printTestConfig function
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 44 +++++++++++++++++++++++++-------------------
1 file changed, 25 insertions(+), 19 deletions(-)
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index 26e2a60a1..141066bb4 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -47,28 +47,11 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
batch = len(cpus)
}
+ printTestConfig(count, batch, image, privileged, freeze)
+
batches := count / batch
remainder := count % batch
- // Print the test header
- privilegedStr := "unprivileged"
- if privileged {
- privilegedStr = "privileged"
- }
- mode := "normal startup"
- if freeze {
- mode = "start and freeze"
- }
- fmt.Printf("Test variables:\n")
- fmt.Printf(" Container count: %d\n", count)
- fmt.Printf(" Container mode: %s\n", privilegedStr)
- fmt.Printf(" Startup mode: %s\n", mode)
- fmt.Printf(" Image: %s\n", image)
- fmt.Printf(" Batches: %d\n", batches)
- fmt.Printf(" Batch size: %d\n", batch)
- fmt.Printf(" Remainder: %d\n", remainder)
- fmt.Printf("\n")
-
// Pre-load the image
var fingerprint string
if strings.Contains(image, ":") {
@@ -330,3 +313,26 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
func logf(format string, args ...interface{}) {
fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...)
}
+
+func printTestConfig(count int, batchSize int, image string, privileged bool, freeze bool) {
+ privilegedStr := "unprivileged"
+ if privileged {
+ privilegedStr = "privileged"
+ }
+ mode := "normal startup"
+ if freeze {
+ mode = "start and freeze"
+ }
+
+ batches := count / batchSize
+ remainder := count % batchSize
+ fmt.Printf("Test variables:\n")
+ fmt.Printf(" Container count: %d\n", count)
+ fmt.Printf(" Container mode: %s\n", privilegedStr)
+ fmt.Printf(" Startup mode: %s\n", mode)
+ fmt.Printf(" Image: %s\n", image)
+ fmt.Printf(" Batches: %d\n", batches)
+ fmt.Printf(" Batch size: %d\n", batchSize)
+ fmt.Printf(" Remainder: %d\n", remainder)
+ fmt.Printf("\n")
+}
From c5f93f0b5486387b1142e3ebfaadb0cc124861d1 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 12:54:22 +0200
Subject: [PATCH 4/7] benchmark: extract getBatchSize function
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 56 +++++++++++++++++++++++++++-----------------------
1 file changed, 30 insertions(+), 26 deletions(-)
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index 141066bb4..d9911fd64 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -36,21 +36,15 @@ func PrintServerInfo(c lxd.ContainerServer) error {
// SpawnContainers launches a set of containers.
func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image string, privileged bool, freeze bool) error {
- batch := parallel
- if batch < 1 {
- // Detect the number of parallel actions
- cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
- if err != nil {
- return err
- }
-
- batch = len(cpus)
+ batchSize, err := getBatchSize(parallel)
+ if err != nil {
+ return err
}
- printTestConfig(count, batch, image, privileged, freeze)
+ printTestConfig(count, batchSize, image, privileged, freeze)
- batches := count / batch
- remainder := count % batch
+ batches := count / batchSize
+ remainder := count % batchSize
// Pre-load the image
var fingerprint string
@@ -112,7 +106,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
spawnedCount := 0
nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d"
wgBatch := sync.WaitGroup{}
- nextStat := batch
+ nextStat := batchSize
startContainer := func(name string) {
defer wgBatch.Done()
@@ -179,7 +173,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
timeStart := time.Now()
for i := 0; i < batches; i++ {
- for j := 0; j < batch; j++ {
+ for j := 0; j < batchSize; j++ {
spawnedCount = spawnedCount + 1
name := fmt.Sprintf(nameFormat, spawnedCount)
@@ -209,16 +203,11 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
return nil
}
+// DeleteContainers removes containers created by the benchmark.
func DeleteContainers(c lxd.ContainerServer, parallel int) error {
- batch := parallel
- if batch < 1 {
- // Detect the number of parallel actions
- cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
- if err != nil {
- return err
- }
-
- batch = len(cpus)
+ batchSize, err := getBatchSize(parallel)
+ if err != nil {
+ return err
}
// List all the containers
@@ -240,11 +229,11 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
count := len(containers)
logf("%d containers to delete", count)
- batches := count / batch
+ batches := count / batchSize
deletedCount := 0
wgBatch := sync.WaitGroup{}
- nextStat := batch
+ nextStat := batchSize
deleteContainer := func(ct api.Container) {
defer wgBatch.Done()
@@ -282,7 +271,7 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
timeStart := time.Now()
for i := 0; i < batches; i++ {
- for j := 0; j < batch; j++ {
+ for j := 0; j < batchSize; j++ {
wgBatch.Add(1)
go deleteContainer(containers[deletedCount])
@@ -310,6 +299,21 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
return nil
}
+func getBatchSize(parallel int) (int, error) {
+ batchSize := parallel
+ if batchSize < 1 {
+ // Detect the number of parallel actions
+ cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+ if err != nil {
+ return -1, err
+ }
+
+ batchSize = len(cpus)
+ }
+
+ return batchSize, nil
+}
+
func logf(format string, args ...interface{}) {
fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...)
}
From b8bf7d20c70e5328c5944261205b146b07a5c8eb Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 13:01:34 +0200
Subject: [PATCH 5/7] benchmark: extract GetContainers function
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index d9911fd64..81bdcdda3 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -203,20 +203,15 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
return nil
}
-// DeleteContainers removes containers created by the benchmark.
-func DeleteContainers(c lxd.ContainerServer, parallel int) error {
- batchSize, err := getBatchSize(parallel)
- if err != nil {
- return err
- }
+// GetContainers returns containers created by the benchmark.
+func GetContainers(c lxd.ContainerServer) ([]api.Container, error) {
+ containers := []api.Container{}
- // List all the containers
allContainers, err := c.GetContainers()
if err != nil {
- return err
+ return containers, err
}
- containers := []api.Container{}
for _, container := range allContainers {
if container.Config["user.lxd-benchmark"] != "true" {
continue
@@ -225,7 +220,22 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
containers = append(containers, container)
}
- // Delete them all
+ return containers, nil
+}
+
+// DeleteContainers removes containers created by the benchmark.
+func DeleteContainers(c lxd.ContainerServer, parallel int) error {
+ batchSize, err := getBatchSize(parallel)
+ if err != nil {
+ return err
+ }
+
+ // List all the containers
+ containers, err := GetContainers(c)
+ if err != nil {
+ return err
+ }
+
count := len(containers)
logf("%d containers to delete", count)
From 311f9a30f0c3fdfb7cb3ac84ffc60fe369fbc245 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 16:25:20 +0200
Subject: [PATCH 6/7] benchmark: add processBatch function, use it in
SpawnContainers and DeleteContainers
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 144 ++++++++++++++++++++-----------------------------
1 file changed, 57 insertions(+), 87 deletions(-)
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index 81bdcdda3..cb54ed9a2 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -43,9 +43,6 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
printTestConfig(count, batchSize, image, privileged, freeze)
- batches := count / batchSize
- remainder := count % batchSize
-
// Pre-load the image
var fingerprint string
if strings.Contains(image, ":") {
@@ -102,14 +99,8 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
logf("Found image in local store: %s", fingerprint)
}
- // Start the containers
- spawnedCount := 0
- nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d"
- wgBatch := sync.WaitGroup{}
- nextStat := batchSize
-
- startContainer := func(name string) {
- defer wgBatch.Done()
+ startContainer := func(index int, wg *sync.WaitGroup) {
+ defer wg.Done()
// Configure
config := map[string]string{}
@@ -119,6 +110,8 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
config["user.lxd-benchmark"] = "true"
// Create
+ nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", count))) + "d"
+ name := fmt.Sprintf(nameFormat, index+1)
req := api.ContainersPost{
Name: name,
Source: api.ContainerSource{
@@ -130,26 +123,26 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
op, err := c.CreateContainer(req)
if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ logf("Failed to spawn container '%s': %s", name, err)
return
}
err = op.Wait()
if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ logf("Failed to spawn container '%s': %s", name, err)
return
}
// Start
op, err = c.UpdateContainerState(name, api.ContainerStatePut{Action: "start", Timeout: -1}, "")
if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ logf("Failed to spawn container '%s': %s", name, err)
return
}
err = op.Wait()
if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ logf("Failed to spawn container '%s': %s", name, err)
return
}
@@ -157,49 +150,19 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
if freeze {
op, err := c.UpdateContainerState(name, api.ContainerStatePut{Action: "freeze", Timeout: -1}, "")
if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ logf("Failed to spawn container '%s': %s", name, err)
return
}
err = op.Wait()
if err != nil {
- logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ logf("Failed to spawn container '%s': %s", name, err)
return
}
}
}
- logf("Starting the test")
- timeStart := time.Now()
-
- for i := 0; i < batches; i++ {
- for j := 0; j < batchSize; j++ {
- spawnedCount = spawnedCount + 1
- name := fmt.Sprintf(nameFormat, spawnedCount)
-
- wgBatch.Add(1)
- go startContainer(name)
- }
- wgBatch.Wait()
-
- if spawnedCount >= nextStat {
- interval := time.Since(timeStart).Seconds()
- logf("Started %d containers in %.3fs (%.3f/s)", spawnedCount, interval, float64(spawnedCount)/interval)
- nextStat = nextStat * 2
- }
- }
-
- for k := 0; k < remainder; k++ {
- spawnedCount = spawnedCount + 1
- name := fmt.Sprintf(nameFormat, spawnedCount)
-
- wgBatch.Add(1)
- go startContainer(name)
- }
- wgBatch.Wait()
-
- logf("Test completed in %.3fs", time.Since(timeStart).Seconds())
-
+ processBatch(count, batchSize, startContainer)
return nil
}
@@ -237,28 +200,24 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
}
count := len(containers)
- logf("%d containers to delete", count)
+ logf("%d containers to delete", len(containers))
- batches := count / batchSize
+ deleteContainer := func(index int, wg *sync.WaitGroup) {
+ defer wg.Done()
- deletedCount := 0
- wgBatch := sync.WaitGroup{}
- nextStat := batchSize
-
- deleteContainer := func(ct api.Container) {
- defer wgBatch.Done()
+ ct := containers[index]
// Stop
if ct.IsActive() {
op, err := c.UpdateContainerState(ct.Name, api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "")
if err != nil {
- logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err))
+ logf("Failed to delete container '%s': %s", ct.Name, err)
return
}
err = op.Wait()
if err != nil {
- logf(fmt.Sprintf("Failed to delete container '%s': %s", ct.Name, err))
+ logf("Failed to delete container '%s': %s", ct.Name, err)
return
}
}
@@ -277,35 +236,7 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) error {
}
}
- logf("Starting the cleanup")
- timeStart := time.Now()
-
- for i := 0; i < batches; i++ {
- for j := 0; j < batchSize; j++ {
- wgBatch.Add(1)
- go deleteContainer(containers[deletedCount])
-
- deletedCount = deletedCount + 1
- }
- wgBatch.Wait()
-
- if deletedCount >= nextStat {
- interval := time.Since(timeStart).Seconds()
- logf("Deleted %d containers in %.3fs (%.3f/s)", deletedCount, interval, float64(deletedCount)/interval)
- nextStat = nextStat * 2
- }
- }
-
- for k := deletedCount; k < count; k++ {
- wgBatch.Add(1)
- go deleteContainer(containers[deletedCount])
-
- deletedCount = deletedCount + 1
- }
- wgBatch.Wait()
-
- logf("Cleanup completed")
-
+ processBatch(count, batchSize, deleteContainer)
return nil
}
@@ -324,6 +255,45 @@ func getBatchSize(parallel int) (int, error) {
return batchSize, nil
}
+func processBatch(count int, batchSize int, process func(index int, wg *sync.WaitGroup)) time.Duration {
+ batches := count / batchSize
+ remainder := count % batchSize
+ processed := 0
+ wg := sync.WaitGroup{}
+ nextStat := batchSize
+
+ logf("Batch processing start")
+ timeStart := time.Now()
+
+ for i := 0; i < batches; i++ {
+ for j := 0; j < batchSize; j++ {
+ wg.Add(1)
+ go process(processed, &wg)
+ processed++
+ }
+ wg.Wait()
+
+ if processed >= nextStat {
+ interval := time.Since(timeStart).Seconds()
+ logf("Processed %d containers in %.3fs (%.3f/s)", processed, interval, float64(processed)/interval)
+ nextStat = nextStat * 2
+ }
+
+ }
+
+ for k := 0; k < remainder; k++ {
+ wg.Add(1)
+ go process(processed, &wg)
+ processed++
+ }
+ wg.Wait()
+
+ timeEnd := time.Now()
+ duration := timeEnd.Sub(timeStart)
+ logf("Batch processing completed in %.3fs", duration.Seconds())
+ return duration
+}
+
func logf(format string, args ...interface{}) {
fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), format), args...)
}
From 093963f7b628ac4728005c56c6dca198d90557f9 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Tue, 5 Sep 2017 16:51:46 +0200
Subject: [PATCH 7/7] benchmark: extract ensureImage function
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
benchmark/benchmark.go | 114 ++++++++++++++++++++++++++-----------------------
1 file changed, 60 insertions(+), 54 deletions(-)
diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index cb54ed9a2..eea503b8c 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -43,60 +43,9 @@ func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image strin
printTestConfig(count, batchSize, image, privileged, freeze)
- // Pre-load the image
- var fingerprint string
- if strings.Contains(image, ":") {
- var remote string
- var err error
-
- defaultConfig := config.DefaultConfig
- defaultConfig.UserAgent = version.UserAgent
-
- remote, fingerprint, err = defaultConfig.ParseRemote(image)
- if err != nil {
- return err
- }
-
- d, err := defaultConfig.GetImageServer(remote)
- if err != nil {
- return err
- }
-
- if fingerprint == "" {
- fingerprint = "default"
- }
-
- alias, _, err := d.GetImageAlias(fingerprint)
- if err == nil {
- fingerprint = alias.Target
- }
-
- _, _, err = c.GetImage(fingerprint)
- if err != nil {
- logf("Importing image into local store: %s", fingerprint)
- image, _, err := d.GetImage(fingerprint)
- if err != nil {
- logf(fmt.Sprintf("Failed to import image: %s", err))
- return err
- }
-
- op, err := c.CopyImage(d, *image, nil)
- if err != nil {
- logf(fmt.Sprintf("Failed to import image: %s", err))
- return err
- }
-
- err = op.Wait()
- if err != nil {
- logf(fmt.Sprintf("Failed to import image: %s", err))
- return err
- }
- } else {
- logf("Found image in local store: %s", fingerprint)
- }
- } else {
- fingerprint = image
- logf("Found image in local store: %s", fingerprint)
+ fingerprint, err := ensureImage(c, image)
+ if err != nil {
+ return err
}
startContainer := func(index int, wg *sync.WaitGroup) {
@@ -255,6 +204,63 @@ func getBatchSize(parallel int) (int, error) {
return batchSize, nil
}
+func ensureImage(c lxd.ContainerServer, image string) (string, error) {
+ var fingerprint string
+
+ if strings.Contains(image, ":") {
+ defaultConfig := config.DefaultConfig
+ defaultConfig.UserAgent = version.UserAgent
+
+ remote, fp, err := defaultConfig.ParseRemote(image)
+ if err != nil {
+ return "", err
+ }
+ fingerprint = fp
+
+ imageServer, err := defaultConfig.GetImageServer(remote)
+ if err != nil {
+ return "", err
+ }
+
+ if fingerprint == "" {
+ fingerprint = "default"
+ }
+
+ alias, _, err := imageServer.GetImageAlias(fingerprint)
+ if err == nil {
+ fingerprint = alias.Target
+ }
+
+ _, _, err = c.GetImage(fingerprint)
+ if err != nil {
+ logf("Importing image into local store: %s", fingerprint)
+ image, _, err := imageServer.GetImage(fingerprint)
+ if err != nil {
+ logf("Failed to import image: %s", err)
+ return "", err
+ }
+
+ op, err := c.CopyImage(imageServer, *image, nil)
+ if err != nil {
+ logf("Failed to import image: %s", err)
+ return "", err
+ }
+
+ err = op.Wait()
+ if err != nil {
+ logf("Failed to import image: %s", err)
+ return "", err
+ }
+ } else {
+ logf("Found image in local store: %s", fingerprint)
+ }
+ } else {
+ fingerprint = image
+ logf("Found image in local store: %s", fingerprint)
+ }
+ return fingerprint, nil
+}
+
func processBatch(count int, batchSize int, process func(index int, wg *sync.WaitGroup)) time.Duration {
batches := count / batchSize
remainder := count % batchSize
More information about the lxc-devel
mailing list