feat(site): homepage 落地页 + docsify 用户手册文档站(/docs /homepage 随二进制发布)
- homepage/:品牌落地页(Hero/特性/快速开始),参考 opsxapi 站点风格,绿色品牌系 - docs/:docsify 文档站 + user-guide 十篇手册(安装/向导/采集/查询/分析/告警/授权/备份/FAQ) - 后端 serveStaticDir 挂载 /docs 与 /homepage(可执行文件相对定位,独立于前端 dist)
This commit is contained in:
1 parent
b8d08ef6d1
commit
3ea7f2dcc6
17 files changed
+605
No files matched your search
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"lokistack/pkg/metrics"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -108,3 +109,8 @@ func (ctrl *SystemController) SaveRetention(c *gin.Context) {
|
||||
func (ctrl *SystemController) Health(c *gin.Context) {
|
||||
utils.Success(c, services.SelfCheck())
|
||||
}
|
||||
|
||||
// MetricsSummary 平台自监控快照(仅 admin;原始 Prometheus 文本在 /api/v1/metrics)
|
||||
func (ctrl *SystemController) MetricsSummary(c *gin.Context) {
|
||||
utils.Success(c, metrics.Snapshot())
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
// PathCount 单个路由模板的累计请求数
|
||||
type PathCount struct {
|
||||
Path string `json:"path"`
|
||||
Count float64 `json:"count"`
|
||||
}
|
||||
|
||||
// Summary 面板自监控快照:从 registry 聚合出的精简结构(/api/v1/system/metrics-summary)
|
||||
type Summary struct {
|
||||
HTTP struct {
|
||||
Total float64 `json:"total"`
|
||||
ByStatus map[string]float64 `json:"byStatus"`
|
||||
InFlight float64 `json:"inFlight"`
|
||||
TopPaths []PathCount `json:"topPaths"`
|
||||
} `json:"http"`
|
||||
DB struct {
|
||||
SizeBytes float64 `json:"sizeBytes"`
|
||||
Saves float64 `json:"saves"`
|
||||
SaveFailures float64 `json:"saveFailures"`
|
||||
Records map[string]float64 `json:"records"`
|
||||
} `json:"db"`
|
||||
Alerts struct {
|
||||
Evaluations map[string]float64 `json:"evaluations"`
|
||||
} `json:"alerts"`
|
||||
Loki struct {
|
||||
Queries map[string]float64 `json:"queries"`
|
||||
} `json:"loki"`
|
||||
Runtime struct {
|
||||
Goroutines float64 `json:"goroutines"`
|
||||
GoVersion string `json:"goVersion"`
|
||||
Version string `json:"version"`
|
||||
} `json:"runtime"`
|
||||
}
|
||||
|
||||
// Snapshot 聚合当前指标为面板结构(聚合失败返回零值,监控页降级展示)
|
||||
func Snapshot() Summary {
|
||||
s := Summary{}
|
||||
s.HTTP.ByStatus = map[string]float64{}
|
||||
s.DB.Records = map[string]float64{}
|
||||
s.Alerts.Evaluations = map[string]float64{}
|
||||
s.Loki.Queries = map[string]float64{}
|
||||
|
||||
families, err := registry.Gather()
|
||||
if err != nil {
|
||||
return s
|
||||
}
|
||||
byPath := map[string]float64{}
|
||||
for _, mf := range families {
|
||||
for _, m := range mf.GetMetric() {
|
||||
labels := labelMap(m)
|
||||
switch mf.GetName() {
|
||||
case "lokistack_http_requests_total":
|
||||
v := m.GetCounter().GetValue()
|
||||
s.HTTP.Total += v
|
||||
s.HTTP.ByStatus[labels["status"]] += v
|
||||
byPath[labels["path"]] += v
|
||||
case "lokistack_http_in_flight_requests":
|
||||
s.HTTP.InFlight = m.GetGauge().GetValue()
|
||||
case "lokistack_db_size_bytes":
|
||||
s.DB.SizeBytes = m.GetGauge().GetValue()
|
||||
case "lokistack_db_save_total":
|
||||
v := m.GetCounter().GetValue()
|
||||
s.DB.Saves += v
|
||||
if labels["result"] != "success" {
|
||||
s.DB.SaveFailures += v
|
||||
}
|
||||
case "lokistack_db_records":
|
||||
s.DB.Records[labels["collection"]] = m.GetGauge().GetValue()
|
||||
case "lokistack_alert_evaluations_total":
|
||||
s.Alerts.Evaluations[labels["result"]] += m.GetCounter().GetValue()
|
||||
case "lokistack_loki_queries_total":
|
||||
s.Loki.Queries[labels["result"]] += m.GetCounter().GetValue()
|
||||
case "go_goroutines":
|
||||
s.Runtime.Goroutines = m.GetGauge().GetValue()
|
||||
case "lokistack_build_info":
|
||||
s.Runtime.GoVersion = labels["go_version"]
|
||||
s.Runtime.Version = labels["version"]
|
||||
}
|
||||
}
|
||||
}
|
||||
paths := make([]PathCount, 0, len(byPath))
|
||||
for p, c := range byPath {
|
||||
paths = append(paths, PathCount{Path: p, Count: c})
|
||||
}
|
||||
sort.Slice(paths, func(i, j int) bool { return paths[i].Count > paths[j].Count })
|
||||
if len(paths) > 8 {
|
||||
paths = paths[:8]
|
||||
}
|
||||
s.HTTP.TopPaths = paths
|
||||
return s
|
||||
}
|
||||
|
||||
func labelMap(m *dto.Metric) map[string]string {
|
||||
out := map[string]string{}
|
||||
for _, lp := range m.GetLabel() {
|
||||
out[lp.GetName()] = lp.GetValue()
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -152,6 +152,7 @@ func SetupRouter() *gin.Engine {
|
||||
system.POST("/restore/apply", systemCtrl.RestoreApply)
|
||||
system.GET("/backups", systemCtrl.Backups)
|
||||
system.GET("/self-check", systemCtrl.Health)
|
||||
system.GET("/metrics-summary", middleware.AdminAuth(), systemCtrl.MetricsSummary)
|
||||
}
|
||||
|
||||
// 审计日志:仅管理员可查(含他人操作记录)
|
||||
@@ -235,6 +236,11 @@ func SetupRouter() *gin.Engine {
|
||||
authorized.POST("/collectors/:id/preview", collectorCtrl.Preview)
|
||||
authorized.GET("/collectors/:id/logs", collectorCtrl.Logs)
|
||||
|
||||
// 文档站(docsify,docs/)与落地页(homepage/)随二进制一起发布;
|
||||
// 独立于前端 dist 存在与否注册,目录不存在时路由不生效
|
||||
serveStaticDir(r, "/docs", "docs")
|
||||
serveStaticDir(r, "/homepage", "homepage")
|
||||
|
||||
// Static files - use executable directory as base
|
||||
distPath := findDistPath()
|
||||
|
||||
@@ -267,6 +273,40 @@ func SetupRouter() *gin.Engine {
|
||||
return r
|
||||
}
|
||||
|
||||
// serveStaticDir 把本地目录挂为只读静态站点:目录回退到 fallbackFile(SPA/docsify 需要)。
|
||||
// baseDir 候选与 findDistPath 同源:可执行文件相对路径优先,再退工作目录。
|
||||
func serveStaticDir(r *gin.Engine, urlPrefix, dirName string) {
|
||||
root := findAuxDir(dirName)
|
||||
if root == "" {
|
||||
return
|
||||
}
|
||||
// http.FileServer 自带目录回退:访问 /docs 自动 301 到 /docs/ 并命中 index.html
|
||||
r.Static(urlPrefix, root)
|
||||
}
|
||||
|
||||
// findAuxDir 定位 dist 之外的附属静态目录(docs/homepage),规则与 findDistPath 对齐
|
||||
func findAuxDir(dirName string) string {
|
||||
if ex, err := os.Executable(); err == nil {
|
||||
execDir := filepath.Dir(ex)
|
||||
if !strings.Contains(execDir, "/tmp/go-build") {
|
||||
for _, rel := range []string{dirName, "../" + dirName, "../../" + dirName} {
|
||||
p := filepath.Join(execDir, rel)
|
||||
if _, err := os.Stat(filepath.Join(p, "index.html")); err == nil {
|
||||
return p
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, p := range []string{"./" + dirName, "../" + dirName, "/app/" + dirName} {
|
||||
if abs, err := filepath.Abs(p); err == nil {
|
||||
if _, err := os.Stat(filepath.Join(abs, "index.html")); err == nil {
|
||||
return abs
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// findDistPath finds the frontend dist directory
|
||||
func findDistPath() string {
|
||||
// Try relative to executable
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>LokiStack 用户手册</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%23059669'/%3E%3Ctext x='32' y='44' font-size='34' font-weight='800' fill='%23fff' text-anchor='middle' font-family='sans-serif'%3EL%3C/text%3E%3C/svg%3E">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css">
|
||||
<style>
|
||||
:root { --theme-color: #059669; --base-font-size: 15px; }
|
||||
body { font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'LokiStack 用户手册',
|
||||
repo: 'https://gitea.91ai.io/rlwj-lab/LokiStack',
|
||||
homepage: 'user-guide/README.md',
|
||||
loadSidebar: 'user-guide/_sidebar.md',
|
||||
loadNavbar: false,
|
||||
search: { placeholder: '搜索手册', noData: '没有匹配结果' },
|
||||
subMaxLevel: 3,
|
||||
};
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
# LokiStack 用户手册
|
||||
|
||||
LokiStack 把 Grafana Loki 日志聚合的部署、采集、查询、分析与告警收进一个平台:向导式部署到 Kubernetes 集群或裸金属主机,内置中文/英文界面,单二进制交付。
|
||||
|
||||
## 平台能做什么
|
||||
|
||||
- **部署 Loki**:三步向导(环境与目标 → 存储 → 部署与采集),支持 Kubernetes(YAML 直发)、裸金属二进制、主机 Docker 三种模式,不需要在平台所在机器安装 kubectl 或 Helm。
|
||||
- **采集日志**:Promtail / Fluent Bit 采集策略可视化配置,命名空间多选、app 标签来源四级回退、敏感路径拦截。
|
||||
- **查询与分析**:LogQL 高亮补全、快捷查询、Live Tail;分析页自动聚合日志量、错误与 Ingress 访问指标。
|
||||
- **告警**:内置九条应用日志规则,绑定默认通道后填入飞书 Webhook 即可接收;支持冷却与风暴抑制。
|
||||
- **运维底座**:凭证加密存储、操作审计、备份恢复、Prometheus 自监控指标。
|
||||
|
||||
## 下一步
|
||||
|
||||
阅读[安装与启动](install.md),或直接进入[部署向导](wizard.md)。
|
||||
@@ -0,0 +1,10 @@
|
||||
- [开始](README.md)
|
||||
- [安装与启动](install.md)
|
||||
- [部署向导](wizard.md)
|
||||
- [日志采集](collectors.md)
|
||||
- [日志查询](query.md)
|
||||
- [日志分析](analysis.md)
|
||||
- [告警通知](alerts.md)
|
||||
- [查询授权](access.md)
|
||||
- [备份与恢复](backup.md)
|
||||
- [常见问题](faq.md)
|
||||
@@ -0,0 +1,10 @@
|
||||
# 查询授权
|
||||
|
||||
管理员在「日志查询 → 查询授权」用策略组控制普通用户可见的日志范围:
|
||||
|
||||
- 策略组 = 数据源 + 允许的标签范围(命名空间 / App / Pod,均精确匹配,未选不限) + 关联用户。
|
||||
- 同一用户在同一数据源只能属于一个策略组;已在其他组的用户置灰。
|
||||
- 支持快捷的「所有人」策略(默认策略的常用形态)。
|
||||
- 授权在服务端强制执行:普通用户的查询必须以流选择器开头,策略标签自动合并,无法越权。
|
||||
|
||||
用户管理(仅管理员)支持创建/重置密码/批量删除;LDAP 用户可在系统设置接入后导入。
|
||||
@@ -0,0 +1,24 @@
|
||||
# 告警通知
|
||||
|
||||
「日志告警」页(`/alerts`)管理规则、通知通道与发送历史。
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. 在「通知通道」中编辑默认通道,填入飞书群机器人 Webhook 并启用。
|
||||
2. 内置九条规则默认启用并已绑定默认通道,命中阈值即发送到群。
|
||||
|
||||
## 规则
|
||||
|
||||
- 查询为 LogQL 日志流匹配;窗口(分钟)、阈值(条数)、冷却(分钟)三要素。
|
||||
- 内置规则已排除 Loki 自身日志(app=loki),避免告警查询日志造成自我误报。
|
||||
- 模板按钮一键填充常用规则(命名空间错误 / Ingress 4xx5xx / FATAL 等)。
|
||||
|
||||
## 通道与发送历史
|
||||
|
||||
- 通道支持飞书 Webhook(可选签名);可建多通道对应不同告警群。
|
||||
- 发送历史记录每次触发的命中数、投递结果与失败原因;保留天数在「系统设置 → 日志保留时间」调整。
|
||||
|
||||
## 防刷屏机制
|
||||
|
||||
- 单规则冷却(默认 ≥ 2 小时);全平台每小时发送上限(风暴抑制)。
|
||||
- 通道未配置/未启用时命中只记入历史,不影响规则评估。
|
||||
@@ -0,0 +1,13 @@
|
||||
# 日志分析
|
||||
|
||||
「日志分析」页(`/analysis`,管理员)自动聚合日志指标,无需手动查询:
|
||||
|
||||
- **统计卡**:日志总量、错误行数、告警行数、活跃应用
|
||||
- **趋势折线**:总量 / 错误 / 告警随时间分布
|
||||
- **命名空间分布**:环形图
|
||||
- **应用流图**:Top 10 应用日志量堆叠
|
||||
- **每日日志量**:近 7 天按日聚合(独立于时间范围筛选)
|
||||
- **应用排行**:Top 10 应用,支持跳转查询页
|
||||
- **Ingress 访问分析**:自动识别 ingress-nginx 访问日志,PV/UV、状态码、慢请求与接口排行;未安装 ingress-nginx 时自动折叠
|
||||
|
||||
时间范围与数据源在页面顶部切换;指标查询经服务端聚合,开销远低于拉取原始日志。
|
||||
@@ -0,0 +1,23 @@
|
||||
# 备份与恢复
|
||||
|
||||
平台状态集中在单个 JSON 文件(`$DATA_DIR/lokistack.json`),备份恢复围绕它展开。
|
||||
|
||||
## 备份
|
||||
|
||||
- 手动:「系统设置 → 备份」立即创建(写入 `$DATA_DIR/backups/`)。
|
||||
- 脚本:`./run.sh backup` 或 `make` 内相应目标,适合 cron 定时。
|
||||
|
||||
## 恢复
|
||||
|
||||
1. 上传统计备份或选择已有备份,校验后暂存。
|
||||
2. 点击应用:平台先把当前库留档(`.before-restore.*`),替换后立即在内存生效,随后建议重启进程。
|
||||
3. 恢复文件损坏时平台拒绝应用并提示。
|
||||
|
||||
## 数据文件保护
|
||||
|
||||
- 启动时若数据文件解析失败,原文件会以 `.corrupt-<时间戳>` 留档后再以空库启动,绝不覆盖可恢复数据。
|
||||
- 历史命名数据文件(opsxloki.json / lokideploy.json)在启动时自动升级为 lokistack.json。
|
||||
|
||||
## 自监控
|
||||
|
||||
`/api/v1/metrics` 输出 Prometheus 指标(HTTP、DB 落盘、告警引擎、Go runtime);「平台监控」页(`/monitor`,管理员)提供可视化快照。
|
||||
@@ -0,0 +1,18 @@
|
||||
# 日志采集
|
||||
|
||||
「日志采集」页管理采集策略:主机采集(Promtail)与集群采集(DaemonSet / Fluent Bit Sidecar)。
|
||||
|
||||
## 采集范围
|
||||
|
||||
- **集群采集**:命名空间多选(默认全集群);app 标签按 `app.kubernetes.io/name → app → controller 名 → pod 名` 四级回退,可自定义来源标签与 app 白名单收敛日志量。
|
||||
- **主机采集**:填写业务日志路径(glob 通配,如 `/var/log/app/*.log`);系统日志自动探测(syslog / messages)。
|
||||
- 敏感路径(/etc/shadow、私钥、/proc 等)会被平台拦截,防止主机凭据进入日志平台。
|
||||
|
||||
## 编辑与下发
|
||||
|
||||
保存后点击「部署」下发到目标;「预览 YAML」可先核对将生成的 promtail 配置。修改后同样以「部署」生效。
|
||||
|
||||
## 排查
|
||||
|
||||
- 采集器卡片可看运行状态与日志(主机 docker logs / journalctl,集群 Pod 日志)。
|
||||
- 常见问题:目标命名空间无工作负载(app 候选为空)、路径无写入权限、Loki 端点不可达。
|
||||
@@ -0,0 +1,29 @@
|
||||
# 常见问题
|
||||
|
||||
## 登录后看不到「部署向导」「用户管理」等菜单?
|
||||
|
||||
这些页面仅管理员可见。普通用户默认只有日志查询与日志告警;查询范围由策略组控制(见[查询授权](access.md))。
|
||||
|
||||
## 部署的 Loki 怎么访问?必须配 Ingress 吗?
|
||||
|
||||
集群内部署经 API Server Service Proxy 查询,不需要 Ingress。跨集群 / 公网查询才需要为 Loki 暴露域名后在「数据源管理」用 URL 直连关联。
|
||||
|
||||
## 「停止部署」会删掉日志数据吗?
|
||||
|
||||
不会。停止只回收工作负载;数据卷(PVC / 主机数据目录)保留,重新启动即可恢复。仅显式删除部署记录不影响远端资源,卸载需按页面指引执行。
|
||||
|
||||
## 告警一直不发送?
|
||||
|
||||
按顺序检查:通道是否启用、Webhook 是否正确(通道页有测试)、规则是否启用且命中阈值、发送历史里是否有「冷却抑制」或失败原因。通道未就绪时历史会明确记录失败原因。
|
||||
|
||||
## 忘记管理员密码?
|
||||
|
||||
管理员可在「用户管理」重置任意账号密码;若唯一管理员丢失密码,参考仓库 README 的离线重置说明。
|
||||
|
||||
## 平台可以多实例部署吗?
|
||||
|
||||
不可以。JSON 文件存储为单实例设计(防抖串行落盘),水平扩展会互相覆盖;需要高可用时用单实例 + 备份策略。
|
||||
|
||||
## 升级安全吗?
|
||||
|
||||
配置与数据向后兼容,结构变更按 schema 版本自动迁移;建议升级前在「系统设置」创建一次备份。
|
||||
@@ -0,0 +1,44 @@
|
||||
# 安装与启动
|
||||
|
||||
## 环境要求
|
||||
|
||||
- 平台自身:任意 Linux / macOS(Go 1.24 构建或直接使用发布二进制),内存 ≥ 256MB
|
||||
- 目标 Kubernetes 集群:≥ 1.20;或裸金属 Linux 主机(SSH 可达)
|
||||
- 端口:平台默认 8080;Loki 默认 3100
|
||||
|
||||
## 必备环境变量
|
||||
|
||||
平台启动强制要求两个密钥(长度均需 ≥ 32 字符):
|
||||
|
||||
```bash
|
||||
export JWT_SECRET=$(openssl rand -hex 32)
|
||||
export ENCRYPTION_KEY=$(openssl rand -hex 32) # 凭证加密密钥,丢失将无法解密已存凭证
|
||||
```
|
||||
|
||||
## 方式一:开发模式
|
||||
|
||||
```bash
|
||||
./run.sh # 同时拉起前端(:3000)与后端(:8080),自动注入开发密钥
|
||||
```
|
||||
|
||||
## 方式二:生产 Docker
|
||||
|
||||
```bash
|
||||
make secrets # 生成 ./secrets/*.txt
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
## 方式三:单二进制
|
||||
|
||||
```bash
|
||||
bash build.sh # 产物 backend/bin/lokistack(前端已内嵌)
|
||||
export JWT_SECRET=... ENCRYPTION_KEY=... DATA_DIR=/data
|
||||
./lokistack
|
||||
```
|
||||
|
||||
首次启动会自动创建管理员账号,初始密码写入 `$DATA_DIR/initial-admin-password.txt`,请立即修改。
|
||||
|
||||
## 数据与升级
|
||||
|
||||
- 全部状态存储在 `$DATA_DIR/lokistack.json`(JSON 文件,无外部数据库依赖,单实例运行)
|
||||
- 升级:替换二进制后重启即可;历史命名数据文件(opsxloki.json / lokideploy.json)会在启动时自动迁移
|
||||
@@ -0,0 +1,20 @@
|
||||
# 日志查询
|
||||
|
||||
「日志查询」页(`/logs`)按 LogQL 与时间范围检索日志。
|
||||
|
||||
## 查询方式
|
||||
|
||||
- **LogQL 编辑器**:语法高亮与标签/管道补全;回车执行。
|
||||
- **快捷查询**:选择命名空间缩小范围后,选中 App / Pod 即自动填入并执行;Ingress 访问日志按钮在选择 ingress 命名空间或 app 时出现。
|
||||
- **Label 浏览器**:按标签与值点选构建查询;点结果行的标签值可快速加过滤。
|
||||
- **Live Tail**:3 秒轮询最近 5 分钟,编辑查询自动防抖后切换。
|
||||
|
||||
## 结果区
|
||||
|
||||
- 顶部时间线展示整个查询范围的日志量,拖选高峰时段自动重查。
|
||||
- 原始日志支持关键字高亮、标签过滤、查看上下文(前后 N 行)。
|
||||
- 翻页向后游标推进,每页最多 1000 条。
|
||||
|
||||
## 数据源
|
||||
|
||||
「数据源管理」维护 Loki 连接:平台部署自动注册(随部署托管),也可手动添加 URL 直连或集群 Service 数据源,支持 Basic / Bearer / 自定义 Header 认证与连接测试。
|
||||
@@ -0,0 +1,27 @@
|
||||
# 部署向导
|
||||
|
||||
部署向导(`/wizard`)三步完成 Loki 上线:环境与目标 → 存储 → 部署 Loki 与采集。
|
||||
|
||||
## 第一步:环境与目标
|
||||
|
||||
- **Kubernetes 集群**:粘贴 kubeconfig,或按页面给出的 RBAC 脚本在集群执行一次后粘贴 ServiceAccount 长期 Token。平台经 API Server 操作集群,无需安装 kubectl/Helm。
|
||||
- **裸金属主机**:填写 SSH 地址与凭证(密码或私钥,AES-GCM 加密存储)。适合二进制或 Docker 模式。
|
||||
|
||||
创建后平台自动验证连通性;Kubernetes 资源默认落在 `log` 命名空间(可改)。
|
||||
|
||||
## 第二步:准备存储
|
||||
|
||||
推荐对象存储(MinIO / RustFS / S3 / OSS):多副本与数据保留更可靠,创建时可用「测试连接」验证凭证并自动建桶;跳过则使用本地磁盘。
|
||||
|
||||
## 第三步:部署与采集
|
||||
|
||||
- 选择目标、Loki 版本(支持 2.8+ / 3.x)、保留天数与资源规格,点击部署。
|
||||
- 部署成功后平台自动注册同名数据源,日志查询页立即可用;按提示添加采集策略(见[日志采集](collectors.md))。
|
||||
|
||||
## 三种部署模式
|
||||
|
||||
| 模式 | 适用 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| k8s-yaml | Kubernetes | StatefulSet + SSA 直发,平台全托管 |
|
||||
| host-binary | 裸金属 | SSH 安装二进制 + systemd 托管 |
|
||||
| host-docker | 裸金属 | Docker 容器运行,自动目录迁移 |
|
||||
@@ -0,0 +1,58 @@
|
||||
:root {
|
||||
--brand: #059669; --brand-dark: #047857; --ink: #0f172a; --muted: #64748b;
|
||||
--bg: #f8fafc; --card: #ffffff; --line: #e2e8f0;
|
||||
--code-bg: #0f172a; --code-ink: #e2e8f0;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: -apple-system, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif; color: var(--ink); background: var(--bg); line-height: 1.7; }
|
||||
a { color: var(--brand); text-decoration: none; }
|
||||
.container { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
|
||||
|
||||
.nav { position: sticky; top: 0; z-index: 10; background: rgba(255,255,255,.92); backdrop-filter: blur(8px); border-bottom: 1px solid var(--line); }
|
||||
.nav-inner { max-width: 1080px; margin: 0 auto; padding: 14px 24px; display: flex; align-items: center; justify-content: space-between; }
|
||||
.brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 20px; color: var(--ink); }
|
||||
.brand-logo { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 9px; background: linear-gradient(135deg, #10b981, #047857); color: #fff; font-weight: 800; }
|
||||
.nav-links { display: flex; gap: 26px; font-size: 15px; }
|
||||
.nav-links a { color: var(--muted); }
|
||||
.nav-links a:hover { color: var(--ink); }
|
||||
|
||||
.hero { padding: 96px 0 84px; text-align: center; background: radial-gradient(1200px 500px at 50% -100px, rgba(16,185,129,.12), transparent); }
|
||||
.hero h1 { font-size: 46px; line-height: 1.25; letter-spacing: -1px; }
|
||||
.hero h1 em { font-style: normal; color: var(--brand); }
|
||||
.hero p.tagline { margin: 18px auto 0; max-width: 640px; font-size: 19px; color: var(--muted); }
|
||||
.hero-actions { margin-top: 34px; display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
|
||||
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 26px; border-radius: 10px; font-size: 16px; font-weight: 600; border: 1px solid transparent; transition: all .15s; }
|
||||
.btn-primary { background: var(--brand); color: #fff; }
|
||||
.btn-primary:hover { background: var(--brand-dark); }
|
||||
.btn-outline { border-color: var(--line); color: var(--ink); background: var(--card); }
|
||||
.btn-outline:hover { border-color: var(--brand); color: var(--brand); }
|
||||
.hero-meta { margin-top: 26px; font-size: 14px; color: var(--muted); }
|
||||
|
||||
section.block { padding: 72px 0; }
|
||||
section.block.alt { background: var(--card); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
|
||||
.block h2 { font-size: 30px; text-align: center; }
|
||||
.block .sub { text-align: center; color: var(--muted); margin-top: 10px; margin-bottom: 44px; }
|
||||
|
||||
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
|
||||
.feature { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 26px; }
|
||||
.feature .icon { width: 42px; height: 42px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: rgba(16,185,129,.1); color: var(--brand); margin-bottom: 14px; }
|
||||
.feature h3 { font-size: 17px; margin-bottom: 8px; }
|
||||
.feature p { font-size: 14.5px; color: var(--muted); }
|
||||
|
||||
.steps { max-width: 760px; margin: 0 auto; }
|
||||
.step { display: flex; gap: 18px; padding: 18px 0; border-bottom: 1px dashed var(--line); }
|
||||
.step:last-child { border-bottom: none; }
|
||||
.step .no { flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; background: var(--brand); color: #fff; font-weight: 700; display: flex; align-items: center; justify-content: center; }
|
||||
.step h3 { font-size: 16px; }
|
||||
.step p { font-size: 14.5px; color: var(--muted); margin-top: 4px; }
|
||||
code.inline { background: rgba(16,185,129,.08); color: var(--brand-dark); padding: 2px 7px; border-radius: 5px; font-size: 13.5px; font-family: ui-monospace, "SF Mono", Menlo, monospace; }
|
||||
|
||||
.footer { padding: 40px 0 56px; color: var(--muted); font-size: 14px; text-align: center; }
|
||||
.footer .links { display: flex; gap: 22px; justify-content: center; margin-bottom: 14px; }
|
||||
|
||||
@media (max-width: 840px) {
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
.hero h1 { font-size: 34px; }
|
||||
.nav-links { display: none; }
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>LokiStack — Loki 日志管理平台</title>
|
||||
<meta name="description" content="LokiStack:把 Grafana Loki 部署到 Kubernetes 或裸金属,内置日志查询、日志分析与告警的一体化平台。部署向导、采集策略、查询授权、飞书告警开箱即用。">
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%23059669'/%3E%3Ctext x='32' y='44' font-size='34' font-weight='800' fill='%23fff' text-anchor='middle' font-family='sans-serif'%3EL%3C/text%3E%3C/svg%3E">
|
||||
<link rel="stylesheet" href="assets/site.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="nav">
|
||||
<div class="nav-inner">
|
||||
<a class="brand" href="#">
|
||||
<span class="brand-logo">L</span>
|
||||
<span>LokiStack</span>
|
||||
</a>
|
||||
<nav class="nav-links">
|
||||
<a href="#features">特性</a>
|
||||
<a href="#quickstart">快速开始</a>
|
||||
<a href="#docs">文档</a>
|
||||
<a href="https://gitea.91ai.io/rlwj-lab/LokiStack" target="_blank" rel="noopener">源码</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1>把 Loki 日志聚合<br>变成<em>五分钟</em>的事</h1>
|
||||
<p class="tagline">向导式部署 Grafana Loki 到 Kubernetes 集群或裸金属主机,内置日志查询、可视化分析与飞书告警 —— 不写 YAML、不装 kubectl/Helm。</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary" href="#quickstart">快速开始</a>
|
||||
<a class="btn btn-outline" href="#docs">阅读文档</a>
|
||||
</div>
|
||||
<p class="hero-meta">Go + React 单二进制交付 · JSON 文件存储零依赖 · 中文/英文界面</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="block" id="features">
|
||||
<div class="container">
|
||||
<h2>为运维而生的完整日志链路</h2>
|
||||
<p class="sub">从部署、采集、查询到告警,一个平台全部覆盖</p>
|
||||
<div class="grid">
|
||||
<div class="feature">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/></svg></div>
|
||||
<h3>向导式部署</h3>
|
||||
<p>三步完成:环境与目标 → 存储 → 部署 Loki 与采集。支持 Kubernetes(YAML 直发)、裸金属二进制与 Docker 三种模式。</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10a1 1 0 0 0 1 1h2.5a1 1 0 0 1 .8.4l.9 1.2a1 1 0 0 0 .8.4H12a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2a1 1 0 0 0-.8.4l-.9 1.2a1 1 0 0 1-.8.4H5a1 1 0 0 0-1 1z"/><path d="M3 5v13a2 2 0 0 0 2 2h3"/></svg></div>
|
||||
<h3>策略化采集</h3>
|
||||
<p>Promtail / Fluent Bit 采集策略可视化配置:命名空间多选、app 标签四级回退、敏感路径拦截、配置预览与一键下发。</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg></div>
|
||||
<h3>专业日志查询</h3>
|
||||
<p>LogQL 高亮补全、快捷查询选中即执行、Live Tail 实时追踪、时间线拖选、上下文查看,细到按用户的查询授权。</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="m7 14 4-4 4 4 6-6"/></svg></div>
|
||||
<h3>日志分析大盘</h3>
|
||||
<p>统计卡、趋势折线、命名空间分布、应用流图、每日日志量、Top 10 排行与 Ingress 访问分析,自动聚合无需配置。</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 8.5a2.5 2.5 0 0 0-2.5-2.5h-15A2.5 2.5 0 0 0 2 8.5v7a2.5 2.5 0 0 0 2.5 2.5h15A2.5 2.5 0 0 0 22 15.5z"/><path d="M6 9v6"/><path d="M10 9v6"/><path d="M14 9v6"/><path d="M18 9v6"/></svg></div>
|
||||
<h3>开箱告警</h3>
|
||||
<p>内置九条应用日志告警规则,默认启用并绑定默认通道;填好飞书 Webhook 即可接收,冷却与风暴抑制防刷屏。</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg></div>
|
||||
<h3>工程级可靠性</h3>
|
||||
<p>Credential 加密存储、操作审计、备份恢复、Prometheus 自监控指标、CI 门禁与测试覆盖,按生产标准构建。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="block alt" id="quickstart">
|
||||
<div class="container">
|
||||
<h2>五分钟启动</h2>
|
||||
<p class="sub">单二进制或 Docker,一条命令拉起完整平台</p>
|
||||
<div class="steps">
|
||||
<div class="step">
|
||||
<span class="no">1</span>
|
||||
<div>
|
||||
<h3>准备密钥并启动</h3>
|
||||
<p><code class="inline">export JWT_SECRET=... ENCRYPTION_KEY=...</code> 后执行 <code class="inline">./run.sh</code>(开发)或 <code class="inline">docker compose up -d</code>(生产)。默认账号在首次启动时自动生成。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<span class="no">2</span>
|
||||
<div>
|
||||
<h3>走完部署向导</h3>
|
||||
<p>接入 Kubernetes 集群或 SSH 主机,选择对象存储或本地磁盘,三步部署 Loki 并下发采集策略。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<span class="no">3</span>
|
||||
<div>
|
||||
<h3>查询、分析、告警</h3>
|
||||
<p>日志查询页检索与实时追踪,日志分析页看聚合大盘,告警页填入飞书 Webhook 开始接收告警。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="block" id="docs">
|
||||
<div class="container">
|
||||
<h2>文档</h2>
|
||||
<p class="sub">用户手册覆盖部署、采集、查询、分析与告警全流程</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary" href="/docs/">打开用户手册</a>
|
||||
<a class="btn btn-outline" href="https://gitea.91ai.io/rlwj-lab/LokiStack" target="_blank" rel="noopener">查看源码</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="links">
|
||||
<a href="#features">特性</a>
|
||||
<a href="#quickstart">快速开始</a>
|
||||
<a href="/docs/">文档</a>
|
||||
<a href="https://gitea.91ai.io/rlwj-lab/LokiStack" target="_blank" rel="noopener">Gitea</a>
|
||||
</div>
|
||||
<p>LokiStack — Loki 日志管理平台 · Grafana Loki 是 Grafana Labs 的商标</p>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user