From 2610918ed43d26c5116c2245c87b31e0c8a40a68 Mon Sep 17 00:00:00 2001 From: Nayana Bidari Date: Mon, 13 Jun 2022 10:54:30 -0700 Subject: [PATCH] Run two containers This change will allow the gVisor sandbox to run multiple containers. - Added new functions to validate the SentryMultiContainerSpec and the container mounts. - Changes to set the target path in container mounts for eg: root target will be "/container0" and the submounts target will be "/container0/dev".. etc in multi-container mode. - Added new fields in the protos: container name in ContainerSpec and MultiContainerConfig in SentryConfig. - Updated the sentry to create the mount namespaces for all the containers during the sentry start as we have all the container mount configs. These mount namespaces are passed to the control server in a map with the container name as the key. - Updated changes in StartContainer(): - Use ContainerSpec instead of SandboxRunSpec. - No longer create the mount namespaces here and use the ones created by the sentry. - Added test to run two containers in multi-container mode using the SentryMultiContainerSpec. PiperOrigin-RevId: 454649646 --- pkg/sentry/control/lifecycle.go | 42 +++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/pkg/sentry/control/lifecycle.go b/pkg/sentry/control/lifecycle.go index 52b44babc..071d893f3 100644 --- a/pkg/sentry/control/lifecycle.go +++ b/pkg/sentry/control/lifecycle.go @@ -26,6 +26,7 @@ import ( "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/limits" "gvisor.dev/gvisor/pkg/sentry/vfs" + "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/urpc" ) @@ -34,13 +35,19 @@ type Lifecycle struct { // Kernel is the kernel where the tasks belong to. Kernel *kernel.Kernel - // Sends a message to the sentry that the task has been started. + // StartedCh is the channel used to send a message to the sentry that + // all the containers in the sandbox have been started. StartedCh chan struct{} - // TODO(b/202052732): Root mount namespace. When running multiple - // containers, create the mount namespace using the mount spec in - // the StartContainerArgs. - MountNamespaceVFS2 *vfs.MountNamespace + // mu protects the fields below. + mu sync.RWMutex + + // containersStarted is the number of containers started in the sandbox. + containersStarted int32 + + // MountNamespacesMap is a map of container id/names and the mount + // namespaces. + MountNamespacesMap map[string]*vfs.MountNamespace } // StartContainerArgs is the set of arguments to start a container. @@ -127,12 +134,22 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error { AbstractSocketNamespace: l.Kernel.RootAbstractSocketNamespace(), ContainerID: args.ContainerID, PIDNamespace: l.Kernel.RootPIDNamespace(), - MountNamespaceVFS2: l.MountNamespaceVFS2, } ctx := initArgs.NewContext(l.Kernel) defer fdTable.DecRef(ctx) + // VFS2 is supported in multi-container mode by default. + l.mu.RLock() + mntns, ok := l.MountNamespacesMap[initArgs.ContainerID] + if !ok { + l.mu.RUnlock() + return fmt.Errorf("mount namespace is nil for %s", initArgs.ContainerID) + } + initArgs.MountNamespaceVFS2 = mntns + l.mu.RUnlock() + initArgs.MountNamespaceVFS2.IncRef() + resolved, err := user.ResolveExecutablePath(ctx, &initArgs) if err != nil { return err @@ -154,12 +171,17 @@ func (l *Lifecycle) StartContainer(args *StartContainerArgs, _ *uint32) error { return err } + l.mu.Lock() + numContainers := int32(len(l.MountNamespacesMap)) + // Start the newly created process. l.Kernel.StartProcess(tg) - - log.Infof("Started the new container") - - l.StartedCh <- struct{}{} + log.Infof("Started the new container %v ", l.containersStarted) + l.containersStarted++ + if numContainers == l.containersStarted { + l.StartedCh <- struct{}{} + } + l.mu.Unlock() return nil }