From f67a01b88fd7c7057767e18a3dd06c24e94c8aa8 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 24 Oct 2022 17:14:06 +0100 Subject: Isolation: wired up cgroup support to the config system. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This hooks the cgroup support up to the config system so it can actually be used. To make use of this in unit a new "cgroup" section has been added to the isolation configuration. e.g "applications": { "python": { "type": "python", "processes": 5, "path": "/opt/unit/unit-cgroup-test/", "module": "app", "isolation": { "cgroup": { "path": "app/python" } } } } Now there are two ways to specify the path, relative, like the above (without a leading '/') and absolute (with a leading '/'). In the above case the "python" application is placed into its own cgroup under CGROUP_ROOT/
/app/python. Whereas if you specified say "path": "/unit/app/python" Then the python application would be placed under CGROUP_ROOT/unit/app/python The first option allows you to easily take advantage of any resource limits that have already been configured for unit. With the second method (absolute pathname) if you know of an already existing cgroup where you'd like to place it, you can, e.g "path": "/system.slice/unit/python" Where system.slice has already been created by systemd and may already have some overall system limits applied which would also apply to unit. Limits apply down the hierarchy and lower groups can't exceed the previous group limits. So what does this actually look like? Lets take the unit-calculator application[0] and have each of its applications placed into their own cgroup. If we give each application a new section like "isolation": { "cgroup": { "path": "/unit/unit-calculator/add" } } changing the path for each one, we can visualise the result with the systemd-cgls command, e.g │ └─session-5.scope (#4561) │ ├─ 6667 sshd: andrew [priv] │ ├─ 6684 sshd: andrew@pts/0 │ ├─ 6685 -bash │ ├─ 12632 unit: main v1.28.0 [/opt/unit/sbin/unitd --control 127.0.0.1:808> │ ├─ 12634 unit: controller │ ├─ 12635 unit: router │ ├─ 13550 systemd-cgls │ └─ 13551 less ├─unit (#4759) │ └─unit-calculator (#5037) │ ├─subtract (#5069) │ │ ├─ 12650 unit: "subtract" prototype │ │ └─ 12651 unit: "subtract" application │ ├─multiply (#5085) │ │ ├─ 12653 unit: "multiply" prototype │ │ └─ 12654 unit: "multiply" application │ ├─divide (#5101) │ │ ├─ 12671 unit: "divide" prototype │ │ └─ 12672 node divide.js │ ├─sqroot (#5117) │ │ ├─ 12679 unit: "sqroot" prototype │ │ └─ 12680 /home/andrew/src/unit-calculator/sqroot/sqroot │ └─add (#5053) │ ├─ 12648 unit: "add" prototype │ └─ 12649 unit: "add" application We used an absolute path so the cgroups will be created relative to the main cgroupfs mount, e.g /sys/fs/cgroup We can see that the main unit processes are in the same cgroup as the shell from where they were started, by default child process are placed into the same cgroup as the parent. Then we can see that each application has been placed into its own cgroup under /sys/fs/cgroup Taking another example of a simple 5 process python application, with "isolation": { "cgroup": { "path": "app/python" } } Here we have specified a relative path and thus the python application will be placed below the existing cgroup that contains the main unit process. E.g │ │ │ ├─app-glib-cinnamon\x2dcustom\x2dlauncher\x2d3-43951.scope (#90951) │ │ │ │ ├─ 988 unit: main v1.28.0 [/opt/unit/sbin/unitd --no-daemon] │ │ │ │ ├─ 990 unit: controller │ │ │ │ ├─ 991 unit: router │ │ │ │ ├─ 43951 xterm -bg rgb:20/20/20 -fg white -fa DejaVu Sans Mono │ │ │ │ ├─ 43956 bash │ │ │ │ ├─ 58828 sudo -i │ │ │ │ ├─ 58831 -bash │ │ │ │ └─app (#107351) │ │ │ │ └─python (#107367) │ │ │ │ ├─ 992 unit: "python" prototype │ │ │ │ ├─ 993 unit: "python" application │ │ │ │ ├─ 994 unit: "python" application │ │ │ │ ├─ 995 unit: "python" application │ │ │ │ ├─ 996 unit: "python" application │ │ │ │ └─ 997 unit: "python" application [0]: Reviewed-by: Alejandro Colomar Signed-off-by: Andrew Clayton --- docs/changes.xml | 6 +++++ src/nxt_conf_validation.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/docs/changes.xml b/docs/changes.xml index e2444a8e..3a24f9ca 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -57,6 +57,12 @@ prefer system crypto policy, instead of hardcoding a default. + + +support per-application cgroups on Linux. + + + compatibility with Python 3.11. diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index e650b44d..0f22c540 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -219,6 +219,11 @@ static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); #endif +#if (NXT_HAVE_CGROUP) +static nxt_int_t nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +#endif + static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[]; @@ -240,6 +245,9 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[]; static nxt_conf_vldt_object_t nxt_conf_vldt_app_namespaces_members[]; +#if (NXT_HAVE_CGROUP) +static nxt_conf_vldt_object_t nxt_conf_vldt_app_cgroup_members[]; +#endif #if (NXT_HAVE_ISOLATION_ROOTFS) static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[]; #endif @@ -1094,6 +1102,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { }, #endif +#if (NXT_HAVE_CGROUP) + { + .name = nxt_string("cgroup"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_app_cgroup_members, + }, +#endif + NXT_CONF_VLDT_END }; @@ -1166,6 +1183,22 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_automount_members[] = { #endif +#if (NXT_HAVE_CGROUP) + +static nxt_conf_vldt_object_t nxt_conf_vldt_app_cgroup_members[] = { + { + .name = nxt_string("path"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + .validator = nxt_conf_vldt_cgroup_path, + }, + + NXT_CONF_VLDT_END +}; + +#endif + + #if (NXT_HAVE_CLONE_NEWUSER) static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { @@ -2798,6 +2831,35 @@ nxt_conf_vldt_target(nxt_conf_validation_t *vldt, nxt_str_t *name, } +#if (NXT_HAVE_CGROUP) + +static nxt_int_t +nxt_conf_vldt_cgroup_path(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + char path[NXT_MAX_PATH_LEN]; + nxt_str_t cgpath; + + nxt_conf_get_string(value, &cgpath); + if (cgpath.length >= NXT_MAX_PATH_LEN - strlen(NXT_CGROUP_ROOT) - 1) { + return nxt_conf_vldt_error(vldt, "The cgroup path \"%V\" is too long.", + &cgpath); + } + + sprintf(path, "/%*s/", (int) cgpath.length, cgpath.start); + + if (cgpath.length == 0 || strstr(path, "/../") != NULL) { + return nxt_conf_vldt_error(vldt, + "The cgroup path \"%V\" is invalid.", + &cgpath); + } + + return NXT_OK; +} + +#endif + + static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) -- cgit