容器化 + 上线到 K8s + 被 Prom 抓取

axing
2025-09-06 / 0 评论 / 3 阅读 / 正在检测是否收录...
容器化 + 上线到 K8s + 被 Prom 抓取

目标:Exporter 以容器形式部署到集群,并被 Prometheus 抓取。
任务

构建镜像并 push:make docker IMG=…。

应用 k8s/deployment.yaml + Service;为你的命名空间调好 selector。

如果你集群用 Prometheus Operator,创建 ServiceMonitor 让它自动发现 scrape 目标。

Prometheus target 变为 Up,图表能看到你的新指标。

若用 Operator,ServiceMonitor 生效(label 选择器正确)。

一、准备条件

你有 Docker、kubectl 权限,能访问集群。

Harbor 私有仓库可用(如果是私有,需要在 K8s 里创建 imagePullSecret,步骤见第 4 步)。

Exporter 监听 0.0.0.0:9090 且有 /metrics(若没有,见“可选:最小 main.go”)。

mf9fdylq.png

二、(可选)最小 main.go(若你项目还没暴露 /metrics)

在你的可执行入口里加入(或新建 main.go):
package main

import (
    "log"
    "net/http"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/collectors"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
    reg := prometheus.NewRegistry()
    reg.MustRegister(collectors.NewGoCollector(), collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
    // TODO: 在这里注册你自己的指标 reg.MustRegister(yourMetric)

    mux := http.NewServeMux()
    mux.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
    //(若你有 /live、/ready 就更好)
    mux.HandleFunc("/live", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) })
    mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) })

    log.Println("listening on :9090")
    log.Fatal(http.ListenAndServe(":9090", mux))
}

三、Dockerfile(多阶段构建,体积小、非 root 运行)
在项目根目录新建 Dockerfile:

root@k8s-03:/woke/go-password-validator-main# cat Dockerfile 
# ---- build ----
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .

# 如果入口在仓库根目录的 main.go:
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-s -w" -o /out/exporter .

# ---- runtime ----
FROM gcr.io/distroless/static:nonroot
USER nonroot:nonroot
COPY --from=build /out/exporter /exporter
EXPOSE 9090
ENTRYPOINT ["/exporter"]

四、Makefile 目标
在项目根目录的 Makefile 增加:

root@k8s-03:/woke/go-password-validator-main# cat Makefile 
# ---------- 现有的目标,保留 ----------
test:
    go test ./...

fmt:
    go fmt ./...

vet:
    go vet ./...

install-lint:
    # 建议用 go install(Go 1.16+)
    go install golang.org/x/lint/golint@latest
    @go env GOPATH >/dev/null

lint:
    go list ./... | grep -v /vendor/ | xargs -L1 golint -set_exit_status

install-staticcheck:
    go install honnef.co/go/tools/cmd/staticcheck@latest

staticcheck:
    staticcheck -f stylish ./...

# ---------- Docker 相关,新增 ----------
REGISTRY ?= 192.168.30.180:30003
REPO     ?= test/go
TAG      ?= $(shell date +%Y%m%d-%H%M%S)
IMG      := $(REGISTRY)/$(REPO):$(TAG)

.PHONY: docker docker-build docker-push
docker: docker-build docker-push

docker-build:
    docker build -t $(IMG) .

docker-push:
    docker push $(IMG)

使用:

# 登录你的 Harbor
docker login harbor.example.com

# 构建并 push(替换成你自己的 Harbor 项目/镜像名)
make docker IMG=harbor.example.com/ops/go-password-exporter:v0.1.0

#如果配置好了Makefile文件 可以直接执行 make docker  也可以像上面这样定制

五、Harbor 私有仓库拉取凭据(K8s imagePullSecret)

注意:imagePullSecret 是命名空间级的,需要在哪个 ns 拉镜像,就在哪个 ns 创建;要全局通用只能在每个 ns 都创建(或用准入控制器自动注入)。
#示例,在 monitoring 命名空间创建:
kubectl -n monitoring create secret docker-registry harbor-cred \
  --docker-server=192.168.30.180:30003 \
  --docker-username='admin' \
  --docker-password='Harbor12345' \
  --docker-email='ops@example.com'

六、部署到 K8s(Deployment + Service)

#deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pass-validator-exporter
  namespace: monitoring
  labels:
    app.kubernetes.io/name: pass-validator-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: pass-validator-exporter
  template:
    metadata:
      labels:
        app.kubernetes.io/name: pass-validator-exporter
    spec:
      imagePullSecrets:
        - name: harbor-cred
      containers:
        - name: exporter
          image: 192.168.30.180:30003/test/go:20250905-154053
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 9090
          readinessProbe:
            httpGet: { path: /ready, port: http }
            initialDelaySeconds: 3
            periodSeconds: 5
          livenessProbe:
            httpGet: { path: /live, port: http }
            initialDelaySeconds: 10
            periodSeconds: 10
          resources:
            requests: { cpu: "50m", memory: "64Mi" }
            limits:   { cpu: "200m", memory: "128Mi" }
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsNonRoot: true
            runAsUser: 65532
