You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by tn...@apache.org on 2015/07/15 02:58:18 UTC

[1/2] mesos git commit: Added cpuacct subsystem to cgroups.

Repository: mesos
Updated Branches:
  refs/heads/master 1b70debfd -> 867ff24ba


Added cpuacct subsystem to cgroups.

Review: https://reviews.apache.org/r/36106


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/9a5eaca9
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/9a5eaca9
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/9a5eaca9

Branch: refs/heads/master
Commit: 9a5eaca9e3ab7ac01ae5f884b32e8c570da4f799
Parents: 1b70deb
Author: Jojy Varghese <jo...@mesosphere.io>
Authored: Tue Jul 14 17:10:03 2015 -0700
Committer: Timothy Chen <tn...@apache.org>
Committed: Tue Jul 14 17:57:50 2015 -0700

----------------------------------------------------------------------
 src/linux/cgroups.cpp       | 51 ++++++++++++++++++++++++++++++++++++++++
 src/linux/cgroups.hpp       | 35 +++++++++++++++++++++++++++
 src/tests/cgroups_tests.cpp | 14 +++++++++++
 3 files changed, 100 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9a5eaca9/src/linux/cgroups.cpp
----------------------------------------------------------------------
diff --git a/src/linux/cgroups.cpp b/src/linux/cgroups.cpp
index 4c006d0..b7d11ac 100644
--- a/src/linux/cgroups.cpp
+++ b/src/linux/cgroups.cpp
@@ -1991,6 +1991,57 @@ Try<Nothing> cfs_quota_us(
 
 } // namespace cpu {
 
