> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cooree.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Grafana 部署

> 在 Kubernetes 中部署 Grafana 并预置 VictoriaLogs 数据源

Grafana 提供日志查询和展示界面。本文部署单副本 Grafana,安装 VictoriaLogs 数据源插件,并自动预置集群内的 VictoriaLogs 数据源。

## 环境前提

* 已配置 `kubeconfig`,可以执行 `kubectl`
* 已完成 [VictoriaLogs 部署](/observability/vector-victorialogs/victorialogs)
* 集群内可以访问 `victorialogs.observability.svc.cluster.local:9428`
* 目标节点可以拉取 Grafana 镜像和下载 `victoriametrics-logs-datasource` 插件

<Warning>
  本清单使用节点本地 `hostPath` 持久卷,并将 Pod 固定在 `worko1` 节点。应用清单前,请把节点名替换为实际值。生产环境请使用 CSI 存储。
</Warning>

## 部署参数

| 配置       | 默认值                                                       | 说明               |
| -------- | --------------------------------------------------------- | ---------------- |
| 镜像       | `uhub.service.ucloud.cn/kubernetes_images/grafana:11.6.0` | Grafana 容器镜像     |
| 数据目录     | `worko1:/var/lib/observability/grafana`                   | 节点本地目录           |
| 存储容量     | `10Gi`                                                    | 保存插件和 Grafana 数据 |
| 数据源      | `VictoriaLogs`                                            | 默认日志数据源          |
| NodePort | `30080`                                                   | Grafana Web 端口   |

## 部署清单

把以下内容保存为 `grafana.yaml`。部署前,将 `<your-strong-password>` 替换为强密码。

```yaml grafana.yaml theme={null}
apiVersion: v1
kind: Namespace
metadata:
  name: observability
  labels:
    app.kubernetes.io/part-of: observability
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: grafana-local-pv
  labels:
    app.kubernetes.io/name: grafana
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: ""
  hostPath:
    path: /var/lib/observability/grafana
    type: DirectoryOrCreate
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - worko1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: grafana-data
  namespace: observability
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  volumeName: grafana-local-pv
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
  namespace: observability
  labels:
    grafana_datasource: "1"
data:
  datasource.yaml: |
    apiVersion: 1
    datasources:
      - name: VictoriaLogs
        type: victoriametrics-logs-datasource
        access: proxy
        url: http://victorialogs.observability.svc.cluster.local:9428
        isDefault: true
        editable: true
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
  namespace: observability
  labels:
    app.kubernetes.io/name: grafana
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: grafana
  template:
    metadata:
      labels:
        app.kubernetes.io/name: grafana
    spec:
      nodeSelector:
        kubernetes.io/hostname: worko1
      securityContext:
        fsGroup: 472
        fsGroupChangePolicy: Always
      initContainers:
        - name: init-grafana-data
          image: uhub.service.ucloud.cn/kubernetes_images/grafana:11.6.0
          imagePullPolicy: IfNotPresent
          command:
            - /bin/sh
            - -ec
            - |
              mkdir -p /var/lib/grafana/plugins
              chown -R 472:472 /var/lib/grafana
              chmod -R u+rwX,g+rwX /var/lib/grafana
          securityContext:
            runAsUser: 0
            runAsGroup: 0
          volumeMounts:
            - name: grafana-data
              mountPath: /var/lib/grafana
      containers:
        - name: grafana
          image: uhub.service.ucloud.cn/kubernetes_images/grafana:11.6.0
          imagePullPolicy: IfNotPresent
          env:
            - name: GF_SECURITY_ADMIN_USER
              value: admin
            - name: GF_SECURITY_ADMIN_PASSWORD
              value: <your-strong-password> # TODO: 部署前修改,生产建议改用 Secret
            - name: GF_INSTALL_PLUGINS
              value: victoriametrics-logs-datasource
            - name: GF_USERS_ALLOW_SIGN_UP
              value: "false"
          ports:
            - name: http
              containerPort: 3000
          readinessProbe:
            httpGet:
              path: /api/health
              port: http
            initialDelaySeconds: 20
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /api/health
              port: http
            initialDelaySeconds: 60
            periodSeconds: 20
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 1Gi
          volumeMounts:
            - name: grafana-data
              mountPath: /var/lib/grafana
            - name: grafana-datasources
              mountPath: /etc/grafana/provisioning/datasources
              readOnly: true
      volumes:
        - name: grafana-data
          persistentVolumeClaim:
            claimName: grafana-data
        - name: grafana-datasources
          configMap:
            name: grafana-datasources
---
apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: observability
  labels:
    app.kubernetes.io/name: grafana
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: grafana
  ports:
    - name: http
      port: 3000
      targetPort: http
      nodePort: 30080
```

## 数据目录权限

Grafana 官方容器以 UID/GID `472` 运行。清单通过 Pod 级 `fsGroup` 和 root 权限的 initContainer 修正 `hostPath` 目录权限。如果 Pod 停在初始化阶段,请先查看 initContainer 日志:

```bash theme={null}
kubectl -n observability logs deployment/grafana -c init-grafana-data
```

## 应用并验证

```bash theme={null}
kubectl apply -f grafana.yaml
kubectl -n observability rollout status deployment/grafana --timeout=180s
kubectl -n observability get pod,service -l app.kubernetes.io/name=grafana
```

检查 Grafana 健康状态和数据源:

```bash theme={null}
curl http://<节点IP>:30080/api/health
curl -u admin:<密码> http://<节点IP>:30080/api/datasources
```

浏览器访问 `http://<节点IP>:30080`,使用账号 `admin` 和部署前设置的密码登录。

<Warning>
  不要长期使用明文环境变量保存管理员密码。生产环境请改用 Kubernetes Secret,并在首次登录后立即修改密码。
</Warning>

## 查询日志

登录 Grafana 后,打开 **Explore**,选择 **VictoriaLogs** 数据源。可以使用以下 LogsQL 查询:

```text theme={null}
_time:1h
service:demo
collector:vector
```

## 常见故障

| 现象                   | 排查方法                                             |
| -------------------- | ------------------------------------------------ |
| initContainer 报目录不可写 | 确认 Pod 调度到 `worko1`,并检查节点数据目录的挂载和权限              |
| 插件未安装                | 查看 Grafana 启动日志,确认 Pod 可以访问插件下载地址                |
| 数据源连接失败              | 检查 VictoriaLogs Service、Endpoints 和 `/health` 接口 |
| 页面无法访问               | 检查 `grafana` Service 的 NodePort 和节点防火墙           |

## 删除组件

```bash theme={null}
kubectl -n observability delete deployment grafana
kubectl -n observability delete service grafana
kubectl -n observability delete configmap grafana-datasources
kubectl -n observability delete pvc grafana-data
```

<Warning>
  PV 的回收策略是 `Retain`。确认 Grafana 数据不再需要后,再删除 `grafana-local-pv` 并清理节点目录。
</Warning>

## 相关文档

* [部署 VictoriaLogs](/observability/vector-victorialogs/victorialogs):检查日志存储和数据源接口
* [部署 Vector](/observability/vector-victorialogs/vector):采集 Kubernetes 容器日志
