diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b8fc93a --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# 复制此文件为 .env 并填写真实值 +# cp .env.example .env + +# MySQL root 密码(同时需要在 configs/ppanel.yaml 的 MySQL.Password 中填写相同的值) +MYSQL_ROOT_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD + +# Grafana 管理员密码 +GRAFANA_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD + +# PPanel Server 镜像标签(留空使用 latest) +PPANEL_SERVER_TAG=latest diff --git a/docker-compose.cloud.yml b/docker-compose.cloud.yml index 6a69b84..b843a61 100644 --- a/docker-compose.cloud.yml +++ b/docker-compose.cloud.yml @@ -1,9 +1,15 @@ # PPanel 服务部署 (云端/无源码版) -# 使用方法: -# 1. 确保已将 docker-compose.cloud.yml, configs/, loki/, grafana/, prometheus/ 目录上传到服务器同一目录 -# 2. 确保 configs/ 目录下有 ppanel.yaml 配置文件 -# 3. 确保 logs/ 目录存在 (mkdir logs) +# 使用方法: +# 1. 确保已将 docker-compose.cloud.yml, configs/, loki/, grafana/, prometheus/, tempo/ 目录上传到服务器同一目录 +# 2. 确保 configs/ 目录下有 ppanel.yaml 配置文件(参考 etc/ppanel.yaml) +# 3. 确保 logs/ 目录存在 (mkdir -p logs tempo_data) # 4. 运行: docker-compose -f docker-compose.cloud.yml up -d +# +# 网络说明: +# 所有服务均在 ppanel_net bridge 网络中,通过容器名互联 +# MySQL / Redis / Tempo 不对外暴露端口(仅内网访问) +# 监控端口(Grafana/Prometheus/Loki/Tempo)绑定到 127.0.0.1,需通过 SSH 隧道或 Nginx 反代访问 +# 对外只暴露 8080 (ppanel API) services: # ---------------------------------------------------- @@ -18,49 +24,22 @@ services: - ./logs:/app/logs environment: - TZ=Asia/Shanghai - # 链路追踪配置 (OTLP) - - OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317 - - OTEL_SERVICE_NAME=ppanel-server - - OTEL_TRACES_EXPORTER=otlp - - OTEL_METRICS_EXPORTER=prometheus # 指标由 tempo 抓取,不使用 OTLP - network_mode: host + ports: + - "8080:8080" + networks: + - ppanel_net ulimits: nproc: 65535 nofile: soft: 65535 hard: 65535 - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" depends_on: - - mysql - - redis - - tempo - - # ---------------------------------------------------- - # 14. Tempo (链路追踪存储 - 替代/增强 Jaeger) - # ---------------------------------------------------- - tempo: - image: grafana/tempo:2.4.1 - container_name: ppanel-tempo - user: root - restart: always - command: - - "-config.file=/etc/tempo.yaml" - - "-target=all" - volumes: - - ./tempo/tempo-config.yaml:/etc/tempo.yaml # - tempo_data:/var/tempo - - ./tempo_data:/var/tempo # 改为映射到当前目录,确保数据彻底干净 - - ports: - - "3200:3200" - - "4317:4317" - - "4318:4318" - - "9095:9095" - networks: - - ppanel_net + mysql: + condition: service_healthy + redis: + condition: service_healthy + tempo: + condition: service_started logging: driver: "json-file" options: @@ -74,10 +53,9 @@ services: image: mysql:8.0 container_name: ppanel-mysql restart: always - ports: - - "3306:3306" # 临时开放外部访问,用完记得关闭! + # 不对外暴露端口,仅内网访问(容器名: mysql,端口: 3306) environment: - MYSQL_ROOT_PASSWORD: "jpcV41ppanel" # 请修改为强密码 + MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?请在 .env 文件中设置 MYSQL_ROOT_PASSWORD}" MYSQL_DATABASE: "ppanel" TZ: Asia/Shanghai command: @@ -97,6 +75,11 @@ services: hard: 65535 networks: - ppanel_net + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 5 logging: driver: "json-file" options: @@ -110,8 +93,7 @@ services: image: redis:8.2.1 container_name: ppanel-redis restart: always - ports: - - "6379:6379" + # 不对外暴露端口,仅内网访问(容器名: redis,端口: 6379) command: - redis-server - --tcp-backlog 65535 @@ -125,6 +107,11 @@ services: hard: 65535 networks: - ppanel_net + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 logging: driver: "json-file" options: @@ -132,19 +119,21 @@ services: max-file: "3" # ---------------------------------------------------- - # 4. Loki (日志存储) + # 4. Tempo (链路追踪存储) # ---------------------------------------------------- - loki: - image: grafana/loki:3.0.0 - container_name: ppanel-loki + tempo: + image: grafana/tempo:2.4.1 + container_name: ppanel-tempo + user: root restart: always + command: + - "-config.file=/etc/tempo.yaml" + - "-target=all" volumes: - # 必须上传 loki 目录到服务器 - - ./loki/loki-config.yaml:/etc/loki/local-config.yaml - - loki_data:/loki - command: -config.file=/etc/loki/local-config.yaml - ports: - - "3100:3100" + - ./tempo/tempo-config.yaml:/etc/tempo.yaml + - ./tempo_data:/var/tempo + # 不对外暴露端口,仅内网访问(容器名: tempo) + # ppanel-server 通过容器名 tempo:4317 发送 trace networks: - ppanel_net logging: @@ -154,7 +143,27 @@ services: max-file: "3" # ---------------------------------------------------- - # 5. Promtail (日志采集) + # 5. Loki (日志存储) + # ---------------------------------------------------- + loki: + image: grafana/loki:3.0.0 + container_name: ppanel-loki + restart: always + volumes: + - ./loki/loki-config.yaml:/etc/loki/local-config.yaml + - loki_data:/loki + command: -config.file=/etc/loki/local-config.yaml + # 不对外暴露端口,仅内网访问 + networks: + - ppanel_net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # ---------------------------------------------------- + # 6. Promtail (日志采集) # ---------------------------------------------------- promtail: image: grafana/promtail:3.0.0 @@ -164,9 +173,7 @@ services: - ./loki/promtail-config.yaml:/etc/promtail/config.yaml - /var/lib/docker/containers:/var/lib/docker/containers:ro - /var/run/docker.sock:/var/run/docker.sock - # 采集当前目录下的 logs 文件夹 - ./logs:/var/log/ppanel-server:ro - # 采集 Nginx 访问日志(用于追踪邀请码来源) - /var/log/nginx:/var/log/nginx:ro command: -config.file=/etc/promtail/config.yaml networks: @@ -180,27 +187,29 @@ services: max-file: "3" # ---------------------------------------------------- - # 6. Grafana (日志界面) + # 7. Grafana (可观测面板) + # 访问: ssh -L 3333:localhost:3333 your-server 后浏览器打开 http://localhost:3333 + # 或配置 Nginx 反代(建议加认证) # ---------------------------------------------------- grafana: image: grafana/grafana:latest container_name: ppanel-grafana restart: always ports: - - "3333:3000" + - "127.0.0.1:3333:3000" # 仅本机可访问,需 SSH 隧道或 Nginx 反代 environment: - - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:?请在 .env 文件中设置 GRAFANA_PASSWORD} - GF_USERS_ALLOW_SIGN_UP=false - - GF_FEATURE_TOGGLES_ENABLE=appObservability #- GF_INSTALL_PLUGINS=redis-datasource + - GF_FEATURE_TOGGLES_ENABLE=appObservability volumes: - grafana_data:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning - extra_hosts: - - "host.docker.internal:host-gateway" networks: - ppanel_net depends_on: - loki + - tempo + - prometheus logging: driver: "json-file" options: @@ -208,25 +217,22 @@ services: max-file: "3" # ---------------------------------------------------- - # 7. Prometheus (指标采集) + # 8. Prometheus (指标采集) # ---------------------------------------------------- prometheus: image: prom/prometheus:latest container_name: ppanel-prometheus restart: always ports: - - "9090:9090" # 暴露端口便于调试 + - "127.0.0.1:9090:9090" # 仅本机可访问 volumes: - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml - prometheus_data:/prometheus - extra_hosts: - - "host.docker.internal:host-gateway" command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.path=/prometheus' - '--web.enable-lifecycle' - '--web.enable-remote-write-receiver' - networks: - ppanel_net logging: @@ -236,7 +242,7 @@ services: max-file: "3" # ---------------------------------------------------- - # 8. Redis Exporter (Redis指标导出) + # 9. Redis Exporter # ---------------------------------------------------- redis-exporter: image: oliver006/redis_exporter:latest @@ -255,13 +261,12 @@ services: max-file: "3" # ---------------------------------------------------- - # 9. Nginx Exporter (监控宿主机 Nginx) + # 10. Nginx Exporter (监控宿主机 Nginx) # ---------------------------------------------------- nginx-exporter: image: nginx/nginx-prometheus-exporter:latest container_name: ppanel-nginx-exporter restart: always - # 使用 host.docker.internal 访问宿主机 command: - -nginx.scrape-uri=http://host.docker.internal:8090/nginx_status extra_hosts: @@ -275,7 +280,7 @@ services: max-file: "3" # ---------------------------------------------------- - # 10. MySQL Exporter (MySQL指标导出) + # 11. MySQL Exporter # ---------------------------------------------------- mysql-exporter: image: prom/mysqld-exporter:latest @@ -347,7 +352,6 @@ volumes: prometheus_data: tempo_data: - networks: ppanel_net: name: ppanel_net diff --git a/etc/ppanel.yaml b/etc/ppanel.yaml index c91d70e..09450d2 100644 --- a/etc/ppanel.yaml +++ b/etc/ppanel.yaml @@ -15,19 +15,19 @@ Logger: # 日志配置 Level: debug # 日志级别: debug, info, warn, error, panic, fatal MySQL: - Addr: 127.0.0.1:3306 # MySQL地址 - Username: root # MySQL用户名 (与创建的用户一致) - Password: rootpassword # MySQL密码 (换成之前生成的随机密码) - Dbname: ppanel # MySQL数据库名 (与脚本创建的数据库一致) + Addr: mysql:3306 # Docker 容器名; 本地开发改为 127.0.0.1:3306 + Username: root # MySQL用户名 + Password: CHANGE_ME_MYSQL_PASSWORD # MySQL密码,与 .env MYSQL_ROOT_PASSWORD 一致 + Dbname: ppanel # MySQL数据库名 Config: charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai MaxIdleConns: 10 MaxOpenConns: 100 - LogMode: debug + LogMode: info # 生产建议 info,开发用 debug LogZap: true SlowThreshold: 1000 Redis: - Host: 127.0.0.1:6379 # Redis地址,格式:host:port + Host: redis:6379 # Docker 容器名; 本地开发改为 127.0.0.1:6379 Pass: # Redis密码,如果没有设置密码可以留空 DB: 0 # Redis数据库编号,默认0 PoolSize: 100 # 连接池大小(最大连接数),根据应用并发量调整,建议:小流量50-100,中流量100-300,大流量300-500 @@ -55,6 +55,12 @@ AppSignature: Signature: EnableSignature: false # 系统签名开关(实际运行会以数据库 system.signature.EnableSignature 为准) +Trace: # 链路追踪配置 (OpenTelemetry) + Name: ppanel # 服务名 + Sampler: 1.0 # 采样率 0.0-1.0,生产建议 0.1 + Batcher: otlpgrpc # 本地开发留空""; 生产填 otlpgrpc + Endpoint: "tempo:4317" # Docker 容器名; 本地开发留空"" + Administrator: Email: admin@ppanel.dev # 后台登录邮箱,请修改 Password: CHANGE_ME_TO_STRONG_PASSWORD # 后台登录密码,请修改为强密码 diff --git a/internal/handler/public/user/unbindDeviceHandler.go b/internal/handler/public/user/unbindDeviceHandler.go index 62b3e11..9429d72 100644 --- a/internal/handler/public/user/unbindDeviceHandler.go +++ b/internal/handler/public/user/unbindDeviceHandler.go @@ -12,7 +12,7 @@ import ( func UnbindDeviceHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { return func(c *gin.Context) { var req types.UnbindDeviceRequest - _ = c.ShouldBindJSON(&req) + _ = c.ShouldBind(&req) validateErr := svcCtx.Validate(&req) if validateErr != nil { result.ParamErrorResult(c, validateErr) diff --git a/internal/server.go b/internal/server.go index 36e5542..3089e7e 100644 --- a/internal/server.go +++ b/internal/server.go @@ -107,11 +107,14 @@ func (m *Service) Start() { MinVersion: tls.VersionTLS12, }, } - trace.StartAgent(trace.Config{ - Name: "ppanel", - Sampler: 1.0, - Batcher: "", - }) + traceCfg := m.svc.Config.Trace + if traceCfg.Name == "" { + traceCfg.Name = "ppanel" + } + if traceCfg.Sampler == 0 { + traceCfg.Sampler = 1.0 + } + trace.StartAgent(traceCfg) proc.AddShutdownListener(func() { trace.StopAgent() })