+namespace cpuacct {
+
+Result<string> cgroup(pid_t pid)
+{
+  return internal::cgroup(pid, "cpuacct");
+}
+
+
+Try<Stats> stat(
+    const string& hierarchy,
+    const string& cgroup)
+{
+  const Try<hashmap<string, uint64_t>> stats =
+    cgroups::stat(hierarchy, cgroup, "cpuacct.stat");
+
+  if (!stats.isSome()) {
+    return Error(stats.error());
+  }
+
+  if (!stats.get().contains("user") || !stats.get().contains("system")) {
+    return Error("Failed to get user/system value from cpuacct.stat");
+  }
+
+  // Get user ticks per second. This value is constant for the lifetime of a
+  // process.
+  // TODO(Jojy): Move system constants to a separate compilation unit.
+  static long userTicks = sysconf(_SC_CLK_TCK);
+  if (userTicks <= 0) {
+    return ErrnoError("Failed to get _SC_CLK_TCK");
+  }
+
+  Try<Duration> user =
+    Duration::create((double) stats.get().at("user") / userTicks);
+
+  if (user.isError()) {
+    return Error(
+        "Failed to convert user ticks to Duration: " + user.error());
+  }
+
+  Try<Duration> system =
+    Duration::create((double) stats.get().at("system") / userTicks);
+
+  if (system.isError()) {
+    return Error(
+        "Failed to convert system ticks to Duration: " + system.error());
+  }
+
+  return Stats({user.get(), system.get()});
+}
+
+} // namespace cpuacct {
 
 namespace memory {
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a5eaca9/src/linux/cgroups.hpp
----------------------------------------------------------------------
diff --git a/src/linux/cgroups.hpp b/src/linux/cgroups.hpp
index 73b9831..a47f9a2 100644
--- a/src/linux/cgroups.hpp
+++ b/src/linux/cgroups.hpp
@@ -432,6 +432,41 @@ Try<Nothing> cfs_quota_us(
 } // namespace cpu {
 
 
+// Cpuacct subsystem.
+namespace cpuacct {
+
+// Returns the cgroup that the specified pid is a member of within the
+// hierarchy that the 'cpuacct' subsytem is mounted or None if the
+// subsystem is not mounted or the pid is not a member of a cgroup.
+//
+// @param   pid   process id for which cgroup is queried within the cpuacct
+//                subsytem.
+// @return  Some cgroup in case there was a valid cgroup found for the pid.
+//          Error if there was any error in processing.
+Result<std::string> cgroup(pid_t pid);
+
+
+// Encapsulates the 'stat' information exposed by the cpuacct subsystem.
+struct Stats
+{
+  const Duration user;
+  const Duration system;
+};
+
+
+// Returns 'Stats' for a given hierarchy and cgroup.
+//
+// @param   hierarchy   hierarchy for the 'cpuacct' subsystem.
+// @param   cgroup      cgroup for a given process.
+// @return  Some<Stats> if sucessful.
+//          Error in case of any error during processing.
+Try<Stats> stat(
+    const std::string& hierarchy,
+    const std::string& cgroup);
+
+} // namespace cpuacct {
+
+
 // Memory controls.
 namespace memory {
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/9a5eaca9/src/tests/cgroups_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/cgroups_tests.cpp b/src/tests/cgroups_tests.cpp
index 475f48a..af4b37b 100644
--- a/src/tests/cgroups_tests.cpp
+++ b/src/tests/cgroups_tests.cpp
@@ -1186,6 +1186,20 @@ TEST_F(CgroupsAnyHierarchyMemoryPressureTest, ROOT_IncreasePageCache)
   EXPECT_LT(0u, low);
 }
 
+// Tests the cpuacct::stat API. This test just tests for ANY value returned by
+// the API.
+TEST_F(CgroupsAnyHierarchyWithCpuAcctMemoryTest, ROOT_CGROUPS_CpuAcctsStats)
+{
+  const std::string hierarchy = path::join(baseHierarchy, "cpuacct");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  CHECK_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()));
+
+  ASSERT_SOME(cgroups::cpuacct::stat(hierarchy, TEST_CGROUPS_ROOT));
+
+  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {


[2/2] mesos git commit: Added cgroups based statistics to Docker containerizer.

Posted by tn...@apache.org.
Added cgroups based statistics to Docker containerizer.

Review: https://reviews.apache.org/r/36326


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/867ff24b
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/867ff24b
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/867ff24b

Branch: refs/heads/master
Commit: 867ff24ba91de54a50fe6b0bcf006152c017791a
Parents: 9a5eaca
Author: Jojy Varghese <jo...@mesosphere.io>
Authored: Tue Jul 14 17:10:25 2015 -0700
Committer: Timothy Chen <tn...@apache.org>
Committed: Tue Jul 14 17:57:55 2015 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp       | 142 +++++++++++++++++++-------
 src/slave/containerizer/docker.hpp       |   8 +-
 src/tests/docker_containerizer_tests.cpp |   1 +
 3 files changed, 105 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/867ff24b/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index cfb6017..e710876 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -1202,71 +1202,135 @@ Future<ResourceStatistics> DockerContainerizerProcess::usage(
     return Failure("Container is being removed: " + stringify(containerId));
   }
 
+  auto collectUsage = [this, containerId](
+      pid_t pid) -> Future<ResourceStatistics> {
+    // First make sure container is still there.
+    if (!containers_.contains(containerId)) {
+      return Failure("Container has been destroyed: " + stringify(containerId));
+    }
+
+    Container* container = containers_[containerId];
+
+    if (container->state == Container::DESTROYING) {
+      return Failure("Container is being removed: " + stringify(containerId));
+    }
+
+    const Try<ResourceStatistics> cgroupStats = cgroupsStatistics(pid);
+    if (cgroupStats.isError()) {
+      return Failure("Failed to collect cgroup stats: " + cgroupStats.error());
+    }
+
+    ResourceStatistics result = cgroupStats.get();
+
+    // Set the resource allocations.
+    const Resources& resource = container->resources;
+    const Option<Bytes> mem = resource.mem();
+    if (mem.isSome()) {
+      result.set_mem_limit_bytes(mem.get().bytes());
+    }
+
+    const Option<double> cpus = resource.cpus();
+    if (cpus.isSome()) {
+      result.set_cpus_limit(cpus.get());
+    }
+
+    return result;
+  };
+
   // Skip inspecting the docker container if we already have the pid.
   if (container->pid.isSome()) {
-    return __usage(containerId, container->pid.get());
+    return collectUsage(container->pid.get());
   }
 
   return docker->inspect(container->name())
-    .then(defer(self(), &Self::_usage, containerId, lambda::_1));
+    .then(defer(
+      self(),
+      [this, containerId, collectUsage]
+        (const Docker::Container& _container) -> Future<ResourceStatistics> {
+        const Option<pid_t> pid = _container.pid;
+        if (pid.isNone()) {
+          return Failure("Container is not running");
+        }
+
+        if (!containers_.contains(containerId)) {
+          return Failure(
+            "Container has been destroyed:" + stringify(containerId));
+        }
+
+        Container* container = containers_[containerId];
+
+        // Update the container's pid now. We ran inspect because we didn't have
+        // a pid for the container.
+        container->pid = pid;
+
+        return collectUsage(pid.get());
+      }));
 #endif // __linux__
 }
 
 
