Skip to content

部署与可观测性 (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-service

2. 可观测性 (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。

🚀 学习遇到瓶颈?想进大厂?

看完这篇技术文章,如果还是觉得不够系统,或者想在实战中快速提升?
王中阳的就业陪跑训练营,提供定制化学习路线 + 企业级实战项目 + 简历优化 + 模拟面试。

了解训练营详情