Browse code

Merge pull request #29692 from yongtang/29492-daemon-shm-size

Add daemon option `--default-shm-size`

Vincent Demeester authored on 2017/02/02 00:56:10
Showing 20 changed files
... ...
@@ -106,7 +106,7 @@ type containerOptions struct {
106 106
 	stopSignal         string
107 107
 	stopTimeout        int
108 108
 	isolation          string
109
-	shmSize            string
109
+	shmSize            opts.MemBytes
110 110
 	noHealthcheck      bool
111 111
 	healthCmd          string
112 112
 	healthInterval     time.Duration
... ...
@@ -270,7 +270,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
270 270
 	flags.StringVar(&copts.ipcMode, "ipc", "", "IPC namespace to use")
271 271
 	flags.StringVar(&copts.isolation, "isolation", "", "Container isolation technology")
272 272
 	flags.StringVar(&copts.pidMode, "pid", "", "PID namespace to use")
273
-	flags.StringVar(&copts.shmSize, "shm-size", "", "Size of /dev/shm, default value is 64MB")
273
+	flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm")
274 274
 	flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use")
275 275
 	flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container")
276 276
 
... ...
@@ -349,14 +349,6 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*container.Config, *c
349 349
 		return nil, nil, nil, fmt.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
350 350
 	}
351 351
 
352
-	var shmSize int64
353
-	if copts.shmSize != "" {
354
-		shmSize, err = units.RAMInBytes(copts.shmSize)
355
-		if err != nil {
356
-			return nil, nil, nil, err
357
-		}
358
-	}
359
-
360 352
 	// TODO FIXME units.RAMInBytes should have a uint64 version
361 353
 	var maxIOBandwidth int64
