diff --git a/CHANGES b/CHANGES index 834b37470..7a763e7a8 100644 --- a/CHANGES +++ b/CHANGES @@ -2516,4 +2516,5 @@ * Randomize certificate generation cronjob to lighten load on Let's Encrypt servers * mail: catch all address can be any domain * mail: accept only STARTTLS servers for relay +* graphs: cgroup v2 support diff --git a/src/collectd/app_cgroup_v1.ejs b/src/collectd/app_cgroup_v1.ejs index ab5d2ce49..544947d1a 100644 --- a/src/collectd/app_cgroup_v1.ejs +++ b/src/collectd/app_cgroup_v1.ejs @@ -1,34 +1,14 @@ LoadPlugin "table" - /memory.stat"> - Instance "<%= appId %>-memory" - Separator " \\n" - - Type gauge - InstancesFrom 0 - ValuesFrom 1 - -
- - /memory.max_usage_in_bytes"> +
/memory.memsw.usage_in_bytes"> Instance "<%= appId %>-memory" Separator "\\n" Type gauge - InstancePrefix "max_usage_in_bytes" + InstancePrefix "memsw_usage_in_bytes" ValuesFrom 0
- - /cpuacct.stat"> - Instance "<%= appId %>-cpu" - Separator " \\n" - - Type gauge - InstancesFrom 0 - ValuesFrom 1 - -
diff --git a/src/collectd/app_cgroup_v2.ejs b/src/collectd/app_cgroup_v2.ejs index fb821ff9d..b947850ac 100644 --- a/src/collectd/app_cgroup_v2.ejs +++ b/src/collectd/app_cgroup_v2.ejs @@ -1,32 +1,22 @@ LoadPlugin "table" - /memory.stat"> +
/memory.current"> Instance "<%= appId %>-memory" Separator " \\n" Type gauge - InstancesFrom 0 - ValuesFrom 1 - -
- - /memory.max"> - Instance "<%= appId %>-memory" - Separator "\\n" - - Type gauge - InstancePrefix "max_usage_in_bytes" + InstancePrefix "memory_current" ValuesFrom 0
- /cpu.stat"> - Instance "<%= appId %>-cpu" - Separator " \\n" +
/memory.swap.current"> + Instance "<%= appId %>-memory" + Separator "\\n" Type gauge - InstancesFrom 0 - ValuesFrom 1 + InstancePrefix "memory_swap_current" + ValuesFrom 0
diff --git a/src/graphs.js b/src/graphs.js index 632933a65..70295a082 100644 --- a/src/graphs.js +++ b/src/graphs.js @@ -6,12 +6,15 @@ exports = module.exports = { const assert = require('assert'), BoxError = require('./boxerror.js'), + fs = require('fs'), safe = require('safetydance'), - superagent = require('superagent'), - debug = require('debug')('box:graphs'); + superagent = require('superagent'); const GRAPHITE_RENDER_URL = 'http://127.0.0.1:8417/graphite-web/render'; +// https://rootlesscontaine.rs/getting-started/common/cgroup2/#checking-whether-cgroup-v2-is-already-enabled +const CGROUP_VERSION = fs.existsSync('/sys/fs/cgroup/cgroup.controllers') ? '2' : '1'; + async function getByAppId(appId, fromMinutes, noNullPoints) { assert.strictEqual(typeof appId, 'string'); assert.strictEqual(typeof fromMinutes, 'number'); @@ -19,14 +22,17 @@ async function getByAppId(appId, fromMinutes, noNullPoints) { const timeBucketSize = fromMinutes > (24 * 60) ? (6*60) : 5; - debug(`getByAppId: ${appId} from ${fromMinutes}`); - const memoryQuery = { - target: `summarize(sum(collectd.localhost.table-${appId}-memory.gauge-rss, collectd.localhost.table-${appId}-memory.gauge-swap), "${timeBucketSize}min", "avg")`, + target: null, // filled below format: 'json', from: `-${fromMinutes}min`, until: 'now' }; + if (CGROUP_VERSION === '1') { + memoryQuery.target = `summarize(collectd.localhost.table-${appId}-memory.gauge-memsw_usage_in_bytes, "${timeBucketSize}min", "avg")`; + } else { + memoryQuery.target = `summarize(sum(collectd.localhost.table-${appId}-memory.gauge-memory_current, collectd.localhost.table-${appId}-memory.gauge-memory_swap_current), "${timeBucketSize}min", "avg")`; + } if (noNullPoints) memoryQuery.noNullPoints = true; const [memoryError, memoryResponse] = await safe(superagent.get(GRAPHITE_RENDER_URL) diff --git a/src/infra_version.js b/src/infra_version.js index 543bc269d..8740c95fc 100644 --- a/src/infra_version.js +++ b/src/infra_version.js @@ -6,7 +6,7 @@ exports = module.exports = { // a version change recreates all containers with latest docker config - 'version': '49.0.0', + 'version': '49.1.0', 'baseImages': [ { repo: 'cloudron/base', tag: 'cloudron/base:3.2.0@sha256:ba1d566164a67c266782545ea9809dc611c4152e27686fd14060332dd88263ea' } diff --git a/src/routes/graphs.js b/src/routes/graphs.js index 59446c5e9..add00602a 100644 --- a/src/routes/graphs.js +++ b/src/routes/graphs.js @@ -7,11 +7,11 @@ exports = module.exports = { const assert = require('assert'), graphs = require('../graphs.js'), - safe = require('safetydance'), - middleware = require('../middleware/index.js'), - url = require('url'), HttpError = require('connect-lastmile').HttpError, - HttpSuccess = require('connect-lastmile').HttpSuccess; + HttpSuccess = require('connect-lastmile').HttpSuccess, + middleware = require('../middleware/index.js'), + safe = require('safetydance'), + url = require('url'); // for testing locally: curl 'http://127.0.0.1:8417/graphite-web/render?format=json&from=-1min&target=absolute(collectd.localhost.du-docker.capacity-usage)' // the datapoint is (value, timestamp) https://buildmedia.readthedocs.org/media/pdf/graphite/0.9.16/graphite.pdf @@ -46,7 +46,7 @@ async function getAppGraphs(req, res, next) { if (!req.query.fromMinutes || !parseInt(req.query.fromMinutes)) return next(new HttpError(400, 'fromMinutes must be a number')); - const fromMinutes = parseInt(req.query.fromMinutes) || 60; + const fromMinutes = parseInt(req.query.fromMinutes); const noNullPoints = !!req.query.noNullPoints; const [error, result] = await safe(graphs.getByAppId(req.app.id, fromMinutes, noNullPoints)); if (error) return next(new HttpError(500, error));