Add execs to stress tests
This improves the exec support so that they can run along with the normal stress tests. You don't have to pick exec stres or container stress. They both run at the same time and report the different values. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
823d3398ea
commit
94602aea63
110
cmd/containerd-stress/exec_worker.go
Normal file
110
cmd/containerd-stress/exec_worker.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd"
|
||||||
|
"github.com/containerd/containerd/cio"
|
||||||
|
"github.com/containerd/containerd/oci"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type execWorker struct {
|
||||||
|
worker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *execWorker) exec(ctx, tctx context.Context) {
|
||||||
|
defer func() {
|
||||||
|
w.wg.Done()
|
||||||
|
logrus.Infof("worker %d finished", w.id)
|
||||||
|
}()
|
||||||
|
// create and start the exec container
|
||||||
|
w.spec.Linux.CgroupsPath = filepath.Join("/", "stress", "exec-container")
|
||||||
|
w.spec.Process.Args = []string{
|
||||||
|
"sleep", "30d",
|
||||||
|
}
|
||||||
|
c, err := w.client.NewContainer(ctx, "exec-container",
|
||||||
|
containerd.WithNewSnapshot("exec-container", w.image),
|
||||||
|
containerd.WithSpec(w.spec, oci.WithUsername("games")),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("create exec container")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Delete(ctx, containerd.WithSnapshotCleanup)
|
||||||
|
|
||||||
|
task, err := c.NewTask(ctx, cio.NullIO)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("create exec container's task")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer task.Delete(ctx, containerd.WithProcessKill)
|
||||||
|
|
||||||
|
statusC, err := task.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("wait exec container's task")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pspec := w.spec.Process
|
||||||
|
pspec.Args = []string{"true"}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tctx.Done():
|
||||||
|
if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
|
||||||
|
logrus.WithError(err).Error("kill exec container's task")
|
||||||
|
}
|
||||||
|
<-statusC
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
w.count++
|
||||||
|
id := w.getID()
|
||||||
|
logrus.Debugf("starting exec %s", id)
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
if err := w.runExec(ctx, task, id, pspec); err != nil {
|
||||||
|
if err != context.DeadlineExceeded ||
|
||||||
|
!strings.Contains(err.Error(), context.DeadlineExceeded.Error()) {
|
||||||
|
w.failures++
|
||||||
|
logrus.WithError(err).Errorf("running exec %s", id)
|
||||||
|
errCounter.WithValues(err.Error()).Inc()
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// only log times are success so we don't scew the results from failures that go really fast
|
||||||
|
execTimer.WithValues(w.commit).UpdateSince(start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *execWorker) runExec(ctx context.Context, task containerd.Task, id string, spec *specs.Process) error {
|
||||||
|
process, err := task.Exec(ctx, id, spec, cio.NullIO)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer process.Delete(ctx, containerd.WithProcessKill)
|
||||||
|
|
||||||
|
statusC, err := process.Wait(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := process.Start(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status := <-statusC
|
||||||
|
_, _, err = status.Result()
|
||||||
|
if err != nil {
|
||||||
|
if err == context.DeadlineExceeded || err == context.Canceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.failures++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -25,6 +25,7 @@ const imageName = "docker.io/library/alpine:latest"
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ct metrics.LabeledTimer
|
ct metrics.LabeledTimer
|
||||||
|
execTimer metrics.LabeledTimer
|
||||||
errCounter metrics.LabeledCounter
|
errCounter metrics.LabeledCounter
|
||||||
binarySizeGauge metrics.LabeledGauge
|
binarySizeGauge metrics.LabeledGauge
|
||||||
)
|
)
|
||||||
@ -34,6 +35,7 @@ func init() {
|
|||||||
// if you want more fine grained metrics then you can drill down with the metrics in prom that
|
// if you want more fine grained metrics then you can drill down with the metrics in prom that
|
||||||
// containerd is outputing
|
// containerd is outputing
|
||||||
ct = ns.NewLabeledTimer("run", "Run time of a full container during the test", "commit")
|
ct = ns.NewLabeledTimer("run", "Run time of a full container during the test", "commit")
|
||||||
|
execTimer = ns.NewLabeledTimer("exec", "Run time of an exec process during the test", "commit")
|
||||||
binarySizeGauge = ns.NewLabeledGauge("binary_size", "Binary size of compiled binaries", metrics.Bytes, "name")
|
binarySizeGauge = ns.NewLabeledGauge("binary_size", "Binary size of compiled binaries", metrics.Bytes, "name")
|
||||||
errCounter = ns.NewLabeledCounter("errors", "Errors encountered running the stress tests", "err")
|
errCounter = ns.NewLabeledCounter("errors", "Errors encountered running the stress tests", "err")
|
||||||
metrics.Register(ns)
|
metrics.Register(ns)
|
||||||
@ -75,9 +77,12 @@ func (r *run) gather(workers []*worker) *result {
|
|||||||
|
|
||||||
type result struct {
|
type result struct {
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
|
Failures int `json:"failures"`
|
||||||
Seconds float64 `json:"seconds"`
|
Seconds float64 `json:"seconds"`
|
||||||
ContainersPerSecond float64 `json:"containersPerSecond"`
|
ContainersPerSecond float64 `json:"containersPerSecond"`
|
||||||
SecondsPerContainer float64 `json:"secondsPerContainer"`
|
SecondsPerContainer float64 `json:"secondsPerContainer"`
|
||||||
|
ExecTotal int `json:"execTotal"`
|
||||||
|
ExecFailures int `json:"execFailures"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -121,12 +126,12 @@ func main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Before = func(context *cli.Context) error {
|
app.Before = func(context *cli.Context) error {
|
||||||
if context.GlobalBool("debug") {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
if context.GlobalBool("json") {
|
if context.GlobalBool("json") {
|
||||||
logrus.SetLevel(logrus.WarnLevel)
|
logrus.SetLevel(logrus.WarnLevel)
|
||||||
}
|
}
|
||||||
|
if context.GlobalBool("debug") {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
app.Action = func(context *cli.Context) error {
|
app.Action = func(context *cli.Context) error {
|
||||||
@ -206,9 +211,6 @@ func test(c config) error {
|
|||||||
)
|
)
|
||||||
logrus.Info("starting stress test run...")
|
logrus.Info("starting stress test run...")
|
||||||
args := oci.WithProcessArgs("true")
|
args := oci.WithProcessArgs("true")
|
||||||
if c.Exec {
|
|
||||||
args = oci.WithProcessArgs("sleep", "10")
|
|
||||||
}
|
|
||||||
v, err := client.Version(ctx)
|
v, err := client.Version(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -230,11 +232,34 @@ func test(c config) error {
|
|||||||
spec: spec,
|
spec: spec,
|
||||||
image: image,
|
image: image,
|
||||||
client: client,
|
client: client,
|
||||||
doExec: c.Exec,
|
|
||||||
commit: v.Revision,
|
commit: v.Revision,
|
||||||
}
|
}
|
||||||
workers = append(workers, w)
|
workers = append(workers, w)
|
||||||
}
|
}
|
||||||
|
var exec *execWorker
|
||||||
|
if c.Exec {
|
||||||
|
wg.Add(1)
|
||||||
|
spec, err := oci.GenerateSpec(ctx, client,
|
||||||
|
&containers.Container{},
|
||||||
|
oci.WithImageConfig(image),
|
||||||
|
args,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
exec = &execWorker{
|
||||||
|
worker: worker{
|
||||||
|
id: c.Concurrency,
|
||||||
|
wg: &wg,
|
||||||
|
spec: spec,
|
||||||
|
image: image,
|
||||||
|
client: client,
|
||||||
|
commit: v.Revision,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
go exec.exec(ctx, tctx)
|
||||||
|
}
|
||||||
|
|
||||||
// start the timer and run the worker
|
// start the timer and run the worker
|
||||||
r.start()
|
r.start()
|
||||||
for _, w := range workers {
|
for _, w := range workers {
|
||||||
@ -245,6 +270,10 @@ func test(c config) error {
|
|||||||
r.end()
|
r.end()
|
||||||
|
|
||||||
results := r.gather(workers)
|
results := r.gather(workers)
|
||||||
|
if c.Exec {
|
||||||
|
results.ExecTotal = exec.count
|
||||||
|
results.ExecFailures = exec.failures
|
||||||
|
}
|
||||||
logrus.Infof("ending test run in %0.3f seconds", results.Seconds)
|
logrus.Infof("ending test run in %0.3f seconds", results.Seconds)
|
||||||
|
|
||||||
logrus.WithField("failures", r.failures).Infof(
|
logrus.WithField("failures", r.failures).Infof(
|
||||||
|
@ -4,10 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
@ -26,7 +24,6 @@ type worker struct {
|
|||||||
client *containerd.Client
|
client *containerd.Client
|
||||||
image containerd.Image
|
image containerd.Image
|
||||||
spec *specs.Spec
|
spec *specs.Spec
|
||||||
doExec bool
|
|
||||||
commit string
|
commit string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,17 +83,6 @@ func (w *worker) runContainer(ctx context.Context, id string) error {
|
|||||||
if err := task.Start(ctx); err != nil {
|
if err := task.Start(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if w.doExec {
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
if err := w.exec(ctx, i, task); err != nil {
|
|
||||||
w.failures++
|
|
||||||
logrus.WithError(err).Error("exec failure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
status := <-statusC
|
status := <-statusC
|
||||||
_, _, err = status.Result()
|
_, _, err = status.Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -108,25 +94,6 @@ func (w *worker) runContainer(ctx context.Context, id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) exec(ctx context.Context, i int, t containerd.Task) error {
|
|
||||||
pSpec := *w.spec.Process
|
|
||||||
pSpec.Args = []string{"true"}
|
|
||||||
process, err := t.Exec(ctx, strconv.Itoa(i), &pSpec, cio.NullIO)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer process.Delete(ctx)
|
|
||||||
status, err := process.Wait(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := process.Start(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
<-status
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *worker) getID() string {
|
func (w *worker) getID() string {
|
||||||
return fmt.Sprintf("%d-%d", w.id, w.count)
|
return fmt.Sprintf("%d-%d", w.id, w.count)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user