362 354
 	if copts.ioMaxBandwidth != "" {
... ...
@@ -629,7 +621,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*container.Config, *c
629 629
 		LogConfig:      container.LogConfig{Type: copts.loggingDriver, Config: loggingOpts},
630 630
 		VolumeDriver:   copts.volumeDriver,
631 631
 		Isolation:      container.Isolation(copts.isolation),
632
-		ShmSize:        shmSize,
632
+		ShmSize:        copts.shmSize.Value(),
633 633
 		Resources:      resources,
634 634
 		Tmpfs:          tmpfs,
635 635
 		Sysctls:        copts.sysctls.GetAll(),
... ...
@@ -411,8 +411,9 @@ func TestParseModes(t *testing.T) {
411 411
 		t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
412 412
 	}
413 413
 	// shm-size ko
414
-	if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
415
-		t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
414
+	expectedErr := `invalid argument "a128m" for --shm-size=a128m: invalid size: 'a128m'`
415
+	if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != expectedErr {
416
+		t.Fatalf("Expected an error with message '%v', got %v", expectedErr, err)
416 417
 	}
417 418
 	// shm-size ok
418 419
 	_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
... ...
@@ -41,7 +41,7 @@ type buildOptions struct {
41 41
 	ulimits        *opts.UlimitOpt
42 42
 	memory         string
43 43
 	memorySwap     string
44
-	shmSize        string
44
+	shmSize        opts.MemBytes
45 45
 	cpuShares      int64
46 46
 	cpuPeriod      int64
47 47
 	cpuQuota       int64
... ...
@@ -89,7 +89,7 @@ func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command {
89 89
 	flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
90 90
 	flags.StringVarP(&options.memory, "memory", "m", "", "Memory limit")
91 91
 	flags.StringVar(&options.memorySwap, "memory-swap", "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
92
-	flags.StringVar(&options.shmSize, "shm-size", "", "Size of /dev/shm, default value is 64MB")
92
+	flags.Var(&options.shmSize, "shm-size", "Size of /dev/shm")
93 93
 	flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
94 94
 	flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
95 95
 	flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
... ...
@@ -274,14 +274,6 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
274 274
 		}
275 275
 	}
276 276
 
277
-	var shmSize int64
278
-	if options.shmSize != "" {
279
-		shmSize, err = units.RAMInBytes(options.shmSize)
280
-		if err != nil {
281
-			return err
282
-		}
283
-	}
284
-
285 277
 	authConfigs, _ := dockerCli.GetAllCredentials()
286 278
 	buildOptions := types.ImageBuildOptions{
287 279
 		Memory:         memory,
... ...
@@ -300,7 +292,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
300 300
 		CPUPeriod:      options.cpuPeriod,
301 301
 		CgroupParent:   options.cgroupParent,
302 302
 		Dockerfile:     relDockerfile,
303
-		ShmSize:        shmSize,
303
+		ShmSize:        options.shmSize.Value(),
304 304
 		Ulimits:        options.ulimits.GetList(),
305 305
 		BuildArgs:      runconfigopts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll()),
306 306
 		AuthConfigs:    authConfigs,
... ...
@@ -11,7 +11,6 @@ import (
11 11
 	"github.com/docker/docker/opts"
12 12
 	runconfigopts "github.com/docker/docker/runconfig/opts"
13 13
 	"github.com/docker/go-connections/nat"
14
-	units "github.com/docker/go-units"
15 14
 	"github.com/spf13/cobra"
16 15
 )
17 16
 
... ...
@@ -19,26 +18,6 @@ type int64Value interface {
19 19
 	Value() int64
20 20
 }
21 21
 
22
-type memBytes int64
23
-
24
-func (m *memBytes) String() string {
25
-	return units.BytesSize(float64(m.Value()))
26
-}
27
-
28
-func (m *memBytes) Set(value string) error {
29
-	val, err := units.RAMInBytes(value)
30
-	*m = memBytes(val)
31
-	return err
32
-}
33
-
34
-func (m *memBytes) Type() string {
35
-	return "bytes"
36
-}
37
-
38
-func (m *memBytes) Value() int64 {
39
-	return int64(*m)
40
-}
41
-
42 22
 // PositiveDurationOpt is an option type for time.Duration that uses a pointer.
43 23
 // It bahave similarly to DurationOpt but only allows positive duration values.
44 24
 type PositiveDurationOpt struct {
... ...
@@ -149,9 +128,9 @@ type updateOptions struct {
149 149
 
150 150
 type resourceOptions struct {
151 151
 	limitCPU      opts.NanoCPUs
152
-	limitMemBytes memBytes
152
+	limitMemBytes opts.MemBytes
153 153
 	resCPU        opts.NanoCPUs
154
-	resMemBytes   memBytes
154
+	resMemBytes   opts.MemBytes
155 155
 }
156 156
 
157 157
 func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
... ...
@@ -11,12 +11,12 @@ import (
11 11
 )
12 12
 
13 13
 func TestMemBytesString(t *testing.T) {
14
-	var mem memBytes = 1048576
14
+	var mem opts.MemBytes = 1048576
15 15
 	assert.Equal(t, mem.String(), "1 MiB")
16 16
 }
17 17
 
18 18
 func TestMemBytesSetAndValue(t *testing.T) {
19
-	var mem memBytes
19
+	var mem opts.MemBytes
20 20
 	assert.NilError(t, mem.Set("5kb"))
21 21
 	assert.Equal(t, mem.Value(), int64(5120))
22 22
 }
... ...
@@ -22,9 +22,7 @@ import (
22 22
 )
23 23
 
24 24
 const (
25
-	// DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container
26
-	DefaultSHMSize           int64 = 67108864
27
-	containerSecretMountPath       = "/run/secrets"
25
+	containerSecretMountPath = "/run/secrets"
28 26
 )
29 27
 
30 28
 // Container holds the fields specific to unixen implementations.
... ...
@@ -14,6 +14,7 @@ var (
14 14
 	defaultPidFile  = "/var/run/docker.pid"
15 15
 	defaultGraph    = "/var/lib/docker"
16 16
 	defaultExecRoot = "/var/run/docker"
17
+	defaultShmSize  = int64(67108864)
17 18
 )
18 19
 
19 20
 // Config defines the configuration of a docker daemon.
... ...
@@ -36,6 +37,7 @@ type Config struct {
36 36
 	Init                 bool                     `json:"init,omitempty"`
37 37
 	InitPath             string                   `json:"init-path,omitempty"`
38 38
 	SeccompProfile       string                   `json:"seccomp-profile,omitempty"`
39
+	ShmSize              opts.MemBytes            `json:"default-shm-size,omitempty"`
39 40
 }
40 41
 
41 42
 // bridgeConfig stores all the bridge driver specific
... ...
@@ -66,6 +68,9 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
66 66
 
67 67
 	config.Ulimits = make(map[string]*units.Ulimit)
68 68
 
69
+	// Set default value for `--default-shm-size`
70
+	config.ShmSize = opts.MemBytes(defaultShmSize)
71
+
69 72
 	// Then platform-specific install flags
70 73
 	flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support")
71 74
 	flags.Var(opts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers")
... ...
@@ -89,6 +94,7 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
89 89
 	flags.Int64Var(&config.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
90 90
 	flags.Int64Var(&config.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds")
91 91
 	flags.StringVar(&config.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile")
92
+	flags.Var(&config.ShmSize, "default-shm-size", "Default shm size for containers")
92 93
 
93 94
 	config.attachExperimentalFlags(flags)
94 95
 }
... ...
@@ -4,7 +4,12 @@ package daemon
4 4
 
5 5
 import (
6 6
 	"io/ioutil"
7
+	"runtime"
8
+
7 9
 	"testing"
10
+
11
+	"github.com/docker/docker/pkg/testutil/assert"
12
+	"github.com/spf13/pflag"
8 13
 )
9 14
 
10 15
 func TestDaemonConfigurationMerge(t *testing.T) {
... ...
@@ -78,3 +83,52 @@ func TestDaemonConfigurationMerge(t *testing.T) {
78 78
 		}
79 79
 	}
80 80
 }
81
+
82
+func TestDaemonParseShmSize(t *testing.T) {
83
+	if runtime.GOOS == "solaris" {
84
+		t.Skip("ShmSize not supported on Solaris\n")
85
+	}
86
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
87
+
88
+	config := &Config{}
89
+	config.InstallFlags(flags)
90
+	// By default `--default-shm-size=64M`
91
+	expectedValue := 64 * 1024 * 1024
92
+	if config.ShmSize.Value() != int64(expectedValue) {
93
+		t.Fatalf("expected default shm size %d, got %d", expectedValue, config.ShmSize.Value())
94
+	}
95
+	assert.NilError(t, flags.Set("default-shm-size", "128M"))
96
+	expectedValue = 128 * 1024 * 1024
97
+	if config.ShmSize.Value() != int64(expectedValue) {
98
+		t.Fatalf("expected default shm size %d, got %d", expectedValue, config.ShmSize.Value())
99
+	}
100
+}
101
+
102
+func TestDaemonConfigurationMergeShmSize(t *testing.T) {
103
+	if runtime.GOOS == "solaris" {
104
+		t.Skip("ShmSize not supported on Solaris\n")
105
+	}
106
+	f, err := ioutil.TempFile("", "docker-config-")
107
+	if err != nil {
108
+		t.Fatal(err)
109
+	}
110
+
111
+	configFile := f.Name()
112
+
113
+	f.Write([]byte(`
114
+		{
115
+			"default-shm-size": "1g"
116
+		}`))
117
+
118
+	f.Close()
119
+
120
+	c := &Config{}
121
+	cc, err := MergeDaemonConfigurations(c, nil, configFile)
122
+	if err != nil {
123
+		t.Fatal(err)
124
+	}
125
+	expectedValue := 1 * 1024 * 1024 * 1024
126
+	if cc.ShmSize.Value() != int64(expectedValue) {
127
+		t.Fatalf("expected default shm size %d, got %d", expectedValue, cc.ShmSize.Value())
128
+	}
129
+}
... ...
@@ -117,7 +117,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
117 117
 				return err
118 118
 			}
119 119
 
120
-			shmSize := container.DefaultSHMSize
120
+			shmSize := int64(daemon.configStore.ShmSize)
121 121
 			if c.HostConfig.ShmSize != 0 {
122 122
 				shmSize = c.HostConfig.ShmSize
123 123
 			}
... ...
@@ -256,7 +256,10 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
256 256
 		hostConfig.MemorySwap = hostConfig.Memory * 2
257 257
 	}
258 258
 	if hostConfig.ShmSize == 0 {
259
-		hostConfig.ShmSize = container.DefaultSHMSize
259
+		hostConfig.ShmSize = defaultShmSize
260
+		if daemon.configStore != nil {
261
+			hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
262
+		}
260 263
 	}
261 264
 	var err error
262 265
 	opts, err := daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode, hostConfig.Privileged)
... ...
@@ -576,6 +579,10 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string {
576 576
 		daemon.configStore.DefaultRuntime = config.DefaultRuntime
577 577
 	}
578 578
 
579
+	if config.IsValueSet("default-shm-size") {
580
+		daemon.configStore.ShmSize = config.ShmSize
581
+	}
582
+
579 583
 	// Update attributes
580 584
 	var runtimeList bytes.Buffer
581 585
 	for name, rt := range daemon.configStore.Runtimes {
... ...
@@ -586,8 +593,9 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string {
586 586
 	}
587 587
 
588 588
 	return map[string]string{
589
-		"runtimes":        runtimeList.String(),
590
-		"default-runtime": daemon.configStore.DefaultRuntime,
589
+		"runtimes":         runtimeList.String(),
590
+		"default-runtime":  daemon.configStore.DefaultRuntime,
591
+		"default-shm-size": fmt.Sprintf("%d", daemon.configStore.ShmSize),
591 592
 	}
592 593
 }
593 594
 
... ...
@@ -49,7 +49,7 @@ Options:
49 49
   -q, --quiet                   Suppress the build output and print image ID on success
50 50
       --rm                      Remove intermediate containers after a successful build (default true)
51 51
       --security-opt value      Security Options (default [])
52
-      --shm-size string         Size of /dev/shm, default value is 64MB.
52
+      --shm-size bytes          Size of /dev/shm
53 53
                                 The format is `<number><unit>`. `number` must be greater than `0`.
54 54
                                 Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes),
55 55
                                 or `g` (gigabytes). If you omit the unit, the system uses bytes.
... ...
@@ -107,7 +107,7 @@ Options:
107 107
       --rm                          Automatically remove the container when it exits
108 108
       --runtime string              Runtime to use for this container
109 109
       --security-opt value          Security Options (default [])
110
-      --shm-size string             Size of /dev/shm, default value is 64MB.
110
+      --shm-size bytes              Size of /dev/shm
111 111
                                     The format is `<number><unit>`. `number` must be greater than `0`.
112 112
                                     Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes),
113 113
                                     or `g` (gigabytes). If you omit the unit, the system uses bytes.
... ...
@@ -37,6 +37,7 @@ Options:
37 37
       --default-gateway value                 Container default gateway IPv4 address
38 38
       --default-gateway-v6 value              Container default gateway IPv6 address
39 39
       --default-runtime string                Default OCI runtime for containers (default "runc")
40
+      --default-shm-size bytes                Set the default shm size for containers (default 64 MiB)
40 41
       --default-ulimit value                  Default ulimits for containers (default [])
41 42
       --disable-legacy-registry               Disable contacting legacy registries
42 43
       --dns value                             DNS server to use (default [])
... ...
@@ -1161,6 +1162,7 @@ This is a full example of the allowed configuration options on Linux:
1161 1161
 	"cluster-advertise": "",
1162 1162
 	"max-concurrent-downloads": 3,
1163 1163
 	"max-concurrent-uploads": 5,
1164
+	"default-shm-size": "64M",
1164 1165
 	"shutdown-timeout": 15,
1165 1166
 	"debug": true,
1166 1167
 	"hosts": [],
... ...
@@ -117,7 +117,7 @@ Options:
117 117
       --rm                          Automatically remove the container when it exits
118 118
       --runtime string              Runtime to use for this container
119 119
       --security-opt value          Security Options (default [])
120
-      --shm-size string             Size of /dev/shm, default value is 64MB.
120
+      --shm-size bytes              Size of /dev/shm
121 121
                                     The format is `<number><unit>`. `number` must be greater than `0`.
122 122
                                     Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes),
