容器化 + 上线到 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”)。
二、(可选)最小 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。
#启动pod测试集群内获取是否正常
kubectl -n monitoring run tmp-curl --rm -it --image=curlimages/curl --restart=Never \
-- http://pass-validator-exporter.monitoring.svc:9090/metrics | head
#可以在浏览器IP+端口访问
kubectl -n monitoring port-forward --address 0.0.0.0 svc/prometheus-k8s 9090:9090
方案 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)