Files
opsx bad2a1e624 fix(hosts,collectors,usage): 主机验证结果失真 + 主机模式系统日志解析 + metrics 单位
=== 一、主机「测试连接」永远显示旧错误(真实 bug)===
HostService.Validate 的失败分支也写 status=active,且从不写 statusMessage:
    if info, err := client.GetHostInfo(ctx); err == nil { 更新 os/arch }
    else { s.Update(id, {status:"active"}) }   // ← 失败也标记成功
结果:连不上也显示正常,页面永远停在几分钟前的旧错误文案,
用户改了 IP 再点测试,还以为"改了还是不行"。

改为区分成功/失败并写清原因:
    成功 → status=active + os/arch + statusMessage="连接成功"
    失败 → status=error + statusMessage=FormatSSHError(err)

=== 二、回环地址在平台里必然连不上 ===
平台跑在容器/Pod 内,127.0.0.1 指向平台自身。
- 新建主机时拦截:hostIP 为 127.0.0.1/localhost/::1 直接报错并解释原因
- 前端表单同步校验 + 文案
- SSH 错误映射补充:连接被拒绝时提示可能是地址填了回环

=== 三、主机模式采集系统日志(两处会导致采集失败)===
1. pipeline 一律套 cri 阶段:cri 只认容器运行时格式,
   套在 /var/log/messages 这类 syslog 上会取不到时间戳、整行被丢。
   → appPipelineStages() 按路径判断:容器日志用 cri,普通文本日志只做限流。
2. promtailExternalLabels 写死 8 空格缩进:主机模板 external_labels 只有 4 空格,
   一旦给采集器配置了标签就生成非法 YAML,Promtail 装不上。
   → 增加 indent 参数,主机 6 空格 / DaemonSet 8 空格。
3. 新增 system 采集任务:
   /var/log/{messages,secure,cron,maillog,syslog,dmesg}
   正则解析 syslog 时间戳(Jan _2 15:04:05,Asia/Shanghai)+ 限流

测试:TestHostPromtailConfigValid / CRIPathKeepsCRIStage / PlainFileHasNoCRIStage
      TestPromtailManifestParses 全部通过

=== 四、资源用量 CPU 解析(nanocores)===
metrics-server 返回的 CPU 常是纳核(如 "194533645n"=194.5m)。
原解析只处理 "m",导致 194 亿核、页面出现"CPU 已达 limit 的 4863341125%"。
→ parseQuantityMilli 支持 n/u/m/整核
实测:CPU 当前=178m,峰值=3300m,建议"峰值占 limit 82%,可考虑提前扩容"

=== 五、其他 ===
- 添加集群默认改用 Token 认证(最小权限、可随时吊销)
- RBAC 片段补 metrics.k8s.io(资源面板需要,独立 API 组常被漏授)
- 文案去掉 Grafana 需求描述(平台自身不部署 Grafana)

镜像:lghost/lokistack:latest + v0.1 = sha256:7bddd0f4d1f1
2026-10-28 17:58:30 +00:00

