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 <[email protected]> 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());
