Skip to content
This repository was archived by the owner on Apr 3, 2018. It is now read-only.

Commit b20ba87

Browse files
author
Sebastien Boeuf
committed
pod: Improve pod create performances
In order to optimize pod creation, this patch does not wait for the VM after it has been started. Instead, it will wait for it during the "start" stage. This will allow our VM to be started while the caller could potentially do other things between create and start. Globally, this improves the boot time of our containers. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
1 parent e62b2ae commit b20ba87

5 files changed

Lines changed: 88 additions & 90 deletions

File tree

‎hypervisor.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,8 @@ func RunningOnVMM(cpuInfoPath string) (bool, error) {
446446
typehypervisorinterface {
447447
init(pod*Pod) error
448448
createPod(podConfigPodConfig) error
449-
startPod(startCh, stopChchanstruct{}) error
449+
startPod() error
450+
waitPod(timeoutint) error
450451
stopPod() error
451452
pausePod() error
452453
resumePod() error

‎mock_hypervisor.go‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ func (m *mockHypervisor) createPod(podConfig PodConfig) error {
3636
returnnil
3737
}
3838

39-
func (m*mockHypervisor) startPod(startCh, stopChchanstruct{}) error {
40-
varmsgstruct{}
41-
startCh<-msg
39+
func (m*mockHypervisor) startPod() error {
40+
returnnil
41+
}
42+
43+
func (m*mockHypervisor) waitPod(timeoutint) error {
4244
returnnil
4345
}
4446

‎mock_hypervisor_test.go‎

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package virtcontainers
1919
import (
2020
"fmt"
2121
"testing"
22-
"time"
2322
)
2423

2524
funcTestMockHypervisorInit(t*testing.T) {
@@ -36,8 +35,7 @@ func TestMockHypervisorInit(t *testing.T) {
3635
}
3736

3837
// wrong config
39-
err:=m.init(pod)
40-
iferr==nil {
38+
iferr:=m.init(pod); err==nil {
4139
t.Fatal()
4240
}
4341

@@ -48,8 +46,7 @@ func TestMockHypervisorInit(t *testing.T) {
4846
}
4947

5048
// right config
51-
err=m.init(pod)
52-
iferr!=nil {
49+
iferr:=m.init(pod); err!=nil {
5350
t.Fatal(err)
5451
}
5552
}
@@ -59,42 +56,39 @@ func TestMockHypervisorCreatePod(t *testing.T) {
5956

6057
config:=PodConfig{}
6158

62-
err:=m.createPod(config)
63-
iferr!=nil {
59+
iferr:=m.createPod(config); err!=nil {
6460
t.Fatal(err)
6561
}
6662
}
6763

6864
funcTestMockHypervisorStartPod(t*testing.T) {
6965
varm*mockHypervisor
7066

71-
startCh:=make(chanstruct{})
72-
stopCh:=make(chanstruct{})
67+
iferr:=m.startPod(); err!=nil {
68+
t.Fatal(err)
69+
}
70+
}
7371

74-
gom.startPod(startCh, stopCh)
72+
funcTestMockHypervisorWaitPod(t*testing.T) {
73+
varm*mockHypervisor
7574

76-
select {
77-
case<-startCh:
78-
break
79-
case<-time.After(time.Second):
80-
t.Fatal("Timeout waiting for start notification")
75+
iferr:=m.waitPod(0); err!=nil {
76+
t.Fatal(err)
8177
}
8278
}
8379

8480
funcTestMockHypervisorStopPod(t*testing.T) {
8581
varm*mockHypervisor
8682

87-
err:=m.stopPod()
88-
iferr!=nil {
83+
iferr:=m.stopPod(); err!=nil {
8984
t.Fatal(err)
9085
}
9186
}
9287

9388
funcTestMockHypervisorAddDevice(t*testing.T) {
9489
varm*mockHypervisor
9590

96-
err:=m.addDevice(nil, imgDev)
97-
iferr!=nil {
91+
iferr:=m.addDevice(nil, imgDev); err!=nil {
9892
t.Fatal(err)
9993
}
10094
}

‎pod.go‎

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"strings"
2424
"sync"
2525
"syscall"
26-
"time"
2726

2827
"github.com/sirupsen/logrus"
2928
)
@@ -39,6 +38,10 @@ const controlSocket = "ctrl.sock"
3938
// to understand if the VM is still alive or not.
4039
constmonitorSocket="monitor.sock"
4140

41+
// vmStartTimeout represents the time in seconds a pod can wait before
42+
// to consider the VM starting operation failed.
43+
constvmStartTimeout=10
44+
4245
// stateString is a string representing a pod state.
4346
typestateStringstring
4447

@@ -748,34 +751,13 @@ func (p *Pod) startSetState() error {
748751
returnnil
749752
}
750753

751-
// startVM starts the VM, ensuring it is started before it returns or issuing
752-
// an error in case of timeout. Then it connects to the agent inside the VM.
754+
// startVM starts the VM.
753755
func (p*Pod) startVM(netNsPathstring) error {
754-
vmStartedCh:=make(chanstruct{})
755-
vmStoppedCh:=make(chanstruct{})
756-
consttimeout=time.Duration(10) *time.Second
757-
758-
l:=p.Logger()
759-
l.Info("Starting VM")
760-
761-
gofunc() {
762-
p.network.run(netNsPath, func() error {
763-
err:=p.hypervisor.startPod(vmStartedCh, vmStoppedCh)
764-
returnerr
765-
})
766-
}()
767-
768-
// Wait for the pod started notification
769-
select {
770-
case<-vmStartedCh:
771-
break
772-
case<-time.After(timeout):
773-
returnfmt.Errorf("Did not receive the pod started notification (timeout %ds)", timeout)
774-
}
775-
776-
l.Info("VM started")
756+
p.Logger().Info("Starting VM")
777757

778-
returnnil
758+
returnp.network.run(netNsPath, func() error {
759+
returnp.hypervisor.startPod()
760+
})
779761
}
780762

781763
// startShims registers all containers to the proxy and starts one
@@ -835,6 +817,14 @@ func (p *Pod) start() error {
835817
returnerr
836818
}
837819

820+
l:=p.Logger()
821+
822+
iferr:=p.hypervisor.waitPod(vmStartTimeout); err!=nil {
823+
returnerr
824+
}
825+
826+
l.Info("VM started")
827+
838828
if_, _, err:=p.proxy.connect(*p, false); err!=nil {
839829
returnerr
840830
}
@@ -855,7 +845,7 @@ func (p *Pod) start() error {
855845
}
856846
}
857847

858-
p.Logger().Info("started")
848+
l.Info("started")
859849

860850
returnnil
861851
}

‎qemu.go‎

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -573,40 +573,6 @@ func (q *qemu) init(pod *Pod) error {
573573
returnnil
574574
}
575575

576-
func (q*qemu) qmpMonitor(connectedChchanstruct{}) {
577-
deferfunc(qemu*qemu) {
578-
ifq.qmpMonitorCh.qmp!=nil {
579-
q.qmpMonitorCh.qmp.Shutdown()
580-
}
581-
582-
q.qmpMonitorCh.wg.Done()
583-
}(q)
584-
585-
cfg:= ciaoQemu.QMPConfig{Logger: newQMPLogger()}
586-
qmp, ver, err:=ciaoQemu.QMPStart(q.qmpMonitorCh.ctx, q.qmpMonitorCh.path, cfg, q.qmpMonitorCh.disconnectCh)
587-
iferr!=nil {
588-
q.Logger().WithError(err).Error("Failed to connect to QEMU instance")
589-
return
590-
}
591-
592-
q.qmpMonitorCh.qmp=qmp
593-
594-
q.Logger().WithFields(logrus.Fields{
595-
"qmp-major-version": ver.Major,
596-
"qmp-minor-version": ver.Minor,
597-
"qmp-micro-version": ver.Micro,
598-
"qmp-capabilities": strings.Join(ver.Capabilities, ","),
599-
}).Infof("QMP details")
600-
601-
err=q.qmpMonitorCh.qmp.ExecuteQMPCapabilities(q.qmpMonitorCh.ctx)
602-
iferr!=nil {
603-
q.Logger().WithError(err).Error(qmpCapErrMsg)
604-
return
605-
}
606-
607-
close(connectedCh)
608-
}
609-
610576
func (q*qemu) setCPUResources(podConfigPodConfig) ciaoQemu.SMP {
611577
vcpus:=q.config.DefaultVCPUs
612578
ifpodConfig.VMConfig.VCPUs>0 {
@@ -775,16 +741,61 @@ func (q *qemu) createPod(podConfig PodConfig) error {
775741
}
776742

777743
// startPod will start the Pod's VM.
778-
func (q*qemu) startPod(startCh, stopChchanstruct{}) error {
744+
func (q*qemu) startPod() error {
779745
strErr, err:=ciaoQemu.LaunchQemu(q.qemuConfig, newQMPLogger())
780746
iferr!=nil {
781747
returnfmt.Errorf("%s", strErr)
782748
}
783749

784-
// Start the QMP monitoring thread
785-
q.qmpMonitorCh.disconnectCh=stopCh
786-
q.qmpMonitorCh.wg.Add(1)
787-
q.qmpMonitor(startCh)
750+
returnnil
751+
}
752+
753+
// waitPod will wait for the Pod's VM to be up and running.
754+
func (q*qemu) waitPod(timeoutint) error {
755+
deferfunc(qemu*qemu) {
756+
ifq.qmpMonitorCh.qmp!=nil {
757+
q.qmpMonitorCh.qmp.Shutdown()
758+
}
759+
}(q)
760+
761+
iftimeout<0 {
762+
returnfmt.Errorf("Invalid timeout %ds", timeout)
763+
}
764+
765+
disconnectCh:=make(chanstruct{})
766+
cfg:= ciaoQemu.QMPConfig{Logger: newQMPLogger()}
767+
768+
varqmp*ciaoQemu.QMP
769+
varver*ciaoQemu.QMPVersion
770+
varerrerror
771+
772+
timeStart:=time.Now()
773+
for {
774+
qmp, ver, err=ciaoQemu.QMPStart(q.qmpMonitorCh.ctx, q.qmpMonitorCh.path, cfg, disconnectCh)
775+
iferr==nil {
776+
break
777+
}
778+
779+
ifint(time.Now().Sub(timeStart).Seconds()) >timeout {
780+
returnfmt.Errorf("Failed to connect to QEMU instance (timeout %ds): %v", timeout, err)
781+
}
782+
783+
time.Sleep(time.Duration(50) *time.Millisecond)
784+
}
785+
786+
q.qmpMonitorCh.qmp=qmp
787+
788+
q.Logger().WithFields(logrus.Fields{
789+
"qmp-major-version": ver.Major,
790+
"qmp-minor-version": ver.Minor,
791+
"qmp-micro-version": ver.Micro,
792+
"qmp-capabilities": strings.Join(ver.Capabilities, ","),
793+
}).Infof("QMP details")
794+
795+
iferr=q.qmpMonitorCh.qmp.ExecuteQMPCapabilities(q.qmpMonitorCh.ctx); err!=nil {
796+
q.Logger().WithError(err).Error(qmpCapErrMsg)
797+
returnerr
798+
}
788799

789800
returnnil
790801
}

0 commit comments

Comments
 (0)