You need to enable JavaScript to run this app.
导航

Grafana监控看板搭建

最近更新时间2023.12.20 19:13:38

首次发布时间2023.02.15 19:04:52

以下以边缘计算节点服务为例,展示如何通过边缘函数搭建监控看板,实现对CPU使用率、网络丢包率及出入向带宽等信息的数据监控。

前提条件

  1. 注册火山引擎账号并完成账号实名认证,如果未注册,请先完成账号注册实名认证
  2. 拥有一个域名,您可以选择在火山引擎或其他云厂商购买域名。购买域名方式请参考:域名服务

配置Grafana数据源

  1. 登录本地Grafana,安装JSON API数据源插件。
  2. 在JSON API数据源中,将URL配置为您将要使用的域名,打开“Basic auth”,并配置相应的账号和密码,用于边缘函数对Grafana的请求做鉴权。

创建并发布边缘函数

添加域名

相关操作,请参见添加域名

创建函数

相关操作,请参见创建函数

为函数关联域名

相关操作,请参见为函数关联域名

开发及测试函数

  1. 登录边缘函数控制台
  2. 函数列表页面,找到目标函数,单击函数名称,进入函数详情页面。
  3. 函数详情页面,单击页面右上角的编辑代码,即可进入代码开发页面(默认示例是HelloWorld代码)。
  4. 将示例代码粘贴到代码编辑框,在代码中找到以下四个变量,并修改。“accessKeyId”和“secretKey”是火山密钥,可在火山引擎控制台密钥管理中获取,“grafanaAccount”和“grafanaKey”是第一步中数据源配置的账号和密码,修改之后,单击保存

变量

var grafanaAccount = "xxx";
var grafanaKey = "xxx";
var accessKeyId = "xxxx";
var secretKey = "xxxx==";

