Browse Source

daemon: load and cache sysInfo on initialization

The `daemon.RawSysInfo()` function can be a heavy operation, as it collects
information about all cgroups on the host, networking, AppArmor, Seccomp, etc.

While looking at our code, I noticed that various parts in the code call this
function, potentially even _multiple times_ per container, for example, it is
called from:

- `verifyPlatformContainerSettings()`
- `oci.WithCgroups()` if the daemon has `cpu-rt-period` or `cpu-rt-runtime` configured
- in `ContainerDecoder.DecodeConfig()`, which is called on boith `container create` and `container commit`

Given that this information is not expected to change during the daemon's
lifecycle, and various information coming from this (such as seccomp and
apparmor status) was already cached, we may as well load it once, and cache
the results in the daemon instance.

This patch updates `daemon.RawSysInfo()` to use a `sync.Once()` so that
it's only executed once for the daemon's lifecycle.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 3 years ago
parent
commit
483aa6294b

+ 4 - 7
daemon/container_linux.go

@@ -11,7 +11,7 @@ import (
 func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error {
 func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error {
 	container.AppArmorProfile = "" // we don't care about the previous value.
 	container.AppArmorProfile = "" // we don't care about the previous value.
 
 
-	if !daemon.apparmorEnabled {
+	if !daemon.RawSysInfo().AppArmor {
 		return nil // if apparmor is disabled there is nothing to do here.
 		return nil // if apparmor is disabled there is nothing to do here.
 	}
 	}
 
 
@@ -19,13 +19,10 @@ func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error {
 		return errdefs.InvalidParameter(err)
 		return errdefs.InvalidParameter(err)
 	}
 	}
 
 
-	if !container.HostConfig.Privileged {
-		if container.AppArmorProfile == "" {
-			container.AppArmorProfile = defaultAppArmorProfile
-		}
-
-	} else {
+	if container.HostConfig.Privileged {
 		container.AppArmorProfile = unconfinedAppArmorProfile
 		container.AppArmorProfile = unconfinedAppArmorProfile
+	} else if container.AppArmorProfile == "" {
+		container.AppArmorProfile = defaultAppArmorProfile
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 16 - 4
daemon/daemon.go

@@ -47,6 +47,7 @@ import (
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/plugingetter"
+	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/truncindex"
 	"github.com/docker/docker/pkg/truncindex"
 	"github.com/docker/docker/plugin"
 	"github.com/docker/docker/plugin"
@@ -92,8 +93,8 @@ type Daemon struct {
 	netController         libnetwork.NetworkController
 	netController         libnetwork.NetworkController
 	volumes               *volumesservice.VolumesService
 	volumes               *volumesservice.VolumesService
 	root                  string
 	root                  string
-	seccompEnabled        bool
-	apparmorEnabled       bool
+	sysInfoOnce           sync.Once
+	sysInfo               *sysinfo.SysInfo
 	shutdown              bool
 	shutdown              bool
 	idMapping             *idtools.IdentityMapping
 	idMapping             *idtools.IdentityMapping
 	graphDriver           string        // TODO: move graphDriver field to an InfoService
 	graphDriver           string        // TODO: move graphDriver field to an InfoService
@@ -1034,8 +1035,6 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
 	d.EventsService = events.New()
 	d.EventsService = events.New()
 	d.root = config.Root
 	d.root = config.Root
 	d.idMapping = idMapping
 	d.idMapping = idMapping
-	d.seccompEnabled = sysInfo.Seccomp
-	d.apparmorEnabled = sysInfo.AppArmor
 
 
 	d.linkIndex = newLinkIndex()
 	d.linkIndex = newLinkIndex()
 
 
@@ -1472,3 +1471,16 @@ func (daemon *Daemon) BuilderBackend() builder.Backend {
 		*images.ImageService
 		*images.ImageService
 	}{daemon, daemon.imageService}
 	}{daemon, daemon.imageService}
 }
 }
+
+// RawSysInfo returns *sysinfo.SysInfo .
+func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo {
+	daemon.sysInfoOnce.Do(func() {
+		// We check if sysInfo is not set here, to allow some test to
+		// override the actual sysInfo.
+		if daemon.sysInfo == nil {
+			daemon.loadSysInfo()
+		}
+	})
+
+	return daemon.sysInfo
+}

+ 6 - 7
daemon/daemon_unix.go

@@ -1731,19 +1731,14 @@ func (daemon *Daemon) setupSeccompProfile() error {
 	return nil
 	return nil
 }
 }
 
 
-// RawSysInfo returns *sysinfo.SysInfo .
-func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo {
+func (daemon *Daemon) loadSysInfo() {
 	var siOpts []sysinfo.Opt
 	var siOpts []sysinfo.Opt
 	if daemon.getCgroupDriver() == cgroupSystemdDriver {
 	if daemon.getCgroupDriver() == cgroupSystemdDriver {
 		if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" {
 		if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" {
 			siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice"))
 			siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice"))
 		}
 		}
 	}
 	}
-	return sysinfo.New(siOpts...)
-}
-
-func recursiveUnmount(target string) error {
-	return mount.RecursiveUnmount(target)
+	daemon.sysInfo = sysinfo.New(siOpts...)
 }
 }
 
 
 func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
 func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
@@ -1757,3 +1752,7 @@ func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
 	)
 	)
 	return err
 	return err
 }
 }
