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>
... | ... |
@@ -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 |
} |
... | ... |
@@ -255,7 +255,10 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf |
255 | 255 |
hostConfig.MemorySwap = hostConfig.Memory * 2 |
256 | 256 |
} |
257 | 257 |
if hostConfig.ShmSize == 0 { |
258 |
- hostConfig.ShmSize = container.DefaultSHMSize |
|
258 |
+ hostConfig.ShmSize = defaultShmSize |
|
259 |
+ if daemon.configStore != nil { |
|
260 |
+ hostConfig.ShmSize = int64(daemon.configStore.ShmSize) |
|
261 |
+ } |
|
259 | 262 |
} |
260 | 263 |
var err error |
261 | 264 |
opts, err := daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode, hostConfig.Privileged) |
... | ... |
@@ -575,6 +578,10 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string { |
575 | 575 |
daemon.configStore.DefaultRuntime = config.DefaultRuntime |
576 | 576 |
} |
577 | 577 |
|
578 |
+ if config.IsValueSet("default-shm-size") { |
|
579 |
+ daemon.configStore.ShmSize = config.ShmSize |
|
580 |
+ } |
|
581 |
+ |
|
578 | 582 |
// Update attributes |
579 | 583 |
var runtimeList bytes.Buffer |
580 | 584 |
for name, rt := range daemon.configStore.Runtimes { |
... | ... |
@@ -585,8 +592,9 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string { |
585 | 585 |
} |
586 | 586 |
|
587 | 587 |
return map[string]string{ |
588 |
- "runtimes": runtimeList.String(), |
|
589 |
- "default-runtime": daemon.configStore.DefaultRuntime, |
|
588 |
+ "runtimes": runtimeList.String(), |
|
589 |
+ "default-runtime": daemon.configStore.DefaultRuntime, |
|
590 |
+ "default-shm-size": fmt.Sprintf("%d", daemon.configStore.ShmSize), |
|
590 | 591 |
} |
591 | 592 |
} |
592 | 593 |
|
... | ... |
@@ -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": [], |
... | ... |
@@ -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, 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,38 @@ 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 |
+ return units.BytesSize(float64(m.Value())) |
|
412 |
+} |
|
413 |
+ |
|
414 |
+// Set sets the value of the MemBytes by passing a string |
|
415 |
+func (m *MemBytes) Set(value string) error { |
|
416 |
+ val, err := units.RAMInBytes(value) |
|
417 |
+ *m = MemBytes(val) |
|
418 |
+ return err |
|
419 |
+} |
|
420 |
+ |
|
421 |
+// Type returns the type |
|
422 |
+func (m *MemBytes) Type() string { |
|
423 |
+ return "bytes" |
|
424 |
+} |
|
425 |
+ |
|
426 |
+// Value returns the value in int64 |
|
427 |
+func (m *MemBytes) Value() int64 { |
|
428 |
+ return int64(*m) |
|
429 |
+} |
|
430 |
+ |
|
431 |
+// UnmarshalJSON is the customized unmarshaler for MemBytes |
|
432 |
+func (m *MemBytes) UnmarshalJSON(s []byte) error { |
|
433 |
+ if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' { |
|
434 |
+ return fmt.Errorf("invalid size: %q", s) |
|
435 |
+ } |
|
436 |
+ val, err := units.RAMInBytes(string(s[1 : len(s)-1])) |
|
437 |
+ *m = MemBytes(val) |
|
438 |
+ return err |
|
439 |
+} |