Disabling the oom-killer for a container without setting a memory limit
is dangerous, as it can result in the container consuming unlimited memory,
without the kernel being able to kill it. A check for this situation is curently
done in the CLI, but other consumers of the API won't receive this warning.
This patch adds a check for this situation to the daemon, so that all consumers
of the API will receive this warning.
This patch will have one side-effect; docker cli's that also perform this check
client-side will print the warning twice; this can be addressed by disabling
the cli-side check for newer API versions, but will generate a bit of extra
noise when using an older CLI.
With this patch applied (and a cli that does not take the new warning into account);
```
docker create --oom-kill-disable busybox
WARNING: OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.
669933b9b237fa27da699483b5cf15355a9027050825146587a0e5be0d848adf
docker run --rm --oom-kill-disable busybox
WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.
WARNING: OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.
```
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -422,7 +422,10 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi |
| 422 | 422 |
} |
| 423 | 423 |
resources.OomKillDisable = nil |
| 424 | 424 |
} |
| 425 |
- |
|
| 425 |
+ if resources.OomKillDisable != nil && *resources.OomKillDisable && resources.Memory == 0 {
|
|
| 426 |
+ warnings = append(warnings, "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.") |
|
| 427 |
+ logrus.Warn("OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.")
|
|
| 428 |
+ } |
|
| 426 | 429 |
if resources.PidsLimit != 0 && !sysInfo.PidsLimit {
|
| 427 | 430 |
warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") |
| 428 | 431 |
logrus.Warn("Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
|
| ... | ... |
@@ -11,6 +11,9 @@ import ( |
| 11 | 11 |
containertypes "github.com/docker/docker/api/types/container" |
| 12 | 12 |
"github.com/docker/docker/container" |
| 13 | 13 |
"github.com/docker/docker/daemon/config" |
| 14 |
+ "github.com/docker/docker/pkg/sysinfo" |
|
| 15 |
+ "gotest.tools/assert" |
|
| 16 |
+ is "gotest.tools/assert/cmp" |
|
| 14 | 17 |
) |
| 15 | 18 |
|
| 16 | 19 |
type fakeContainerGetter struct {
|
| ... | ... |
@@ -266,3 +269,110 @@ func TestNetworkOptions(t *testing.T) {
|
| 266 | 266 |
t.Fatal("Expected networkOptions error, got nil")
|
| 267 | 267 |
} |
| 268 | 268 |
} |
| 269 |
+ |
|
| 270 |
+func TestVerifyContainerResources(t *testing.T) {
|
|
| 271 |
+ t.Parallel() |
|
| 272 |
+ var ( |
|
| 273 |
+ no = false |
|
| 274 |
+ yes = true |
|
| 275 |
+ ) |
|
| 276 |
+ |
|
| 277 |
+ withMemoryLimit := func(si *sysinfo.SysInfo) {
|
|
| 278 |
+ si.MemoryLimit = true |
|
| 279 |
+ } |
|
| 280 |
+ withSwapLimit := func(si *sysinfo.SysInfo) {
|
|
| 281 |
+ si.SwapLimit = true |
|
| 282 |
+ } |
|
| 283 |
+ withOomKillDisable := func(si *sysinfo.SysInfo) {
|
|
| 284 |
+ si.OomKillDisable = true |
|
| 285 |
+ } |
|
| 286 |
+ |
|
| 287 |
+ tests := []struct {
|
|
| 288 |
+ name string |
|
| 289 |
+ resources containertypes.Resources |
|
| 290 |
+ sysInfo sysinfo.SysInfo |
|
| 291 |
+ update bool |
|
| 292 |
+ expectedWarnings []string |
|
| 293 |
+ }{
|
|
| 294 |
+ {
|
|
| 295 |
+ name: "no-oom-kill-disable", |
|
| 296 |
+ resources: containertypes.Resources{},
|
|
| 297 |
+ sysInfo: sysInfo(t, withMemoryLimit), |
|
| 298 |
+ expectedWarnings: []string{},
|
|
| 299 |
+ }, |
|
| 300 |
+ {
|
|
| 301 |
+ name: "oom-kill-disable-disabled", |
|
| 302 |
+ resources: containertypes.Resources{
|
|
| 303 |
+ OomKillDisable: &no, |
|
| 304 |
+ }, |
|
| 305 |
+ sysInfo: sysInfo(t, withMemoryLimit), |
|
| 306 |
+ expectedWarnings: []string{},
|
|
| 307 |
+ }, |
|
| 308 |
+ {
|
|
| 309 |
+ name: "oom-kill-disable-not-supported", |
|
| 310 |
+ resources: containertypes.Resources{
|
|
| 311 |
+ OomKillDisable: &yes, |
|
| 312 |
+ }, |
|
| 313 |
+ sysInfo: sysInfo(t, withMemoryLimit), |
|
| 314 |
+ expectedWarnings: []string{
|
|
| 315 |
+ "Your kernel does not support OomKillDisable. OomKillDisable discarded.", |
|
| 316 |
+ }, |
|
| 317 |
+ }, |
|
| 318 |
+ {
|
|
| 319 |
+ name: "oom-kill-disable-without-memory-constraints", |
|
| 320 |
+ resources: containertypes.Resources{
|
|
| 321 |
+ OomKillDisable: &yes, |
|
| 322 |
+ Memory: 0, |
|
| 323 |
+ }, |
|
| 324 |
+ sysInfo: sysInfo(t, withMemoryLimit, withOomKillDisable, withSwapLimit), |
|
| 325 |
+ expectedWarnings: []string{
|
|
| 326 |
+ "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.", |
|
| 327 |
+ }, |
|
| 328 |
+ }, |
|
| 329 |
+ {
|
|
| 330 |
+ name: "oom-kill-disable-with-memory-constraints-but-no-memory-limit-support", |
|
| 331 |
+ resources: containertypes.Resources{
|
|
| 332 |
+ OomKillDisable: &yes, |
|
| 333 |
+ Memory: linuxMinMemory, |
|
| 334 |
+ }, |
|
| 335 |
+ sysInfo: sysInfo(t, withOomKillDisable), |
|
| 336 |
+ expectedWarnings: []string{
|
|
| 337 |
+ "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.", |
|
| 338 |
+ "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.", |
|
| 339 |
+ }, |
|
| 340 |
+ }, |
|
| 341 |
+ {
|
|
| 342 |
+ name: "oom-kill-disable-with-memory-constraints", |
|
| 343 |
+ resources: containertypes.Resources{
|
|
| 344 |
+ OomKillDisable: &yes, |
|
| 345 |
+ Memory: linuxMinMemory, |
|
| 346 |
+ }, |
|
| 347 |
+ sysInfo: sysInfo(t, withMemoryLimit, withOomKillDisable, withSwapLimit), |
|
| 348 |
+ expectedWarnings: []string{},
|
|
| 349 |
+ }, |
|
| 350 |
+ } |
|
| 351 |
+ for _, tc := range tests {
|
|
| 352 |
+ t.Run(tc.name, func(t *testing.T) {
|
|
| 353 |
+ t.Parallel() |
|
| 354 |
+ warnings, err := verifyContainerResources(&tc.resources, &tc.sysInfo, tc.update) |
|
| 355 |
+ assert.NilError(t, err) |
|
| 356 |
+ for _, w := range tc.expectedWarnings {
|
|
| 357 |
+ assert.Assert(t, is.Contains(warnings, w)) |
|
| 358 |
+ } |
|
| 359 |
+ }) |
|
| 360 |
+ } |
|
| 361 |
+} |
|
| 362 |
+ |
|
| 363 |
+func sysInfo(t *testing.T, opts ...func(*sysinfo.SysInfo)) sysinfo.SysInfo {
|
|
| 364 |
+ t.Helper() |
|
| 365 |
+ si := sysinfo.SysInfo{}
|
|
| 366 |
+ |
|
| 367 |
+ for _, opt := range opts {
|
|
| 368 |
+ opt(&si) |
|
| 369 |
+ } |
|
| 370 |
+ |
|
| 371 |
+ if si.OomKillDisable {
|
|
| 372 |
+ t.Log(t.Name(), "OOM disable supported") |
|
| 373 |
+ } |
|
| 374 |
+ return si |
|
| 375 |
+} |