CubeObservability:CubeSandbox 沙箱可观测性部署
English | 简体中文
可观测性支持 one-click 与 Kubernetes 安装流程,默认关闭。通过 one-click 环境变量或 Helm Value 启用,不需要额外安装命令参数。
默认采集:
- 指标:Cubelet
127.0.0.1:9998/v1/metrics/resource暴露的cubesandbox_*沙箱资源指标。 - 日志:沙箱标准输出与标准错误,路径为
/data/cubelet/log/<sandbox-id>/{stdout,stderr}。
Helm 还可采集 /var/log/pods 下允许列表内的 CubeSandbox 容器 CRI 日志,由独立 Collector DaemonSet 负责;默认关闭,one-click 不支持。
两种安装方式都可部署 Prometheus、Grafana 和一个日志后端(默认 Loki,可选 Elasticsearch),也都支持把数据发往外部后端。
目录职责:
- Grafana 镜像与 Dashboard 源文件:
CubeObservability/; - one-click CVM/systemd:
deploy/one-click/observability/; - Helm Collector 与后端:
deploy/kubernetes/chart/。
1. 能力、版本与安装前自检
1.1 Cubelet 能力基线
本方案使用当前 Cubelet 提供的以下数据:
/v1/metrics/resource暴露的沙箱 CPU/Memory 指标。逐沙箱序列带sandbox_id,guest_workload序列还带container_id。cubesandbox_node_sandbox_count与cubesandbox_node_sandbox_available_count节点级 Gauge;无活跃沙箱时返回0。/data/cubelet/log/<sandbox-id>/{stdout,stderr}中的沙箱标准输出/标准错误日志。
完整指标名、类型、单位和标签以 CubeSandbox GitHub 沙箱资源指标文档 为准;其中 host_sandbox 序列包含 sandbox_id,guest_workload 序列包含 sandbox_id 与 container_id。需要核对实现时可查看 Cubelet Prometheus 源码。 节点计数 Gauge 不带动态标签,也不包含 Sandbox ID 列表。
安装前自检
Cubelet 安装完成后执行以下检查。全新 one-click 环境先按 2.1 完成第一台节点安装:
# Cubelet 版本与服务状态
/usr/local/services/cubetoolbox/Cubelet/bin/cubecli version
sudo systemctl is-active cube-sandbox-cubelet.service
# 沙箱资源指标接口与沙箱 ID 标签
curl -fsS http://127.0.0.1:9998/v1/metrics/resource | \
grep -E '^cubesandbox_(host_sandbox|guest_workload|node_sandbox_)'
curl -fsS http://127.0.0.1:9998/v1/metrics/resource | \
grep 'sandbox_id='
# 沙箱运行期间的 stdout/stderr 日志路径
SANDBOX_ID=019abc-example
ls -l "/data/cubelet/log/${SANDBOX_ID}"/{stdout,stderr}没有活跃沙箱时,sandbox_id 检查可以没有输出,两个 cubesandbox_node_sandbox_* Gauge 应返回 0。插件未启用或首次采样尚未完成时, 指标也可能为空,应结合插件配置和服务状态判断。
1.2 安装器版本与兼容基线
Loki 与 Elasticsearch 是二选一的沙箱日志后端:默认安装 Loki;仅在显式选择 CUBE_LOGS_BACKEND=elasticsearch(Helm 为 observability.logs.backend=elasticsearch)时选择 Elasticsearch。同一部署仅启用 其中一个日志后端,也不会双写。
| 组件 | 安装器固定并已验证版本 | 外部/自备组件兼容基线 | 默认端口 | 角色与兼容说明 |
|---|---|---|---|---|
| OTel Collector Contrib | 0.162.0 | 建议保持 0.162.x | 127.0.0.1:13133(健康检查) | 节点采集器。当前 YAML 使用该版本的组件名、OTLP/HTTP 与 Elasticsearch exporter 配置;不能笼统承诺任意更高版本兼容,升级后必须重新执行 otelcol validate。 |
| Prometheus | one-click 3.13.4;Helm v3.2.1 | 支持 OTLP receiver 与 Remote Write receiver 的 Prometheus 3.x | 9092 | 沙箱指标后端。必须支持 --web.enable-otlp-receiver、--web.enable-remote-write-receiver 和本文的 otlp 配置。 |
| Loki(日志后端二选一,默认) | 3.7.9 | Loki 3.x,且启用原生 OTLP 与 structured metadata | 3100 | 默认沙箱日志后端;Collector 写入 /otlp,Grafana 使用 LogQL 查询。 |
| Elasticsearch(日志后端二选一,可选) | 8.17.3 | Elasticsearch 8.x | 9200 | 显式选择后替代 Loki,使用 raw mapping 和 Bulk API;其他大版本需要单独验证。 |
| Grafana OSS | 10.4.18 | >= 10.4,并兼容 dashboard schema 39 | 3001 | 指标与日志展示。更高版本通常可导入,但升级前仍需验证所选日志数据源、三张指标看板和日志看板。 |
固定版本用于一键安装的可重复性;“兼容基线”只适用于自行维护的外部组件,不等于对 所有未来版本的无条件兼容承诺。
上游配置参考: Prometheus receiver、 filelog receiver、 Elasticsearch exporter、 Prometheus OTLP 接入和 Loki OTLP 接入。
CVM/systemd 内置安装器要求 64 位 Linux、systemd、curl、tar 和 python3; python3 用于安全解包并校验 Loki 发布包内容。Kubernetes / Helm 形态不需要节点 预装这些二进制。
1.3 Loki 还是 Elasticsearch
- Loki(默认):资源占用较少,适合快速落地及以标签筛选为主的日志查询。
- Elasticsearch(可选):适合已有 ES 运维体系或需要全文检索的团队。
- 两者二选一;生产环境优先复用团队已有或托管的日志平台。
新装时在 .env 中选择:
CUBE_LOGS_BACKEND=loki
# 或者:
# CUBE_LOGS_BACKEND=elasticsearch深入阅读:Loki 官方文档、 Loki OTLP ingestion、 Elasticsearch 日志文档和 Elasticsearch Query DSL。
2. 随标准部署启用
| 部署形态 | 入口 | 启用方式 |
|---|---|---|
| one-click 第一台节点(默认 all-in-one) | deploy/one-click/install.sh | 设置 ONE_CLICK_ENABLE_OBSERVABILITY=1 后正常执行安装 |
| one-click 计算节点 | deploy/one-click/install-compute.sh | 设置 ONE_CLICK_ENABLE_OBSERVABILITY=1 和第一台节点地址;仅安装节点 Collector |
| one-click 存量部署 | 原 install.sh / install-compute.sh | 修改原 .env 后以 --mode=upgrade --yes 执行,详见 2.1 C |
| Kubernetes Helm | deploy/kubernetes/chart | 安装时传 --set observability.enabled=true |
2.1 one-click
one-click 继续使用原有 .env 和安装入口,默认不开启可观测性。第一台节点默认是 控制面与计算节点合一的 all-in-one;后续计算节点把指标和日志发送到第一台节点, 不需要另建“监控控制面”。
A. 安装第一台 all-in-one 节点
- 进入源码的
deploy/one-click目录;如果使用 release 压缩包,则进入解压后的 one-click 根目录。仅在首次安装时复制模板:
# 源码仓库:
cd deploy/one-click
# 解压后的发布包:
# cd /path/to/cube-sandbox-one-click
[[ -f .env ]] || cp env.example .env
chmod 600 .env- 确认一张后续计算节点可访问的内网 IP,例如
10.0.0.10,然后在.env中设置:
# .env
CUBE_SANDBOX_NODE_IP=10.0.0.10
ONE_CLICK_ENABLE_OBSERVABILITY=1
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod
CUBE_LOGS_BACKEND=loki
CUBE_GRAFANA_ADMIN_PASSWORD='<随机且至少16字符的密码>'密码未设置或少于 16 个字符时安装会失败。选择 Elasticsearch 时只需把 CUBE_LOGS_BACKEND 改为 elasticsearch。如果 CUBE_SANDBOX_NODE_IP 留空,安装器会自动探测,但仍应在安装后确认结果。
- 执行原有安装命令:
sudo ./install.sh- 记录安装器最终使用的第一台节点 IP;后续计算节点填写的必须是这个可达内网 IP:
grep '^CUBE_SANDBOX_NODE_IP=' \
/usr/local/services/cubetoolbox/.one-click.env在安全组或防火墙中按最小范围放通:
- 计算节点到第一台节点:TCP
9092,以及 Loki 的3100或 Elasticsearch 的9200; - 管理员到第一台节点:Grafana
3001; - 不要把
9092/3100/9200直接暴露到公网。
- 计算节点到第一台节点:TCP
在第一台节点验收:
sudo /usr/local/services/cubetoolbox/observability/install.sh status
sudo systemctl --no-pager --full status cube-sandbox-otelcol.service
curl -fsS http://127.0.0.1:9092/-/ready
curl -fsS http://127.0.0.1:3100/ready
# 使用 Elasticsearch 时,将上一行替换为:
# curl -fsS http://127.0.0.1:9200/Grafana 地址是 http://10.0.0.10:3001。首次进入后确认 CubeSandbox sandbox resources 和 CubeSandbox sandbox logs 看板能够查询 当前节点;没有活跃沙箱时,逐沙箱序列和日志可以为空。
B. 安装后续计算节点
- 在每台计算节点准备同版本 one-click 包和
.env:
[[ -f .env ]] || cp env.example .env
chmod 600 .env- 在
.env中填写第一台节点 IP,并保持集群 ID 和日志后端与第一台节点一致:
# .env
ONE_CLICK_CONTROL_PLANE_IP=10.0.0.10
ONE_CLICK_ENABLE_OBSERVABILITY=1
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod
CUBE_LOGS_BACKEND=lokiONE_CLICK_CONTROL_PLANE_IP 就是第一台 CubeSandbox 节点的内网 IP,不是单独 新建的监控节点。install-compute.sh 会自动设置 ONE_CLICK_DEPLOY_ROLE=compute,无需在 .env 中重复配置。没有显式外部 endpoint 时,安装器会从该 IP 推导 Prometheus 和所选日志后端地址。
- 安装并检查本机 Collector:
sudo ./install-compute.sh
sudo systemctl --no-pager --full status cube-sandbox-otelcol.service
sudo journalctl -u cube-sandbox-otelcol.service -n 100 --no-pager- 创建一个测试沙箱后,在第一台节点的 Grafana 中按
node_id、sandbox_id查询; 如果没有数据,先检查本机127.0.0.1:9998、日志目录和到第一台节点所需端口。
C. 在 one-click 存量部署中启用
使用包含可观测性部署文件的 one-click 发布包,并继续维护原安装目录中的 .env; 不要用 env.example 覆盖现有配置,也不要直接编辑安装目录下生成的 .one-click.env。
- 在第一台节点的
.env中增加或更新:
ONE_CLICK_ENABLE_OBSERVABILITY=1
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod
CUBE_LOGS_BACKEND=loki
CUBE_GRAFANA_ADMIN_PASSWORD='<随机且至少16字符的密码>'- 使用原 one-click 入口执行升级:
sudo ./install.sh --mode=upgrade --yes- 第一台节点验收通过后,在每台计算节点的
.env中增加或更新:
ONE_CLICK_CONTROL_PLANE_IP=10.0.0.10
ONE_CLICK_ENABLE_OBSERVABILITY=1
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod
CUBE_LOGS_BACKEND=lokisudo ./install-compute.sh --mode=upgrade --yes将 10.0.0.10 替换为第一台节点的可达内网 IP,并确保所有节点使用相同的集群 ID 和日志后端。上述命令会按原 .env 协调 one-click 主组件和可观测性组件,执行前应按 现有运维流程备份配置。升级时,复制整份 env.example 带入的默认 ONE_CLICK_ENABLE_OBSERVABILITY=0 不会关闭已启用的监控;如需明确关闭,请使用 sudo env ONE_CLICK_ENABLE_OBSERVABILITY=0 ./install.sh --mode=upgrade --yes(计算节点使用 ./install-compute.sh)。
D. 在线安装
在线安装与内置 MinIO 使用相同的环境变量透传方式:
# 第一台 all-in-one 节点。内置 Grafana 要求至少 16 位的管理员密码,
# 请把生成的密码保存到密码管理器中。
GRAFANA_ADMIN_PASSWORD="$(openssl rand -hex 16)"
echo "Grafana admin password: ${GRAFANA_ADMIN_PASSWORD}"
curl -fsSL https://raw.githubusercontent.com/tencentcloud/CubeSandbox/master/deploy/one-click/online-install.sh \
| sudo env ONE_CLICK_ENABLE_OBSERVABILITY=1 \
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod \
CUBE_LOGS_BACKEND=loki \
CUBE_GRAFANA_ADMIN_PASSWORD="${GRAFANA_ADMIN_PASSWORD}" \
bash
# 计算节点
curl -fsSL https://raw.githubusercontent.com/tencentcloud/CubeSandbox/master/deploy/one-click/online-install.sh \
| sudo env ONE_CLICK_DEPLOY_ROLE=compute \
ONE_CLICK_CONTROL_PLANE_IP=10.0.0.10 \
ONE_CLICK_ENABLE_OBSERVABILITY=1 \
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod \
CUBE_LOGS_BACKEND=loki \
bashonline-install.sh 只是下载安装包并调用同一份 install.sh,因此不是新的部署形态, 也不使用单独的监控参数。需要填写鉴权 Token 等敏感值时,优先下载 release 包后写入 权限为 0600 的 .env,不要把秘密留在 shell 历史中。
E. 外部后端
ONE_CLICK_OBSERVABILITY_ROLE=auto
CUBE_LOGS_BACKEND=loki
CUBE_METRICS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:9092/api/v1/otlp
CUBE_LOGS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:3100/otlp显式配置外部 endpoint 后,auto 只在节点安装 Collector,不再安装本地后端。 外部后端的鉴权配置见4.1。
2.2 Kubernetes Helm
部署集群内 Prometheus、Loki(默认)、Grafana,以及计算节点 Collector:
helm upgrade --install cube ./deploy/kubernetes/chart \
-n cube-system --create-namespace \
-f /path/to/runtime-values.yaml \
--set observability.enabled=true \
--set observability.clusterId=k8s-prod \
--set observability.logs.backend=lokiChart 会生成 release 级 Grafana 管理员 Secret,密码随机且升级时复用。从集群读取密码, 不要把凭据写在命令行里:
kubectl get secret -n cube-system \
-l app.kubernetes.io/component=grafana-admin \
-o jsonpath='{.items[0].data.admin-password}' | base64 --decode; echo如需在 Helm 外管理凭据,将 observability.backend.grafana.existingSecret 指向含 admin-user、admin-password 的 Secret。内联 adminPassword 至少 16 个字符。
如需同时采集组件容器日志,增加 --set observability.logs.components.enabled=true,或写入 runtime-values.yaml:
observability:
logs:
components:
enabled: true该选项启动第二个 Collector,只读取 release namespace 内允许列表中的容器 (observability.logs.components.containers,默认 Master、API、Ops、TemplateCenter、 Cubelet、Proxy、Lifecycle Manager),挂载 /var/log/pods 为只读,并按 observability.logs.components.nodeSelector、affinity、tolerations 限定节点。 采集从文件末尾开始,不回放历史日志。在 Grafana 中用 service_name="cubesandbox-cube-master" 查询,或使用日志 Dashboard 的 Component logs 分组。启用前确认组件日志不含密钥等敏感字段,必要时配置脱敏。
Collector、Prometheus、日志后端和 Grafana Pod 都带 readiness/liveness probe, Kubernetes 据此控制 rollout 并重启不健康的监控 Pod。这不是告警能力,不会安装 Alertmanager 规则。
只部署计算节点 Collector,并推送到外部后端:
helm upgrade --install cube ./deploy/kubernetes/chart \
-n cube-system --create-namespace \
-f /path/to/runtime-values.yaml \
--set observability.enabled=true \
--set observability.backend.enabled=false \
--set observability.clusterId=k8s-prod \
--set observability.metrics.otlpEndpoint="http://10.0.0.10:9092/api/v1/otlp" \
--set observability.logs.backend=loki \
--set observability.logs.otlpEndpoint="http://10.0.0.10:3100/otlp"runtime-values.yaml 与正常 Chart 安装使用同一份配置,必须包含非默认的数据库、 Redis 凭据及节点选择等部署参数;可观测性 Value 只是在原有安装命令上追加。 如需 Elasticsearch,将 observability.logs.backend 改为 elasticsearch,并在 使用外部后端时设置 observability.logs.esEndpoint;不要同时设置两个日志 endpoint。
外部后端需要鉴权时,先用现有 Secret 管理工具在目标 namespace 创建 Secret,并在 runtime-values.yaml 中引用;Secret 值应保存完整 Authorization Header(例如 Bearer ...、ES 的 ApiKey ...),不要通过 --set 明文传入:
# 仅作示例。请通过集群的 Secret 管理流程创建这些 Secret。
apiVersion: v1
kind: Secret
metadata:
name: cube-observability-auth
namespace: cube-system
type: Opaque
stringData:
metrics-authorization: "Bearer <metrics-token>"
metrics-tenant-id: "cube-prod"
logs-authorization: "Bearer <logs-token>"
logs-tenant-id: "cube-prod"
---
apiVersion: v1
kind: Secret
metadata:
name: cube-grafana-admin
namespace: cube-system
type: Opaque
stringData:
admin-user: "admin"
admin-password: "<strong-password>"使用 Elasticsearch API Key 时,将 logs-authorization 改为 ApiKey <key>;后端不要求租户 Header 时删除对应 tenant 键。
在 runtime-values.yaml 中引用 Secret 的键:
observability:
metrics:
auth:
existingSecret: cube-observability-auth
authorizationKey: metrics-authorization
tenantIdKey: metrics-tenant-id # optional; empty disables X-Scope-OrgID
logs:
auth:
existingSecret: cube-observability-auth
authorizationKey: logs-authorization
tenantIdKey: logs-tenant-id # set "" when the backend needs no tenant header
backend:
grafana:
existingSecret: cube-grafana-admin
adminUserKey: admin-user
adminPasswordKey: admin-passwordHTTPS endpoint 使用容器系统 CA;当前 Chart 未提供自定义 CA Secret。私有 CA 场景需 先扩展 CA 挂载,不能通过关闭证书校验代替。
Helm Collector 固定使用宿主机网络,并通过 127.0.0.1:9998 抓取同节点 Cubelet, 因此当前内置拓扑要求保持默认的 cubeNode.hostNetwork=true。将 Cubelet 切换为 Pod 网络后,Chart 暂未提供可靠的逐节点指标目标发现机制。
常用 Value:
| Value | 默认值 | 说明 |
|---|---|---|
observability.enabled | false | 可观测性总开关 |
observability.clusterId | cubesandbox | 指标和日志的集群标签 |
observability.otelCollector.scrapeInterval | 15s | Cubelet 指标抓取周期;当前抓取超时固定为 10s,因此周期不得小于 10s |
observability.otelCollector.cubeletMetricsTarget | 127.0.0.1:9998 | 节点本地 Cubelet 指标地址;默认值要求 cubeNode.hostNetwork=true |
observability.otelCollector.stateHostPath | /var/lib/cube-sandbox/otelcol-k8s | 每节点 filelog 游标、队列和 WAL 的持久目录 |
observability.backend.enabled | true | 启用总开关后,是否同时部署 Prometheus、所选日志后端、Grafana |
observability.metrics.otlpEndpoint | "" | 外部 Prometheus OTLP/HTTP 基础地址 |
observability.metrics.remoteWriteUrl | "" | 外部 Prometheus Remote Write 地址,可替代 OTLP |
observability.metrics.auth.existingSecret | "" | 指标 Authorization / tenant Secret |
observability.logs.enabled | true | 是否采集沙箱标准输出/标准错误日志 |
observability.logs.components.enabled | false | 由第二个 DaemonSet 采集 /var/log/pods 下允许列表内的组件 CRI 日志 |
observability.logs.components.hostPath | /var/log/pods | 含 Pod 日志目录树的绝对非根路径 |
observability.logs.components.containers | Cube 组件列表 | 容器名允许列表,只 tail 这些目录 |
observability.logs.components.nodeSelector / affinity / tolerations | Linux 节点;control/compute 标签与污点 | 组件日志 Collector 的调度范围 |
observability.logs.backend | loki | 日志后端,可选 loki 或 elasticsearch |
observability.logs.otlpEndpoint | "" | backend=loki 时的外部 OTLP/HTTP 基础地址 |
observability.logs.esEndpoint | "" | backend=elasticsearch 时的外部 Elasticsearch 地址 |
observability.logs.auth.existingSecret | "" | 日志 Authorization / tenant Secret |
observability.backend.<component>.persistence.enabled | true | 内置 Prometheus、日志后端、Grafana 默认使用 PVC |
observability.backend.grafana.adminPassword | "" | 留空时在 release 级 Secret 中生成随机密码;内联值至少 16 个字符 |
observability.backend.grafana.existingSecret | "" | Grafana 管理员 Secret(admin-user / admin-password);生产环境建议设置 |
3. 独立安装(可选)
也可以直接安装可观测性组件,不依赖 one-click 或 Helm release。独立安装器管理 cube-sandbox-otelcol、Prometheus、Loki/Elasticsearch 和 Grafana,不修改 CubeMaster、Cubelet 等主服务。 开始前先执行安装前自检,并选定一台其他计算节点可访问的后端节点。
3.1 单节点或第一台后端节点
如果后端节点本身也运行沙箱,安装后端和本机 Collector:
sudo env \
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod \
CUBE_LOGS_BACKEND=loki \
CUBE_GRAFANA_ADMIN_PASSWORD='REPLACE_WITH_STRONG_PASSWORD' \
./deploy/one-click/observability/install.sh all如果该节点只承载后端、不运行沙箱,则不安装本机 Collector:
sudo env \
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod \
CUBE_LOGS_BACKEND=loki \
CUBE_GRAFANA_ADMIN_PASSWORD='REPLACE_WITH_STRONG_PASSWORD' \
./deploy/one-click/observability/install.sh backend执行前必须替换 Grafana 密码。选择 Elasticsearch 时将 CUBE_LOGS_BACKEND=loki 改为 elasticsearch。后端节点需按 2.1 A 中的端口规则限制访问来源。
3.2 其他 CVM / 裸金属计算节点
在每个计算节点安装 Collector,并将 10.0.0.10 替换为后端节点内网 IP:
sudo env \
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod \
CUBE_METRICS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:9092/api/v1/otlp \
CUBE_LOGS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:3100/otlp \
./deploy/one-click/observability/install.sh cvmElasticsearch 场景删除 CUBE_LOGS_OTLP_HTTP_ENDPOINT,改为:
sudo env \
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod \
CUBE_LOGS_BACKEND=elasticsearch \
CUBE_METRICS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:9092/api/v1/otlp \
CUBE_LOGS_ES_ENDPOINT=http://10.0.0.10:9200 \
./deploy/one-click/observability/install.sh cvm外部后端需要 Token、租户 Header、ES API Key 或 Basic Auth 时,按 4.1 增加对应环境变量。
3.3 验收与卸载
# 检查 systemd 全栈节点
sudo ./deploy/one-click/observability/install.sh status
# 检查仅安装 Collector 的 systemd 计算节点
sudo systemctl --no-pager --full status cube-sandbox-otelcol.service
# 卸载手动安装的 systemd 组件,保留数据目录
sudo ./deploy/one-click/observability/install.sh uninstall allKubernetes release 改为检查 Collector DaemonSet(名称以 release fullname 为前缀, 下例为 cube):
kubectl -n cube-system rollout status daemonset/cube-node-otelcol
# 仅在 observability.logs.components.enabled=true 时存在
kubectl -n cube-system rollout status daemonset/cube-component-otelcol4. OTel Collector 配置
节点采集器使用 otelcol-contrib 0.162.0,根据已配置的输出最多生成两个独立数据管道:
| Pipeline | Receiver | 输入 | Exporter |
|---|---|---|---|
metrics | prometheus | Cubelet /v1/metrics/resource | otlp_http/metrics 或 prometheus_remote_write |
logs/sandbox | filelog/sandbox | /data/cubelet/log/*/{stdout,stderr} | elasticsearch/logs 或 otlp_http/logs |
表中的名称来自 systemd 生成器;Helm 使用功能相同的 prometheus/cubelet_resource_metrics、filelog/sandbox_stdio 和 logs 名称, 并在独立的组件日志 Collector 中为每个允许的容器生成一条 logs/component_<container> 管道。 实现细节见 OTel 方案设计。
4.1 核心环境变量与上报鉴权
| 变量 | 默认值 | 示例 | 说明 |
|---|---|---|---|
CUBE_OBSERVABILITY_CLUSTER_ID | cubesandbox | cube-prod | 写入指标和日志的集群标签 |
CUBE_LOGS_BACKEND | loki | elasticsearch | one-click/systemd 选择的日志后端 |
CUBE_SANDBOX_NODE_IP | 自动探测 | 10.0.0.20 | 仅 systemd:节点标签;无法自动探测或需要固定标签时显式设置 |
CUBE_METRICS_OTLP_HTTP_ENDPOINT | 空 | http://10.0.0.10:9092/api/v1/otlp | OTLP/HTTP 基础地址,不要附加 /v1/metrics |
CUBE_METRICS_REMOTE_WRITE_URL | 空 | http://10.0.0.10:9092/api/v1/write | Prometheus Remote Write 地址;与 OTLP 指标地址二选一 |
CUBE_METRICS_AUTHORIZATION / CUBE_METRICS_REMOTE_WRITE_TOKEN | 空 | Bearer ... | one-click/systemd 的指标鉴权,完整 Header 与纯 Token 二选一 |
CUBE_METRICS_TENANT_ID | 空 | cube-prod | 指标输出的 X-Scope-OrgID |
CUBE_LOGS_ES_ENDPOINT | 空 | http://10.0.0.10:9200 | Elasticsearch 地址 |
CUBE_LOGS_ES_INDEX | cubesandbox-logs | cube-prod-logs | 日志索引名 |
CUBE_LOGS_ES_USER / CUBE_LOGS_ES_PASSWORD | 空 | cube / ... | one-click/systemd:ES Basic Auth,必须成对设置 |
CUBE_LOGS_ES_API_KEY | 空 | ... | one-click/systemd:ES API Key,与 Basic Auth 二选一 |
CUBE_LOGS_ES_TLS_INSECURE_SKIP_VERIFY | false | true | 跳过 ES TLS 校验,只建议临时诊断 |
CUBE_LOGS_OTLP_HTTP_ENDPOINT | 空 | http://10.0.0.10:3100/otlp | OTLP/HTTP 日志基础地址;与 Elasticsearch 二选一 |
CUBE_LOGS_AUTHORIZATION / CUBE_LOGS_TOKEN | 空 | Bearer ... | one-click/systemd 日志鉴权,完整 Header 与纯 Token 二选一 |
CUBE_LOGS_TENANT_ID | 空 | cube-prod | 日志输出的 X-Scope-OrgID |
CUBE_LOGS_SANDBOX_ID | * | 019abc-example | systemd 形态可限制为一个沙箱 ID |
CUBE_LOGS_FILE_START_AT | beginning | end | 仅 systemd:首次读取位置,可选 beginning 或 end |
内置 Grafana provisioning 会复用上述指标/日志凭证配置数据源。执行 grafana/install.sh dashboards 时会按 UID 创建或更新数据源。
Helm 使用 Kubernetes Node 名称作为节点标签,不读取上述 systemd 专属变量。其 start_at 固定为 beginning,但只在没有已保存游标时生效: Collector 状态按节点持久化在 /var/lib/cube-sandbox/otelcol-k8s,同一节点上的 Pod 重建会从 file_storage 续读,不会从头重读现存文件。
one-click 用户可直接使用 deploy/one-click/env.example 中的 Observability 配置段。按 2.1 复制为 .env 后,按需取消注释并 修改,例如:
ONE_CLICK_ENABLE_OBSERVABILITY=1
CUBE_OBSERVABILITY_CLUSTER_ID=cube-prod
CUBE_LOGS_BACKEND=loki
# 标准 all-in-one/计算节点会自动推导这些地址。
# 仅在使用自建或外部后端时手动设置。
CUBE_METRICS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:9092/api/v1/otlp
CUBE_LOGS_OTLP_HTTP_ENDPOINT=http://10.0.0.10:3100/otlp至少配置一个指标或日志输出端。指标的两个输出变量不能同时设置;日志的两个输出变量 也不能同时设置。选择 Elasticsearch 时改为 CUBE_LOGS_BACKEND=elasticsearch、 CUBE_LOGS_ES_ENDPOINT=http://10.0.0.10:9200,并可用 CUBE_LOGS_ES_INDEX 修改索引名。one-click 标准拓扑会按后端选择自动补全内置地址, 不要求手工填写。
鉴权(所有部署形态)
one-click 与 Kubernetes 安装流程支持向带鉴权的外部指标和日志后端上报。最简单的 Bearer 示例:
CUBE_METRICS_OTLP_HTTP_ENDPOINT=https://prometheus.example.com/api/v1/otlp
CUBE_METRICS_AUTHORIZATION='Bearer <metrics-token>'
CUBE_LOGS_OTLP_HTTP_ENDPOINT=https://loki.example.com/otlp
CUBE_LOGS_AUTHORIZATION='Bearer <logs-token>'- one-click/systemd:在对应节点的
.env中设置上述变量; - Helm:创建 Secret 后通过
observability.metrics.auth.existingSecret和observability.logs.auth.existingSecret引用,完整示例见 2.2; - Elasticsearch:使用
CUBE_LOGS_ES_API_KEY,或成对设置CUBE_LOGS_ES_USER/CUBE_LOGS_ES_PASSWORD。
*_AUTHORIZATION 填完整的单行 Header;*_TOKEN 只填裸 Token,安装器会补成 Bearer。这两种写法不能同时设置。以上变量只配置 Collector/Grafana 客户端 Header, 不会给内置 Prometheus、Loki 或 Elasticsearch 自动开启服务端鉴权;内置后端需要鉴权时, 应在前面部署认证网关/反向代理,或使用托管后端。
官方参考: OTLP HTTP exporter、 Prometheus Remote Write exporter、 Loki authentication、 Elasticsearch API keys和 Kubernetes Secret。
不要提交真实凭据;.env 应设为 0600。HTTPS 默认使用系统 CA,私有 CA 应安装或 挂载信任链,不应长期关闭证书校验。
4.2 配置位置与渲染
render 与实际安装使用同一套生成逻辑,会将 4.1 中已经设置的环境变量写入最终 配置,无需再编辑生成的 YAML。one-click 的统一入口会自动读取 .env;独立执行时, 可先 export 这些变量,或在命令前通过 env 传入。
| 形态 | 安装后的配置位置 | 使用已配置变量查看渲染结果 |
|---|---|---|
| systemd | /etc/cube-sandbox/otelcol/otelcol.yaml | ./deploy/one-click/observability/install.sh render cvm |
| Helm | ConfigMap <release>-node-otelcol-config 与 <release>-component-otelcol-config | 使用下方 helm template 命令 |
Helm 使用 values.yaml / --set 中的 observability.* Value,不会自动读取 CUBE_* 环境变量。systemd Collector 使用 /var/lib/cube-sandbox/otelcol/storage 保存 filelog 游标和 OTLP 发送队列; Kubernetes Collector 容器内使用 /var/lib/otelcol/storage,默认映射到节点 /var/lib/cube-sandbox/otelcol-k8s。
仅预览 Helm Collector 配置时,仍需为 Chart 的全局凭据校验提供非默认占位值:
helm template cube ./deploy/kubernetes/chart \
--set observability.enabled=true \
--set-string mysql.password=render-only \
--set-string mysql.rootPassword=render-only \
--set-string redis.password=render-only \
-s templates/observability.yamlrender-only 只能用于本地渲染,实际安装必须通过 Secret values 提供真实凭据。
4.3 生成配置示例
下面假设使用 4.1 的示例变量,并将节点地址设置为 10.0.0.20,节选 systemd 形态同时向 Prometheus OTLP 接收端和 Loki 发送数据时的核心配置。为便于 阅读,省略了部分 exporter discover/queue/retry 和 service.telemetry 字段;完整 结果以 render 命令输出为准。代码块中的注释仅用于说明变量映射,实际生成文件不 一定保留这些注释。
这是渲染结果示例,不是需要再次维护的配置模板。render 和 install 都会自动将 已经配置的环境变量写入 YAML,并补全鉴权、队列和节点标签;无需手工替换下面的 示例值。环境变量变更后重新执行安装即可重新生成配置。
receivers:
# 仅从本机 Cubelet 抓取沙箱资源指标。
prometheus:
trim_metric_suffixes: false
config:
scrape_configs:
- job_name: cubesandbox-resource
scrape_interval: 5s
scrape_timeout: 3s
metrics_path: /v1/metrics/resource
static_configs:
- targets: ["127.0.0.1:9998"]
# CUBE_LOGS_SANDBOX_ID 可替换“*”;默认采集所有沙箱。
filelog/sandbox:
include:
- /data/cubelet/log/*/stdout
- /data/cubelet/log/*/stderr
start_at: beginning # CUBE_LOGS_FILE_START_AT
storage: file_storage
include_file_path: true
include_file_name: false
max_log_size: 1MiB
operators:
- type: regex_parser
parse_from: attributes["log.file.path"]
regex: '^/data/cubelet/log/(?P<sandbox_id>[^/]+)/(?P<stream>stdout|stderr)$'
- type: copy
from: attributes["log.file.path"]
to: attributes.log_file_path
- type: copy
from: attributes.sandbox_id
to: attributes.cubesandbox_sandbox_id
- type: move
from: attributes.sandbox_id
to: resource["cubesandbox.sandbox.id"]
- type: copy
from: attributes.stream
to: attributes.log_iostream
- type: move
from: attributes.stream
to: attributes["log.iostream"]
processors:
memory_limiter:
check_interval: 1s
limit_mib: 256
# 值来自 CUBE_OBSERVABILITY_CLUSTER_ID 和 CUBE_SANDBOX_NODE_IP;
# 未设置时会自动探测节点地址。
resource/metrics:
attributes:
- {key: cluster, value: "cube-prod", action: upsert}
- {key: node, value: "10.0.0.20", action: upsert}
- {key: cubesandbox.cluster.id, value: "cube-prod", action: upsert}
- {key: host.name, value: "10.0.0.20", action: upsert}
resource/logs_common:
attributes:
- {key: host.name, value: "10.0.0.20", action: upsert}
- {key: cubesandbox.cluster.id, value: "cube-prod", action: upsert}
attributes/logs_common:
actions:
- {key: host_name, value: "10.0.0.20", action: upsert}
- {key: cubesandbox_cluster_id, value: "cube-prod", action: upsert}
- {key: cubesandbox_sandbox_id, value: "none", action: insert}
- {key: log_iostream, value: "none", action: insert}
resource/sandbox:
attributes:
- {key: service.name, value: "cubesandbox-sandbox", action: upsert}
- {key: cubesandbox.log.source, value: "sandbox-init", action: upsert}
attributes/sandbox:
actions:
- {key: service_name, value: "cubesandbox-sandbox", action: upsert}
- {key: cubesandbox_log_source, value: "sandbox-init", action: upsert}
batch:
timeout: 2s
send_batch_size: 1024
exporters:
otlp_http/metrics:
# 使用 CUBE_METRICS_OTLP_HTTP_ENDPOINT;Collector 会追加 /v1/metrics。
endpoint: "http://10.0.0.10:9092/api/v1/otlp"
compression: gzip
sending_queue:
enabled: true
storage: file_storage
retry_on_failure:
enabled: true
max_elapsed_time: 0
otlp_http/logs:
# 使用 CUBE_LOGS_OTLP_HTTP_ENDPOINT;Collector 会追加 /v1/logs。
endpoint: "http://10.0.0.10:3100/otlp"
compression: gzip
sending_queue:
enabled: true
storage: file_storage
retry_on_failure:
enabled: true
max_elapsed_time: 0
extensions:
health_check:
endpoint: 127.0.0.1:13133
# 在进程重启后保留文件读取位置和 OTLP 发送队列。
file_storage:
directory: /var/lib/cube-sandbox/otelcol/storage
create_directory: true
service:
extensions: [health_check, file_storage]
pipelines:
# 指标和日志使用相互独立的数据管道。
metrics:
receivers: [prometheus]
processors: [memory_limiter, resource/metrics, batch]
exporters: [otlp_http/metrics]
logs/sandbox:
receivers: [filelog/sandbox]
processors: [memory_limiter, resource/logs_common, resource/sandbox, attributes/logs_common, attributes/sandbox, batch]
exporters: [otlp_http/logs]选择 Elasticsearch 时,日志 exporter 会替换为 elasticsearch/logs,地址和索引 分别来自 CUBE_LOGS_ES_ENDPOINT、CUBE_LOGS_ES_INDEX;其余 receiver、字段提取 和 pipeline 不变。
4.4 支持范围、规则与建议参数
指标和日志采集规则
| 类型 | 当前支持规则 | 可配置入口 |
|---|---|---|
| 沙箱指标 | 只抓取本机 Cubelet /v1/metrics/resource;包含沙箱 CPU、Memory 和节点沙箱计数 | 集群/节点标签、OTLP 或 Remote Write 目标;Helm 还可配置 observability.otelCollector.scrapeInterval |
| 沙箱日志 | 按行读取 /data/cubelet/log/<sandbox-id>/{stdout,stderr},从路径提取 Sandbox ID 和 stdout/stderr | 默认通过 OTLP 写 Loki,也可写 Elasticsearch;systemd 可通过 CUBE_LOGS_SANDBOX_ID 和 CUBE_LOGS_FILE_START_AT 控制范围与首次读取位置 |
| 组件日志(Helm 可选) | 只读取 /var/log/pods 中 observability.logs.components.containers 列出的 containerd 日志;每个组件独立 pipeline,首次从末尾开始 | 独立只读组件日志 Collector DaemonSet,仅调度到匹配的 control/compute 节点;one-click 暂不支持 |
| 状态与重试 | filelog 游标和 OTLP 队列使用 file_storage;Remote Write 使用 WAL;Elasticsearch 使用内存队列且最多重试 5 次 | systemd 文件状态保存在宿主机目录;Kubernetes 主 Collector 按节点保存在 /var/lib/cube-sandbox/otelcol-k8s,组件日志使用单独的 -components 路径 |
日志 Receiver 按行保留原始 Body;systemd 生成器将单条日志记录上限设为 1MiB。
两个后端都保留以下语义字段(Loki 会把 OTel 属性名中的 . 规范化为 _):
service_name=cubesandbox-sandboxcubesandbox_log_source=sandbox-initcubesandbox_cluster_idhost_namecubesandbox_sandbox_idlog_iostream=stdout|stderrlog_file_path- 日志正文和时间戳
组件 CRI 日志使用 service_name=cubesandbox-<container>、 cubesandbox_log_source=component 和 cubesandbox_component_name=<container>; 不包含上面仅适用于沙箱日志的 ID 与 stream 字段。
Loki 只将低基数字段 service_name、cubesandbox_cluster_id、host_name 建为索引 label;cubesandbox_sandbox_id、log_iostream 等保留为 structured metadata。 这避免每个沙箱生成新的高基数 stream。常用 LogQL:
# 所有沙箱的 stderr
{service_name="cubesandbox-sandbox"} | log_iostream="stderr"
# 单个沙箱中的错误类消息
{service_name="cubesandbox-sandbox"}
| cubesandbox_sandbox_id="<sandbox-id>"
|~ "(?i)error|fatal|panic"选择 Elasticsearch 时,文档另外包含 Body 与 @timestamp,常用 Lucene 过滤条件:
# 所有沙箱的 stderr
service_name:"cubesandbox-sandbox" AND log_iostream:"stderr"
# 沙箱 stdout/stderr 中的错误类消息
cubesandbox_log_source:"sandbox-init" AND Body:(ERROR OR FATAL OR panic)
# 单个沙箱
cubesandbox_sandbox_id:"<sandbox-id>"Collector 建议参数
| 参数 | 当前内置值 | 建议 |
|---|---|---|
| 指标抓取周期 / 超时 | one-click/systemd:5s / 3s;Helm:15s / 10s | one-click/systemd 保持内置值;Helm 周期不得小于固定的 10s 超时,较大规模建议保留默认 15s |
CUBE_LOGS_FILE_START_AT(仅 systemd) | beginning | 需要读取当前文件全部内容时保留 beginning;只采集 Collector 启动后新增内容时使用 end |
CUBE_LOGS_SANDBOX_ID(仅 systemd) | * | 正常运行保持 *;只在单沙箱排障或临时降低日志量时指定 ID |
| Memory limiter | 固定 256MiB;Kubernetes Pod memory request/limit 为 128Mi / 512Mi | 先使用默认值;仅提高 Pod limit 不会改变 limiter,目前安装器未提供 limiter 调参入口 |
| Batch | 1024 条;one-click/systemd 超时 2s,Helm 超时 5s | 低延迟优先保持当前值;不要直接修改生成文件,后续重新安装会覆盖手工修改 |
| OTLP 发送队列 | 1000,使用磁盘 file_storage,失败持续重试 | 保证 /var/lib/cube-sandbox/otelcol 或 Kubernetes 节点状态目录有足够空间 |
| Elasticsearch 发送队列 | 内存队列 1000、10 个 consumer,最多重试 5 次 | 持续故障可能最终丢弃日志,应监控 Collector 日志和后端可用性 |
| Remote Write | 使用 WAL | 保证 WAL 所在目录有足够空间,并监控远端写入失败 |
| Prometheus 保留策略 | one-click:15d / 40GB;Helm:15d | one-click 使用 CUBE_PROMETHEUS_RETENTION_TIME / CUBE_PROMETHEUS_RETENTION_SIZE;Helm 使用 observability.backend.prometheus.retention,当前没有 size Value |
| Loki 保留策略 | one-click、Helm:15d | one-click 使用 CUBE_LOKI_RETENTION_PERIOD;Helm 使用 observability.backend.loki.retention |
| Elasticsearch Heap | one-click、Helm:1g | one-click 可通过 CUBE_ES_HEAP_SIZE、Helm 可通过 observability.backend.elasticsearch.javaOpts 调整;通常不超过可用内存的 50% |
除表中明确列出的环境变量或 Helm Value 外,抓取超时、Memory limiter、Batch 和发送 队列目前都是安装器内置值,不应通过手工编辑生成文件长期维护。
Helm 内置 Prometheus、所选日志后端和 Grafana 默认使用 PVC。可在 observability.backend 各组件的 persistence 下配置 storageClassName、 existingClaim 和容量;只有一次性验证环境才建议设置 enabled=false。需要高可用 时仍建议使用外部托管后端。内置 Loki 默认保留 15d;Elasticsearch 索引生命周期 (ILM)需要在后端单独配置,本安装器不会自动创建删除策略。
建议监控表达式
当前安装器提供看板,但不自动安装 Alertmanager、Prometheus 告警规则或 Grafana 告警。以下表达式可作为起点:CPU、节流、内存占比和采样不可用建议使用 for: 5m; 内存失败计数应立即触发或只使用很短的等待时间,并根据业务负载调整阈值:
# 有限 CPU 配额的沙箱使用率超过 80%
100 * rate(cubesandbox_host_sandbox_cpu_usage_seconds_total[5m])
/ clamp_min(cubesandbox_host_sandbox_cpu_limit_cores, 0.001) > 80
# CPU 节流周期占比超过 20%
100 * rate(cubesandbox_host_sandbox_cpu_throttled_periods_total[5m])
/ clamp_min(rate(cubesandbox_host_sandbox_cpu_periods_total[5m]), 0.001) > 20
# 有限内存配额的沙箱使用率超过 85%
100 * cubesandbox_host_sandbox_memory_current_bytes
/ clamp_min(cubesandbox_host_sandbox_memory_limit_bytes, 1) > 85
# 最近五分钟内出现内存限制失败
increase(cubesandbox_host_sandbox_memory_failures_total[5m]) > 0
# 沙箱存在但当前没有可用资源采样
cubesandbox_node_sandbox_count
- cubesandbox_node_sandbox_available_count > 0CPU/Memory 百分比表达式只适用于导出了有限资源上限的沙箱。
4.5 扩展采集其他组件日志
Helm 已内置允许列表内的组件容器日志采集(见 2.2),参考实现在 deploy/kubernetes/chart/templates/observability.yaml。one-click/systemd 安装器 只采集沙箱日志。
不要直接修改安装后的 /etc/cube-sandbox/otelcol/otelcol.yaml 或 ConfigMap:重新安装会覆盖 生成文件。改生成器:
- systemd:
deploy/one-click/observability/otelcol/install.sh的write_otel_config(); - Helm:
deploy/kubernetes/chart/templates/observability.yaml的 Collector ConfigMap 与 volumes,并在values.yaml暴露默认关闭的开关。
每个组件使用独立 receiver 和 pipeline(filelog/component_cubelet、 logs/component_cubelet),避免不同格式共享解析器;同时写入 OTel 资源属性和 Elasticsearch 扁平字段:
service.name/service_name:稳定低基数服务名,例如cubesandbox-cubelet;cubesandbox.log.source/cubesandbox_log_source:component;cubesandbox.component.name/cubesandbox_component_name:例如cubelet;- 继续复用
cubesandbox.cluster.id、host.name、log.file.path和时间戳。
扩展时的约束:
- 用
start_at: end读取文件;JSON 或多行日志增加专用 parser,解析失败必须保留原始Body,不能阻断其他 pipeline; - 只索引稳定字段,PID、请求 ID、Sandbox ID 保留为 structured metadata;
- 只挂载必需的宿主机目录且只读,不得挂载宿主机根目录;
- 排除凭据、Token、请求 Header,必要时增加
redaction/transform处理器; - 验证
file_storage游标在日志轮转、重启和后端故障下的行为; - 同步生成器、双语文档、看板和 Collector
validate测试,并说明未支持的部署形态。
5. 后端与看板
后端固定版本、默认端口与外部组件兼容基线见 1.2。
一个 Grafana 实例自动加载三张独立指标看板和一张日志看板:
cubesandbox-cluster.json:所选集群内的沙箱资源汇总。cubesandbox-node.json:所选集群、单机内的沙箱资源汇总。cubesandbox-sandbox.json:单个沙箱的 Host Sandbox 和 Guest Workload 详情, 可继续筛选 Guest 容器。cubesandbox-logs.json(默认 Loki)或cubesandbox-logs-elasticsearch.json(Elasticsearch),统一 UID 为cubesandbox-logs:stdout/stderr 日志量趋势、 日志查询,以及默认折叠的 Component logs 分组(Helm 按需开启)。
指标下拉按 cluster → node → sandbox_id → container_id 逐级联动,覆盖现有全部 24 个指标族。CPU 速率单位为使用核数;内存限额为已导出有限限额之和,不能当作 整机或集群物理容量。Host 与 Guest 记账口径不同,不能相加;缺样本不等于零。 Guest 速率排除已观察到 epoch 变化的窗口,但不代表精确回滚事件记账。 Loki 的沙箱 ID 保留正则文本筛选,方便检索历史日志且避免高基数索引; Elasticsearch 下拉按集群、节点、沙箱联动。
one-click/systemd 安装后可用 sudo ./deploy/one-click/observability/install.sh status 检查 Collector 与后端,并用 curl -fsS http://127.0.0.1:3100/ready 检查默认 Loki;选择 Elasticsearch 时使用 curl -fsS 'http://127.0.0.1:9200/cubesandbox-logs*/_count'。沙箱销毁会删除本地 日志目录,因此 Collector 必须在沙箱存活期间持续 tail 并写入远端后端。