示例代码

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// node_modules/dayjs/dayjs.min.js
var require_dayjs_min = __commonJS({
  "node_modules/dayjs/dayjs.min.js"(exports, module) {
    !function(t, e) {
      "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
    }(exports, function() {
      "use strict";
      var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", f = "month", h = "quarter", c = "year", d = "date", $ = "Invalid Date", l = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_") }, m = function(t2, e2, n2) {
        var r2 = String(t2);
        return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2;
      }, g = { s: m, z: function(t2) {
        var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60;
        return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0");
      }, m: function t2(e2, n2) {
        if (e2.date() < n2.date())
          return -t2(n2, e2);
        var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, f), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), f);
        return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0);
      }, a: function(t2) {
        return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2);
      }, p: function(t2) {
        return { M: f, y: c, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: h }[t2] || String(t2 || "").toLowerCase().replace(/s$/, "");
      }, u: function(t2) {
        return void 0 === t2;
      } }, v = "en", D = {};
      D[v] = M;
      var p = function(t2) {
        return t2 instanceof _;
      }, S = function t2(e2, n2, r2) {
        var i2;
        if (!e2)
          return v;
        if ("string" == typeof e2) {
          var s2 = e2.toLowerCase();
          D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2);
          var u2 = e2.split("-");
          if (!i2 && u2.length > 1)
            return t2(u2[0]);
        } else {
          var a2 = e2.name;
          D[a2] = e2, i2 = a2;
        }
        return !r2 && i2 && (v = i2), i2 || !r2 && v;
      }, w = function(t2, e2) {
        if (p(t2))
          return t2.clone();
        var n2 = "object" == typeof e2 ? e2 : {};
        return n2.date = t2, n2.args = arguments, new _(n2);
      }, O = g;
      O.l = S, O.i = p, O.w = function(t2, e2) {
        return w(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset });
      };
      var _ = function() {
        function M2(t2) {
          this.$L = S(t2.locale, null, true), this.parse(t2);
        }
        var m2 = M2.prototype;
        return m2.parse = function(t2) {
          this.$d = function(t3) {
            var e2 = t3.date, n2 = t3.utc;
            if (null === e2)
              return new Date(NaN);
            if (O.u(e2))
              return new Date();
            if (e2 instanceof Date)
              return new Date(e2);
            if ("string" == typeof e2 && !/Z$/i.test(e2)) {
              var r2 = e2.match(l);
              if (r2) {
                var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3);
                return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2);
              }
            }
            return new Date(e2);
          }(t2), this.$x = t2.x || {}, this.init();
        }, m2.init = function() {
          var t2 = this.$d;
          this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds();
        }, m2.$utils = function() {
          return O;
        }, m2.isValid = function() {
          return !(this.$d.toString() === $);
        }, m2.isSame = function(t2, e2) {
          var n2 = w(t2);
          return this.startOf(e2) <= n2 && n2 <= this.endOf(e2);
        }, m2.isAfter = function(t2, e2) {
          return w(t2) < this.startOf(e2);
        }, m2.isBefore = function(t2, e2) {
          return this.endOf(e2) < w(t2);
        }, m2.$g = function(t2, e2, n2) {
          return O.u(t2) ? this[e2] : this.set(n2, t2);
        }, m2.unix = function() {
          return Math.floor(this.valueOf() / 1e3);
        }, m2.valueOf = function() {
          return this.$d.getTime();
        }, m2.startOf = function(t2, e2) {
          var n2 = this, r2 = !!O.u(e2) || e2, h2 = O.p(t2), $2 = function(t3, e3) {
            var i2 = O.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2);
            return r2 ? i2 : i2.endOf(a);
          }, l2 = function(t3, e3) {
            return O.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2);
          }, y2 = this.$W, M3 = this.$M, m3 = this.$D, g2 = "set" + (this.$u ? "UTC" : "");
          switch (h2) {
            case c:
              return r2 ? $2(1, 0) : $2(31, 11);
            case f:
              return r2 ? $2(1, M3) : $2(0, M3 + 1);
            case o:
              var v2 = this.$locale().weekStart || 0, D2 = (y2 < v2 ? y2 + 7 : y2) - v2;
              return $2(r2 ? m3 - D2 : m3 + (6 - D2), M3);
            case a:
            case d:
              return l2(g2 + "Hours", 0);
            case u:
              return l2(g2 + "Minutes", 1);
            case s:
              return l2(g2 + "Seconds", 2);
            case i:
              return l2(g2 + "Milliseconds", 3);
            default:
              return this.clone();
          }
        }, m2.endOf = function(t2) {
          return this.startOf(t2, false);
        }, m2.$set = function(t2, e2) {
          var n2, o2 = O.p(t2), h2 = "set" + (this.$u ? "UTC" : ""), $2 = (n2 = {}, n2[a] = h2 + "Date", n2[d] = h2 + "Date", n2[f] = h2 + "Month", n2[c] = h2 + "FullYear", n2[u] = h2 + "Hours", n2[s] = h2 + "Minutes", n2[i] = h2 + "Seconds", n2[r] = h2 + "Milliseconds", n2)[o2], l2 = o2 === a ? this.$D + (e2 - this.$W) : e2;
          if (o2 === f || o2 === c) {
            var y2 = this.clone().set(d, 1);
            y2.$d[$2](l2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d;
          } else
            $2 && this.$d[$2](l2);
          return this.init(), this;
        }, m2.set = function(t2, e2) {
          return this.clone().$set(t2, e2);
        }, m2.get = function(t2) {
          return this[O.p(t2)]();
        }, m2.add = function(r2, h2) {
          var d2, $2 = this;
          r2 = Number(r2);
          var l2 = O.p(h2), y2 = function(t2) {
            var e2 = w($2);
            return O.w(e2.date(e2.date() + Math.round(t2 * r2)), $2);
          };
          if (l2 === f)
            return this.set(f, this.$M + r2);
          if (l2 === c)
            return this.set(c, this.$y + r2);
          if (l2 === a)
            return y2(1);
          if (l2 === o)
            return y2(7);
          var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[l2] || 1, m3 = this.$d.getTime() + r2 * M3;
          return O.w(m3, this);
        }, m2.subtract = function(t2, e2) {
          return this.add(-1 * t2, e2);
        }, m2.format = function(t2) {
          var e2 = this, n2 = this.$locale();
          if (!this.isValid())
            return n2.invalidDate || $;
          var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = O.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, f2 = n2.months, h2 = function(t3, n3, i3, s3) {
            return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3);
          }, c2 = function(t3) {
            return O.s(s2 % 12 || 12, t3, "0");
          }, d2 = n2.meridiem || function(t3, e3, n3) {
            var r3 = t3 < 12 ? "AM" : "PM";
            return n3 ? r3.toLowerCase() : r3;
          }, l2 = { YY: String(this.$y).slice(-2), YYYY: this.$y, M: a2 + 1, MM: O.s(a2 + 1, 2, "0"), MMM: h2(n2.monthsShort, a2, f2, 3), MMMM: h2(f2, a2), D: this.$D, DD: O.s(this.$D, 2, "0"), d: String(this.$W), dd: h2(n2.weekdaysMin, this.$W, o2, 2), ddd: h2(n2.weekdaysShort, this.$W, o2, 3), dddd: o2[this.$W], H: String(s2), HH: O.s(s2, 2, "0"), h: c2(1), hh: c2(2), a: d2(s2, u2, true), A: d2(s2, u2, false), m: String(u2), mm: O.s(u2, 2, "0"), s: String(this.$s), ss: O.s(this.$s, 2, "0"), SSS: O.s(this.$ms, 3, "0"), Z: i2 };
          return r2.replace(y, function(t3, e3) {
            return e3 || l2[t3] || i2.replace(":", "");
          });
        }, m2.utcOffset = function() {
          return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
        }, m2.diff = function(r2, d2, $2) {
          var l2, y2 = O.p(d2), M3 = w(r2), m3 = (M3.utcOffset() - this.utcOffset()) * e, g2 = this - M3, v2 = O.m(this, M3);
          return v2 = (l2 = {}, l2[c] = v2 / 12, l2[f] = v2, l2[h] = v2 / 3, l2[o] = (g2 - m3) / 6048e5, l2[a] = (g2 - m3) / 864e5, l2[u] = g2 / n, l2[s] = g2 / e, l2[i] = g2 / t, l2)[y2] || g2, $2 ? v2 : O.a(v2);
        }, m2.daysInMonth = function() {
          return this.endOf(f).$D;
        }, m2.$locale = function() {
          return D[this.$L];
        }, m2.locale = function(t2, e2) {
          if (!t2)
            return this.$L;
          var n2 = this.clone(), r2 = S(t2, e2, true);
          return r2 && (n2.$L = r2), n2;
        }, m2.clone = function() {
          return O.w(this.$d, this);
        }, m2.toDate = function() {
          return new Date(this.valueOf());
        }, m2.toJSON = function() {
          return this.isValid() ? this.toISOString() : null;
        }, m2.toISOString = function() {
          return this.$d.toISOString();
        }, m2.toString = function() {
          return this.$d.toUTCString();
        }, M2;
      }(), T = _.prototype;
      return w.prototype = T, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", f], ["$y", c], ["$D", d]].forEach(function(t2) {
        T[t2[1]] = function(e2) {
          return this.$g(e2, t2[0], t2[1]);
        };
      }), w.extend = function(t2, e2) {
        return t2.$i || (t2(e2, _, w), t2.$i = true), w;
      }, w.locale = S, w.isDayjs = p, w.unix = function(t2) {
        return w(1e3 * t2);
      }, w.en = D[v], w.Ls = D, w.p = {}, w;
    });
  }
});

