545 lines
12 KiB
Markdown
545 lines
12 KiB
Markdown
# 部署与运维设计文档
|
||
|
||
## 一、部署方式
|
||
|
||
### 1.1 Docker Compose(推荐)
|
||
|
||
一键部署,适合中小团队。
|
||
|
||
```
|
||
docker compose up -d
|
||
```
|
||
|
||
包含的服务:
|
||
|
||
| 服务 | 镜像 | 端口 | 说明 |
|
||
|------|------|------|------|
|
||
| light-sentry-api | 自建 (Node.js) | 9000 | API 服务 + 管理后台 |
|
||
| loki | grafana/loki | 3100 | 日志存储 |
|
||
| promtail | grafana/promtail | - | 日志收集(容器日志) |
|
||
| grafana | grafana/grafana | 3000 | 数据可视化 |
|
||
| dozzle | amir20/dozzle | 8080 | 容器日志查看 |
|
||
| nginx | (系统已有) | 80/443 | 反向代理 + SSL |
|
||
|
||
### 1.2 二进制部署
|
||
|
||
适合对 Docker 不熟悉的场景:
|
||
|
||
1. 安装 Node.js 18+
|
||
2. 安装 Loki(单二进制)
|
||
3. 安装 Grafana
|
||
4. 配置 Nginx
|
||
5. 启动 API 服务(pm2 守护)
|
||
|
||
### 1.3 Kubernetes 部署
|
||
|
||
适合大规模、多租户场景:
|
||
|
||
- Helm Chart 部署
|
||
- HPA 自动扩缩容
|
||
- Loki 分布式模式
|
||
- MySQL / PostgreSQL 集群
|
||
|
||
---
|
||
|
||
## 二、资源需求
|
||
|
||
### 2.1 最小配置(测试/个人用)
|
||
|
||
| 资源 | 配置 | 说明 |
|
||
|------|------|------|
|
||
| CPU | 1 核 | 低流量下足够 |
|
||
| 内存 | 1GB | 紧张,可能需要调小 Loki 缓存 |
|
||
| 磁盘 | 20GB | 存 7 天日志(每天 ~2GB) |
|
||
| 网络 | 1Mbps | 上报流量不大 |
|
||
|
||
**承载能力**:
|
||
- 10 个项目以内
|
||
- 每天 1 万事件以内
|
||
|
||
### 2.2 推荐配置(小团队)
|
||
|
||
| 资源 | 配置 | 说明 |
|
||
|------|------|------|
|
||
| CPU | 2 核 | 应对突发流量 |
|
||
| 内存 | 4GB | Loki + API + Grafana 都够 |
|
||
| 磁盘 | 100GB SSD | 存 30 天日志 |
|
||
| 网络 | 5Mbps | 足够 |
|
||
|
||
**承载能力**:
|
||
- 50 个项目以内
|
||
- 每天 100 万事件以内
|
||
|
||
### 2.3 生产配置(中大型团队)
|
||
|
||
| 资源 | 配置 | 说明 |
|
||
|------|------|------|
|
||
| CPU | 4 核以上 | 高并发上报 |
|
||
| 内存 | 8GB 以上 | Loki 内存索引 |
|
||
| 磁盘 | 500GB+ SSD | 大量日志存储 |
|
||
| 网络 | 10Mbps+ | 大流量上报 |
|
||
|
||
**承载能力**:
|
||
- 200 个项目以内
|
||
- 每天 1000 万事件以内
|
||
|
||
---
|
||
|
||
## 三、Docker Compose 配置详解
|
||
|
||
### 3.1 完整配置说明
|
||
|
||
```yaml
|
||
version: '3.8'
|
||
|
||
services:
|
||
# API 服务 + 管理后台
|
||
light-sentry-api:
|
||
build: .
|
||
container_name: light-sentry-api
|
||
network_mode: host
|
||
environment:
|
||
- NODE_ENV=production
|
||
- PORT=9000
|
||
- LOKI_URL=http://127.0.0.1:3100
|
||
- DATA_DIR=/app/data
|
||
volumes:
|
||
- ./data:/app/data
|
||
restart: unless-stopped
|
||
depends_on:
|
||
- loki
|
||
|
||
# Loki - 日志存储
|
||
loki:
|
||
image: grafana/loki:2.9.4
|
||
container_name: light-sentry-loki
|
||
network_mode: host
|
||
command: -config.file=/etc/loki/local-config.yaml
|
||
volumes:
|
||
- ./loki/config:/etc/loki
|
||
- loki-data:/loki
|
||
restart: unless-stopped
|
||
|
||
# Promtail - 收集容器日志
|
||
promtail:
|
||
image: grafana/promtail:2.9.4
|
||
container_name: light-sentry-promtail
|
||
network_mode: host
|
||
volumes:
|
||
- ./promtail/config:/etc/promtail
|
||
- /var/lib/docker/containers:/var/lib/docker/containers:ro
|
||
restart: unless-stopped
|
||
depends_on:
|
||
- loki
|
||
|
||
# Grafana - 可视化
|
||
grafana:
|
||
image: grafana/grafana:10.2.0
|
||
container_name: light-sentry-grafana
|
||
network_mode: host
|
||
environment:
|
||
- GF_SECURITY_ADMIN_PASSWORD=admin123
|
||
volumes:
|
||
- grafana-data:/var/lib/grafana
|
||
restart: unless-stopped
|
||
|
||
# Dozzle - 容器日志查看
|
||
dozzle:
|
||
image: amir20/dozzle:latest
|
||
container_name: light-sentry-dozzle
|
||
network_mode: host
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
restart: unless-stopped
|
||
|
||
volumes:
|
||
loki-data:
|
||
grafana-data:
|
||
```
|
||
|
||
### 3.2 Loki 配置
|
||
|
||
`loki/config/local-config.yaml`
|
||
|
||
```yaml
|
||
auth_enabled: false
|
||
|
||
server:
|
||
http_listen_port: 3100
|
||
|
||
ingester:
|
||
lifecycler:
|
||
ring:
|
||
kvstore:
|
||
store: inmemory
|
||
replication_factor: 1
|
||
chunk_idle_period: 30m
|
||
chunk_retain_period: 1m
|
||
max_transfer_retries: 0
|
||
|
||
schema_config:
|
||
configs:
|
||
- from: 2024-01-01
|
||
store: boltdb-shipper
|
||
object_store: filesystem
|
||
schema: v12
|
||
index:
|
||
prefix: index_
|
||
period: 24h
|
||
|
||
storage_config:
|
||
boltdb_shipper:
|
||
active_index_directory: /loki/index
|
||
cache_location: /loki/index_cache
|
||
shared_store: filesystem
|
||
filesystem:
|
||
directory: /loki/chunks
|
||
|
||
limits_config:
|
||
retention_period: 168h # 7 天
|
||
per_stream_rate_limit: 10MB
|
||
ingestion_rate_mb: 50
|
||
ingestion_burst_size_mb: 100
|
||
max_streams_per_user: 10000
|
||
max_chunks_per_query: 2000000
|
||
max_query_series: 5000
|
||
|
||
table_manager:
|
||
retention_deletes_enabled: true
|
||
retention_period: 168h # 7 天
|
||
```
|
||
|
||
---
|
||
|
||
## 四、Nginx 配置
|
||
|
||
### 4.1 反向代理配置
|
||
|
||
```nginx
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name log.example.com;
|
||
|
||
# SSL 配置
|
||
ssl_certificate /path/to/fullchain.pem;
|
||
ssl_certificate_key /path/to/privkey.pem;
|
||
|
||
# API 服务(Sentry 上报 + 管理后台 API)
|
||
location /api/ {
|
||
proxy_pass http://127.0.0.1:9000/api/;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
||
# 上报接口超时
|
||
proxy_read_timeout 30s;
|
||
proxy_connect_timeout 10s;
|
||
proxy_send_timeout 30s;
|
||
|
||
# CORS(SDK 上报需要)
|
||
add_header Access-Control-Allow-Origin * always;
|
||
add_header Access-Control-Allow-Methods GET,POST,OPTIONS always;
|
||
add_header Access-Control-Allow-Headers Content-Type,X-Sentry-Auth always;
|
||
if ($request_method = OPTIONS) {
|
||
return 204;
|
||
}
|
||
}
|
||
|
||
# 管理后台
|
||
location /manage/ {
|
||
proxy_pass http://127.0.0.1:9000/manage/;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
# Grafana
|
||
location /grafana/ {
|
||
proxy_pass http://127.0.0.1:3000/;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
# Dozzle
|
||
location /dozzle/ {
|
||
proxy_pass http://127.0.0.1:8080/;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
||
# WebSocket 支持
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
}
|
||
|
||
# Loki(可选,直接暴露给 Grafana 用,一般不需要外部访问)
|
||
location /loki/ {
|
||
proxy_pass http://127.0.0.1:3100/;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
deny all; # 禁止外部访问,只允许内网
|
||
}
|
||
|
||
# 根路径重定向到管理后台
|
||
location = / {
|
||
return 301 /manage/;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.2 限流配置
|
||
|
||
```nginx
|
||
# 定义限流 zone
|
||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
|
||
|
||
location /api/ {
|
||
limit_req zone=api_limit burst=200 nodelay;
|
||
# ... 其他配置
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、CI/CD 部署
|
||
|
||
### 5.1 Gitea Actions 示例
|
||
|
||
```yaml
|
||
name: Deploy Light-Sentry
|
||
|
||
on:
|
||
push:
|
||
branches: [ main ]
|
||
workflow_dispatch:
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Deploy via SSH
|
||
uses: appleboy/ssh-action@v1.0.0
|
||
with:
|
||
host: ${{ secrets.SERVER_HOST }}
|
||
username: ${{ secrets.SERVER_USER }}
|
||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||
script: |
|
||
cd ~/light-sentry
|
||
git pull
|
||
docker compose build light-sentry-api
|
||
docker compose up -d
|
||
echo "Deploy completed"
|
||
```
|
||
|
||
### 5.2 智能重启(只重启变更的服务)
|
||
|
||
检测变更的文件,只重启相关服务,减少停机时间:
|
||
|
||
- `src/**` → 重启 API 服务
|
||
- `loki/**` → 重启 Loki
|
||
- `grafana/**` → 重启 Grafana
|
||
- `docker-compose.yml` → 全部重启
|
||
- `nginx/**` → reload Nginx
|
||
|
||
---
|
||
|
||
## 六、监控与告警
|
||
|
||
### 6.1 自身监控
|
||
|
||
Light-Sentry 自己也要监控自己:
|
||
|
||
| 监控项 | 方式 | 告警阈值 |
|
||
|--------|------|----------|
|
||
| API 服务存活 | 健康检查接口 + Prometheus | 502 持续 1 分钟 |
|
||
| Loki 服务存活 | Grafana 内置监控 | 连接失败 |
|
||
| 磁盘使用率 | Node Exporter | > 80% 告警 |
|
||
| 内存使用率 | Node Exporter | > 85% 告警 |
|
||
| CPU 使用率 | Node Exporter | > 90% 告警 |
|
||
| 事件上报量 | Loki 查询 | 突增 200% |
|
||
| 错误率 | Loki 查询 | > 5% |
|
||
|
||
### 6.2 健康检查
|
||
|
||
```
|
||
GET /health
|
||
|
||
{
|
||
"status": "ok",
|
||
"timestamp": "2024-01-01T00:00:00Z",
|
||
"uptime": 86400,
|
||
"queue_size": 123,
|
||
"loki": "connected",
|
||
"mysql": "connected"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 七、备份与恢复
|
||
|
||
### 7.1 需要备份的数据
|
||
|
||
| 数据 | 位置 | 备份频率 | 保留时间 |
|
||
|------|------|----------|----------|
|
||
| 项目配置 | data/projects.json | 每天 | 30 天 |
|
||
| Loki 数据 | loki-data 卷 | 每周 | 4 周 |
|
||
| Grafana 配置 | grafana-data 卷 | 每天 | 30 天 |
|
||
| 告警规则 | MySQL | 每天 | 永久 |
|
||
| 错误聚合 | MySQL | 每天 | 永久 |
|
||
|
||
### 7.2 备份脚本
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# backup.sh - Light-Sentry 备份脚本
|
||
|
||
BACKUP_DIR="/backup/light-sentry"
|
||
DATE=$(date +%Y%m%d_%H%M%S)
|
||
|
||
# 创建备份目录
|
||
mkdir -p $BACKUP_DIR
|
||
|
||
# 备份配置文件
|
||
tar czf $BACKUP_DIR/config_$DATE.tar.gz /opt/light-sentry/data/
|
||
|
||
# 备份 Loki 数据(可选,量大)
|
||
# docker run --rm -v loki-data:/data -v $BACKUP_DIR:/backup \
|
||
# alpine tar czf /backup/loki_$DATE.tar.gz -C /data .
|
||
|
||
# 备份 Grafana
|
||
docker run --rm -v grafana-data:/data -v $BACKUP_DIR:/backup \
|
||
alpine tar czf /backup/grafana_$DATE.tar.gz -C /data .
|
||
|
||
# 清理 30 天前的备份
|
||
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
|
||
|
||
echo "Backup completed: $DATE"
|
||
```
|
||
|
||
### 7.3 恢复流程
|
||
|
||
```bash
|
||
# 1. 停止服务
|
||
docker compose down
|
||
|
||
# 2. 恢复配置
|
||
tar xzf backup/config_20240101_000000.tar.gz -C /opt/light-sentry/
|
||
|
||
# 3. 恢复 Grafana
|
||
docker run --rm -v grafana-data:/data -v /backup:/backup \
|
||
alpine tar xzf /backup/grafana_20240101_000000.tar.gz -C /data
|
||
|
||
# 4. 启动服务
|
||
docker compose up -d
|
||
```
|
||
|
||
---
|
||
|
||
## 八、性能优化
|
||
|
||
### 8.1 API 服务优化
|
||
|
||
- **Node.js 集群模式**:利用多核 CPU
|
||
- **内存队列调优**:根据流量调整队列大小和刷盘频率
|
||
- **连接池**:数据库连接池、HTTP 连接池
|
||
- **缓存**:项目配置缓存,不用每次查存储
|
||
|
||
### 8.2 Loki 优化
|
||
|
||
- **调整保留时间**:从 7 天调整到实际需要的时间
|
||
- **降低索引基数**:减少 label 数量
|
||
- **批量写入**:增加 chunk 大小,减少写入次数
|
||
- **内存调优**:根据实际数据量调整内存
|
||
|
||
### 8.3 Nginx 优化
|
||
|
||
- **启用 gzip 压缩**:减少传输体积
|
||
- **启用缓存**:静态文件缓存
|
||
- **TCP 参数调优**:增加连接数限制
|
||
|
||
---
|
||
|
||
## 九、安全加固
|
||
|
||
### 9.1 网络安全
|
||
|
||
- 管理后台加访问密码(HTTP Basic Auth 或登录)
|
||
- Loki 不对外暴露(只允许 Grafana 访问)
|
||
- Dozzle 加访问密码
|
||
- 只开放必要端口(80/443)
|
||
|
||
### 9.2 数据安全
|
||
|
||
- SDK 上报接口 CORS 限制域名(可选)
|
||
- 项目 DSN 泄露后可重置 publicKey
|
||
- 敏感数据自动脱敏
|
||
- 定期备份,异地存储
|
||
|
||
### 9.3 依赖安全
|
||
|
||
- 定期更新依赖
|
||
- 镜像扫描漏洞
|
||
- 最小权限运行
|
||
|
||
---
|
||
|
||
## 十、常见问题排查
|
||
|
||
### 10.1 事件上报失败
|
||
|
||
1. 检查网络是否通:`curl -I https://log.example.com/api/1001/envelope/`
|
||
2. 检查 DSN 是否正确:publicKey 和 projectId 对应
|
||
3. 查看 API 日志:`docker logs light-sentry-api`
|
||
4. 查看 Nginx 日志:`tail -f /var/log/nginx/access.log`
|
||
|
||
### 10.2 Grafana 查不到数据
|
||
|
||
1. 检查 Loki 数据源是否配置正确
|
||
2. 检查时间范围是否正确
|
||
3. 检查 label 拼写是否正确
|
||
4. 查看 Loki 日志:`docker logs light-sentry-loki`
|
||
|
||
### 10.3 服务无法启动
|
||
|
||
1. 检查端口是否被占用:`netstat -tlnp | grep 9000`
|
||
2. 查看容器日志:`docker logs light-sentry-api`
|
||
3. 检查配置文件格式:JSON / YAML 语法
|
||
4. 检查磁盘空间:`df -h`
|
||
|
||
---
|
||
|
||
## 十一、升级策略
|
||
|
||
### 11.1 版本升级
|
||
|
||
```bash
|
||
# 拉取最新代码
|
||
git pull
|
||
|
||
# 重新构建并启动
|
||
docker compose build light-sentry-api
|
||
docker compose up -d
|
||
|
||
# 验证
|
||
curl https://log.example.com/health
|
||
```
|
||
|
||
### 11.2 数据迁移
|
||
|
||
- 配置向后兼容,新版本能读旧版本数据
|
||
- 启动时自动检测并执行数据迁移
|
||
- 迁移前自动备份
|