Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
MXS-4155: Check CPU and MEM limits in Docker
If MaxScale is being run inside of a container with restricted CPU or
memory resources, `threads=auto` used to use the host's core count
instead of the one defined for the cgroup the container runs in. This
commits adds correct detection of the CPU limits for the active cgroup.

The new code checks for both v1 and v2 cgroup paths in /sys/fs/
for the `--cpus` and `--memory` docker options. In addition, the CPU
affinity option `--cpuset-cpus` is detected if it is being used to pin
the container to certain CPUs.
  • Loading branch information
markus456 committed Jun 17, 2022
1 parent 4449fb9 commit 10ffb53
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 8 deletions.
5 changes: 5 additions & 0 deletions Documentation/Getting-Started/Configuration-Guide.md
Expand Up @@ -411,6 +411,11 @@ You can explicitly enable automatic configuration of this value by setting the
value to `auto`. This way MariaDB MaxScale will detect the number of available
processors and set the amount of threads to be equal to that number.

Starting with MaxScale 22.08, if the `maxscale` process is started in a
container with limited CPU resources (i.e. the `--cpus` and `--cpuset-cpus`
options in Docker), the `auto` values uses the container's limits instead of the
host system's limits.

The maximum value for threads is 256.

```
Expand Down
10 changes: 6 additions & 4 deletions server/core/gateway.cc
Expand Up @@ -1970,14 +1970,16 @@ int main(int argc, char** argv)
}

// Config successfully read and we are a unique MaxScale, time to log some info.
MXB_NOTICE("Host: '%s' OS: %s@%s, %s, %s with %lu processor cores.",
MXB_NOTICE("Host: '%s' OS: %s@%s, %s, %s with %u processor cores (%ld online).",
cnf.nodename.c_str(), cnf.sysname.c_str(), cnf.release.c_str(),
cnf.version.c_str(), cnf.machine.c_str(), get_processor_count());
cnf.version.c_str(), cnf.machine.c_str(), std::thread::hardware_concurrency(),
get_processor_count());

struct sysinfo info;
sysinfo(&info);
MXB_NOTICE("Total usable main memory: %s.",
mxb::pretty_size(info.mem_unit * info.totalram).c_str());
MXB_NOTICE("Total usable main memory: %s (%s available).",
mxb::pretty_size(info.mem_unit * info.totalram).c_str(),
mxb::pretty_size(get_total_memory()).c_str());
MXB_NOTICE("MariaDB MaxScale %s started (Commit: %s)", MAXSCALE_VERSION, MAXSCALE_COMMIT);
MXB_NOTICE("MaxScale is running in process %i", getpid());

Expand Down
81 changes: 77 additions & 4 deletions server/core/utils.cc
Expand Up @@ -43,6 +43,8 @@
#include <thread>
#include <curl/curl.h>
#include <crypt.h>
#include <sched.h>
#include <fstream>

#include <maxscale/config.hh>
#include <maxscale/secrets.hh>
Expand Down Expand Up @@ -595,13 +597,64 @@ int open_unix_socket(mxs_socket_type type, sockaddr_un* addr, const char* path)
return fd;
}

long get_processor_count()
long get_current_processor_count()
{
mxb_assert(sysconf(_SC_NPROCESSORS_ONLN) == std::thread::hardware_concurrency());
return std::max(std::thread::hardware_concurrency(), 1U);
unsigned int cpus = std::thread::hardware_concurrency();

cpu_set_t cpuset;
if (sched_getaffinity(getpid(), sizeof(cpuset), &cpuset) == 0)
{
cpus = std::min((unsigned int)CPU_COUNT(&cpuset), cpus);
}

int quota = 0;
int period = 0;

if (std::ifstream cpu_v2("/sys/fs/cgroup/cpu.max"); cpu_v2)
{
if (std::string line; std::getline(cpu_v2, line))
{
auto tok = mxb::strtok(line, " ");

if (tok.size() == 2 && tok[0] != "-1" && tok [0] != "max")
{
quota = atoi(tok[0].c_str());
period = atoi(tok[1].c_str());
}
}
}
else if (std::ifstream cpu_v1_quota("/sys/fs/cgroup/cpu/cpu.cfs_quota_us"); cpu_v1_quota)
{
if (std::ifstream cpu_v1_period("/sys/fs/cgroup/cpu/cpu.cfs_period_us"); cpu_v1_period)
{
int tmp_quota = 0;
int tmp_period = 0;

if ((cpu_v1_quota >> tmp_quota) && (cpu_v1_period >> tmp_period) && tmp_quota > 0)
{
quota = tmp_quota;
period = tmp_period;
}
}
}

if (quota && period)
{
unsigned int vcpu = std::ceil((double)quota / period);
cpus = std::min(vcpu, cpus);
}

return std::max(cpus, 1U);
}

int64_t get_total_memory()
long get_processor_count()
{
static long cpus = get_current_processor_count();
return cpus;
}

int64_t get_current_total_memory()
{
int64_t pagesize = 0;
int64_t num_pages = 0;
Expand All @@ -616,7 +669,27 @@ int64_t get_total_memory()
#error _SC_PAGESIZE and _SC_PHYS_PAGES are not defined
#endif
mxb_assert(pagesize * num_pages > 0);
return pagesize * num_pages;
int64_t memory = pagesize * num_pages;

for (auto path : {"/sys/fs/cgroup/memory.max", "/sys/fs/cgroup/memory/memory.limit_in_bytes"})
{
if (std::ifstream mem(path); mem)
{
if (int64_t mem_tmp = 0; (mem >> mem_tmp))
{
memory = std::min(mem_tmp, memory);
break;
}
}
}

return std::max(memory, 0L);
}

int64_t get_total_memory()
{
static int64_t total_memory = get_current_total_memory();
return total_memory;
}

namespace maxscale
Expand Down

0 comments on commit 10ffb53

Please sign in to comment.