// node_modules/auth/src/sign.ts
var import_dayjs = __toESM(require_dayjs_min());
var util = {
  crypto: {
    hmac: async function hmac(rawKey, content) {
      var algorithm = {
        name: "HMAC",
        hash: "SHA-256"
      };
      var key = await crypto.subtle.importKey(
        "raw",
        rawKey,
        algorithm,
        false,
        ["sign", "verify"]
      );
      return await crypto.subtle.sign("HMAC", key, content);
    },
    sha256: async function sha256(data) {
      return await crypto.subtle.digest("SHA-256", data);
    }
  }
};
var unsignableHeaders = [
  "authorization",
  "content-type",
  "content-length",
  "user-agent",
  "presigned-expires",
  "expect"
];
var constant = {
  algorithm: "HMAC-SHA256",
  v4Identifier: "request",
  dateHeader: "X-Date",
  tokenHeader: "x-security-token",
  contentSha256Header: "X-Content-Sha256",
  kDatePrefix: ""
};
var uriEscape = (str) => {
  try {
    return encodeURIComponent(str).replace(/[^A-Za-z0-9_.~\-%]+/g, escape).replace(
      /[*]/g,
      (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`
    );
  } catch (e) {
    return "";
  }
};
var queryParamsToString = (params) => Object.keys(params).map((key) => {
  const val = params[key];
  if (typeof val === "undefined" || val === null) {
    return;
  }
  const escapedKey = uriEscape(key);
  if (!escapedKey) {
    return;
  }
  if (Array.isArray(val)) {
    return `${escapedKey}=${val.map(uriEscape).sort().join(`&${escapedKey}=`)}`;
  }
  return `${escapedKey}=${uriEscape(val)}`;
}).filter((v) => v).join("&");
var Signer = class {
  constructor(request, serviceName, options) {
    this.request = request;
    this.request.headers = request.headers || {};
    this.serviceName = serviceName;
    options = options || {};
    this.bodySha256 = options.bodySha256;
    this.request.params = this.sortParams(this.request.params);
  }
  sortParams(params) {
    const newParams = {};
    if (params) {
      Object.keys(params).sort().map((key) => {
        newParams[key] = params[key];
      });
    }
    return newParams;
  }
  async addAuthorization(credentials, date) {
    const datetime = this.iso8601(date).replace(/[:\-]|\.\d{3}/g, "");
    await this.addHeaders(credentials, datetime);
    this.request.headers["Authorization"] = await this.authorization(credentials, datetime);
  }
  async addHeaders(credentials, datetime) {
    this.request.headers[constant.dateHeader] = datetime;
    if (credentials.sessionToken) {
      this.request.headers[constant.tokenHeader] = credentials.sessionToken;
    }
    if (this.request.body) {
      let enc = new TextEncoder();
      let body = this.request.body;
      if (typeof body !== "string") {
        if (body instanceof URLSearchParams) {
          body = enc.encode(body.toString());
        } else if (body instanceof Blob) {
          body = await body.arrayBuffer();
        } else if (body instanceof FormData) {
          throw new Error("unsupport FormData");
        } else {
          body = enc.encode(JSON.stringify(body));
        }
      }
      this.request.headers[constant.contentSha256Header] = this.bodySha256 || this.buf2hex(await util.crypto.sha256(body));
    }
  }
  async authorization(credentials, datetime) {
    const parts = [];
    const credString = this.credentialString(datetime);
    parts.push(`${constant.algorithm} Credential=${credentials.accessKeyId}/${credString}`);
    parts.push(`SignedHeaders=${this.signedHeaders()}`);
    const signature = await this.signature(credentials, datetime);
    parts.push(`Signature=${this.buf2hex(signature)}`);
    return parts.join(", ");
  }
  async signature(credentials, datetime) {
    let enc = new TextEncoder();
    const signingKey = await this.getSigningKey(
      credentials,
      datetime.substr(0, 8),
      this.request.region,
      this.serviceName
    );
    return await util.crypto.hmac(
      signingKey,
      enc.encode(await this.stringToSign(datetime))
    );
  }
  async stringToSign(datetime) {
    const parts = [];
    parts.push(constant.algorithm);
    parts.push(datetime);
    parts.push(this.credentialString(datetime));
    parts.push(
      (await this.hexEncodedHash(await this.canonicalString())).toString()
    );
    return parts.join("\n");
  }
  async canonicalString() {
    const parts = [], pathname = this.request.pathname || "/";
    parts.push(this.request.method.toUpperCase());
    parts.push(pathname);
    const queryString = queryParamsToString(this.request.params) || "";
    parts.push(queryString);
    parts.push(`${this.canonicalHeaders()}
`);
    parts.push(this.signedHeaders());
    parts.push(await this.hexEncodedBodyHash());
    return parts.join("\n");
  }
  canonicalHeaders() {
    const headers = [];
    Object.keys(this.request.headers).forEach((key) => {
      headers.push([key, this.request.headers[key]]);
    });
    headers.sort((a, b) => a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1);
    const parts = [];
    headers.forEach((item) => {
      const key = item[0].toLowerCase();
      if (this.isSignableHeader(key)) {
        const value = item[1];
        if (typeof value === "undefined" || value === null || typeof value.toString !== "function") {
          throw new Error(`Header ${key} contains invalid value`);
        }
        parts.push(`${key}:${this.canonicalHeaderValues(value.toString())}`);
      }
    });
    return parts.join("\n");
  }
  canonicalHeaderValues(values) {
    return values.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
  }
  signedHeaders() {
    const keys = [];
    Object.keys(this.request.headers).forEach((key) => {
      key = key.toLowerCase();
      if (this.isSignableHeader(key)) {
        keys.push(key);
      }
    });
    return keys.sort().join(";");
  }
  signedQueries() {
    return Object.keys(this.request.params).join(";");
  }
  credentialString(datetime) {
    return this.createScope(
      datetime.substr(0, 8),
      this.request.region,
      this.serviceName
    );
  }
  buf2hex(buffer) {
    return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, "0")).join("");
  }
  async hexEncodedHash(str) {
    let enc = new TextEncoder();
    let arraybuffer = await util.crypto.sha256(enc.encode(str));
    return this.buf2hex(arraybuffer);
  }
  async hexEncodedBodyHash() {
    if (this.request.headers[constant.contentSha256Header]) {
      return this.request.headers[constant.contentSha256Header];
    }
    if (this.request.body) {
      return await this.hexEncodedHash(queryParamsToString(this.request.body));
    }
    return await this.hexEncodedHash("");
  }
  isSignableHeader(key) {
    return unsignableHeaders.indexOf(key) < 0;
  }
  iso8601(date) {
    if (date === void 0) {
      date = Date.now();
    }
    let time = (0, import_dayjs.default)(date);
    return time.toISOString().replace(/\.\d{3}Z$/, "Z");
  }
  async getSigningKey(credentials, date, region, service) {
    let enc = new TextEncoder();
    const kDate = await util.crypto.hmac(
      enc.encode(`${constant.kDatePrefix}${credentials.secretKey}`),
      enc.encode(date)
    );
    const kRegion = await util.crypto.hmac(kDate, enc.encode(region));
    const kService = await util.crypto.hmac(kRegion, enc.encode(service));
    const signingKey = await util.crypto.hmac(kService, enc.encode(constant.v4Identifier));
    return signingKey;
  }
  createScope(date, region, serviceName) {
    return [date.substr(0, 8), region, serviceName, constant.v4Identifier].join(
      "/"
    );
  }
};

// index_adaptor.js
var grafanaAccount = "xxxx";
var grafanaKey = "xxxx";
var accessKeyId = "xxxx";
var secretKey = "xxxx==";
function constructRequest(action) {
  return {
    region: "cn-north-1",
    method: "GET",
    params: {
      "Version": "2021-04-30",
      "Action": action
    },
    pathname: "/",
    headers: {},
    body: ""
  };
}
async function getInstancesDetail(ids) {
  let requestObj = constructRequest("ListInstances");
  requestObj.params["instance_identity"] = ids.join(",");
  let resp = await metrixQuery(requestObj);
  let json = await resp.json();
  let result = {};
  for (const inst of json["Result"]["instances"]) {
    result[inst["instance_identity"]] = inst;
  }
  return result;
}
async function onIspVariable(args) {
  let requestObj = constructRequest("ListInstances");
  let resp = await metrixQuery(requestObj);
  let json = await resp.json();
  let isps = [];
  for (const inst of json["Result"]["instances"]) {
    isps.push(inst["cluster"]["isp"]);
  }
  isps = [...new Set(isps)];
  let result = [];
  for (const isp of isps) {
    result.push({
      "text": isp,
      "value": isp
    });
  }
  return result;
}
async function onClusterVariable(args) {
  let requestObj = constructRequest("ListInstances");
  if (args["isps"]) {
    requestObj.params["isps"] = args["isps"].join(",");
  }
  let resp = await metrixQuery(requestObj);
  let json = await resp.json();
  let clusters = [];
  for (const inst of json["Result"]["instances"]) {
    clusters.push(inst["cluster"]["cluster_name"]);
  }
  clusters = [...new Set(clusters)];
  let renameCluster = function(name) {
    const prefix = "bdcdn";
    if (name.startsWith(prefix)) {
      return "veen" + name.slice(prefix.length);
    }
    return name;
  };
  let result = [];
  for (const cluster of clusters) {
    result.push({
      "text": renameCluster(cluster),
      "value": cluster
    });
  }
  return result;
}
async function onInstanceVariable(args) {
  let requestObj = constructRequest("ListInstances");
  if (args["isps"]) {
    requestObj.params["isps"] = args["isps"].join(",");
  }
  if (args["clusters"]) {
    requestObj.params["clusters"] = args["clusters"].join(",");
  }
  let resp = await metrixQuery(requestObj);
  let json = await resp.json();
  let result = [];
  for (const inst of json["Result"]["instances"]) {
    result.push({
      "text": inst["instance_identity"],
      "value": inst["instance_identity"]
    });
  }
  return result;
}
async function onNetQuery(requestObj, args) {
  const vars = args["scopedVars"];
  if (vars === void 0) {
    throw Error("invalid format");
  }
  let instances = [];
  if (vars["InstanceId"]) {
    for (const inst of vars["InstanceId"]["value"]) {
      instances.push(...inst.split(","));
    }
  }
  let clusters = [];
  if (vars["RegionId"]) {
    clusters = vars["RegionId"]["value"];
  }
  const mode = args["targets"][0]["data"]["mode"] || "top";
  const type = args["targets"][0]["data"]["type"] || "rx";
  const idx = type == "rx" ? 0 : 1;
  let rateConverter = function(unit, value) {
    let result = value;
    if (unit > 2) {
      result = value * ((unit - 2) * 1e3);
    }
    if (unit < 2) {
      result = value / ((2 - unit) * 1e3);
    }
    return result;
  };
  let aggHandle = async function(_) {
    requestObj.params["instances"] = instances.join(",");
    if (vars["ISP"]) {
      requestObj.params["isps"] = vars["ISP"]["value"].join(",");
    }
    if (vars["RegionId"]) {
      requestObj.params["clusters"] = vars["RegionId"]["value"].join(",");
    }
    let resp = await metrixQuery(requestObj);
    let j = await resp.json();
    let result = [];
    if (type == "all") {
      result.push({
        "target": "rx",
        "datapoints": j["Result"][0]["data"].map(
          (x) => [rateConverter(j["Result"][0]["unit"], x.value), x.timestamp]
        )
      });
      result.push({
        "target": "tx",
        "datapoints": j["Result"][1]["data"].map(
          (x) => [rateConverter(j["Result"][1]["unit"], x.value), x.timestamp]
        )
      });
    } else if (type == "rx") {
      result.push({
        "target": "rx",
        "datapoints": j["Result"][0]["data"].map(
          (x) => [rateConverter(j["Result"][0]["unit"], x.value), x.timestamp]
        )
      });
    } else if (type == "tx") {
      result.push({
        "target": "tx",
        "datapoints": j["Result"][1]["data"].map(
          (x) => [rateConverter(j["Result"][1]["unit"], x.value), x.timestamp]
        )
      });
    }
    return result;
  };
  let topHandle = async function(topk) {
    requestObj.params["Action"] = "QueryCloudInstanceMetricNonAggregate";
    let instancesDetail = await getInstancesDetail(instances);
    let getInstanceDetailName = function(id) {
      let result2 = instancesDetail[id]["cluster"]["alias"];
      result2 += " id: " + id;
      return result2;
    };
    let closures = [];
    const groupInst = sliceArray(instances, 20);
    for (const insts of groupInst) {
      requestObj.params["instances"] = insts.join(",");
      let obj = JSON.parse(JSON.stringify(requestObj));
      closures.push(metrixQuery.bind(null, obj));
    }
    let resps = await batch(closures, 10, 3, (resp) => {
      return resp.ok;
    });
    let result = [];
    for (let i = 0; i < resps.length; ++i) {
      let j = await resps[i].json();
      if (j["Result"] === void 0 || j["Result"].length < 2) {
        continue;
      }
      for (let n = 0; n < j["Result"].length - 1; ) {
        if (j["Result"][n]["metrics"] === void 0) {
          ++n;
          continue;
        }
        if (j["Result"][n]["metrics"]["name"] === void 0 || j["Result"][n + 1]["metrics"]["name"] === void 0 || j["Result"][n]["metrics"]["name"] != j["Result"][n + 1]["metrics"]["name"]) {
          ++n;
          continue;
        }
        result.push({
          "target": getInstanceDetailName(j["Result"][n + idx]["metrics"]["name"]),
          "max": j["Result"][n + idx]["max_value"],
          "datapoints": j["Result"][n + idx]["data"].map(
            (x) => [
              rateConverter(j["Result"][n + idx]["unit"], x.value),
              x.timestamp
            ]
          )
        });
        n += 2;
      }
    }
    result.sort(function(lhs, rhs) {
      return lhs.max - rhs.max;
    });
    return result.slice(-topk);
  };
  let nodeTopHandle = async function(topk) {
    let clusterMapAlias = {};
    const instancesDetail = await getInstancesDetail(instances);
    for (const item of Object.values(instancesDetail)) {
      clusterMapAlias[item["cluster"]["cluster_name"]] = item["cluster"]["alias"];
    }
    let getClusterDetailName = function(clusterId) {
      let result2 = clusterMapAlias[clusterId];
      result2 += " id: " + clusterId;
      return result2;
    };
    let closures = [];
    for (const cluster of clusters) {
      requestObj.params["clusters"] = cluster;
      let obj = JSON.parse(JSON.stringify(requestObj));
      closures.push(metrixQuery.bind(null, obj));
    }
    let resps = await batch(closures, 10, 3, (resp) => {
      return resp.ok;
    });
    let result = [];
    for (let i = 0; i < resps.length; ++i) {
      let j = await resps[i].json();
      if (j["Result"] === void 0 || j["Result"].length != 2) {
        continue;
      }
      result.push({
        "target": getClusterDetailName(clusters[i]),
        "max": j["Result"][idx]["max_value"],
        "datapoints": j["Result"][idx]["data"].map(
          (x) => [
            rateConverter(j["Result"][idx]["unit"], x.value),
            x.timestamp
          ]
        )
      });
    }
    result.sort(function(lhs, rhs) {
      return lhs.max - rhs.max;
    });
    return result.slice(-topk);
  };
  if (mode == "agg") {
    return aggHandle();
  } else if (mode == "top") {
    let topk = parseInt(vars["TopN"]["value"]);
    if (isNaN(topk)) {
      topk = 30;
    }
    return topHandle(topk);
  } else if (mode == "node-top") {
    let topk = parseInt(vars["TopN"]["value"]);
    if (isNaN(topk)) {
      topk = 30;
    }
    return nodeTopHandle(topk);
  }
  throw Error("unsupported network query");
}
async function batch(closures, batchSize, retryTimes, validate) {
  let results = [];
  let count = 0;
  const rounds = Math.ceil(closures.length / batchSize);
  for (let i = 0; i < rounds; ++i) {
    while (retryTimes >= 0) {
      let arr = [];
      try {
        for (let j = 0; j < batchSize && count < closures.length; ++j, ++count) {
          arr.push(closures[count]());
        }
        let success = [];
        let res = await Promise.all(arr);
        for (const entry of res) {
          if (validate(entry)) {
            success.push(entry);
          }
        }
        if (success.length == arr.length) {
          results.push(...success);
          break;
        }
      } catch (e) {
      }
      --retryTimes;
      count -= arr.length;
    }
    if (retryTimes < 0) {
      throw new Error("batch failed");
    }
  }
  return results;
}
function sliceArray(arr, size) {
  var result = [];
  for (var i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size));
  }
  return result;
}
async function onDropQuery(requestObj, args) {
  const vars = args["scopedVars"];
  if (vars === void 0) {
    throw Error("invalid format");
  }
  let instances = [];
  if (vars["InstanceId"]) {
    for (const inst of vars["InstanceId"]["value"]) {
      instances.push(...inst.split(","));
    }
  }
  const mode = args["targets"][0]["data"]["mode"] || "top";
  let aggHandle = async function() {
    requestObj.params["instances"] = instances.join(",");
    if (vars["ISP"]) {
      requestObj.params["isps"] = vars["ISP"]["value"].join(",");
    }
    if (vars["RegionId"]) {
      requestObj.params["clusters"] = vars["RegionId"]["value"].join(",");
    }
    let resp = await metrixQuery(requestObj);
    let j = await resp.json();
    let result = [];
    result.push({
      "target": "drop",
      "datapoints": j["Result"][0]["data"].map((x) => [x.value, x.timestamp])
    });
    return result;
  };
  let topHandle = async function(topk) {
    requestObj.params["Action"] = "QueryCloudInstanceMetricNonAggregate";
    let instancesDetail = await getInstancesDetail(instances);
    let getInstanceDetailName = function(id) {
      let result2 = instancesDetail[id]["cluster"]["alias"];
      result2 += " id: " + id;
      return result2;
    };
    let closures = [];
    const groupInst = sliceArray(instances, 50);
    for (const insts of groupInst) {
      requestObj.params["instances"] = insts.join(",");
      let obj = JSON.parse(JSON.stringify(requestObj));
      closures.push(metrixQuery.bind(null, obj));
    }
    let resps = await batch(closures, 10, 3, (resp) => {
      return resp.ok;
    });
    let result = [];
    for (let i = 0; i < resps.length; ++i) {
      let j = await resps[i].json();
      if (j["Result"] === void 0) {
        continue;
      }
      for (let n = 0; n < j["Result"].length; ++n) {
        if (j["Result"][n]["metrics"] === void 0 || j["Result"][n]["metrics"]["name"] === void 0) {
          continue;
        }
        result.push({
          "target": getInstanceDetailName(j["Result"][n]["metrics"]["name"]),
          "max": j["Result"][n]["max_value"],
          "datapoints": j["Result"][n]["data"].map((x) => [x.value, x.timestamp])
        });
      }
    }
    result.sort(function(lhs, rhs) {
      return lhs.max - rhs.max;
    });
    return result.slice(-topk);
  };
  if (mode == "agg") {
    return aggHandle();
  } else if (mode == "top") {
    let topk = parseInt(vars["TopN"]["value"]);
    if (isNaN(topk)) {
      topk = 30;
    }
    return topHandle(topk);
  }
  throw Error("unsupported drop query");
}
async function onQuery(request) {
  let requestObj = constructRequest("QueryCloudInstanceMetric");
  const args = await request.json();
  const range = args["range"];
  if (range["from"] === void 0 || range["to"] === void 0) {
    throw Error("invalid format");
  }
  requestObj.params["end_time"] = Math.round(new Date(range["to"]).valueOf() / 1e3);
  requestObj.params["start_time"] = Math.round(new Date(range["from"]).valueOf() / 1e3);
  if (args["targets"] === void 0) {
    throw Error("invalid format");
  }
  const target = args["targets"][0];
  requestObj.params["type"] = target["target"];
  if (target["target"] == "network") {
    return onNetQuery(requestObj, args);
  } else if (target["target"] == "drop") {
    return onDropQuery(requestObj, args);
  } else if (target["target"] == "cpu") {
  }
  throw Error("unsupported query");
}
var metricList = ["network", "cpu", "drop"];
async function onSearch(request) {
  const args = await request.json();
  if (args["type"] == "timeseries") {
    return metricList;
  }
  const target = args["target"];
  try {
    const json = JSON.parse(target);
    if (json["type"] == "isp") {
      return onIspVariable(json);
    } else if (json["type"] == "cluster") {
      return onClusterVariable(json);
    } else if (json["type"] == "instance") {
      return onInstanceVariable(json);
    }
  } catch (e) {
    throw Error("invalid format");
  }
  throw Error("unsupported search type");
}
async function handle(event) {
  if (!AuthRequest(event.request.headers)) {
    return new Response("Unauthorized", {
      status: 401,
      statusText: "Unauthorized"
    });
  }
  try {
    let result = {};
    let path = event.request.path;
    if (path.endsWith("/search")) {
      result = await onSearch(event.request);
    } else if (path.endsWith("/query")) {
      result = await onQuery(event.request);
    } else {
      return new Response("OK");
    }
    return createResponse(result);
  } catch (e) {
    console.error(e.stack);
    return new Response(e.stack, {
      status: 500
    });
  }
}
function AuthRequest(headers) {
  let auth = headers.get("Authorization") || headers.get("authorization");
  if (auth == null) {
    return false;
  }
  return auth === `Basic ${btoa(grafanaAccount + ":" + grafanaKey)}`;
}
async function metrixQuery(requestObj) {
  let signer = new Signer(requestObj, "veenedge");
  let credentials = {
    accessKeyId,
    secretKey
  };
  await signer.addAuthorization(credentials, Date.now());
  let url = `http://veenedge.volcengineapi.com/?${queryParamsToString(requestObj.params)}`;
  let req = new Request(url, {
    method: requestObj.method,
    headers: requestObj.headers
  });
  return fetch(req);
}
function createResponse(json) {
  let headers = new Headers();
  headers.append("Content-Type", "application/json");
  return new Response(JSON.stringify(json), {
    status: 200,
    statusText: "OK",
    headers
  });
}
addEventListener("fetch", (event) => {
  event.respondWith(handle(event));
});

发布

  1. 编辑代码页面,单击发布
  2. (可选)在发布页面填写备注信息。
  3. 单击确定完成发布。

配置Grafana面板

  1. 登录本地Grafana,新建dashboard。
  2. 选择配置的JSON API数据源。
  3. 通过配置Grafana变量,完成CPU使用率、网络丢包率及出入向带宽监控面板的搭建。