#service.yaml
apiVersion: v1
kind: Service
metadata:
  name: pass-validator-exporter
  namespace: monitoring
  labels:
    app.kubernetes.io/name: pass-validator-exporter
  # 若你是“裸 Prometheus + 注解发现”,可打开以下注解:
  # annotations:
  #   prometheus.io/scrape: "true"
  #   prometheus.io/path: "/metrics"
  #   prometheus.io/port: "9090"
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: pass-validator-exporter
  ports:
    - name: http
      port: 9090
      targetPort: http
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml

# 验证:
kubectl -n monitoring get pods -l app.kubernetes.io/name=pass-validator-exporter -w
kubectl -n monitoring get svc pass-validator-exporter
# 本地看指标(可选):
kubectl -n monitoring port-forward svc/pass-validator-exporter 19090:9090
curl http://127.0.0.1:19090/metrics | head

七、让 Prometheus 抓取

方案 A:你在用 Prometheus Operator / kube-prometheus-stack(推荐)

关键点:ServiceMonitor 的 selector 要能匹配到 Service 的 label;同时 ServiceMonitor 自己的 label 要匹配到 Prometheus 实例的 serviceMonitorSelector。大多数 Helm 安装用的 label 是 release: kube-prometheus-stack(按你的集群实际为准)。
# 看 Prometheus 实例的 selector 要求(取第一个 Prometheus 资源)
kubectl -n monitoring get prometheus -o jsonpath='{.items[0].spec.serviceMonitorSelector.matchLabels}{"\n"}'
# 一般会看到 {"release":"<你的 Helm release 名>"}
#创建 servicemonitor.yaml  如果上面没有查到直接删除  labels:
#                                                    release: kube-prometheus-stack  # ←改成你 Prometheus 实例在选的那个 label
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: pass-validator-exporter
  namespace: monitoring
  labels:
    release: kube-prometheus-stack  # ←改成你 Prometheus 实例在选的那个 label
spec:
  namespaceSelector:
    matchNames: ["monitoring"]
  selector:
    matchLabels:
      app.kubernetes.io/name: pass-validator-exporter  # 要和 Service 的 label 完全一致
  endpoints:
    - port: http          # 要和 Service 端口名一致
      path: /metrics
      interval: 15s
      scrapeTimeout: 10s
#应用并验证:
kubectl apply -f servicemonitor.yaml

# 在 Prometheus UI -> Status -> Targets 里应该出现你的 target,并且是 UP。

mf72p1is.png

#启动pod测试集群内获取是否正常
kubectl -n monitoring run tmp-curl --rm -it --image=curlimages/curl --restart=Never \
  -- http://pass-validator-exporter.monitoring.svc:9090/metrics | head

mf72pmru.png

#可以在浏览器IP+端口访问
kubectl -n monitoring port-forward --address 0.0.0.0 svc/prometheus-k8s 9090:9090

mf72k3xa.png

方案 B:裸 Prometheus(没装 Operator)
你需要在 Prometheus 的 prometheus.yml 里启用 kubernetes_sd_configs,并用注解过滤(上面 Service 已给注解示例)。典型片段如下(放到你的 scrape_configs 里):
- job_name: 'kubernetes-services'
  kubernetes_sd_configs:
    - role: endpoints
  relabel_configs:
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
      action: keep
      regex: "true"
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
      action: replace
      target: __metrics_path__
      regex: "(.+)"
    - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
      action: replace
      target: __address__
      regex: (.+?)(?::\d+)?;(\d+)
      replacement: $1:$2
#改好配置后重启 Prometheus,去 Targets 看是否 UP。

八、迭代上线(改代码后快速发版)

# 1) 构建并推送新镜像
make docker IMG=harbor.example.com/ops/go-password-exporter:v0.1.1

# 2) 滚动更新 Deployment(或直接改 yaml)
kubectl -n monitoring set image deploy/pass-validator-exporter exporter=harbor.example.com/ops/go-password-exporter:v0.1.1
kubectl -n monitoring rollout status deploy/pass-validator-exporter

九、验收清单

Prometheus Targets 里看到 pass-validator-exporter → UP。

在 Prometheus 或 Grafana 中能查询到你的新指标(例如:your_metric_name)。

如果是 Operator:ServiceMonitor 的 labels.release 与 Prometheus 实例选择器 匹配;ServiceMonitor.spec.selector.matchLabels 与 Service 的 labels 匹配;endpoints.port 与 Service 端口名 一致。

十、常见坑速查

镜像拉不下来:Deployment 没配置 imagePullSecrets,或 secret 在错的命名空间。

Target 一直 DOWN:容器没监听 0.0.0.0;端口/路径不一致;Service 选择器写错;endpoints.port 名字不对。

Operator 抓不到:ServiceMonitor 的 labels 与 Prometheus 的 serviceMonitorSelector 不匹配。

/metrics 超时:指标收集阻塞(锁/IO);建议拆分收集逻辑并设置 scrapeTimeout。

权限:Exporter 是 HTTP 只读,不需要额外 RBAC。
0

评论 (0)

取消