3. Exporter 项目结构:标准目录组织、模块划分、配置文件管理、Makefile 与 CI 集成
一个标准化的 Exporter 项目结构是团队协作、自动化构建与持续交付的基础。本章将定义一套通用的目录组织规范、模块划分原则、配置文件管理策略以及 Makefile 与 CI 集成方案,确保所有 Exporter 项目在结构上保持一致,降低维护成本。
3.1 标准目录组织
推荐采用以下目录结构,适用于基于 Go 语言开发的 Exporter 项目。该结构遵循 Go 社区的最佳实践,并针对 Exporter 场景进行了优化。
my-exporter/
├── cmd/
│ └── my-exporter/
│ └── main.go # 程序入口
├── internal/
│ ├── collector/ # 采集器核心逻辑
│ │ ├── collector.go # 采集器接口定义
│ │ └── metrics.go # 指标定义与注册
│ ├── config/ # 配置管理
│ │ └── config.go # 配置结构体与加载
│ ├── handler/ # HTTP 处理器
│ │ └── handler.go # /metrics 端点处理
│ └── exporter/ # Exporter 主逻辑
│ └── exporter.go # 启动、关闭、健康检查
├── pkg/
│ ├── utils/ # 通用工具函数
│ │ └── utils.go
│ └── logger/ # 日志封装
│ └── logger.go
├── configs/
│ └── my-exporter.yaml # 默认配置文件
├── scripts/
│ ├── build.sh # 构建脚本
│ └── test.sh # 测试脚本
├── deploy/
│ ├── Dockerfile # 容器化部署
│ └── docker-compose.yaml # 本地开发环境
├── Makefile # 构建、测试、发布
├── go.mod
├── go.sum
├── README.md
└── CHANGELOG.md
关键原则:
- cmd/:只包含 main 函数,不包含业务逻辑。
- internal/:私有包,外部项目无法导入,强制封装。
- pkg/:可被外部项目引用的公共库。
- configs/:存放默认配置,不包含敏感信息。
3.2 模块划分与职责
模块划分遵循单一职责原则,每个模块只负责一个明确的领域。下表列出了核心模块及其职责:
| 模块 | 职责 | 关键文件 |
|---|---|---|
| collector | 定义采集器接口,实现具体指标的采集逻辑 | collector.go, metrics.go |
| config | 定义配置结构体,加载与验证配置 | config.go |
| handler | 处理 HTTP 请求,暴露 /metrics 端点 | handler.go |
| exporter | 管理 Exporter 生命周期(启动、关闭、健康检查) | exporter.go |
| utils | 提供通用工具函数(字符串处理、网络请求等) | utils.go |
| logger | 封装日志库,提供统一的日志接口 | logger.go |
模块依赖关系: handler 依赖 collector 和 exporter;exporter 依赖 config 和 logger;collector 依赖 utils。禁止循环依赖。
3.3 配置文件管理
配置文件采用 YAML 格式,支持环境变量覆盖。配置管理模块需要实现以下功能:
- 默认值:所有配置项必须有合理的默认值。
- 环境变量覆盖:支持通过环境变量覆盖配置项,命名规则为
EXPORTER_<SECTION>_<KEY>。 - 配置验证:加载配置后立即进行合法性校验,避免运行时错误。
- 热加载:支持监听配置文件变化并自动重载(可选)。
# configs/my-exporter.yaml 示例
server:
listen_address: "0.0.0.0"
listen_port: 8080
metrics_path: "/metrics"
collector:
scrape_interval: 15s
timeout: 10s
targets:
- "http://api.example.com/health"
- "http://db.example.com/status"
logging:
level: "info"
format: "json"
output: "stdout"
安全提醒: 不要在配置文件中硬编码密码、 等敏感信息。应使用环境变量或外部密钥管理服务(如 Vault)注入。
3.4 Makefile 与 CI 集成
Makefile 是项目自动化的核心,CI 流水线则确保每次提交都经过标准化检查。以下是一个标准的 Makefile 模板:
# Makefile
APP_NAME := my-exporter
VERSION := $(shell git describe --tags --always --dirty)
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
LDFLAGS := -ldflags "-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)"
.PHONY: build test lint clean docker
build:
go build $(LDFLAGS) -o bin/$(APP_NAME) ./cmd/$(APP_NAME)
test:
go test -v -race -coverprofile=coverage.out ./...
lint:
golangci-lint run ./...
clean:
rm -rf bin/ coverage.out
docker:
docker build -t $(APP_NAME):$(VERSION) -f deploy/Dockerfile .
CI 集成(以 GitHub Actions 为例)应包含以下阶段:
| 阶段 | 操作 | 触发条件 |
|---|---|---|
| Lint | 运行 golangci-lint,检查代码风格与潜在错误 | 每次 push 和 pull request |
| Unit Test | 运行单元测试,生成覆盖率报告 | 每次 push 和 pull request |
| Build | 编译二进制文件,验证构建是否成功 | 每次 push 和 pull request |
| Docker Build | 构建 Docker 镜像,验证 Dockerfile 正确性 | push 到 main 分支或创建 tag |
| Release | 创建 GitHub Release,上传二进制与镜像 | 创建 tag(如 v1.0.0) |
CI 最佳实践:
- Lint 和 Test 阶段应并行执行,缩短流水线时间。
- Docker Build 阶段使用多阶段构建,减小镜像体积。
- Release 阶段自动生成 CHANGELOG 并更新版本号。
3.5 项目结构关系图
下图展示了 Exporter 项目各模块之间的调用关系与数据流向:
3.6 标准化检查清单
在提交代码前,请对照以下清单进行自查:
| 检查项 | 要求 | 验证方式 |
|---|---|---|
| 目录结构 | 符合 3.1 节定义的标准结构 | tree 命令对比 |
| 模块划分 | 无循环依赖,每个模块职责单一 | go vet 检查 |
| 配置文件 | 存在默认配置,支持环境变量覆盖 | 单元测试覆盖 |
| Makefile | 包含 build、test、lint、clean 目标 | make help 输出 |
| CI 流水线 | 包含 Lint、Test、Build、Docker Build 阶段 | CI 运行日志 |
| 代码规范 | 通过 golangci-lint 检查 | make lint 无报错 |
推荐工具:
- cookiecutter:基于模板快速生成标准项目结构。
- pre-commit:在提交前自动运行 lint 和测试。
- act:本地运行 GitHub Actions 流水线,加速调试。