部署与可观测性 (Deployment & Observability)
开发完成只是第一步,如何将 RAG 服务稳定地运行在生产环境,并对其进行监控,是工程化的最后“一公里”。
1. Docker 容器化部署
Golang 的最大优势之一是编译产物极小(静态链接二进制)。我们可以使用 多阶段构建 (Multi-stage Build) 来制作超小的 Docker 镜像。
Dockerfile 示例
dockerfile
# 阶段 1: 编译
FROM golang:1.21-alpine AS builder
WORKDIR /app
# 设置代理 (国内环境)
ENV GOPROXY=https://goproxy.cn,direct
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 编译为名为 server 的二进制文件
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/api
# 阶段 2: 运行 (使用 distroless 或 alpine)
FROM alpine:latest
WORKDIR /root/
# 安装 ca-certificates (调用 HTTPS API 需要)
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/server .
COPY --from=builder /app/config.yaml .
EXPOSE 8080
CMD ["./server"]构建并运行:
bash
docker build -t go-rag-service .
docker run -p 8080:8080 -e OPENAI_API_KEY=sk-xxx go-rag-service2. 可观测性 (Observability)
RAG 系统链路长(API -> Embedding -> VectorDB -> LLM),一旦变慢,很难排查是哪一步的问题。 我们需要引入 OpenTelemetry 进行链路追踪 (Tracing)。
Golang 接入 OpenTelemetry
go
package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)
// 初始化 Tracer
func initTracer(url string) func(context.Context) error {
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("go-rag-service"),
)),
)
otel.SetTracerProvider(tp)
return tp.Shutdown
}
// 在业务代码中打点
func (uc *ChatUseCase) Chat(ctx context.Context, query string) (string, error) {
tr := otel.Tracer("usecase")
ctx, span := tr.Start(ctx, "Chat")
defer span.End()
// 1. Embedding
_, embedSpan := tr.Start(ctx, "Embedding")
// ... 调用 Embedding API ...
embedSpan.End()
// 2. Retrieval
_, searchSpan := tr.Start(ctx, "VectorSearch")
// ... 调用 Milvus ...
searchSpan.SetAttributes(attribute.Int("top_k", 5))
searchSpan.End()
// 3. LLM Generation
_, llmSpan := tr.Start(ctx, "LLMGenerate")
// ... 调用 OpenAI ...
llmSpan.End()
return "result", nil
}3. 监控指标 (Metrics)
使用 Prometheus 监控关键指标:
- QPS: 每秒请求数。
- Latency: 接口响应时间 (P99, P95)。
- Token Usage: 消耗的 Token 数量(直接关联成本)。
go
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
requestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP请求耗时分布",
},
[]string{"path"},
)
)
func init() {
prometheus.MustRegister(requestDuration)
}
// 在 Handler 中记录
func Handler(c *gin.Context) {
timer := prometheus.NewTimer(requestDuration.WithLabelValues(c.FullPath()))
defer timer.ObserveDuration()
// ... 处理业务 ...
}4. 总结
Golang 在云原生时代的统治地位,使得 RAG 服务可以轻松集成到现有的 K8s、Prometheus、Jaeger 体系中。
- 小镜像:节省存储和传输带宽。
- OpenTelemetry:标准化全链路监控。
- 高稳定性:类型安全和错误处理机制减少运行时 Panic。