123 123
                                     or `g` (gigabytes). If you omit the unit, the system uses bytes.
... ...
@@ -39,7 +39,7 @@ Options:
39 39
       --hostname string                  Container hostname
40 40
   -l, --label list                       Service labels (default [])
41 41
       --limit-cpu decimal                Limit CPUs (default 0.000)
42
-      --limit-memory bytes               Limit Memory (default 0 B)
42
+      --limit-memory bytes               Limit Memory
43 43
       --log-driver string                Logging driver for service
44 44
       --log-opt list                     Logging driver options (default [])
45 45
       --mode string                      Service mode (replicated or global) (default "replicated")
... ...
@@ -51,7 +51,7 @@ Options:
51 51
       --read-only                        Mount the container's root filesystem as read only
52 52
       --replicas uint                    Number of tasks
53 53
       --reserve-cpu decimal              Reserve CPUs (default 0.000)
54
-      --reserve-memory bytes             Reserve Memory (default 0 B)
54
+      --reserve-memory bytes             Reserve Memory
55 55
       --restart-condition string         Restart when condition is met (none, on-failure, or any)
56 56
       --restart-delay duration           Delay between restart attempts (ns|us|ms|s|m|h)
57 57
       --restart-max-attempts uint        Maximum number of restarts before giving up
