Skip to content

Conversation

JaD1ng
Copy link
Collaborator

@JaD1ng JaD1ng commented Sep 30, 2025

变更背景和解决方案

关联issue: #

文档更新(架构文档、API文档、升级文档)

Checklist

  • 确认是否有文档更新
  • 确认是否自测
  • 确认是否考虑兼容升级,不兼容升级需要有对应的解决方案
  • 确认是否考虑添加监控指标
  • API 设计是否符合 API 设计规范, 如果没有 API 变更,请忽略
  • 我已经添加了相关人员到 Reviewers 列表中
  • 我已经设置了 Development 关联的 Issue

实现HTTP中间件记录请求时延并导出到Prometheus指标
添加服务信息到指标收集器
统一代码格式和修复缩进问题
添加Prometheus Adapter API文档
重构指标注入器以支持服务版本维度
移除冗余的exported_job标签和实例ID生成
新增HTTP延迟注入器并与中间件集成
更新HTTP时延指标名称使其更简洁,同时改进健康检查脚本以支持批量检查多个端口
实现Prometheus适配器模块,包括以下主要功能:
- 添加Prometheus客户端封装,支持指标查询和范围查询
- 实现指标服务层,提供指标列表获取和指标数据查询
- 添加API路由和控制器,提供RESTful接口
- 定义模型结构体和错误处理机制
- 更新依赖添加Prometheus客户端库
- 编写详细API文档说明接口使用方式
新增告警规则同步API及服务实现,支持将规则同步到Prometheus并触发重载
重构API层提取公共错误处理和工具方法到通用模块
添加相关模型定义和文档更新
- 在prometheus.yml中配置告警规则文件路径
- 修改docker-compose.yml挂载规则目录
- 重构AlertService,移除本地文件存储,直接写入容器
- 添加容器内规则文件写入的容错机制
- 将watch_time字段从AlertRuleMeta移到AlertRule中
- 移除全量同步接口,改为增量更新方式
- 实现批量更新规则元信息的API
- 重构服务层代码结构,提高可维护性
- 更新文档
添加告警规则本地文件持久化功能,支持启动时加载和关闭时保存规则
重构关闭逻辑实现优雅关闭,包括保存当前规则状态
更新构建和部署脚本以处理规则文件目录
修改测试脚本以适配新的增量更新接口
添加prometheus_adapter.yml配置文件支持
重构alert_service使用配置而非环境变量
新增alert_webhook_service实现告警轮询推送
更新build.sh和deploy.sh支持配置文件部署
更新README文档说明新的webhook架构
- 新增Alertmanager API v2兼容接口用于接收Prometheus告警
- 重构告警服务架构,替换原有的轮询模式为推送模式
- 添加docker-compose配置支持Prometheus管理API
- 移除过时的AlertWebhookService实现
实现删除告警规则模板及其关联元信息的功能,包括:
1. 添加DELETE /v1/alert-rules/:rule_name接口删除规则模板
2. 添加DELETE /v1/alert-rules-meta/:rule_name接口删除特定元信息
3. 更新相关文档说明删除操作的使用方法
- 在deploy.sh和build.sh中添加PID文件管理,优化服务启动和停止流程
- 修复alert_service.go中告警表达式生成的标签处理逻辑
- 使用%g代替%f格式化浮点数以避免科学计数法显示
- 新增部署任务状态常量"error"表示失败状态
- 重构部署任务表结构,使用服务名和版本作为联合主键
- 实现部署适配器处理与deploy模块的交互
- 新增部署任务API支持服务名和版本作为路径参数
- 完善部署任务创建、更新、暂停、继续和回滚逻辑
- 新增配置模块支持数据库配置加载
- 更新SQL schema文件反映部署任务表结构变更
- 优化API分组注释和代码结构
- 添加本地部署包存储路径支持,修改包URL构建逻辑
- 优化部署服务异步执行上下文传递
- 新增部署测试脚本和打包脚本
- 添加数据库schema文件用于部署模块
- 更新配置文件包含数据库连接和私钥信息
检查表达式是否已包含比较操作符,避免重复添加
更新告警Webhook服务地址配置
Copy link
Contributor

Summary of Changes

Hello @JaD1ng, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

本次拉取请求引入了一个新的Prometheus Adapter服务,旨在集中化指标查询和告警规则管理,从而连接内部系统与Prometheus。同时,它将部署系统集成到服务管理模块中,增强了其管理服务部署和回滚的能力。这些更改还通过添加HTTP延迟注入和详细的HTTP指标收集,改进了模拟服务的可观测性。

