Bläddra i källkod

Add daemon option --default-shm-size

This fix fixes issue raised in 29492 where it was not
possible to specify a default `--default-shm-size` in daemon
configuration for each `docker run``.

The flag `--default-shm-size` which is reloadable, has been
added to the daemon configuation.
Related docs has been updated.

This fix fixes 29492.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Yong Tang 8 år sedan
förälder
incheckning
db575ef626

+ 2 - 23
cli/command/service/opts.go

@@ -11,7 +11,6 @@ import (
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/go-connections/nat"
-	units "github.com/docker/go-units"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
@@ -19,26 +18,6 @@ type int64Value interface {
 	Value() int64
 	Value() int64
 }
 }
 
 
-type memBytes int64
-
-func (m *memBytes) String() string {
-	return units.BytesSize(float64(m.Value()))
-}
-
-func (m *memBytes) Set(value string) error {
-	val, err := units.RAMInBytes(value)
-	*m = memBytes(val)
-	return err
-}
-
-func (m *memBytes) Type() string {
-	return "bytes"
-}
-
-func (m *memBytes) Value() int64 {
-	return int64(*m)
-}
-
 // PositiveDurationOpt is an option type for time.Duration that uses a pointer.
 // PositiveDurationOpt is an option type for time.Duration that uses a pointer.
 // It bahave similarly to DurationOpt but only allows positive duration values.
 // It bahave similarly to DurationOpt but only allows positive duration values.
 type PositiveDurationOpt struct {
 type PositiveDurationOpt struct {
@@ -149,9 +128,9 @@ type updateOptions struct {
 
 
 type resourceOptions struct {
 type resourceOptions struct {
 	limitCPU      opts.NanoCPUs
 	limitCPU      opts.NanoCPUs
-	limitMemBytes memBytes
+	limitMemBytes opts.MemBytes
 	resCPU        opts.NanoCPUs
 	resCPU        opts.NanoCPUs
-	resMemBytes   memBytes
+	resMemBytes   opts.MemBytes
 }
 }
 
 
 func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
 func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {

+ 2 - 2
cli/command/service/opts_test.go

@@ -11,12 +11,12 @@ import (
 )
 )
 
 
 func TestMemBytesString(t *testing.T) {
 func TestMemBytesString(t *testing.T) {
-	var mem memBytes = 1048576
+	var mem opts.MemBytes = 1048576
 	assert.Equal(t, mem.String(), "1 MiB")
 	assert.Equal(t, mem.String(), "1 MiB")
 }
 }
 
 
 func TestMemBytesSetAndValue(t *testing.T) {
 func TestMemBytesSetAndValue(t *testing.T) {
-	var mem memBytes
+	var mem opts.MemBytes
 	assert.NilError(t, mem.Set("5kb"))
 	assert.NilError(t, mem.Set("5kb"))
 	assert.Equal(t, mem.Value(), int64(5120))
 	assert.Equal(t, mem.Value(), int64(5120))
 }
 }

+ 1 - 3
container/container_unix.go

@@ -22,9 +22,7 @@ import (
 )
 )
 
 
 const (
 const (
-	// DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container
-	DefaultSHMSize           int64 = 67108864
-	containerSecretMountPath       = "/run/secrets"
+	containerSecretMountPath = "/run/secrets"
 )
 )
 
 
 // Container holds the fields specific to unixen implementations.
 // Container holds the fields specific to unixen implementations.

+ 6 - 0
daemon/config_unix.go

@@ -14,6 +14,7 @@ var (
 	defaultPidFile  = "/var/run/docker.pid"
 	defaultPidFile  = "/var/run/docker.pid"
 	defaultGraph    = "/var/lib/docker"
 	defaultGraph    = "/var/lib/docker"
 	defaultExecRoot = "/var/run/docker"
 	defaultExecRoot = "/var/run/docker"
+	defaultShmSize  = int64(67108864)
 )
 )
 
 
 // Config defines the configuration of a docker daemon.
 // Config defines the configuration of a docker daemon.
@@ -36,6 +37,7 @@ type Config struct {
 	Init                 bool                     `json:"init,omitempty"`
 	Init                 bool                     `json:"init,omitempty"`
 	InitPath             string                   `json:"init-path,omitempty"`
 	InitPath             string                   `json:"init-path,omitempty"`
 	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
 	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
+	ShmSize              opts.MemBytes            `json:"default-shm-size,omitempty"`
 }
 }
 
 
 // bridgeConfig stores all the bridge driver specific
 // bridgeConfig stores all the bridge driver specific
@@ -66,6 +68,9 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
 
 
 	config.Ulimits = make(map[string]*units.Ulimit)
 	config.Ulimits = make(map[string]*units.Ulimit)
 
 
+	// Set default value for `--default-shm-size`
+	config.ShmSize = opts.MemBytes(defaultShmSize)
+
 	// Then platform-specific install flags
 	// Then platform-specific install flags
 	flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support")
 	flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support")
 	flags.Var(opts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers")
 	flags.Var(opts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers")
@@ -89,6 +94,7 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
 	flags.Int64Var(&config.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
 	flags.Int64Var(&config.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
 	flags.Int64Var(&config.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds")
 	flags.Int64Var(&config.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds")
 	flags.StringVar(&config.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile")
 	flags.StringVar(&config.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile")
+	flags.Var(&config.ShmSize, "default-shm-size", "Default shm size for containers")
 
 
 	config.attachExperimentalFlags(flags)
 	config.attachExperimentalFlags(flags)
 }
 }

+ 54 - 0
daemon/config_unix_test.go

@@ -4,7 +4,12 @@ package daemon
 
 
 import (
 import (
 	"io/ioutil"
 	"io/ioutil"
+	"runtime"
+
 	"testing"
 	"testing"
+
+	"github.com/docker/docker/pkg/testutil/assert"
+	"github.com/spf13/pflag"
 )
 )
 
 
 func TestDaemonConfigurationMerge(t *testing.T) {
 func TestDaemonConfigurationMerge(t *testing.T) {
@@ -78,3 +83,52 @@ func TestDaemonConfigurationMerge(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
+
+func TestDaemonParseShmSize(t *testing.T) {
+	if runtime.GOOS == "solaris" {
+		t.Skip("ShmSize not supported on Solaris\n")
+	}
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+
+	config := &Config{}
+	config.InstallFlags(flags)
+	// By default `--default-shm-size=64M`
+	expectedValue := 64 * 1024 * 1024
+	if config.ShmSize.Value() != int64(expectedValue) {
+		t.Fatalf("expected default shm size %d, got %d", expectedValue, config.ShmSize.Value())
+	}
+	assert.NilError(t, flags.Set("default-shm-size", "128M"))
+	expectedValue = 128 * 1024 * 1024
+	if config.ShmSize.Value() != int64(expectedValue) {
+		t.Fatalf("expected default shm size %d, got %d", expectedValue, config.ShmSize.Value())
+	}
+}
+
+func TestDaemonConfigurationMergeShmSize(t *testing.T) {
+	if runtime.GOOS == "solaris" {
+		t.Skip("ShmSize not supported on Solaris\n")
+	}
+	f, err := ioutil.TempFile("", "docker-config-")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	configFile := f.Name()
+
+	f.Write([]byte(`
+		{
+			"default-shm-size": "1g"
+		}`))
+
+	f.Close()
+
+	c := &Config{}
+	cc, err := MergeDaemonConfigurations(c, nil, configFile)
+	if err != nil {
+		t.Fatal(err)
+	}
+	expectedValue := 1 * 1024 * 1024 * 1024
+	if cc.ShmSize.Value() != int64(expectedValue) {
+		t.Fatalf("expected default shm size %d, got %d", expectedValue, cc.ShmSize.Value())
+	}
+}

+ 1 - 1
daemon/container_operations_unix.go

@@ -117,7 +117,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
 				return err
 				return err
 			}
 			}
 
 
-			shmSize := container.DefaultSHMSize
+			shmSize := int64(daemon.configStore.ShmSize)
 			if c.HostConfig.ShmSize != 0 {
 			if c.HostConfig.ShmSize != 0 {
 				shmSize = c.HostConfig.ShmSize
 				shmSize = c.HostConfig.ShmSize
 			}
 			}

+ 11 - 3
daemon/daemon_unix.go

@@ -255,7 +255,10 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
 		hostConfig.MemorySwap = hostConfig.Memory * 2
 		hostConfig.MemorySwap = hostConfig.Memory * 2
 	}
 	}
 	if hostConfig.ShmSize == 0 {
 	if hostConfig.ShmSize == 0 {
-		hostConfig.ShmSize = container.DefaultSHMSize
+		hostConfig.ShmSize = defaultShmSize
+		if daemon.configStore != nil {
+			hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
+		}
 	}
 	}
 	var err error
 	var err error
 	opts, err := daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode, hostConfig.Privileged)
 	opts, err := daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode, hostConfig.Privileged)
@@ -575,6 +578,10 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string {
 		daemon.configStore.DefaultRuntime = config.DefaultRuntime
 		daemon.configStore.DefaultRuntime = config.DefaultRuntime
 	}
 	}
 
 
+	if config.IsValueSet("default-shm-size") {
+		daemon.configStore.ShmSize = config.ShmSize
+	}
+
 	// Update attributes
 	// Update attributes
 	var runtimeList bytes.Buffer
 	var runtimeList bytes.Buffer
 	for name, rt := range daemon.configStore.Runtimes {
 	for name, rt := range daemon.configStore.Runtimes {
@@ -585,8 +592,9 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string {
 	}
 	}
 
 
 	return map[string]string{
 	return map[string]string{
-		"runtimes":        runtimeList.String(),
-		"default-runtime": daemon.configStore.DefaultRuntime,
+		"runtimes":         runtimeList.String(),
+		"default-runtime":  daemon.configStore.DefaultRuntime,
+		"default-shm-size": fmt.Sprintf("%d", daemon.configStore.ShmSize),
 	}
 	}
 }
 }
 
 

+ 2 - 0
docs/reference/commandline/dockerd.md

@@ -37,6 +37,7 @@ Options:
       --default-gateway value                 Container default gateway IPv4 address
       --default-gateway value                 Container default gateway IPv4 address
       --default-gateway-v6 value              Container default gateway IPv6 address
       --default-gateway-v6 value              Container default gateway IPv6 address
       --default-runtime string                Default OCI runtime for containers (default "runc")
       --default-runtime string                Default OCI runtime for containers (default "runc")
+      --default-shm-size bytes                Set the default shm size for containers (default 64 MiB)
       --default-ulimit value                  Default ulimits for containers (default [])
       --default-ulimit value                  Default ulimits for containers (default [])
       --disable-legacy-registry               Disable contacting legacy registries
       --disable-legacy-registry               Disable contacting legacy registries
       --dns value                             DNS server to use (default [])
       --dns value                             DNS server to use (default [])
@@ -1161,6 +1162,7 @@ This is a full example of the allowed configuration options on Linux:
 	"cluster-advertise": "",
 	"cluster-advertise": "",
 	"max-concurrent-downloads": 3,
 	"max-concurrent-downloads": 3,
 	"max-concurrent-uploads": 5,
 	"max-concurrent-uploads": 5,
+	"default-shm-size": "64M",
 	"shutdown-timeout": 15,
 	"shutdown-timeout": 15,
 	"debug": true,
 	"debug": true,
 	"hosts": [],
 	"hosts": [],

+ 57 - 0
integration-cli/docker_cli_daemon_test.go

@@ -2879,3 +2879,60 @@ func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
 	out, err = s.d.Cmd("stop", id)
 	out, err = s.d.Cmd("stop", id)
 	c.Assert(err, check.IsNil, check.Commentf("output: %s", out))
 	c.Assert(err, check.IsNil, check.Commentf("output: %s", out))
 }
 }
+
+func (s *DockerDaemonSuite) TestShmSize(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+
+	size := 67108864 * 2
+	pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
+
+	s.d.StartWithBusybox(c, "--default-shm-size", fmt.Sprintf("%v", size))
+
+	name := "shm1"
+	out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	c.Assert(pattern.MatchString(out), checker.True)
+	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
+}
+
+func (s *DockerDaemonSuite) TestShmSizeReload(c *check.C) {
+	testRequires(c, DaemonIsLinux)
+
+	configPath, err := ioutil.TempDir("", "test-daemon-shm-size-reload-config")
+	c.Assert(err, checker.IsNil, check.Commentf("could not create temp file for config reload"))
+	defer os.RemoveAll(configPath) // clean up
+	configFile := filepath.Join(configPath, "config.json")
+
+	size := 67108864 * 2
+	configData := []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
+	c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
+	pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
+
+	s.d.StartWithBusybox(c, "--config-file", configFile)
+
+	name := "shm1"
+	out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	c.Assert(pattern.MatchString(out), checker.True)
+	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
+
+	size = 67108864 * 3
+	configData = []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
+	c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
+	pattern = regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
+
+	err = s.d.ReloadConfig()
+	c.Assert(err, checker.IsNil, check.Commentf("error reloading daemon config"))
+
+	name = "shm2"
+	out, err = s.d.Cmd("run", "--name", name, "busybox", "mount")
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	c.Assert(pattern.MatchString(out), checker.True)
+	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
+}

+ 1 - 1
integration-cli/docker_cli_events_unix_test.go

@@ -427,7 +427,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 
 
-	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
+	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
 }
 }
 
 
 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {

+ 4 - 0
man/dockerd.8.md

@@ -21,6 +21,7 @@ dockerd - Enable daemon mode
 [**--default-gateway**[=*DEFAULT-GATEWAY*]]
 [**--default-gateway**[=*DEFAULT-GATEWAY*]]
 [**--default-gateway-v6**[=*DEFAULT-GATEWAY-V6*]]
 [**--default-gateway-v6**[=*DEFAULT-GATEWAY-V6*]]
 [**--default-runtime**[=*runc*]]
 [**--default-runtime**[=*runc*]]
+[**--default-shm-size**[=*64MiB*]]
 [**--default-ulimit**[=*[]*]]
 [**--default-ulimit**[=*[]*]]
 [**--disable-legacy-registry**]
 [**--disable-legacy-registry**]
 [**--dns**[=*[]*]]
 [**--dns**[=*[]*]]
@@ -164,6 +165,9 @@ $ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-ru
 **--default-runtime**="runc"
 **--default-runtime**="runc"
   Set default runtime if there're more than one specified by `--add-runtime`.
   Set default runtime if there're more than one specified by `--add-runtime`.
 
 
+**--default-shm-size**=*64MiB*
+  Set the daemon-wide default shm size for containers. Default is `64MiB`.
+
 **--default-ulimit**=[]
 **--default-ulimit**=[]
   Default ulimits for containers.
   Default ulimits for containers.
 
 

+ 36 - 0
opts/opts.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
+	units "github.com/docker/go-units"
 )
 )
 
 
 var (
 var (
@@ -402,3 +403,38 @@ func ValidateLink(val string) (string, error) {
 	_, _, err := ParseLink(val)
 	_, _, err := ParseLink(val)
 	return val, err
 	return val, err
 }
 }
+
+// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
+type MemBytes int64
+
+// String returns the string format of the human readable memory bytes
+func (m *MemBytes) String() string {
+	return units.BytesSize(float64(m.Value()))
+}
+
+// Set sets the value of the MemBytes by passing a string
+func (m *MemBytes) Set(value string) error {
+	val, err := units.RAMInBytes(value)
+	*m = MemBytes(val)
+	return err
+}
+
+// Type returns the type
+func (m *MemBytes) Type() string {
+	return "bytes"
+}
+
+// Value returns the value in int64
+func (m *MemBytes) Value() int64 {
+	return int64(*m)
+}
+
+// UnmarshalJSON is the customized unmarshaler for MemBytes
+func (m *MemBytes) UnmarshalJSON(s []byte) error {
+	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
+		return fmt.Errorf("invalid size: %q", s)
+	}
+	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
+	*m = MemBytes(val)
+	return err
+}