... ...
@@ -50,7 +50,7 @@ Options:
50 50
       --label-add list                   Add or update a service label (default [])
51 51
       --label-rm list                    Remove a label by its key (default [])
52 52
       --limit-cpu decimal                Limit CPUs (default 0.000)
53
-      --limit-memory bytes               Limit Memory (default 0 B)
53
+      --limit-memory bytes               Limit Memory
54 54
       --log-driver string                Logging driver for service
55 55
       --log-opt list                     Logging driver options (default [])
56 56
       --mount-add mount                  Add or update a mount on a service
... ...
@@ -61,7 +61,7 @@ Options:
61 61
       --read-only                        Mount the container's root filesystem as read only
62 62
       --replicas uint                    Number of tasks
63 63
       --reserve-cpu decimal              Reserve CPUs (default 0.000)
64
-      --reserve-memory bytes             Reserve Memory (default 0 B)
64
+      --reserve-memory bytes             Reserve Memory
65 65
       --restart-condition string         Restart when condition is met (none, on-failure, or any)
66 66
       --restart-delay duration           Delay between restart attempts (ns|us|ms|s|m|h)
67 67
       --restart-max-attempts uint        Maximum number of restarts before giving up
... ...
@@ -2879,3 +2879,60 @@ func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
2879 2879
 	out, err = s.d.Cmd("stop", id)
2880 2880
 	c.Assert(err, check.IsNil, check.Commentf("output: %s", out))
2881 2881
 }
2882
+
2883
+func (s *DockerDaemonSuite) TestShmSize(c *check.C) {
2884
+	testRequires(c, DaemonIsLinux)
2885
+
2886
+	size := 67108864 * 2
2887
+	pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
2888
+
2889
+	s.d.StartWithBusybox(c, "--default-shm-size", fmt.Sprintf("%v", size))
2890
+
2891
+	name := "shm1"
2892
+	out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
2893
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
2894
+	c.Assert(pattern.MatchString(out), checker.True)
2895
+	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
2896
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
2897
+	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
2898
+}
2899
+
2900
+func (s *DockerDaemonSuite) TestShmSizeReload(c *check.C) {
2901
+	testRequires(c, DaemonIsLinux)
2902
+
2903
+	configPath, err := ioutil.TempDir("", "test-daemon-shm-size-reload-config")
2904
+	c.Assert(err, checker.IsNil, check.Commentf("could not create temp file for config reload"))
2905
+	defer os.RemoveAll(configPath) // clean up
2906
+	configFile := filepath.Join(configPath, "config.json")
2907
+
2908
+	size := 67108864 * 2
2909
+	configData := []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
2910
+	c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
2911
+	pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
2912
+
2913
+	s.d.StartWithBusybox(c, "--config-file", configFile)
2914
+
2915
+	name := "shm1"
2916
+	out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
2917
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
2918
+	c.Assert(pattern.MatchString(out), checker.True)
2919
+	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
2920
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
2921
+	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
2922
+
2923
+	size = 67108864 * 3
2924
+	configData = []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
2925
+	c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
2926
+	pattern = regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
2927
+
2928
+	err = s.d.ReloadConfig()
2929
+	c.Assert(err, checker.IsNil, check.Commentf("error reloading daemon config"))
2930
+
2931
+	name = "shm2"
2932
+	out, err = s.d.Cmd("run", "--name", name, "busybox", "mount")
2933
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
2934
+	c.Assert(pattern.MatchString(out), checker.True)
2935
+	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
2936
+	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
2937
+	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
2938
+}
... ...
@@ -427,7 +427,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
427 427
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
428 428
 	c.Assert(err, checker.IsNil)
429 429
 
430
-	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))
430
+	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, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
431 431
 }
432 432
 
433 433
 func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
... ...
@@ -21,6 +21,7 @@ dockerd - Enable daemon mode
21 21
 [**--default-gateway**[=*DEFAULT-GATEWAY*]]
22 22
 [**--default-gateway-v6**[=*DEFAULT-GATEWAY-V6*]]
23 23
 [**--default-runtime**[=*runc*]]
24
+[**--default-shm-size**[=*64MiB*]]
24 25
 [**--default-ulimit**[=*[]*]]
25 26
 [**--disable-legacy-registry**]
26 27
 [**--dns**[=*[]*]]
... ...
@@ -164,6 +165,9 @@ $ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-ru
164 164
 **--default-runtime**="runc"
165 165
   Set default runtime if there're more than one specified by `--add-runtime`.
166 166
 
167
+**--default-shm-size**=*64MiB*
168
+  Set the daemon-wide default shm size for containers. Default is `64MiB`.
169
+
167 170
 **--default-ulimit**=[]
168 171
   Default ulimits for containers.
169 172
 
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/docker/docker/api/types/filters"
12
+	units "github.com/docker/go-units"
12 13
 )
13 14
 
14 15
 var (
... ...
@@ -402,3 +403,44 @@ func ValidateLink(val string) (string, error) {
402 402
 	_, _, err := ParseLink(val)
403 403
 	return val, err
404 404
 }
405
+
406
+// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc)
407
+type MemBytes int64
408
+
409
+// String returns the string format of the human readable memory bytes
410
+func (m *MemBytes) String() string {
411
+	// NOTE: In spf13/pflag/flag.go, "0" is considered as "zero value" while "0 B" is not.
412
+	// We return "0" in case value is 0 here so that the default value is hidden.
413
+	// (Sometimes "default 0 B" is actually misleading)
414
+	if m.Value() != 0 {
415
+		return units.BytesSize(float64(m.Value()))
416
+	}
417
+	return "0"
418
+}
419
+
420
+// Set sets the value of the MemBytes by passing a string
421
+func (m *MemBytes) Set(value string) error {
422
+	val, err := units.RAMInBytes(value)
423
+	*m = MemBytes(val)
424
+	return err
425
+}
426
+
427
+// Type returns the type
428
+func (m *MemBytes) Type() string {
429
+	return "bytes"
430
+}
431
+
432
+// Value returns the value in int64
433
+func (m *MemBytes) Value() int64 {
434
+	return int64(*m)
435
+}
436
+
437
+// UnmarshalJSON is the customized unmarshaler for MemBytes
438
+func (m *MemBytes) UnmarshalJSON(s []byte) error {
439
+	if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
440
+		return fmt.Errorf("invalid size: %q", s)
441
+	}
442
+	val, err := units.RAMInBytes(string(s[1 : len(s)-1]))
443
+	*m = MemBytes(val)
444
+	return err
445
+}