Highlights

  • Prometheus Adapter集成: 新增了一个名为prometheus_adapter的模块,作为内部系统与Prometheus之间的桥梁。它提供统一的REST API,用于指标查询和告警规则管理。
  • 告警规则管理API: prometheus_adapter模块引入了全面的API,用于管理Prometheus告警规则。这包括更新规则模板、批量更新特定服务/版本的规则元数据(如阈值),以及删除规则或元数据。
  • Prometheus Alertmanager Webhook兼容: prometheus_adapter现在可以作为Alertmanager兼容的Webhook接收器。它会轮询Prometheus以获取活跃告警,并将其转发到配置的监控/告警模块。
  • 服务管理模块部署系统集成: service_manager模块已更新,集成了部署系统。这包括修改deploy_tasks表的Schema,使用serviceversion作为主键,并调整部署相关的API以触发实际的部署和回滚流程。
  • 可观测性增强: 模拟服务现在具备HTTP延迟注入功能,并能将HTTP请求持续时间作为Prometheus指标进行记录,并带有service_version标签,以实现更细粒度的监控和故障注入。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 引入了发布系统与服务管理模块的集成,并新增了一个完整的 prometheus_adapter 服务,功能变更范围非常大。代码整体结构清晰,特别是 prometheus_adapter 的分层设计值得肯定。然而,在实现中发现了一些严重问题,需要优先解决:

  1. 严重安全漏洞internal/deploy/config.yaml 文件中硬编码了 RSA 私钥,这是一个严重的安全风险,私钥绝不应该提交到版本控制系统中。
  2. 严重设计问题prometheus_adapter 服务通过执行 docker 命令行来修改容器内的文件和重载配置。这种做法破坏了容器化的原则,非常脆弱且不安全,强耦合了应用和外部环境。
  3. 硬编码路径:多处代码和脚本中包含了开发者的本地绝对路径,导致代码不可移植。

此外,还存在一些配置加载逻辑混乱、数据库 schema 类型不匹配等问题。建议在合并前优先处理上述严重问题,以确保系统的安全性、稳定性和可维护性。

Comment on lines +9 to +24
privateKey: |
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y
7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7
Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB
AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM
ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1
XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB
/jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40
IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG
4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9
DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8
9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw
DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO
AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O
-----END RSA PRIVATE KEY-----
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

严重安全漏洞:私钥被硬编码在配置文件中并提交到版本控制系统。这会造成极大的安全风险,任何能访问代码仓库的人都能获取到该私钥。

强烈建议立即从代码库中移除此私钥,并采取以下任一方式进行管理:

  1. 使用环境变量加载私钥。
  2. 通过安全的密钥管理服务(如 Vault)在运行时获取。
  3. 将私钥存放在服务器上受保护的文件中,并通过配置指定文件路径来读取。

Comment on lines +553 to +576
cmd := exec.Command("docker", "exec", containerName, "sh", "-c",
fmt.Sprintf("cat > /etc/prometheus/rules/alert_rules.yml << 'EOF'\n%s\nEOF", string(data)))