486 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# lokistack 部署到 Kubernetes
命名空间固定:**`log`**(日志相关资源统一放这里)
---
## 一条命令部署(推荐)
```bash
bash kubernetes/install.sh
```
脚本会依次完成:
1. 创建 `log` 命名空间
2. **生成随机密钥** `JWT_SECRET` / `ENCRYPTION_KEY` 写入 Secret `lokistack-secrets`
(已存在则跳过,**不会覆盖**)
3. 创建 PVC `lokistack-data`(30 GiB,使用集群默认 StorageClass)
4. 部署 `lokistack`(单副本 + Recreate)
5. 创建 Service `lokistack:8080`(ClusterIP)
6. 等待 Pod 就绪并输出访问方式
**需要 NodePort(配 ingress 时)**:
```bash
WITH_NODEPORT=1 bash kubernetes/install.sh
```
**幂等性**:重复执行只做增量更新,不会覆盖密钥、不会重建数据卷。
---
## 全部用法速查
| 场景 | 命令 |
|------|------|
| **标准部署**(自动探测云平台 + StorageClass)| `bash kubernetes/install.sh` |
| 同时开 NodePort(配 ingress 用)| `WITH_NODEPORT=1 bash kubernetes/install.sh` |
| 指定 StorageClass | `STORAGE_CLASS=cbs bash kubernetes/install.sh` |
| 指定 SC(自建用 local-path)| `STORAGE_CLASS=local-path bash kubernetes/install.sh` |
| **自建集群强制用节点本地盘**(hostPath)| `LOCAL_STORAGE=force bash kubernetes/install.sh` |
| 只诊断不动手(无变更)| `LOCAL_STORAGE=off bash kubernetes/install.sh` |
| **指定本地存储的节点/路径/容量** | `NODE_NAME=node1 PV_PATH=/data/lokistack PV_SIZE=50Gi bash kubernetes/install.sh` |
| 组合:自建 + 指定节点 + NodePort | `LOCAL_STORAGE=force NODE_NAME=node1 WITH_NODEPORT=1 bash kubernetes/install.sh` |
**环境变量一览**:
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `WITH_NODEPORT` | `0` | `1` = 额外创建 NodePort Service(30080)|
| `STORAGE_CLASS` | 空(自动探测)| 显式指定 StorageClass 名称 |
| `LOCAL_STORAGE` | `auto` | `auto` 无 SC 时自动建本地 PV / `off` 只诊断 / `force` 强制本地盘 |
| `NODE_NAME` | 空(第一个 Ready 节点)| hostPath PV 绑定的节点 |
| `PV_PATH` | `/var/lib/lokistack-data` | hostPath 在宿主机上的路径 |
| `PV_SIZE` | `30Gi` | PV 容量声明 |
---
## 环境要求
| 项 | 要求 |
|----|------|
| Kubernetes | 1.20+ |
| StorageClass | **脚本自动探测**(default 注解 → 唯一 SC → 手动 `STORAGE_CLASS=`);自建集群无 SC 时会提前报错并给出解决方案,不会白等 |
| 镜像 | 节点可拉 `lghost/lokistack:latest`(或改成自己的 Harbor/ACR/TCR/SWR) |
| 权限 | 有 `log` 命名空间的 create/apply 权限 |
## 前置条件(`install.sh` 会自动检查,不通过直接给出具体修法)
脚本启动时做**三级预检 + 一级软检查**,任何一级不通过都会立即退出并打印「是哪一类问题、
下一步做什么」,不会跑到一半才报错:
| 预检项 | 不通过时的提示内容 |
|--------|-------------------|
| **kubectl 是否安装** | 按 CentOS/TencentOS、Ubuntu/Debian、macOS 分别给安装命令;并给出「机器没有外网」时的替代方案(控制台下载 / 内网跳板机执行 / 只 scp 二进制) |
| **kubeconfig 是否存在** | 打印实际查找路径(`$KUBECONFIG` 或 `~/.kube/config`),给出腾讯云 TKE / 阿里云 ACK / AWS EKS 各自下载 kubeconfig 的入口,以及 `chmod 600` 建议 |
| **能否连上 API Server** | **按错误类型分类**(见下表),不是笼统一句「请检查 kubeconfig」 |
| **部署权限(软检查)** | `kubectl auth can-i create deployments.apps -n <ns>` 非 `yes` 时告警,提示绑定 Role 或改用管理员 kubeconfig;**不阻断**,避免首次部署时命名空间尚未创建导致误拦 |
### 连不上集群时的分类提示
| 实际错误 | 脚本的判断 | 给出的处理方向 |
|---------|-----------|---------------|
| `current-context is not set` / `context does not exist` | kubeconfig 没有可用 context | `kubectl config get-contexts` → `use-context` → 重新下载 kubeconfig |
| `Unauthorized` / `Authentication required` | 认证失败(云厂商 token 几小时就过期) | 回控制台重新下载 kubeconfig;自建集群查客户端证书 |
| `Forbidden` / `403` | 已连上集群但 RBAC 拒绝 | `kubectl auth whoami`、`auth can-i --list -n <ns>`,列出部署所需的具体资源权限 |
| `x509` / `certificate` / `tls:` | 证书过期或域名不匹配 | 确认 API Server 证书;可信内网可临时 `--insecure-skip-tls-verify` 排障 |
| `connection refused` / `no such host` / `context deadline exceeded` / `i/o timeout` | 网络不通 | ①`curl -k <API>/version` ②**内网地址需 VPN/专线** ③安全组放行 6443 ④查集群状态 ⑤换成跳板机试 |
失败时的完整输出形如:
```text
❌ 连不上集群 API Server(网络不通)
kubeconfig : /root/.kube/config
当前 context : tke-pre
API Server : https://172.19.16.13:443
当前用户 : admin
实际报错: Unable to connect to the server: context deadline exceeded (...)
── 按顺序排查 ───────────────────────────────────────────────────────
1) 地址对不对?用上面打印的 API Server 测一下: curl -k --max-time 5 <API Server>/version
2) 要不要 VPN / 专线?云上 API Server 常是内网地址(10.x / 172.16~31.x / 192.168.x)...
3) 安全组 / 防火墙:云厂商默认只放行运维网段访问 6443 端口
4) 集群本身是否正常:控制台看集群状态是否「运行中」
5) 换台机器试:在能访问集群的跳板机上执行本脚本,可排除本机网络问题
```
> 预检只读不写、耗时 < 1 秒。API Server 连接超时可用 `KUBECTL_TIMEOUT=15s` 放宽(默认 8s)。
## 脚本自动处理什么
`install.sh` 不是无脑 apply,它会先探测环境再动手:
| 场景 | 脚本行为 |
|------|---------|
| **云平台识别** | 读节点 `providerID`,识别出 腾讯云 TKE / AWS EKS / 阿里云 ACK / 华为云 CCE / GCP GKE / Azure AKS / K3s / 自建 |
| **StorageClass** | ① 显式 `STORAGE_CLASS=xxx` → ② `is-default-class` 注解 → ③ 集群唯一 SC → ④ **都没有则自动创建节点本地存储**(静态 local PV,零外部依赖) |
| **Secret** | 不存在则用 `openssl rand -hex 32` 生成;**已存在则跳过,绝不覆盖** |
| **PVC** | 把探测到的 SC 显式写进渲染后的 pvc.yaml(避免依赖隐式默认行为) |
| **就绪等待** | `rollout status` 最多 180s,超时后按**失败原因分类诊断** |
| **镜像拉不到** | 诊断为 `ImagePullBackOff` 时,按云给出对应仓库格式(TCR / ACR / SWR / Harbor) |
| **PVC 未绑定** | 诊断为 StorageClass 问题时,按云给出推荐 SC 名(cbs / gp3 / alicloud-disk-essd / csi-disk) |
**脚本不做什么**(需要人工决策的部分):
- ❌ 不自动改镜像仓库(不知道你的仓库地址,只给格式提示)
- ❌ 不覆盖已存在的 Secret / 已有数据卷
---
## 自建 K8s(kubeadm 裸装)的存储怎么办
自建集群最常见的坑就是**没有 StorageClass** → PVC 永久 Pending。脚本内置三种处理方式:
### 默认行为(`LOCAL_STORAGE=auto`):自动创建节点本地存储
检测到集群完全没有 SC 时,脚本自动创建一个**静态 local PV**:
```yaml
StorageClass: lokistack-local
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer # 先定 Pod 再绑 PV
reclaimPolicy: Retain # 误删 PVC 不丢数据
PersistentVolume: lokistack-data-pv
hostPath: { path: /var/lib/lokistack-data, type: DirectoryOrCreate }
nodeAffinity: 锁定一台节点
```
**特点**:
| 优点 | 代价 |
|------|------|
| **零外部依赖**(不拉任何镜像,国内网络友好)| 数据固定在单节点,Pod 不会漂移到别处 |
| kubelet 自动建目录(`DirectoryOrCreate`)| 只能供给一个 PVC(单实例场景够用)|
| Pod 一定调度到数据所在节点(`nodeAffinity`)| 节点损毁需从备份恢复(见「持久化」的 CSI 快照)|
**实测数据**:PVC 从 Pending 到 Bound **5 秒**,Pod 正常读写宿主机目录。
### 想要动态供给(支持多 PVC / 多实例)
装 Rancher **local-path-provisioner**(自建集群事实标准):
```bash
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
STORAGE_CLASS=local-path bash kubernetes/install.sh
```
### 想先看着不动手(`LOCAL_STORAGE=off`)
```bash
LOCAL_STORAGE=off bash kubernetes/install.sh
```
只打印诊断和 4 种解法,不做任何变更。
### 自建 K8s 存储方案选型
| 方案 | 外部依赖 | 适用 | 备注 |
|------|---------|------|------|
| **静态 local PV**(本脚本默认)| **零** | 单副本、数据固定 | 最省事,国内网络友好 |
| **local-path-provisioner** | 2 个镜像 | 需要动态供给/多 PVC | 自建集群首选,社区标准 |
| **OpenEBS LocalPV** | Helm + 较多镜像 | 要 LVM/ZFS 精细控制 | 较重 |
| **Longhorn** | Helm,每节点装 | 需要副本/高可用 | 每节点约 1C2G 开销 |
| **NFS + nfs-subdir-provisioner** | 需现成 NFS 服务器 | 多节点共享 | 依赖外部 NFS |
> 对 lokistack 这类**单副本 + JSON DB 单写者**的应用,**local PV 完全够用**,
> 不需要为了"高可用"上 Longhorn —— 副本存储反而会和"单写者"约束冲突。
---
## hostPath 数据的运维(自建集群必读)
自建集群用 hostPath 时,**数据在宿主机目录里**,不在云盘上。下面这些操作要会。
### 数据在哪
```bash
# 1) 找到 PV 绑定的节点和路径
kubectl get pv lokistack-data-pv -o custom-columns=\
'NAME:.metadata.name,PATH:.spec.hostPath.path,NODE:.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[0].values[0]'
# 2) 去那台节点上看
NODE=<上面的 NODE 值>
ssh root@${NODE} 'ls -la /var/lib/lokistack-data/'
# 应看到:lokistack.json kubeconfigs/ known_hosts
```
### 备份(不用停机)
```bash
NODE=<节点>
TS=$(date +%Y%m%d-%H%M%S)
ssh root@${NODE} "tar czf /tmp/lokistack-backup-${TS}.tar.gz -C /var/lib lokistack-data"
scp root@${NODE}:/tmp/lokistack-backup-${TS}.tar.gz ./
```
> `lokistack.json` 写入是原子的(临时文件 + fsync + rename),**热备份安全**,不用停 Pod。
### 迁移到另一台节点
```bash
OLD=<旧节点> NEW=<新节点>
# 1) 停止写入
kubectl -n log scale deploy lokistack --replicas=0
# 2) 从旧节点拉取数据
ssh root@${OLD} 'tar czf - -C /var/lib lokistack-data' > /tmp/lokistack-data.tar.gz
# 3) 推到新节点并解包
scp /tmp/lokistack-data.tar.gz root@${NEW}:/tmp/
ssh root@${NEW} 'mkdir -p /var/lib && tar xzf /tmp/lokistack-data.tar.gz -C /var/lib && ls -la /var/lib/lokistack-data'
# 4) 改 PV 的 nodeAffinity 指向新节点
kubectl patch pv lokistack-data-pv --type=json -p \
"[{\"op\":\"replace\",\"path\":\"/spec/nodeAffinity/required/nodeSelectorTerms/0/matchExpressions/0/values\",\"value\":[\"${NEW}\"]}]"
# 5) 恢复
kubectl -n log scale deploy lokistack --replicas=1
kubectl -n log rollout status deploy/lokistack
```
### 卸载但保留数据
```bash
# 删工作负载和服务(数据卷保留)
kubectl -n log delete deploy lokistack svc lokistack
# PVC 保留(Retain 策略,删了也不会丢 PV 里的数据)
# 想彻底清掉的话:
# kubectl delete pvc lokistack-data
# kubectl delete pv lokistack-data-pv
# 然后去节点上 rm -rf /var/lib/lokistack-data
# 如果要连脏数据一起重置重装:
kubectl -n log delete pvc lokistack-data
kubectl delete pv lokistack-data-pv
kubectl delete sc lokistack-local
ssh root@${NODE} 'rm -rf /var/lib/lokistack-data'
# 然后重新 bash install.sh
```
### 常见坑
| 现象 | 原因 | 处理 |
|------|------|------|
| Pod 一直 Pending | PV 的 nodeAffinity 指向的节点不可用 | `kubectl get nodes` 确认该节点 Ready,或按上面「迁移」流程改节点 |
| Pod Pending 且报 `node(s) didn't match node affinity` | Pod 被调度到其他节点 | 正常,scheduler 会重试;若持续,检查 PV 的 nodeAffinity 是否写错节点名 |
| 换节点后数据"消失" | 数据在新节点上不存在 | 走「迁移到另一台节点」流程,别只改 nodeAffinity |
| 磁盘写满 | hostPath 用节点本地盘 | `df -h /var/lib`,迁移到大盘或改 `PV_PATH` |
---
## 目录结构
```
kubernetes/
├── install.sh # 一键部署脚本(推荐入口)
├── namespace.yaml # log 命名空间
├── pvc.yaml # 数据卷 30Gi(storageClassName 留空 → 用默认 SC)
├── deployment.yaml # 平台本体(单副本 + Recreate,必须)
├── service.yaml # ClusterIP lokistack:8080
├── service-nodeport.yaml # NodePort 30080(配 ingress 时用)
└── examples/ # 云相关配置,不参与默认部署
├── ingress.yaml # 阿里云 ACK(alb class)
├── ingress-tke.yaml # 腾讯云 TKE(qcloud class)
└── secret.example.yaml # Secret 模板(仅供手工创建参考)
```
> ⚠️ `examples/` 里的文件**故意不放在 `kubernetes/` 根目录**:
> 它们是云厂商相关的(ingress class 各云不同),混在默认目录里会让
> `kubectl apply -f kubernetes/` 在不同云上互相覆盖或报错。
---
## 手动部署(等价于 install.sh,供需要精细控制时用)
```bash
# 1) 命名空间
kubectl apply -f kubernetes/namespace.yaml
# 2) 密钥(⚠️ 必须先做,否则 Pod 起不来)
# 密钥只生成一次,丢失会导致已保存的凭证无法解密
kubectl -n log create secret generic lokistack-secrets \
--from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
--from-literal=ENCRYPTION_KEY="$(openssl rand -hex 32)"
# 3) 数据卷 + 平台 + 服务
kubectl apply -f kubernetes/pvc.yaml
kubectl apply -f kubernetes/deployment.yaml
kubectl apply -f kubernetes/service.yaml
# 4) 等待就绪
kubectl -n log rollout status deployment/lokistack --timeout=180s
```
> ❗**不要直接 `kubectl apply -f kubernetes/`**:Secret 不在目录里(密钥不能进 Git),
> 缺 Secret 时 Pod 会一直 `CreateContainerConfigError`。
---
## 访问
**临时(本地端口转发)**:
```bash
kubectl -n log port-forward svc/lokistack 8080:8080
# 浏览器打开 http://localhost:8080
```
**初始账号**:`admin` / `admin123` ← **首次登录请立即改密**
---
## 配 Ingress(可选)
以腾讯云 TKE 为例(其他云见 `examples/`):
```bash
# 1) 先开 NodePort(TKE 的 qcloud ingress 后端要求 NodePort 类型 Service)
WITH_NODEPORT=1 bash kubernetes/install.sh
# 2) 改域名后应用
vi kubernetes/examples/ingress-tke.yaml # 改 host
kubectl apply -f kubernetes/examples/ingress-tke.yaml
# 3) 查 CLB 分配的公网 IP,把域名解析过去
kubectl -n log get ingress lokistack
```
| 云 | ingressClassName | 备注 |
|----|------------------|------|
| 腾讯云 TKE | `qcloud` | 后端 Service 必须 NodePort |
| 阿里云 ACK | `alb` | 用 `alb.ingress.kubernetes.io/*` 注解 |
| AWS EKS | `alb` 或 `nginx` | 需装 AWS Load Balancer Controller |
| 华为云 CCE | `nginx` 或 `cce` | — |
| 自建 | `nginx` | 需装 ingress-nginx |
---
## 持久化(重要)
- `/app/data` 挂 PVC,保存 `lokistack.json`、`kubeconfigs/`、`known_hosts`。
- **必须 `replicas: 1` + `strategy: Recreate`**:JSON DB 是单写者;滚动更新时两个 Pod
同时挂同一个 RWO PVC 会互相覆盖。
- DB 写入本身是原子的(临时文件 + fsync + rename),容器被杀不会写坏文件。
### 备份:CSI VolumeSnapshot
各云都支持同一套 `snapshot.storage.k8s.io` API(AWS EBS / 腾讯 CBS / 华为 EVS / 阿里云 ESSD):
```yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: lokistack-data-20261025
namespace: log
spec:
volumeSnapshotClassName: <集群的 snapshotClass>
source:
persistentVolumeClaimName: lokistack-data
```
---
## 多云兼容
| 项 | 做法 |
|---|---|
| 存储类 | `pvc.yaml` **不写** `storageClassName`,用集群默认 SC;没有默认 SC 时填对应云的名字(AWS `gp3` / 腾讯 `cbs` / 华为 `csi-disk` / 阿里云 `alicloud-disk-essd`)|
| 镜像仓库 | `deployment.yaml` 的 `image` 填目标云可拉的仓库(Harbor / ACR / TCR / SWR)|
| Loki 存储 | 平台已支持 S3 / OSS / COS / OBS,均 S3 兼容,无需云厂商 SDK |
| 查询链路 | 平台与 Loki 同集群时,数据源选「集群服务」(namespace `log`、service `loki`、port `3100`),无需 Ingress/WAF |
| Namespace | 固定 `log`,跨云一致 |
---
## 与外部实例的关系
平台的 JSON DB 单写者,**不要两个实例共用同一份数据卷**。要么:
- 集群内新实例 + 迁移旧数据文件(停旧实例后拷贝);或
- 保留外部实例做带外管理,集群内实例使用独立数据卷(两套数据)。
---
## 日志
- 平台**只写 stdout**(Go/GIN 默认),不写日志文件;`kubectl logs` 可直接看。
- Kubernetes 由 kubelet 统一轮转,节点级配置建议:
```yaml
# /var/lib/kubelet/config.yaml
containerLogMaxSize: 50Mi
containerLogMaxFiles: 5
```
- Loki / Promtail 同样输出到 stdout,由容器运行时轮转;不要在容器内写业务日志文件。
- 应用自身的部署日志写在 DB(`deployment_logs`),可在部署详情里查看,不占容器日志空间。
---
## 常见问题
| 现象 | 原因 | 解决 |
|------|------|------|
| Pod `CreateContainerConfigError` | 缺 Secret `lokistack-secrets` | 跑 `install.sh`,或手动 `kubectl create secret` |
| Pod `Pending`,PVC `Pending` | 集群没有默认 StorageClass | `pvc.yaml` 里填 `storageClassName` |
| Pod `ImagePullBackOff` | 节点拉不到 `lghost/lokistack:latest` | 换镜像地址(ACR/TCR/SWR/Harbor) |
| `kubectl apply -f kubernetes/` 报 ingress 错误 | 误把 examples 里的 ingress 一起 apply 了 | 只 apply 根目录的 yaml,ingress 单独处理 |
| 重启后数据丢失 | `/app/data` 没挂 PVC | 确认 `deployment.yaml` 的 volumeMounts |
| 两个 Pod 同时写 DB | 用了 RollingUpdate 或多副本 | 必须 `replicas: 1` + `strategy: Recreate` |
---
## 集群凭证的最小权限(「资源」面板相关)
部署卡片上的「资源」按钮读的是**部署目标集群**的用量。平台里登记集群的 kubeconfig/token
可以是**最小权限**,只需下面几条:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: lokistack-viewer
namespace: log # 只授 Loki 所在的命名空间即可
rules:
# 读 StatefulSet 的 requests/limits
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["get"]
# 读 Pod 就绪状态、重启次数
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
# 读实时 CPU/内存用量(metrics-server 的 API 组,常被漏授)
- apiGroups: ["metrics.k8s.io"]
resources: ["pods"]
verbs: ["get", "list"]
```
> ⚠️ `metrics.k8s.io` 是**独立 API 组**,很多「只读」ClusterRole(如内置 `view`)在国内云
> 的 TKE/ACK 上并不包含它。缺这一条时:
- 页面**不会报错**,仍显示 limits、就绪状态、重启次数;
- 实时用量区域会提示「当前集群凭证缺少 metrics.k8s.io 权限」并给出需要补的规则;
- 补授后无需重启平台,刷新弹框即可看到用量。
集群若**没装 metrics-server**(`kubectl top` 报 `Metrics API not available`),
提示会变成「集群未安装 metrics-server」。
### 非 Kubernetes 部署
| 模式 | 采集方式 | 能力 |
|------|---------|------|
| `host-docker`(docker / docker-compose) | SSH 到主机执行 `docker stats --no-stream` + `docker inspect` | 实时 CPU/内存、限额(若 `docker run --memory/--cpus` 有设)、重启次数 |
| `host-binary`(裸进程) | SSH 执行 `ps` 采集进程 CPU%/RSS | 仅实时占用(无 cgroup 限额,无峰值) |
Docker 模式下若容器**没有**设置 `--memory` / `--cpus`,页面会显示「未配置资源限额」,
容量参考值是宿主机内存(`docker stats` 的上限列),此时没有百分比可用于判断扩容。