From e2aef6341de2d371cec3e6085cb539dfadede8db Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 17 Oct 2018 13:43:36 -0500 Subject: run prepare in parallel run prepare() -- which consists of creating a network namespace and mounting the container image is now run in parallel. This saves 25-40ms. Signed-off-by: baude --- libpod/container_internal.go | 34 ++++++-------------- libpod/container_internal_linux.go | 66 ++++++++++++++++++++++++++++++-------- libpod/networking_linux.go | 30 ++++++++++------- 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index cb6b940fd..2af216358 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -750,30 +750,31 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e // TODO: Add ability to override mount label so we can use this for Mount() too // TODO: Can we use this for export? Copying SHM into the export might not be // good -func (c *Container) mountStorage() (err error) { +func (c *Container) mountStorage() (string, error) { + var err error // Container already mounted, nothing to do if c.state.Mounted { - return nil + return c.state.Mountpoint, nil } if !rootless.IsRootless() { // TODO: generalize this mount code so it will mount every mount in ctr.config.Mounts mounted, err := mount.Mounted(c.config.ShmDir) if err != nil { - return errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir) + return "", errors.Wrapf(err, "unable to determine if %q is mounted", c.config.ShmDir) } if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil { - return errors.Wrapf(err, "failed to chown %s", c.config.ShmDir) + return "", errors.Wrapf(err, "failed to chown %s", c.config.ShmDir) } if !mounted { shmOptions := fmt.Sprintf("mode=1777,size=%d", c.config.ShmSize) if err := c.mountSHM(shmOptions); err != nil { - return err + return "", err } if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil { - return errors.Wrapf(err, "failed to chown %s", c.config.ShmDir) + return "", errors.Wrapf(err, "failed to chown %s", c.config.ShmDir) } } } @@ -782,28 +783,11 @@ func (c *Container) mountStorage() (err error) { if mountPoint == "" { mountPoint, err = c.mount() if err != nil { - return err + return "", err } } - c.state.Mounted = true - c.state.Mountpoint = mountPoint - if c.state.UserNSRoot == "" { - c.state.RealMountpoint = c.state.Mountpoint - } else { - c.state.RealMountpoint = filepath.Join(c.state.UserNSRoot, "mountpoint") - } - - logrus.Debugf("Created root filesystem for container %s at %s", c.ID(), c.state.Mountpoint) - - defer func() { - if err != nil { - if err2 := c.cleanupStorage(); err2 != nil { - logrus.Errorf("Error unmounting storage for container %s: %v", c.ID(), err) - } - } - }() - return c.save() + return mountPoint, nil } // cleanupStorage unmounts and cleans up the container's root filesystem diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index b25645e5c..94704fc04 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -12,10 +12,12 @@ import ( "path" "path/filepath" "strings" + "sync" "syscall" "time" cnitypes "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ns" crioAnnotations "github.com/containers/libpod/pkg/annotations" "github.com/containers/libpod/pkg/chrootuser" "github.com/containers/libpod/pkg/criu" @@ -49,24 +51,61 @@ func (c *Container) unmountSHM(mount string) error { // prepare mounts the container and sets up other required resources like net // namespaces func (c *Container) prepare() (err error) { + var ( + wg sync.WaitGroup + netNS ns.NetNS + networkStatus []*cnitypes.Result + createNetNSErr, mountStorageErr error + mountPoint string + saveNetworkStatus bool + ) + + wg.Add(2) + + go func() { + defer wg.Done() + // Set up network namespace if not already set up + if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS { + saveNetworkStatus = true + netNS, networkStatus, createNetNSErr = c.runtime.createNetNS(c) + } + }() // Mount storage if not mounted - if err := c.mountStorage(); err != nil { - return err - } + go func() { + defer wg.Done() + mountPoint, mountStorageErr = c.mountStorage() + }() - // Set up network namespace if not already set up - if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS { - if err := c.runtime.createNetNS(c); err != nil { - // Tear down storage before exiting to make sure we - // don't leak mounts - if err2 := c.cleanupStorage(); err2 != nil { - logrus.Errorf("Error cleaning up storage for container %s: %v", c.ID(), err2) - } - return err + wg.Wait() + if createNetNSErr != nil { + if mountStorageErr != nil { + logrus.Error(createNetNSErr) + return mountStorageErr } + return createNetNSErr + } + if mountStorageErr != nil { + return mountStorageErr } - return nil + // Assign NetNS attributes to container + if saveNetworkStatus { + c.state.NetNS = netNS + c.state.NetworkStatus = networkStatus + } + + // Finish up mountStorage + c.state.Mounted = true + c.state.Mountpoint = mountPoint + if c.state.UserNSRoot == "" { + c.state.RealMountpoint = c.state.Mountpoint + } else { + c.state.RealMountpoint = filepath.Join(c.state.UserNSRoot, "mountpoint") + } + + logrus.Debugf("Created root filesystem for container %s at %s", c.ID(), c.state.Mountpoint) + // Save the container + return c.save() } // cleanupNetwork unmounts and cleans up the container's network @@ -104,7 +143,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, c.state.NetNS.Path()) } } - // Check if the spec file mounts contain the label Relabel flags z or Z. // If they do, relabel the source directory and then remove the option. for _, m := range g.Mounts() { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index acb4e2a90..0d9ec2809 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -48,12 +48,12 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port } // Create and configure a new network namespace for a container -func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (err error) { +func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) { podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP) results, err := r.netPlugin.SetUpPod(podNetwork) if err != nil { - return errors.Wrapf(err, "error configuring network namespace for container %s", ctr.ID()) + return nil, errors.Wrapf(err, "error configuring network namespace for container %s", ctr.ID()) } defer func() { if err != nil { @@ -63,15 +63,14 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (err error) { } }() - ctr.state.NetNS = ctrNS - ctr.state.NetworkStatus = make([]*cnitypes.Result, 0) + networkStatus := make([]*cnitypes.Result, 1) for idx, r := range results { logrus.Debugf("[%d] CNI result: %v", idx, r.String()) resultCurrent, err := cnitypes.GetResult(r) if err != nil { - return errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.String(), err) + return nil, errors.Wrapf(err, "error parsing CNI plugin result %q: %v", r.String(), err) } - ctr.state.NetworkStatus = append(ctr.state.NetworkStatus, resultCurrent) + networkStatus = append(ctr.state.NetworkStatus, resultCurrent) } // Add firewall rules to ensure the container has network access. @@ -82,18 +81,18 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (err error) { PrevResult: netStatus, } if err := r.firewallBackend.Add(firewallConf); err != nil { - return errors.Wrapf(err, "error adding firewall rules for container %s", ctr.ID()) + return nil, errors.Wrapf(err, "error adding firewall rules for container %s", ctr.ID()) } } - return nil + return networkStatus, nil } // Create and configure a new network namespace for a container -func (r *Runtime) createNetNS(ctr *Container) (err error) { +func (r *Runtime) createNetNS(ctr *Container) (ns.NetNS, []*cnitypes.Result, error) { ctrNS, err := netns.NewNS() if err != nil { - return errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) + return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) } defer func() { if err != nil { @@ -104,7 +103,9 @@ func (r *Runtime) createNetNS(ctr *Container) (err error) { }() logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID()) - return r.configureNetNS(ctr, ctrNS) + + networkStatus, err := r.configureNetNS(ctr, ctrNS) + return ctrNS, networkStatus, err } // Configure the network namespace for a rootless container @@ -173,7 +174,12 @@ func (r *Runtime) setupNetNS(ctr *Container) (err error) { if err != nil { return err } - return r.configureNetNS(ctr, netNS) + networkStatus, err := r.configureNetNS(ctr, netNS) + + // Assign NetNS attributes to container + ctr.state.NetNS = netNS + ctr.state.NetworkStatus = networkStatus + return err } // Join an existing network namespace -- cgit v1.2.3-54-g00ecf