if output, err := cmd.CombinedOutput(); err != nil {
// 如果直接写入容器失败,尝试使用临时文件+docker cp
log.Warn().
Err(err).
Str("output", string(output)).
Msg("Failed to write directly to container, trying docker cp")

// 写入临时文件
tmpFile := "/tmp/prometheus_alert_rules.yml"
if err := os.WriteFile(tmpFile, data, 0644); err != nil {
return fmt.Errorf("failed to write temp rules file: %w", err)
}

// 使用docker cp复制到容器
if err := s.syncRuleFileToContainer(tmpFile); err != nil {
return fmt.Errorf("failed to sync to container: %w", err)
}

// 清理临时文件
os.Remove(tmpFile)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

严重设计问题:在应用内部通过 exec.Command 调用 docker CLI 来修改容器内的文件,这是非常危险且脆弱的做法。

这种方式存在以下问题:

  1. 破坏容器化原则:应用不应该感知到自己运行在容器中,更不应该依赖外部的 docker 命令。
  2. 安全性差:如果 containerName 等变量可被外部影响,可能导致命令注入漏洞。
  3. 可移植性差:强依赖于运行环境中存在并配置好了 docker CLI。
  4. 脆弱性:任何 docker 命令的语法变化或环境问题都可能导致应用失败。

推荐的替代方案:

  • 使用卷挂载(Volume Mounts):将 Prometheus 的规则目录挂载到 prometheus_adapter 容器的一个路径下,adapter 直接读写这个挂载的文件系统即可,Prometheus 会自动感知到变化。

func NewDeployAdapter(instanceManager InstanceManager) *DeployAdapter {
return &DeployAdapter{
instanceManager: instanceManager,
baseURL: "/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages", // 本地包仓库路径
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

严重配置问题:代码中硬编码了一个开发者的本地绝对路径 "/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages"

这会导致代码在任何其他环境或其他开发者机器上都无法正常工作。这个路径必须通过配置文件或环境变量进行配置,使其在不同部署环境中可以被修改。

Suggested change
baseURL: "/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages", // 本地包仓库路径
baseURL: os.Getenv("PACKAGE_REPO_BASE_URL"), // 或者从配置中读取

Comment on lines +24 to +45
adapter, err := prometheusadapter.NewPrometheusAdapterServer(&config.Config{})
if err != nil {
log.Fatal().Err(err).Msg("Failed to create Prometheus Adapter server")
}

// 获取 Prometheus Adapter 内部配置的绑定地址
bindAddr := ":9999" // 默认端口
if adapter.GetBindAddr() != "" {
bindAddr = adapter.GetBindAddr()
}

// 如果有环境变量,优先使用环境变量的端口
if port := os.Getenv("ADAPTER_PORT"); port != "" {
bindAddr = ":" + port
}

// 更新配置(虽然已经创建了 adapter,但需要端口信息用于启动服务器)
cfg := &config.Config{
Server: config.ServerConfig{
BindAddr: bindAddr,
},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

这里的配置加载和使用方式比较混乱,可能会导致潜在的 bug。

  1. 在第24行,NewPrometheusAdapterServer 使用了一个空的 config.Config{} 进行初始化,这意味着 adapter 实例内部没有获取到任何有效的配置信息。
  2. 在第41行,又创建了一个新的 cfg 变量,它只包含了 bindAddr,并且仅用于第67行的 router.Run()

这种分离的配置处理方式,使得 adapter 实例在创建时无法获取到完整的配置,如果后续 adapter 的其他方法需要依赖配置,就会出问题。建议重构此处的逻辑,确保配置只加载一次,并在初始化时就传递给所有需要它的组件。

id VARCHAR(255) NOT NULL PRIMARY KEY, -- VARCHAR类型主键,非自增,不为空
service_name VARCHAR(255),
service_version VARCHAR(255),
host_id VARCHAR(255),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

数据库列类型不匹配。
instances 表中的 host_id 列定义为 VARCHAR(255),但它似乎是作为外键关联到 hosts 表的 id 列。而 hosts.id 列的类型是 SERIAL,即自增整数(integer)。

为了保证数据完整性和查询性能,建议将 host_id 的类型修改为 INTEGERBIGINT,并添加外键约束。

-- 建议的修改
CREATE TABLE instances (
    ...
    host_id INTEGER,
    ...
    FOREIGN KEY (host_id) REFERENCES hosts(id)
);

Comment on lines +127 to +129
if err.Error() == fmt.Sprintf("rule '%s' not found", ruleName) {
SendErrorResponse(c, http.StatusNotFound, model.ErrorCodeInvalidParameter,
err.Error(), nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

直接比较错误字符串 err.Error() == ... 的方式非常脆弱。如果底层的错误信息文本发生任何变化(例如,添加了空格或修改了标点),这里的逻辑就会失效。

推荐的做法是在 service 层定义并返回自定义的错误类型(例如 ErrRuleNotFound),然后在 api 层使用 errors.Is()errors.As() 来进行类型安全的错误判断。这样可以解耦错误处理和具体的错误信息文本。

Comment on lines +101 to +118
func getDefaultConfig() *PrometheusAdapterConfig {
return &PrometheusAdapterConfig{
Prometheus: PrometheusConfig{
Address: "http://10.210.10.33:9090",
ContainerName: "mock-s3-prometheus",
},
AlertWebhook: AlertWebhookConfig{
URL: "http://alert-module:8080/v1/integrations/alertmanager/webhook",
PollingInterval: "10s",
},
AlertRules: AlertRulesConfig{
LocalFile: "../rules/alert_rules.yml",
PrometheusRulesDir: "/etc/prometheus/rules/",
},
Server: ServerConfig{
BindAddr: "0.0.0.0:9999",
},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

默认配置中包含了大量硬编码的、特定于环境的IP地址和路径,例如 10.210.10.33:9090../rules/alert_rules.yml。这使得服务在其他环境中部署变得困难和易错。

建议将默认值修改为更通用的形式,例如:

  • address: "http://localhost:9090"
  • url: "http://localhost:8080/v1/..."
  • local_file: "rules/alert_rules.yml" (相对于配置文件的路径)

环境特定的值应该通过部署时的配置文件或环境变量来覆盖,而不是硬编码在代码中。

log_info "打包服务..."

local package_name="storage-${VERSION}.tar.gz"
local project_root="/Users/dingnanjia/workspace/mock/zeroops"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

脚本中硬编码了开发者的本地绝对路径。这使得该脚本无法在其他开发者或CI/CD环境中使用。

建议修改为动态获取项目根目录,例如使用 cd "$(dirname "$0")"/../../.. && pwd 来定位到项目根目录。

Suggested change
local project_root="/Users/dingnanjia/workspace/mock/zeroops"
local project_root=$(cd "$(dirname "$0")"/../../.. && pwd)

# 配置参数
SERVICE_NAME="storage"
BASE_URL="http://localhost:8080"
PACKAGE_PATH="/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

测试脚本中硬编码了开发者的本地绝对路径。这使得其他人在运行此测试脚本时会失败。

建议将此路径作为脚本的参数或从环境变量中读取,以提高脚本的可移植性。

Suggested change
PACKAGE_PATH="/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages"
PACKAGE_PATH="${PACKAGE_PATH:-/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages}"

func NewDeployAdapter(instanceManager InstanceManager) *DeployAdapter {
return &DeployAdapter{
instanceManager: instanceManager,
baseURL: "/Users/dingnanjia/workspace/mock/zeroops/internal/deploy/packages", // 本地包仓库路径
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 CRITICAL: Hardcoded absolute path

This hardcoded path will fail in any environment except your local machine. This breaks production deployments, CI/CD, and other developers' environments.

Fix: Use environment variable or configuration:

baseURL := os.Getenv("PACKAGE_REPO_PATH")
if baseURL == "" {
    baseURL = "./packages" // Relative default
}


// 创建元信息
if len(rule.Labels) > 0 {
labelsJSON, _ := json.Marshal(rule.Labels)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 HIGH: Ignored JSON error causes silent data corruption

If marshaling fails, labelsJSON will be nil and the error is ignored. This can lead to invalid data being stored without any indication.

Fix:

labelsJSON, err := json.Marshal(rule.Labels)
if err != nil {
    log.Warn().Err(err).Msg("Failed to marshal labels")
    continue
}

service := &AlertService{
promClient: promClient,
config: config,
currentRules: []model.AlertRule{},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ CONCURRENCY: Missing synchronization

currentRules and currentRuleMetas are accessed from HTTP handlers (concurrent goroutines) without any mutex protection. This can cause data races, panics, or corrupted state.

Fix: Add mutex:

type AlertService struct {
    mu               sync.RWMutex
    currentRules     []model.AlertRule
    currentRuleMetas []model.AlertRuleMeta
    // ...
}

Then use s.mu.Lock()/s.mu.RLock() in all methods that access these fields.

return &AlertmanagerService{
config: config,
webhookURL: config.AlertWebhook.URL,
httpClient: &http.Client{Timeout: 30 * time.Second},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ PERFORMANCE: Missing connection pooling

HTTP client without configured Transport will not reuse connections, causing:

  • Increased latency (TCP handshake overhead)
  • Port exhaustion under load
  • High memory usage

Fix:

httpClient: &http.Client{
    Timeout: 30 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
}


// 解析 Prometheus 发送的告警
var alerts []model.AlertmanagerAlert
body, err := io.ReadAll(r.Body)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 SECURITY: Unbounded request body read

Reading entire request body without size limits can cause OOM with large/malicious payloads.

Fix:

body, err := io.ReadAll(io.LimitReader(r.Body, 10*1024*1024)) // 10MB limit

@@ -0,0 +1,122 @@
package api
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 SECURITY CRITICAL: No authentication or authorization

All API endpoints are completely unauthenticated. Anyone with network access can:

  • Create/update/delete alert rules
  • Query metrics
  • Trigger deployments

This is a critical security vulnerability for production use.

Recommendations:

  1. Implement API key or JWT authentication middleware
  2. Add authorization checks for different operations (RBAC)
  3. Add rate limiting to prevent abuse
  4. Consider mTLS for service-to-service communication

This should be addressed before merging to production.

Copy link

niupilot bot commented Sep 30, 2025

综合评审总结

通过代码质量、性能、安全和文档四个维度的专业评审,发现以下关键问题:

🚨 关键问题(必须修复)

  1. 硬编码绝对路径 - deploy_adapter.go:24 使用个人路径,生产环境无法运行
  2. 命令注入漏洞 - alert_service.go:553 使用 sh -c 存在安全风险
  3. 无认证授权 - 所有 API 端点完全开放,生产环境严重安全隐患
  4. 并发数据竞争 - AlertService 的切片字段无同步保护

⚠️ 重要问题(强烈建议修复)

  1. HTTP 客户端缺少连接池配置(3处),高负载下性能严重下降
  2. Goroutine 泄漏风险 - 后台任务使用 context.Background() 无法取消
  3. JSON 解析错误被忽略,可能导致数据损坏

📋 建议改进

  • 请求体大小限制(防 OOM 攻击)
  • 统一使用结构化日志
  • 数据库连接池配置
  • 输入验证加强

优点: 代码结构清晰,日志记录完善,错误处理基本得当,文档较为详细。

建议: 关键问题修复后再合并到生产分支。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant