# Grafana

> 以 pgcli 插件方式运行 Grafana——开源可观测性面板，单容器直接跑上游官方镜像，自动生成 Web 登录

---

LLMS 索引： [llms.txt](/zh/llms.txt)

---

`pg addon install grafana` 以受管容器方式运行 [Grafana](https://grafana.com/oss/grafana/)——开源可观测性面板——直接跑官方上游镜像。与 pgAdmin 一样，它是**顶层插件**：不依附于任何单个 PostgreSQL 实例，也不会自动前置它们——数据源和仪表盘都在 Web UI 里自己添加。v1 是纯 Web UI：配置只经环境变量（`GF_*`）交付，不挂载任何 datasource/dashboard 的 provisioning。

Web 登录密码在首次安装时自动生成、打印一次、存入 `pg.yaml`，之后用 `pg addon password grafana` 取回。与 pgAdmin 不同，`Listen` 默认 `0.0.0.0`：监控面板天然要给别的机器看，而容器跑在宿主网络上。暴露面由自动生成的强密码与关闭自助注册兜底——只想本机访问就传 `--listen 127.0.0.1`。

> **平台**：Linux 是已实测的平台。设计上也支持 macOS（镜像多架构；容器在
> `pgcli-net` bridge 上以发布端口的方式跑在 podman machine 里），但尚未在
> Mac 上验证过。

## 参数

| 参数 | 默认值 | 说明 |
|---|---|---|
| `--name` | `grafana` | 实例名（map 键） |
| `--image` | `docker.io/grafana/grafana:13.2.4` | 覆盖镜像 |
| `--port` | 自动（基址 3000） | Web 宿主端口；自 `grafana_start_port` 自动分配 |
| `--listen` | `0.0.0.0` | 绑定地址——默认开放全部网卡；`127.0.0.1` 收紧为仅回环 |
| `--admin-user` | `admin` | Web 登录账号（`GF_SECURITY_ADMIN_USER`） |
| `--password` | 自动生成 | Web 登录密码（`GF_SECURITY_ADMIN_PASSWORD`；缺省时生成，**只在首次启动时生效**，见下） |
| `--data-dir` | 自动 | 覆盖数据目录（默认在 base 目录下） |
| `--force` | false | 即使正在运行也重建——用于应用改动的端口、凭据或监听地址 |

数据目录存放 Grafana 自己的 sqlite 元数据库（`grafana.db`）：仪表盘、数据源、用户与设置都在里面。它跨 `pg addon remove` 存活（用 `--clean-data` 才删除），重装同名实例即复活一切。镜像的 entrypoint **不做**属主修复（它以固化的 uid 472、root 组运行，目录不可写时只会告警），所以 pgcli 在创建容器前先在宿主上把数据目录 chown 过去：rootless podman 下经 `podman unshare chown 472:0`，rootful 宿主且 pgcli 以 root 运行时直接递归 `chown`。

## 安装

```bash
pg addon install grafana
```

命令会按需拉取镜像、创建容器，并打印 URL 与登录密码：

```
✓ Grafana installed: "grafana"
  Container:  pgcli-grafana-default-grafana
  URL:        http://0.0.0.0:3000/
  Login user:     admin
  Login password: <auto-generated>
```

用浏览器打开 URL，以打印的凭据登录。在 UI 里添加 PostgreSQL 数据源（Connect data → PostgreSQL；每个受管实例的宿主端口在其 `pg.yaml` 条目里，或由 `pg status <instance>` 显示），再基于它搭建仪表盘。

`GF_SECURITY_ADMIN_USER` / `GF_SECURITY_ADMIN_PASSWORD` **只在 `grafana.db` 不存在时生效**——它们初始化登录，不覆盖登录。在保留的数据目录上重装，Web 登录仍是原来的；此次打印的密码无效（install 会明说）。要修改既有存储的登录，进容器跑 `grafana-cli admin reset-admin-password <new>`（`pg exec` 风格：`podman exec -it <container> ...`），或 `--clean-data` 移除后重装。

重复 `install` 会复用运行中的容器、启动已停的容器——配置改动只有 `remove` + 重装或 `--force` 之后才生效。

## 监听与暴露

Grafana 的 `Listen` 默认 `0.0.0.0`：容器共享宿主网络命名空间，所以面板在宿主每个网卡的分配端口上可达。这是有意为之——监控面板就是给别的机器看的——并由自动生成的强 admin 密码加 `GF_USERS_ALLOW_SIGN_UP=false`（关闭自助注册）保护。想要仅回环的部署，传 `--listen 127.0.0.1`（用 `--force` 重建）；`pg addon list` 会显示 `http://127.0.0.1:<port>/`。`--listen <addr>` 也接受任意指定网卡的地址。

## 取回 Web 登录

这个登录是 Grafana 的 Web 签名，与任何 PostgreSQL 角色无关。随时可重读密码：

```bash
pg addon password grafana              # 裸打印到 stdout
pg addon password grafana --file ./pw  # 或写入 0600 文件
```

它也存于 `pg.yaml`（`addons.grafana.<name>.password`）。

## 端口

每个 Grafana 实例占有**一个**端口：Web UI。它取自以 `grafana_start_port`（默认 `3000`——Grafana 惯例端口；从 3000 起步是因为书签和文档都默认它）为基址的端口池，每多一个实例递增，并跳过任何已被受管容器或 `used_ports` 列表占用的端口。分配结果写入 `pg.yaml`，所以重装绝不会把别的插件在用的端口再发一遍：

```bash
pg addon install grafana --name dash2          # 第二个实例：下一个空闲端口（3000 被占则 3001）
pg addon install grafana --name dash3 --port 3100  # 显式端口优先
```

Grafana 的端口池基址与其他任何插件都不重号；显式 `--port` 仍然优先。

## 配置

```yaml
addons:
  grafana:
    ui:
      name: ui
      image_tag: docker.io/grafana/grafana:13.2.4
      host_port: 3000
      listen: 0.0.0.0
      admin_user: admin
      password: <generated>
      autostart: false
      # data_dir: /srv/grafana-data    # 可选，覆盖默认位置
```

配置只经环境变量（`GF_*`）交付：镜像内的 `grafana.ini` 从宿主上不可编辑。pgcli 设置的变量有：登录对、监听器（端口 + 绑定地址）、关闭上报（`GF_ANALYTICS_REPORTING_ENABLED` / `GF_ANALYTICS_CHECK_FOR_UPDATES`）、`GF_USERS_ALLOW_SIGN_UP=false`。更多配置不在 v1 范围内；需要不同构建就用 `--image` 覆盖镜像。

### 列出

```bash
pg addon list
```

```
Web add-ons (grafana):
  grafana (name: ui)
    Status:      running
    URL:         http://0.0.0.0:3000/
    Login user:  admin
    Data:        /home/you/.pgcli/addon/grafana/ui/data
    Image:       docker.io/grafana/grafana:13.2.4
    Container:   pgcli-grafana-ui
```

默认不显示密码以保持列表可安全粘贴；`pg addon list --show-password` 揭示明文，`pg addon password grafana` 则为脚本裸打印单个密码。

## 启停

容器以 podman 的 `unless-stopped` 重启策略运行，宿主重启后随 podman 服务自动回来。`start`/`stop` 显式管理它们：

```bash
pg addon stop grafana
pg addon start grafana
```

## 开机自启

要让 Grafana 实例在宿主开机时自动启动，开启它：

```bash
pg autostart enable --grafana --name ui
```

这会在 `pg.yaml` 里给实例打标记，并安装/刷新一个开机服务（Linux 上是 systemd `--user` unit，macOS 上是 LaunchAgent）来执行 `pg start --autostart`。开机启动只负责把容器拉起来——数据目录在创建时已 chown 过，除 podman 本身外不需要任何宿主特权。完整机制、状态与关闭方式见 [开机自启](/zh/docs/autostart/)。

## 移除

```bash
pg addon remove grafana              # 保留数据目录——仪表盘与登录跨重装存活
pg addon remove grafana --clean-data # 连数据目录一起删（Grafana 的数据就没了）
```

rootless podman 下，`--clean-data` 经 `podman unshare rm` 删除数据目录——Grafana 以容器内 uid（472）写文件，它映射到宿主的 subordinate uid，宿主用户无法直接 `rm`。

## 日志

```bash
pg logs addon grafana            # 最近的控制台输出
pg logs addon grafana -f         # 跟踪
pg logs addon grafana -n 200     # 最近 200 行（也支持 -i / --tail）
```

## 排障

- **`install` 失败于 "chowning Grafana data dir ... to 472:0"** —— 镜像以 uid 472 运行且从不自己修复属主，所以 pgcli 在创建容器前先把宿主数据目录 chown 过去：**rootless** podman 下经 `podman unshare chown`（映射后的 subordinate uid 才是正确目标），**rootful** 宿主且 pgcli 自身以 root 运行时（`sudo podman`，或以 root 跑 `pg`）直接递归 `chown`。pgcli 是直接调用 `podman` CLI，所以只在这两种情形之外才会失败——例如非 root 的 `pg` 被显式指向别人的 rootful daemon。遇到时以 root 跑 install，或改用 rootless podman；单纯 `sudo chown -R 472:0 <data-dir>` 不能消除该错误，因为每次 install 都会重新校验属主。
- **登录密码不对** —— `GF_SECURITY_ADMIN_PASSWORD` 只在 `grafana.db` 不存在时初始化登录。对既有存储用容器内 `grafana-cli admin reset-admin-password <new>`，或 `pg addon remove grafana --clean-data` + 重装（会清掉仪表盘）。
- **从别的机器打不开面板** —— `Listen` 默认 `0.0.0.0`，端口在宿主所有网卡上是开的；打不开就是宿主防火墙的问题，不是绑定地址。用 `pg addon list` 确认端口，然后放行。
- **端口已被分配** —— 已分配的端口记在 `pg.yaml` 里并为后续实例跳过；先 `pg addon remove` 释放，或用 `--port N` 另选一个。
- **数据加载失败 / 容器以权限告警退出** —— 数据目录必须保持 uid 472 所有；在宿主上手动 `chown` 过、或用错误属主恢复过备份都会破坏它。重跑 install（`--force`）重新应用属主修复。

## 已知限制

- v1 是裸 Web UI：没有 datasource/dashboard provisioning，不支持反代子路径（没有 `--root-url` flag——`GF_SERVER_ROOT_URL` 不设置，所以要在根路径上用 [nginx](/zh/docs/addon/nginx/) 前置），且是明文 HTTP（TLS 终止是 nginx 插件的职责）。
- 宿主侧的属主 chown 在 rootless podman（`podman unshare chown`）下自动完成，pgcli 以 root 跑在 rootful 宿主上也自动完成（直接 `chown`）。非 root 的 pgcli 连接一个显式配置的远程 rootful daemon 时没有自动路径。
- macOS 上容器跑在 podman machine 里；目前只在 Linux 上验证过。

---

反链：

- [插件 (Addons)](/zh/docs/addon/)
- [平台支持](/zh/docs/platform/)
