From c3f8bd20b30271c97328f536edff3919ffc779f1 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 13 Mar 2026 11:30:42 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=AD=89=E7=BA=A7=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- atsf_server/common/init.go | 1 + atsf_server/common/logger.go | 133 ++++++++++++++++-------- atsf_server/main.go | 2 +- docs/app-config.md | 194 ++++++++++++++++++----------------- 4 files changed, 190 insertions(+), 140 deletions(-) diff --git a/atsf_server/common/init.go b/atsf_server/common/init.go index e4c37341..30f9b6c9 100644 --- a/atsf_server/common/init.go +++ b/atsf_server/common/init.go @@ -54,6 +54,7 @@ func init() { if os.Getenv("AGENT_TOKEN") != "" { AgentToken = os.Getenv("AGENT_TOKEN") } + SetLogLevel(os.Getenv("LOG_LEVEL")) if *LogDir != "" { var err error *LogDir, err = filepath.Abs(*LogDir) diff --git a/atsf_server/common/logger.go b/atsf_server/common/logger.go index 0b8b2cfb..5d326a22 100644 --- a/atsf_server/common/logger.go +++ b/atsf_server/common/logger.go @@ -1,44 +1,89 @@ -package common - -import ( - "fmt" - "github.com/gin-gonic/gin" - "io" - "log" - "os" - "path/filepath" - "time" -) - -func SetupGinLog() { - if *LogDir != "" { - commonLogPath := filepath.Join(*LogDir, "common.log") - errorLogPath := filepath.Join(*LogDir, "error.log") - commonFd, err := os.OpenFile(commonLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - log.Fatal("failed to open log file") - } - errorFd, err := os.OpenFile(errorLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - log.Fatal("failed to open log file") - } - gin.DefaultWriter = io.MultiWriter(os.Stdout, commonFd) - gin.DefaultErrorWriter = io.MultiWriter(os.Stderr, errorFd) - } -} - -func SysLog(s string) { - t := time.Now() - _, _ = fmt.Fprintf(gin.DefaultWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s) -} - -func SysError(s string) { - t := time.Now() - _, _ = fmt.Fprintf(gin.DefaultErrorWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s) -} - -func FatalLog(v ...any) { - t := time.Now() - _, _ = fmt.Fprintf(gin.DefaultErrorWriter, "[FATAL] %v | %v \n", t.Format("2006/01/02 - 15:04:05"), v) - os.Exit(1) -} +package common + +import ( + "fmt" + "github.com/gin-gonic/gin" + "io" + "log" + "os" + "path/filepath" + "strings" + "time" +) + +type logLevel int + +const ( + logLevelDebug logLevel = iota + logLevelInfo + logLevelWarn + logLevelError +) + +var currentLogLevel = logLevelInfo +var currentLogLevelName = "info" + +func SetLogLevel(level string) { + normalized := strings.TrimSpace(strings.ToLower(level)) + switch normalized { + case "debug": + currentLogLevel = logLevelDebug + currentLogLevelName = "debug" + case "warn", "warning": + currentLogLevel = logLevelWarn + currentLogLevelName = "warn" + case "error": + currentLogLevel = logLevelError + currentLogLevelName = "error" + default: + currentLogLevel = logLevelInfo + currentLogLevelName = "info" + } +} + +func GetLogLevel() string { + return currentLogLevelName +} + +func shouldLog(level logLevel) bool { + return level >= currentLogLevel +} + +func SetupGinLog() { + if *LogDir != "" { + commonLogPath := filepath.Join(*LogDir, "common.log") + errorLogPath := filepath.Join(*LogDir, "error.log") + commonFd, err := os.OpenFile(commonLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatal("failed to open log file") + } + errorFd, err := os.OpenFile(errorLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatal("failed to open log file") + } + gin.DefaultWriter = io.MultiWriter(os.Stdout, commonFd) + gin.DefaultErrorWriter = io.MultiWriter(os.Stderr, errorFd) + } +} + +func SysLog(s string) { + if !shouldLog(logLevelInfo) { + return + } + t := time.Now() + _, _ = fmt.Fprintf(gin.DefaultWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s) +} + +func SysError(s string) { + if !shouldLog(logLevelError) { + return + } + t := time.Now() + _, _ = fmt.Fprintf(gin.DefaultErrorWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s) +} + +func FatalLog(v ...any) { + t := time.Now() + _, _ = fmt.Fprintf(gin.DefaultErrorWriter, "[FATAL] %v | %v \n", t.Format("2006/01/02 - 15:04:05"), v) + os.Exit(1) +} diff --git a/atsf_server/main.go b/atsf_server/main.go index b1621eb4..70a442ef 100644 --- a/atsf_server/main.go +++ b/atsf_server/main.go @@ -83,7 +83,7 @@ func main() { if port == "" { port = strconv.Itoa(*common.Port) } - common.SysLog(fmt.Sprintf("server config: port=%s gin_mode=%s sqlite_path=%s redis_enabled=%t upload_path=%s log_dir=%s agent_token_configured=%t node_offline_threshold=%s", port, gin.Mode(), common.SQLitePath, common.RedisEnabled, common.UploadPath, valueOrDefault(*common.LogDir, "stdout"), common.AgentToken != "", common.NodeOfflineThreshold)) + common.SysLog(fmt.Sprintf("server config: port=%s gin_mode=%s log_level=%s sqlite_path=%s redis_enabled=%t upload_path=%s log_dir=%s agent_token_configured=%t node_offline_threshold=%s", port, gin.Mode(), common.GetLogLevel(), common.SQLitePath, common.RedisEnabled, common.UploadPath, valueOrDefault(*common.LogDir, "stdout"), common.AgentToken != "", common.NodeOfflineThreshold)) common.SysLog(fmt.Sprintf("server listening on :%s", port)) err = server.Run(":" + port) if err != nil { diff --git a/docs/app-config.md b/docs/app-config.md index 96149e3f..ae179ff2 100644 --- a/docs/app-config.md +++ b/docs/app-config.md @@ -11,9 +11,9 @@ Server 当前支持两类启动配置: 1. 命令行参数 2. 环境变量 -此外,部分运行时参数已迁入数据库 `Option` 表,可在管理端设置页中热更新,例如 Agent 运行参数与限流阈值。 - -第五版(0.5.x)继续复用 `Option` 表承载 OpenResty 性能优化参数与缓存参数,不单独引入新的配置中心。 +此外,部分运行时参数已迁入数据库 `Option` 表,可在管理端设置页中热更新,例如 Agent 运行参数与限流阈值。 + +第五版(0.5.x)继续复用 `Option` 表承载 OpenResty 性能优化参数与缓存参数,不单独引入新的配置中心。 ### 1.1 Server 命令行参数 @@ -53,6 +53,7 @@ cd atsf_server export SESSION_SECRET='replace-with-random-string' export SQLITE_PATH='./atsflare.db' export GIN_MODE='release' +export LOG_LEVEL='info' export PORT='3000' go run . ``` @@ -70,6 +71,7 @@ services: SESSION_SECRET: replace-with-random-string SQLITE_PATH: /data/atsflare.db GIN_MODE: release + LOG_LEVEL: info PORT: "3000" volumes: - atsflare-data:/data @@ -84,6 +86,7 @@ volumes: | --- | --- | --- | --- | | `PORT` | 指定 Server 实际监听端口 | `3000` | `PORT=3000` | | `GIN_MODE` | 指定 Gin 运行模式;仅当值为 `debug` 时启用 debug,其余情况按 release 运行 | 非 `debug` 默认按 release 运行 | `GIN_MODE=release` | +| `LOG_LEVEL` | 指定系统日志等级;支持 `debug`/`info`/`warn`/`error`(`warning` 兼容为 `warn`) | `info` | `LOG_LEVEL=warn` | | `SESSION_SECRET` | Session 签名密钥;生产环境必须显式设置,避免重启后会话失效 | 启动时随机生成 UUID | `SESSION_SECRET=replace-with-random-string` | | `SQLITE_PATH` | SQLite 数据库文件路径 | `atsflare.db` | `SQLITE_PATH=/data/atsflare.db` | | `SQL_DSN` | MySQL DSN;设置后优先使用 MySQL,而不是 SQLite | 未设置时使用 SQLite | `SQL_DSN=user:pass@tcp(127.0.0.1:3306)/atsflare` | @@ -95,6 +98,7 @@ volumes: * `SQL_DSN` 与 `SQLITE_PATH` 同时存在时,优先使用 `SQL_DSN` * `SESSION_SECRET` 未固定时,每次重启都会生成新的随机值,已登录用户的 Cookie 会失效 +* `LOG_LEVEL` 未设置或设置为不支持的值时,将回退为 `info` * `REDIS_CONN_STRING` 未配置时,相关能力将回退为进程内实现 * `UPLOAD_PATH` 目录在启动时若不存在会自动创建 @@ -104,7 +108,7 @@ volumes: | 配置项 | 作用 | 默认值 | | --- | --- | --- | -| `AgentHeartbeatInterval` | Agent 心跳间隔(毫秒) | `10000` | +| `AgentHeartbeatInterval` | Agent 心跳间隔(毫秒) | `10000` | | `NodeOfflineThreshold` | 节点离线判定阈值(毫秒) | `120000` | | `AgentUpdateRepo` | Agent 自更新仓库 | `Rain-kl/ATSFlare` | | `GlobalApiRateLimitNum` / `GlobalApiRateLimitDuration` | 全局 API 限流次数 / 时间窗口(秒) | `300` / `180` | @@ -115,51 +119,51 @@ volumes: 说明: -* 限流窗口上限不能超过 `RateLimitKeyExpirationDuration`,当前为 20 分钟 -* 限流按来源 IP 统计,若前置了 Nginx/CDN/LB,应正确透传真实客户端 IP - -### 1.2.2 第五版当前支持的 OpenResty 优化配置项 - -以下配置项当前已接入管理端「运维设置」,并统一保存在 `Option` 表中。它们会参与第五版后续的配置渲染与发布链路;当前阶段优先完成参数录入、默认值和校验能力。 - -| 配置项 | 作用 | 计划默认值 | -| --- | --- | --- | -| `OpenRestyWorkerProcesses` | `worker_processes` 配置;支持 `auto` 或正整数 | `auto` | -| `OpenRestyWorkerConnections` | `events { worker_connections }` 上限 | `4096` | -| `OpenRestyWorkerRlimitNofile` | `worker_rlimit_nofile` 上限 | `65535` | -| `OpenRestyEventsUse` | `events { use ... }` 指令;为空表示不显式渲染 | 空 | -| `OpenRestyEventsMultiAcceptEnabled` | 是否启用 `multi_accept on` | `false` | -| `OpenRestyKeepaliveTimeout` | `keepalive_timeout` 秒数 | `65` | -| `OpenRestyKeepaliveRequests` | `keepalive_requests` 上限 | `1000` | -| `OpenRestyClientHeaderTimeout` | `client_header_timeout` 秒数 | `15` | -| `OpenRestyClientBodyTimeout` | `client_body_timeout` 秒数 | `15` | -| `OpenRestySendTimeout` | `send_timeout` 秒数 | `30` | -| `OpenRestyProxyConnectTimeout` | `proxy_connect_timeout` 秒数 | `5` | -| `OpenRestyProxySendTimeout` | `proxy_send_timeout` 秒数 | `60` | -| `OpenRestyProxyReadTimeout` | `proxy_read_timeout` 秒数 | `60` | -| `OpenRestyProxyBufferingEnabled` | 是否启用 `proxy_buffering` | `true` | -| `OpenRestyProxyBuffers` | `proxy_buffers` 组合值,例如 `16 16k` | `16 16k` | -| `OpenRestyProxyBufferSize` | `proxy_buffer_size` | `8k` | -| `OpenRestyProxyBusyBuffersSize` | `proxy_busy_buffers_size` | `64k` | -| `OpenRestyGzipEnabled` | 是否启用 `gzip on` | `true` | -| `OpenRestyGzipMinLength` | `gzip_min_length` 字节数 | `1024` | -| `OpenRestyGzipCompLevel` | `gzip_comp_level` | `5` | -| `OpenRestyCacheEnabled` | 是否启用代理缓存 | `false` | -| `OpenRestyCachePath` | `proxy_cache_path` 目录 | 空 | -| `OpenRestyCacheLevels` | `proxy_cache_path levels=` 值 | `1:2` | -| `OpenRestyCacheInactive` | `proxy_cache_path inactive=` 时长 | `30m` | -| `OpenRestyCacheMaxSize` | `proxy_cache_path max_size=` 大小 | `1g` | -| `OpenRestyCacheKeyTemplate` | 缓存 Key 模板 | `$scheme$proxy_host$request_uri` | -| `OpenRestyCacheLockEnabled` | 是否启用 `proxy_cache_lock` | `true` | -| `OpenRestyCacheLockTimeout` | `proxy_cache_lock_timeout` 时长 | `5s` | -| `OpenRestyCacheUseStale` | `proxy_cache_use_stale` 场景列表 | `error timeout updating http_500 http_502 http_503 http_504` | - -说明: - -* 第五版第一批当前仅开放稳定、可校验、可回滚的常用性能项;更多指令后续按相同模式扩展 -* 所有大小、时长、布尔和整数参数都应在 Server 保存前完成校验 -* `OpenRestyCacheEnabled=false` 时,缓存目录与缓存参数应允许留空或回退到默认值 -* 任何包含路径的配置项都必须在 Agent 落盘前再次校验可写性与安全边界 +* 限流窗口上限不能超过 `RateLimitKeyExpirationDuration`,当前为 20 分钟 +* 限流按来源 IP 统计,若前置了 Nginx/CDN/LB,应正确透传真实客户端 IP + +### 1.2.2 第五版当前支持的 OpenResty 优化配置项 + +以下配置项当前已接入管理端「运维设置」,并统一保存在 `Option` 表中。它们会参与第五版后续的配置渲染与发布链路;当前阶段优先完成参数录入、默认值和校验能力。 + +| 配置项 | 作用 | 计划默认值 | +| --- | --- | --- | +| `OpenRestyWorkerProcesses` | `worker_processes` 配置;支持 `auto` 或正整数 | `auto` | +| `OpenRestyWorkerConnections` | `events { worker_connections }` 上限 | `4096` | +| `OpenRestyWorkerRlimitNofile` | `worker_rlimit_nofile` 上限 | `65535` | +| `OpenRestyEventsUse` | `events { use ... }` 指令;为空表示不显式渲染 | 空 | +| `OpenRestyEventsMultiAcceptEnabled` | 是否启用 `multi_accept on` | `false` | +| `OpenRestyKeepaliveTimeout` | `keepalive_timeout` 秒数 | `65` | +| `OpenRestyKeepaliveRequests` | `keepalive_requests` 上限 | `1000` | +| `OpenRestyClientHeaderTimeout` | `client_header_timeout` 秒数 | `15` | +| `OpenRestyClientBodyTimeout` | `client_body_timeout` 秒数 | `15` | +| `OpenRestySendTimeout` | `send_timeout` 秒数 | `30` | +| `OpenRestyProxyConnectTimeout` | `proxy_connect_timeout` 秒数 | `5` | +| `OpenRestyProxySendTimeout` | `proxy_send_timeout` 秒数 | `60` | +| `OpenRestyProxyReadTimeout` | `proxy_read_timeout` 秒数 | `60` | +| `OpenRestyProxyBufferingEnabled` | 是否启用 `proxy_buffering` | `true` | +| `OpenRestyProxyBuffers` | `proxy_buffers` 组合值,例如 `16 16k` | `16 16k` | +| `OpenRestyProxyBufferSize` | `proxy_buffer_size` | `8k` | +| `OpenRestyProxyBusyBuffersSize` | `proxy_busy_buffers_size` | `64k` | +| `OpenRestyGzipEnabled` | 是否启用 `gzip on` | `true` | +| `OpenRestyGzipMinLength` | `gzip_min_length` 字节数 | `1024` | +| `OpenRestyGzipCompLevel` | `gzip_comp_level` | `5` | +| `OpenRestyCacheEnabled` | 是否启用代理缓存 | `false` | +| `OpenRestyCachePath` | `proxy_cache_path` 目录 | 空 | +| `OpenRestyCacheLevels` | `proxy_cache_path levels=` 值 | `1:2` | +| `OpenRestyCacheInactive` | `proxy_cache_path inactive=` 时长 | `30m` | +| `OpenRestyCacheMaxSize` | `proxy_cache_path max_size=` 大小 | `1g` | +| `OpenRestyCacheKeyTemplate` | 缓存 Key 模板 | `$scheme$proxy_host$request_uri` | +| `OpenRestyCacheLockEnabled` | 是否启用 `proxy_cache_lock` | `true` | +| `OpenRestyCacheLockTimeout` | `proxy_cache_lock_timeout` 时长 | `5s` | +| `OpenRestyCacheUseStale` | `proxy_cache_use_stale` 场景列表 | `error timeout updating http_500 http_502 http_503 http_504` | + +说明: + +* 第五版第一批当前仅开放稳定、可校验、可回滚的常用性能项;更多指令后续按相同模式扩展 +* 所有大小、时长、布尔和整数参数都应在 Server 保存前完成校验 +* `OpenRestyCacheEnabled=false` 时,缓存目录与缓存参数应允许留空或回退到默认值 +* 任何包含路径的配置项都必须在 Agent 落盘前再次校验可写性与安全边界 ### 1.3 前端构建环境变量 @@ -215,32 +219,32 @@ go run ./cmd/agent -config ./agent.json { "server_url": "http://127.0.0.1:3000", "discovery_token": "replace-with-global-discovery-token", - "data_dir": "./data", - "openresty_container_name": "atsflare-openresty", - "openresty_docker_image": "openresty/openresty:alpine", - "heartbeat_interval": 10000, - "request_timeout": 10000 -} + "data_dir": "./data", + "openresty_container_name": "atsflare-openresty", + "openresty_docker_image": "openresty/openresty:alpine", + "heartbeat_interval": 10000, + "request_timeout": 10000 +} ``` 使用节点专属 Token 的示例: ```json -{ - "server_url": "http://127.0.0.1:3000", - "agent_token": "replace-with-node-auth-token", - "node_name": "node-01", - "node_ip": "192.168.1.20", - "data_dir": "./data", - "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", - "main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf", - "route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf", - "cert_dir": "/usr/local/openresty/nginx/conf/certs", - "openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs", - "state_path": "./data/agent-state.json", - "heartbeat_interval": 10000, - "request_timeout": 10000 -} +{ + "server_url": "http://127.0.0.1:3000", + "agent_token": "replace-with-node-auth-token", + "node_name": "node-01", + "node_ip": "192.168.1.20", + "data_dir": "./data", + "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", + "main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf", + "route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf", + "cert_dir": "/usr/local/openresty/nginx/conf/certs", + "openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs", + "state_path": "./data/agent-state.json", + "heartbeat_interval": 10000, + "request_timeout": 10000 +} ``` ### 2.3 Agent 配置字段 @@ -254,36 +258,36 @@ go run ./cmd/agent -config ./agent.json | `node_ip` | 节点 IP | 否 | 自动探测第一个可用 IPv4 | `192.168.1.20` | | `openresty_path` | 本机 OpenResty 可执行文件路径;设置后按本机 OpenResty 模式运行 | 否 | 空;未设置时按 Docker OpenResty 模式处理 | `/usr/local/openresty/nginx/sbin/openresty` | | `openresty_container_name` | Docker 模式下的 OpenResty 容器名 | 否 | `atsflare-openresty` | `atsflare-openresty` | -| `openresty_docker_image` | Docker 模式下用于初始化/管理的 OpenResty 镜像 | 否 | `openresty/openresty:alpine` | `openresty/openresty:alpine` | -| `docker_binary` | Docker 可执行文件名或路径 | 否 | `docker` | `/usr/bin/docker` | -| `data_dir` | Agent 数据目录,用于存储托管配置、证书和状态文件 | 否 | 配置文件所在目录下的 `data` 子目录 | `./data` | -| `main_config_path` | 第五版主配置接管时 OpenResty 主配置文件写入路径 | 第五版本机模式建议必填 | Docker 模式可使用受管默认路径;本机模式建议显式设置 | `/usr/local/openresty/nginx/conf/nginx.conf` | -| `route_config_path` | 路由配置文件写入路径 | 否 | 默认为 `data_dir` 下托管路径 | `/etc/nginx/conf.d/atsflare_routes.conf` | +| `openresty_docker_image` | Docker 模式下用于初始化/管理的 OpenResty 镜像 | 否 | `openresty/openresty:alpine` | `openresty/openresty:alpine` | +| `docker_binary` | Docker 可执行文件名或路径 | 否 | `docker` | `/usr/bin/docker` | +| `data_dir` | Agent 数据目录,用于存储托管配置、证书和状态文件 | 否 | 配置文件所在目录下的 `data` 子目录 | `./data` | +| `main_config_path` | 第五版主配置接管时 OpenResty 主配置文件写入路径 | 第五版本机模式建议必填 | Docker 模式可使用受管默认路径;本机模式建议显式设置 | `/usr/local/openresty/nginx/conf/nginx.conf` | +| `route_config_path` | 路由配置文件写入路径 | 否 | 默认为 `data_dir` 下托管路径 | `/etc/nginx/conf.d/atsflare_routes.conf` | | `cert_dir` | Agent 在本机写入证书文件的目录 | 否 | 默认为 `data_dir` 下托管证书目录 | `./data/etc/nginx/certs` | | `openresty_cert_dir` | OpenResty 实际读取证书的目录 | 否 | 本机模式默认等于 `cert_dir`;Docker 模式默认 `/etc/nginx/atsflare-certs` | `/usr/local/openresty/nginx/conf/certs` | | `state_path` | Agent 本地状态文件路径 | 否 | 默认为 `data_dir` 下托管状态文件 | `./data/agent-state.json` | -| `heartbeat_interval` | 心跳间隔 | 否 | `10000` 毫秒 | `10000` | +| `heartbeat_interval` | 心跳间隔 | 否 | `10000` 毫秒 | `10000` | | `request_timeout` | HTTP 请求超时时间 | 否 | `10000` 毫秒 | `10000` | 说明: * `agent_token` 与 `discovery_token` 不能同时为空 -* `heartbeat_interval`、`request_timeout` 支持两种写法: - * 毫秒整数,例如 `10000` +* `heartbeat_interval`、`request_timeout` 支持两种写法: + * 毫秒整数,例如 `10000` * Go duration 字符串,例如 `"30s"` -* `node_name` 与 `node_ip` 未填写时会自动探测;若自动探测失败,配置校验会报错 -* 未配置 `openresty_path` 时,默认为 Docker OpenResty 模式 -* 配置保存时,`agent_version`、`nginx_version` 由程序运行时维护,不需要写入 JSON -* 第五版主配置接管完成后,本机模式下应优先通过 `main_config_path` 由 Agent 写入受管主配置,而不是依赖节点手工维护 include 规则 +* `node_name` 与 `node_ip` 未填写时会自动探测;若自动探测失败,配置校验会报错 +* 未配置 `openresty_path` 时,默认为 Docker OpenResty 模式 +* 配置保存时,`agent_version`、`nginx_version` 由程序运行时维护,不需要写入 JSON +* 第五版主配置接管完成后,本机模式下应优先通过 `main_config_path` 由 Agent 写入受管主配置,而不是依赖节点手工维护 include 规则 ### 2.4 Agent 托管路径默认值 当未显式设置以下字段时,Agent 会根据 `data_dir` 自动生成托管路径: -| 字段 | 默认值 | -| --- | --- | -| `main_config_path` | 第五版 Docker 模式默认可落在 `data_dir/etc/nginx/nginx.conf`;本机模式建议显式配置 | -| `route_config_path` | `data_dir/etc/nginx/conf.d/atsflare_routes.conf` | +| 字段 | 默认值 | +| --- | --- | +| `main_config_path` | 第五版 Docker 模式默认可落在 `data_dir/etc/nginx/nginx.conf`;本机模式建议显式配置 | +| `route_config_path` | `data_dir/etc/nginx/conf.d/atsflare_routes.conf` | | `cert_dir` | `data_dir/etc/nginx/certs` | | `state_path` | `data_dir/var/lib/atsflare/agent-state.json` | @@ -312,15 +316,15 @@ Docker OpenResty 模式下: 适用于节点已经安装了宿主机 OpenResty,且 Agent 直接执行 `openresty -t` 与 `openresty -s reload`。 ```json -{ - "server_url": "http://127.0.0.1:3000", - "agent_token": "replace-with-node-auth-token", - "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", - "main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf", - "route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf", - "cert_dir": "/usr/local/openresty/nginx/conf/certs", - "openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs" -} +{ + "server_url": "http://127.0.0.1:3000", + "agent_token": "replace-with-node-auth-token", + "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", + "main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf", + "route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf", + "cert_dir": "/usr/local/openresty/nginx/conf/certs", + "openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs" +} ``` ---