+
+func recursiveUnmount(target string) error {
+	return mount.RecursiveUnmount(target)
+}

+ 2 - 3
daemon/daemon_unsupported.go

@@ -13,7 +13,6 @@ const platformSupported = false
 func setupResolvConf(config *config.Config) {
 func setupResolvConf(config *config.Config) {
 }
 }
 
 
-// RawSysInfo returns *sysinfo.SysInfo .
-func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo {
-	return sysinfo.New()
+func (daemon *Daemon) loadSysInfo() {
+	daemon.sysInfo = sysinfo.New()
 }
 }

+ 2 - 3
daemon/daemon_windows.go

@@ -650,9 +650,8 @@ func (daemon *Daemon) initRuntimes(_ map[string]types.Runtime) error {
 func setupResolvConf(config *config.Config) {
 func setupResolvConf(config *config.Config) {
 }
 }
 
 
-// RawSysInfo returns *sysinfo.SysInfo .
-func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo {
-	return sysinfo.New()
+func (daemon *Daemon) loadSysInfo() {
+	daemon.sysInfo = sysinfo.New()
 }
 }
 
 
 func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {
 func (daemon *Daemon) initLibcontainerd(ctx context.Context) error {

+ 1 - 1
daemon/seccomp_linux.go

@@ -26,7 +26,7 @@ func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
 		if c.HostConfig.Privileged {
 		if c.HostConfig.Privileged {
 			return nil
 			return nil
 		}
 		}
-		if !daemon.seccompEnabled {
+		if !daemon.RawSysInfo().Seccomp {
 			if c.SeccompProfile != "" && c.SeccompProfile != dconfig.SeccompProfileDefault {
 			if c.SeccompProfile != "" && c.SeccompProfile != dconfig.SeccompProfileDefault {
 				return fmt.Errorf("seccomp is not enabled in your kernel, cannot run a custom seccomp profile")
 				return fmt.Errorf("seccomp is not enabled in your kernel, cannot run a custom seccomp profile")
 			}
 			}

+ 11 - 9
daemon/seccomp_linux_test.go

@@ -11,6 +11,7 @@ import (
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container"
 	dconfig "github.com/docker/docker/daemon/config"
 	dconfig "github.com/docker/docker/daemon/config"
 	doci "github.com/docker/docker/oci"
 	doci "github.com/docker/docker/oci"
+	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/profiles/seccomp"
 	"github.com/docker/docker/profiles/seccomp"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert"
@@ -31,7 +32,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "unconfined seccompProfile runs unconfined",
 			comment: "unconfined seccompProfile runs unconfined",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo: &sysinfo.SysInfo{Seccomp: true},
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
 				SeccompProfile: dconfig.SeccompProfileUnconfined,
 				SeccompProfile: dconfig.SeccompProfileUnconfined,
@@ -45,7 +46,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "privileged container w/ custom profile runs unconfined",
 			comment: "privileged container w/ custom profile runs unconfined",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo: &sysinfo.SysInfo{Seccomp: true},
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }",
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }",
@@ -59,7 +60,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "privileged container w/ default runs unconfined",
 			comment: "privileged container w/ default runs unconfined",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo: &sysinfo.SysInfo{Seccomp: true},
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
 				SeccompProfile: "",
 				SeccompProfile: "",
@@ -73,7 +74,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "privileged container w/ daemon profile runs unconfined",
 			comment: "privileged container w/ daemon profile runs unconfined",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo:        &sysinfo.SysInfo{Seccomp: true},
 				seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"),
 				seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"),
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
@@ -88,7 +89,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "custom profile when seccomp is disabled returns error",
 			comment: "custom profile when seccomp is disabled returns error",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: false,
+				sysInfo: &sysinfo.SysInfo{Seccomp: false},
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }",
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }",
@@ -103,7 +104,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "empty profile name loads default profile",
 			comment: "empty profile name loads default profile",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo: &sysinfo.SysInfo{Seccomp: true},
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
 				SeccompProfile: "",
 				SeccompProfile: "",
@@ -122,7 +123,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "load container's profile",
 			comment: "load container's profile",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo: &sysinfo.SysInfo{Seccomp: true},
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }",
 				SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }",
@@ -143,7 +144,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "load daemon's profile",
 			comment: "load daemon's profile",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo:        &sysinfo.SysInfo{Seccomp: true},
 				seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"),
 				seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"),
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
@@ -165,7 +166,7 @@ func TestWithSeccomp(t *testing.T) {
 		{
 		{
 			comment: "load prioritise container profile over daemon's",
 			comment: "load prioritise container profile over daemon's",
 			daemon: &Daemon{
 			daemon: &Daemon{
-				seccompEnabled: true,
+				sysInfo:        &sysinfo.SysInfo{Seccomp: true},
 				seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"),
 				seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"),
 			},
 			},
 			c: &container.Container{
 			c: &container.Container{
@@ -185,6 +186,7 @@ func TestWithSeccomp(t *testing.T) {
 			}(),
 			}(),
 		},
 		},
 	} {
 	} {
+		x := x
 		t.Run(x.comment, func(t *testing.T) {
 		t.Run(x.comment, func(t *testing.T) {
 			opts := WithSeccomp(x.daemon, x.c)
 			opts := WithSeccomp(x.daemon, x.c)
 			err := opts(nil, nil, nil, &x.inSpec)
 			err := opts(nil, nil, nil, &x.inSpec)