-Future<ResourceStatistics> DockerContainerizerProcess::_usage(
-    const ContainerID& containerId,
-    const Docker::Container& _container)
+Try<ResourceStatistics> DockerContainerizerProcess::cgroupsStatistics(
+    pid_t pid) const
 {
-  if (!containers_.contains(containerId)) {
-    return Failure("Container has been destroyed:" + stringify(containerId));
-  }
-
-  Container* container = containers_[containerId];
+#ifndef __linux__
+  return Error("Does not support cgroups on non-linux platform");
+#else
+  const Result<string> cpuHierarchy = cgroups::hierarchy("cpuacct");
+  const Result<string> memHierarchy = cgroups::hierarchy("memory");
 
-  if (container->state == Container::DESTROYING) {
-    return Failure("Container is being removed: " + stringify(containerId));
+  if (cpuHierarchy.isError()) {
+    return Error(
+        "Failed to determine the cgroup 'cpu' subsystem hierarchy: " +
+        cpuHierarchy.error());
   }
 
-  Option<pid_t> pid = _container.pid;
-  if (pid.isNone()) {
-    return Failure("Container is not running");
+  if (memHierarchy.isError()) {
+    return Error(
+        "Failed to determine the cgroup 'memory' subsystem hierarchy: " +
+        memHierarchy.error());
   }
 
-  container->pid = pid;
-
-  return __usage(containerId, pid.get());
-}
+  const Result<string> cpuCgroup = cgroups::cpuacct::cgroup(pid);
+  if (cpuCgroup.isError()) {
+    return Error(
+        "Failed to determine cgroup for the 'cpu' subsystem: " +
+        cpuCgroup.error());
+  }
 
+  const Result<string> memCgroup = cgroups::memory::cgroup(pid);
+  if (memCgroup.isError()) {
+    return Error(
+        "Failed to determine cgroup for the 'memory' subsystem: " +
+        memCgroup.error());
+  }
 
-Future<ResourceStatistics> DockerContainerizerProcess::__usage(
-    const ContainerID& containerId,
-    pid_t pid)
-{
-  Container* container = containers_[containerId];
+  const Try<cgroups::cpuacct::Stats> cpuAcctStat =
+    cgroups::cpuacct::stat(cpuHierarchy.get(), cpuCgroup.get());
 
-  // Note that here getting the root pid is enough because
-  // the root process acts as an 'init' process in the docker
-  // container, so no other child processes will escape it.
-  Try<ResourceStatistics> statistics = mesos::internal::usage(pid, true, true);
-  if (statistics.isError()) {
-    return Failure(statistics.error());
+  if (cpuAcctStat.isError()) {
+    return Error("Failed to get cpu.stat: " + cpuAcctStat.error());
   }
 
-  ResourceStatistics result = statistics.get();
+  const Try<hashmap<string, uint64_t>> memStats =
+    cgroups::stat(memHierarchy.get(), memCgroup.get(), "memory.stat");
 
-  // Set the resource allocations.
-  const Resources& resource = container->resources;
-  Option<Bytes> mem = resource.mem();
-  if (mem.isSome()) {
-    result.set_mem_limit_bytes(mem.get().bytes());
+  if (memStats.isError()) {
+    return Error(
+        "Error getting memory statistics from cgroups memory subsystem: " +
+        memStats.error());
   }
 
-  Option<double> cpus = resource.cpus();
-  if (cpus.isSome()) {
-    result.set_cpus_limit(cpus.get());
+  if (!memStats.get().contains("rss")) {
+    return Error("cgroups memory stats does not contain 'rss' data");
   }
 
+  ResourceStatistics result;
+  result.set_cpus_system_time_secs(cpuAcctStat.get().system.secs());
+  result.set_cpus_user_time_secs(cpuAcctStat.get().user.secs());
+  result.set_mem_rss_bytes(memStats.get().at("rss"));
+
   return result;
+#endif // __linux__
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/867ff24b/src/slave/containerizer/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp
index 9a7a951..e43d300 100644
--- a/src/slave/containerizer/docker.hpp
+++ b/src/slave/containerizer/docker.hpp
@@ -219,13 +219,7 @@ private:
       const Resources& resources,
       pid_t pid);
 
-  Future<ResourceStatistics> _usage(
-      const ContainerID& containerId,
-      const Docker::Container& container);
-
-  Future<ResourceStatistics> __usage(
-      const ContainerID& containerId,
-      pid_t pid);
+  Try<ResourceStatistics> cgroupsStatistics(pid_t pid) const;
 
   // Call back for when the executor exits. This will trigger
   // container destroy.

http://git-wip-us.apache.org/repos/asf/mesos/blob/867ff24b/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index a3da786..77dbeaa 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -961,6 +961,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
             statistics.mem_limit_bytes());
   EXPECT_LT(0, statistics.cpus_user_time_secs());
   EXPECT_LT(0, statistics.cpus_system_time_secs());
+  EXPECT_GT(statistics.mem_rss_bytes(), 0);
 
   Future<containerizer::Termination> termination =
     dockerContainerizer.wait(containerId.get());