You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by qi...@apache.org on 2020/05/05 08:12:57 UTC

[mesos] 05/05: Updated Docker containerizer's `usage()` to support resource limits.

This is an automated email from the ASF dual-hosted git repository.

qianzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 95b806474da6f63ec8d50904a4336f903c0c5d08
Author: Qian Zhang <zh...@gmail.com>
AuthorDate: Tue Apr 21 09:30:26 2020 +0800

    Updated Docker containerizer's `usage()` to support resource limits.
    
    Review: https://reviews.apache.org/r/72402
---
 src/slave/containerizer/docker.cpp                 | 105 +++++++++++++++++++--
 .../containerizer/docker_containerizer_tests.cpp   |  25 +++--
 2 files changed, 116 insertions(+), 14 deletions(-)

diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 8aed025..431f7c6 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -2082,16 +2082,105 @@ Future<ResourceStatistics> DockerContainerizerProcess::usage(
     result = cgroupStats.get();
 #endif // __linux__
 
-    // Set the resource allocations.
-    const Resources& resource = container->resourceRequests;
-    const Option<Bytes> mem = resource.mem();
-    if (mem.isSome()) {
-      result.set_mem_limit_bytes(mem->bytes());
+    Option<double> cpuRequest, cpuLimit, memLimit;
+    Option<Bytes> memRequest;
+
+    // For command tasks, we should subtract the default resources (0.1 cpus and
+    // 32MB memory) for command executor from the container's resource requests
+    // and limits, otherwise we would report wrong resource statistics.
+    if (container->resourceRequests.cpus().isSome()) {
+      if (container->generatedForCommandTask) {
+        cpuRequest =
+          container->resourceRequests.cpus().get() - DEFAULT_EXECUTOR_CPUS;
+      } else {
+        cpuRequest = container->resourceRequests.cpus();
+      }
+    }
+
+    if (container->resourceRequests.mem().isSome()) {
+      if (container->generatedForCommandTask) {
+        memRequest =
+          container->resourceRequests.mem().get() - DEFAULT_EXECUTOR_MEM;
+      } else {
+        memRequest = container->resourceRequests.mem();
+      }
+    }
+
+    foreach (auto&& limit, container->resourceLimits) {
+      if (limit.first == "cpus") {
+        if (container->generatedForCommandTask &&
+            !std::isinf(limit.second.value())) {
+          cpuLimit = limit.second.value() - DEFAULT_EXECUTOR_CPUS;
+        } else {
+          cpuLimit = limit.second.value();
+        }
+      } else if (limit.first == "mem") {
+        if (container->generatedForCommandTask &&
+            !std::isinf(limit.second.value())) {
+          memLimit = limit.second.value() -
+                     DEFAULT_EXECUTOR_MEM.bytes() / Bytes::MEGABYTES;
+        } else {
+          memLimit = limit.second.value();
+        }
+      }
+    }
+
+    if (cpuRequest.isSome()) {
+      result.set_cpus_soft_limit(cpuRequest.get());
+    }
+
+    if (cpuLimit.isSome()) {
+      // Get the total CPU numbers of this node, we will use
+      // it to set container's hard CPU limit if the CPU limit
+      // specified by framework is infinity.
+      static Option<long> totalCPUs;
+      if (totalCPUs.isNone()) {
+        Try<long> cpus = os::cpus();
+        if (cpus.isError()) {
+          return Failure(
+              "Failed to auto-detect the number of cpus: " + cpus.error());
+        }
+
+        totalCPUs = cpus.get();
+      }
+
+      CHECK_SOME(totalCPUs);
+
+      result.set_cpus_limit(
+          std::isinf(cpuLimit.get()) ? totalCPUs.get() : cpuLimit.get());
+#ifdef __linux__
+    } else if (flags.cgroups_enable_cfs && cpuRequest.isSome()) {
+      result.set_cpus_limit(cpuRequest.get());
+#endif
     }
 
-    const Option<double> cpus = resource.cpus();
-    if (cpus.isSome()) {
-      result.set_cpus_limit(cpus.get());
+    if (memLimit.isSome()) {
+      // Get the total memory of this node, we will use it to
+      // set container's hard memory limit if the memory limit
+      // specified by framework is infinity.
+      static Option<Bytes> totalMem;
+      if (totalMem.isNone()) {
+        Try<os::Memory> mem = os::memory();
+        if (mem.isError()) {
+          return Failure(
+              "Failed to auto-detect the size of main memory: " + mem.error());
+        }
+
+        totalMem = mem->total;
+      }
+
+      CHECK_SOME(totalMem);
+
+      result.set_mem_limit_bytes(
+          std::isinf(memLimit.get())
+            ? totalMem->bytes()
+            : Megabytes(static_cast<uint64_t>(memLimit.get())).bytes());
+
+      if (memRequest.isSome()) {
+        result.set_mem_soft_limit_bytes(memRequest->bytes());
+      }
+    } else if (memRequest.isSome()) {
+      result.set_mem_limit_bytes(memRequest->bytes());
     }
 
     return result;
diff --git a/src/tests/containerizer/docker_containerizer_tests.cpp b/src/tests/containerizer/docker_containerizer_tests.cpp
index 42692dc..fc3a651 100644
--- a/src/tests/containerizer/docker_containerizer_tests.cpp
+++ b/src/tests/containerizer/docker_containerizer_tests.cpp
@@ -946,7 +946,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
   ASSERT_SOME(master);
 
   slave::Flags flags = CreateSlaveFlags();
-  flags.resources = Option<string>("cpus:2;mem:1024");
+  flags.resources = Option<string>("cpus:1;mem:1024");
 
   MockDocker* mockDocker =
     new MockDocker(tests::flags.docker, tests::flags.docker_socket);
@@ -1000,10 +1000,22 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
   command.set_value("dd if=/dev/zero of=/dev/null");
 #endif // __WINDOWS__
 
+  Value::Scalar cpuLimit, memLimit;
+  cpuLimit.set_value(2);
+  memLimit.set_value(2048);
+
+  google::protobuf::Map<string, Value::Scalar> resourceLimits;
+  resourceLimits.insert({"cpus", cpuLimit});
+  resourceLimits.insert({"mem", memLimit});
+
   TaskInfo task = createTask(
       offers->front().slave_id(),
       offers->front().resources(),
-      command);
+      command,
+      None(),
+      "test-task",
+      id::UUID::random().toString(),
+      resourceLimits);
 
   // TODO(tnachen): Use local image to test if possible.
   task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
@@ -1056,10 +1068,11 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
     waited += Milliseconds(200);
   } while (waited < Seconds(3));
 
-  // Usage includes the executor resources.
-  EXPECT_EQ(2.0 + slave::DEFAULT_EXECUTOR_CPUS, statistics.cpus_limit());
-  EXPECT_EQ((Gigabytes(1) + slave::DEFAULT_EXECUTOR_MEM).bytes(),
-            statistics.mem_limit_bytes());
+  EXPECT_EQ(1, statistics.cpus_soft_limit());
+  EXPECT_EQ(2, statistics.cpus_limit());
+  EXPECT_EQ(Gigabytes(1).bytes(), statistics.mem_soft_limit_bytes());
+  EXPECT_EQ(Gigabytes(2).bytes(), statistics.mem_limit_bytes());
+
 #ifndef __WINDOWS__
   // These aren't provided by the Windows Container APIs, so skip them.
   EXPECT_LT(0, statistics.cpus_user_time_secs());