Linux: Prometheus
- TAGS: Linux
exporter
告警规则
prometheus 安装
多云采集
| 名称 | 平台 | 采集方式 | 是不需要打通内网 |
| 外部 promethes | ali | 接收端 | |
| 外部 grafana | ali | 展示 | |
| EKS | aws | 集群内部署一套 Prometheus(agent 模式) → | 否。出网可访问 ali prom |
| remote_write 推到外部 Prometheus | |||
| RDS 基础指标 | aws | 走公网调CloudWatch API | 否 |
| ElastiCache(Redis)基础指标 | aws | 走公网调CloudWatch API | 否 |
| Amazon MQ 的基础指标 | aws | 走公网调CloudWatch API | 否 |
| RDS/Redis/MQ 的引擎深度指标 | aws | EKS 里跑 mysqld/redis/rabbitmq-exporter | 否 |
| (慢查询、keyspace、队列深度等) | |||
| 内网端口连通性探测 | aws | blackbox-exporter 部署到 EKS 里 | 否 |
即:推(remote_write)+ CloudWatch API 拉,完全不用 VPN。
架构
┌─────────────────────── 阿里云 ECS 10.14.0.219 / 公网 IP ────────────────────────┐
│ Prometheus v3.13.3 (HTTPS 9090, basic auth, remote-write receiver) │
│ Grafana 13.0.8 / Alertmanager / node-exporter / cadvisor / blackbox-exporter │
└───────────────────────────────────▲──────────────────────────────────────────────────┘
│ remote_write (HTTPS + Basic Auth, 出公网)
│
┌───────────────────────────────────┴──── AWS VPC 172.31.0.0/16 ───────────────────────┐
│ EKS │
│ ├─ kube-prometheus-stack (本地 12h 保留,负责所有集群内抓取 + 统一外推) │
│ │ ├─ kubelet / cAdvisor / kube-state-metrics / node-exporter / apiserver │
│ │ ├─ YACE ──(公网 CloudWatch API)──▶ AWS/RDS, AWS/ElastiCache, AWS/AmazonMQ │
│ │ ├─ mysqld_exporter / redis_exporter / rabbitmq_exporter (内网直连,深度指标) │
│ │ └─ blackbox-exporter (内网端口探活) │
└──────────────────────────────────────────────────────────────────────────────────────┘
外部 prometheus 相关
开启 remote-write 接收 + 鉴权 + TLS
- 建议走 私有 CA → 签发服务端证书 两级结构,而不是直接 req -x509 一把梭:这样 AWS 侧只需要信任一个 ca.crt,以后换 IP / 加域名重签服务端证书,客户端不用动。
- Go(Prometheus / Grafana 都是 Go)自 1.15 起完全忽略 CN,只认 SAN。所以 SAN 里必须补上 DNS:prometheus(compose 里 Grafana 是用 https://prometheus:9090 连的)和 IP:127.0.0.1,否则容器内互访会报 x509: certificate is valid for …, not prometheus。
生成证书
mkdir -p /opt/docker/monitor/prometheus/certs cd /opt/docker/monitor/prometheus/certs # 1 SAN 扩展文件 cat > fd.ext <<'EOF' basicConstraints = CA:FALSE keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth, clientAuth subjectAltName = DNS:*.xxx.com, DNS:xxx.com, DNS:prometheus, DNS:localhost, IP:<内网IP>, IP:<你的IP>, IP:127.0.0.1 EOF # 2 私有 CA(100 年) openssl genrsa -out ca.key 4096 openssl req -x509 -new -nodes -sha256 -days 36500 \ -key ca.key -out ca.crt \ -subj "/C=CN/ST=hangzhou/L=hangzhou/O=ops:prometheus/CN=ops-internal-ca" \ -addext "basicConstraints=critical,CA:TRUE,pathlen:0" \ -addext "keyUsage=critical,keyCertSign,cRLSign" # CA 的 CN 不要和服务端证书的 CN 重名,否则部分工具链路校验会犯迷糊,这里用 ops-internal-ca。 # 3 服务端私钥 + CSR(用你给的 subj) openssl genrsa -out tls.key 2048 openssl req -new -sha256 -key tls.key -out tls.csr \ -subj "/C=CN/ST=hangzhou/L=hangzhou/O=ops:prometheus/CN=prometheus" # 4 用 CA 签发 openssl x509 -req -sha256 -days 36500 \ -in tls.csr \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -extfile fd.ext \ -out tls.crt # 5 校验 # 看 SAN 有没有全进去 openssl x509 -in tls.crt -noout -text | grep -A2 'Subject Alternative Name' openssl x509 -in tls.crt -noout -subject -issuer -dates # 验签链 openssl verify -CAfile ca.crt tls.crt # 应输出 tls.crt: OK # 确认公私钥配对(两个 md5 必须一致) openssl x509 -in tls.crt -noout -pubkey | openssl md5 openssl rsa -in tls.key -pubout | openssl md5 # 6 权限 chmod 600 ca.key tls.key chmod 644 ca.crt tls.crt rm -f tls.csr # ca.key 请离线备份,别留在容器能读到的地方之外也别丢 #容器里 Prometheus 以 user: '0' 跑,root 能读 600 的 key,没问题。
grafana 模板
https://grafana.com/grafana/dashboards/
- k8s clustr: 13105
- prometheus:25537
- node: 8919(中文汉化版) 1860(首选) node
- docker:14282
- mysql:7362 mysql-exporter 14057
- redis: 763
- blackbox: 14928
- JVM:4701
- nodejs
- Node.js Exporter 14058
- https://stackabuse.com/nodejs-application-monitoring-with-prometheus-and-grafana/
- rabbitmq:
安装外部 promethues
prometheus compose
web.yml TLS + Basic Auth
mkdir monitor/prometheus/rulues -pv cd monitor # Prometheus 的 web.yml 只支持 bcrypt # 生成密码-方法1 apt install apache2-utils -y htpasswd -nBC 10 remote_writer → 取冒号后面那串,注意 $ 在 yaml 单引号里是安全的 # 生成密码-方法2 docker run --rm httpd:alpine htpasswd -nBC 10 remote_writer #生成密码-方法3 # 生成密码 cat p.py <<EOF import getpass import bcrypt password = getpass.getpass("password: ") hashed_password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()) print(hashed_password.decode()) EOF python3 p.py # 输入密码 # 生成配置 cat > prometheus/web.yml <<\EOF # https://prometheus.io/docs/prometheus/latest/configuration/https/ tls_server_config: cert_file: /etc/prometheus/certs/tls.crt key_file: /etc/prometheus/certs/tls.key min_version: TLS12 basic_auth_users: admin: $2b$12$Bq/xxx remote_write: $2y$10$PX4xxxxx EOF
# prometheus/alertmanager.yml cat <<\EOF> prometheus/alertmanager.yml route: group_by: ['alertname'] group_wait: 10s group_interval: 10s repeat_interval: 20m receiver: 'default-receiver' receivers: - name: default-receiver EOF # 黑盒监控 cat > prometheus/blackbox.yml <<EOF modules: http_2xx: prober: http timeout: 5s http: preferred_ip_protocol: "ip4" valid_status_codes: [200, 401, 301, 302] method: GET http_post_2xx: http: method: POST preferred_ip_protocol: ip4 prober: http tcp_connect: prober: tcp timeout: 5s icmp: prober: icmp timeout: 5s EOF
# prometheus/prometheus.yml cat > prometheus/prometheus.yml <<\EOF global: scrape_interval: 30s scrape_timeout: 10s evaluation_interval: 30s external_labels: cluster: aliyun storage: tsdb: out_of_order_time_window: 30m # 跨云网络抖动/重传时,乱序样本不被丢弃 alerting: alertmanagers: - follow_redirects: true scheme: http timeout: 10s api_version: v2 static_configs: - targets: - alertmanager:9093 rule_files: - /etc/prometheus-rules/*.yml #https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/ #remote_write: # - url: '<Your Prometheus remote_write endpoint>' # basic_auth: # username: '<Your Grafana Username>' # password: '<Your Grafana API key>' scrape_configs: - job_name: 'prometheus' scrape_interval: 1m scheme: https static_configs: - targets: ['localhost:9090'] tls_config: ca_file: /etc/prometheus/certs/ca.crt server_name: localhost basic_auth: username: admin password: xxx - job_name: 'cadvisor' scrape_interval: 5s static_configs: - targets: ['cadvisor:8080'] - job_name: 'node' static_configs: - targets: ['node-exporter:9100'] - job_name: 'blackbox-exporter' static_configs: - targets: ['blackbox-exporter:9115'] - job_name: domain-url scrape_interval: 30s scrape_timeout: 20s metrics_path: /probe params: modelue: [http_2xx] static_configs: # 内网不互通 #- targets: # - http://argocd.xxx.com/login # labels: # bussiness: ops # serverty: info - targets: - https://devgw.xxx.com/api/ labels: bussiness: pt-dev service: web serverty: info relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: blackbox-exporter:9115 - source_labels: [instance] regex: https?://(.*) replacement: "$1" target_label: target - job_name: 'mysql' static_configs: - targets: ['mysqld_exporter:9104'] - job_name: 'redis' static_configs: - targets: ['redis_exporter:9121'] EOF
docker-compose.yml
cat > docker-compose.yml <<EOF
networks:
monitor_net:
name: monitor_net
driver: bridge
ipam:
config:
- subnet: 192.168.222.0/24
services:
prometheus:
image: prom/prometheus:v3.13.3
container_name: prometheus
hostname: prometheus
restart: unless-stopped
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/web.yml:/etc/prometheus/web.yml
- ./prometheus/certs:/etc/prometheus/certs:ro
- ./prometheus/rules:/etc/prometheus-rules
- ./prometheus/data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--web.config.file=/etc/prometheus/web.yml'
- '--web.enable-remote-write-receiver'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
- '--storage.tsdb.retention.time=60d'
user: '0'
ports:
- 9090:9090
networks:
- monitor_net
alertmanager:
hostname: alertmanager
container_name: alertmanager
image: prom/alertmanager:v0.34.0
volumes:
- ./prometheus/alertmanager.yml:/config/alertmanager.yml
command:
- "--config.file=/config/alertmanager.yml"
- '--data.retention=240h'
ports:
- 9093:9093
networks:
- monitor_net
restart: always
grafana:
container_name: grafana
image: grafana/grafana:13.0.8
depends_on:
- prometheus
user: '0'
ports:
- 3000:3000
volumes:
- ./grafana-data:/var/lib/grafana
networks:
- monitor_net
restart: always
cadvisor:
image: ghcr.io/google/cadvisor:v0.57.0
hostname: cadvisor
container_name: cadvisor
#platform: linux/aarch64
privileged: true
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
command:
- --docker_only=true
- --housekeeping_interval=10s
- --store_container_labels=false
- --disable_metrics=percpu,sched,tcp,udp,hugetlb,referenced_memory,cpu_topology,resctrl
ports:
- 8080:8080
networks:
- monitor_net
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
hostname: node-exporter
container_name: node-exporter
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
ports:
- 9100:9100
networks:
- monitor_net
blackbox-exporter:
image: quay.io/prometheus/blackbox-exporter:latest
hostname: blackbox-exporter
container_name: blackbox-exporter
restart: unless-stopped
volumes:
- ./prometheus/blackbox.yml:/etc/blackbox_exporter/config.yml
command:
- --config.file=/etc/blackbox_exporter/config.yml
ports:
- "9115:9115"
networks:
- monitor_net
# 开启 icmp ping探测
cap_add:
- NET_RAW
# mysqld_exporter:
# image: prom/mysqld-exporter:v0.15.1
# hostname: mysqld-exporter
# container_name: mysqld_exporter
# ports:
# - 9104
# command:
# - "--mysqld.username=root:kot2LT4jgJEo2rsnvduF"
# - "--mysqld.address=mysql-8-slots:3306"
# - '--tls.insecure-skip-verify'
# restart: always
# networks:
# - monitor_net
#
# redis_exporter:
# image: oliver006/redis_exporter:v1.62.0
# container_name: redis_exporter
# ports:
# - 9121
# command:
# - "--redis.addr=redis://redis-stack-slots:6379"
# - "--redis.password=OzTKYlJ9KvGZAkYf9B"
# - '--debug'
# restart: always
# networks:
# - monitor_net
# debian:
# image: debian:unstable-slim
# container_name: debian
# restart: always
# tty: true
# #entrypoint:
# # - cat
# networks:
# - monitor_net
EOF
启动
docker compose up -d docker compose logs --tail=50 prometheus # 从 ECS 本机验证(带 CA 校验,不要用 -k) curl --cacert /opt/docker/monitor/prometheus/certs/ca.crt \ -u remote_writer:'你的密码' \ https://10.14.0.219:9090/-/healthy # 公网 IP 也应通过(SAN 里有) curl --cacert ... -u ... https://<你的IP>:9090/-/healthy 如果日志里出现 tls: failed to find any PEM data in key input,说明 key 被加密了(生成时漏了 -nodes/用了 -aes256)。
各客户端怎么信任它
Prometheus 自监控 job
- job_name: 'prometheus' scheme: https tls_config: ca_file: /etc/prometheus/certs/ca.crt server_name: prometheus basic_auth: username: remote_writer password: '你的密码' static_configs: - targets: ['prometheus:9090']
Grafana 数据源
URL 改成 https://prometheus:9090,勾上 With CA Cert,把 ca.crt 内容粘进去,Server Name 填 prometheus。(cat prometheus/certs/ca.crt 复制即可。不想粘就勾 Skip TLS Verify)
prometheus 重新加载配置脚本
# 建一个 600 权限的密码文件 umask 077 printf 'admin:xxxpassword' > /opt/docker/monitor/.reload-cred chmod 600 /opt/docker/monitor/.reload-cred # 脚本内容 cat >reload-prom <<\EOF #!/usr/bin/bash # prometheus 重加载配置 ppath=/opt/docker/monitor /usr/bin/curl -fsS --connect-timeout 5 -m 30 \ -XPOST --cacert ${ppath}/prometheus/certs/tls.crt \ -u "$(cat ${ppath}/.reload-cred)" \ https://127.0.0.1:9090/-/reload \ && echo "reload OK" || echo "reload FAILED" EOF chmod +x reload-prom ln -sv /opt/docker/monitor/reload-prom /usr/local/bin #这样普通用户 cat 脚本看不到密码
promehtues agent 采集 EKS 信息
EKS 指标:集群内 Prometheus agent → remote_write
部署(kube-prometheus-stack,最省事)
# 把 CA 塞进 Secret: kubectl create ns monitoring kubectl -n monitoring create secret generic prom-rw-ca \ --from-file=ca.crt=./ca.crt kubectl -n monitoring create secret generic prom-rw \ --from-literal=username=remote_writer --from-literal=password='xxx' cat >values-agent.yaml << EOF grafana: { enabled: false } alertmanager: { enabled: false } prometheus: agentMode: true # 开启转发器。不本地存储、省内存 prometheusSpec: retention: 12h # 本地只留短期,方便就近排查 externalLabels: platform: kubernetes prometheus_agent: eks-xx environment: test cloud_provider: aws cloud_account: xxx cloud_account_id: "9xxx" cluster_service: eks cluster_name: xxx region: ap-southeast-1 project: jp additionalScrapeConfigsSecret: # 静态配置 enabled: true name: additional-scrape-configs key: prometheus-additional.yaml remoteWrite: - url: https://<你的IP>:9090/api/v1/write basicAuth: username: { name: prom-rw, key: username } password: { name: prom-rw, key: password } writeRelabelConfigs: # 丢掉高基数直方图,跨云出流量是按 GB 收费的 - sourceLabels: [__name__] regex: '(apiserver_request_duration_seconds_bucket|etcd_request_duration_seconds_bucket|.*_bucket)' action: drop - sourceLabels: [__name__] regex: 'go_.*|process_.*' action: drop tlsConfig: ca: secret: name: prom-rw-ca key: ca.crt #serverName: 'xx.me' #Prometheus 每 10s 抓一次、用 cAdvisor 自带的时间戳写入,而 cAdvisor 的数据刷新比 10s 慢且不规律,必然出现「这次拿到的 ts 比上次写进去的还旧」 # - Prometheus 每 10s 抓一次,用的是 cAdvisor 埋在数据里的 ts; # - 某次抓取拿到的 ts 比上一次已经写入的还旧 → WAL appender 判定 out-of-order → 整批丢弃。 # scrape_interval 设置为 30s,减少样本量 # 解决:关掉时间戳透传、减少样本量 kubelet: serviceMonitor: honorTimestamps: false trackTimestampsStaleness: false interval: 30s EOF 用域名就把 xx.com 解析到 <你的IP>(;不想配 DNS 就直接写 https://<你的IP>:9090/api/v1/write 并把 serverName 去掉——SAN 里有这个 IP,一样能通过校验。 helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm search repo prometheus-community helm install -n monitoring kps \ prometheus-community/kube-prometheus-stack \ -f values-agent.yaml helm upgrade --install -n monitoring kps \ prometheus-community/kube-prometheus-stack \ -f values-agent.yaml # helm -n monitoring uninstall prometheus # 验证 kubectl -n monitoring get secrets prom-agent-kps-kube-prometheus-stack-prometheus \ -o jsonpath='{.data.prometheus\.yaml\.gz}' | base64 -d | gunzip \ | grep -A6 'kubelet/1' kubectl -n monitoring logs -f --tail 20 prom-agent-kps-kube-prometheus-stack-prometheus-0
装完就自带 kubelet / cAdvisor / kube-state-metrics / node-exporter / apiserver / coredns 全套抓取规则,不用自己写。
流量预估
- remote_write 是 snappy 压缩的 protobuf,经验值约 1.5~2 bytes/sample。
- 10 个节点、约 15 万 series、30s 一次 ≈ 5000 samples/s ≈ 10 KB/s ≈ 25 GB/月,AWS 出公网约 $2~3/月。做了上面的 drop 后通常能砍掉一半以上。
RDS / Redis / MQ 基础采集-YACE
基础指标:YACE 拉 CloudWatch(零网络打通)
所有 AWS 托管服务的官方指标都在 CloudWatch,而 CloudWatch 是公网 endpoint,理论上 exporter 放哪都行。放进 EKS 的好处是可以用 IRSA / Pod Identity 拿临时凭证,彻底不落地 AccessKey;代价是指标要经 remote_write 才能到阿里云,链路断了 CloudWatch 指标也一起看不到(可接受)。
IAM 授权
CLUSTER=eks-dev REGION=ap-southeast-1 ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
IAM:建一个最小权限策略:
cat > yace-policy.json <<'EOF' { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "tag:GetResources", "cloudwatch:ListMetrics", "cloudwatch:GetMetricData", "cloudwatch:GetMetricStatistics", "iam:ListAccountAliases", "rds:DescribeDBInstances", "elasticache:DescribeCacheClusters", "mq:ListBrokers" ], "Resource": "*" }] } EOF aws iam create-policy --policy-name YaceCloudWatchRead \ --policy-document file://yace-policy.json
CLUSTER=eks-jp-test REGION=ap-southeast-1 ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
方式 A:IRSA(通用,推荐)
eksctl 一条命令把 OIDC provider、IAM Role、ServiceAccount 注解全部搞定:
eksctl utils associate-iam-oidc-provider --cluster $CLUSTER --region $REGION --approve kubectl create ns monitoring --dry-run=client -o yaml | kubectl apply -f - eksctl create iamserviceaccount \ --cluster $CLUSTER --region $REGION \ --namespace monitoring --name yace \ --attach-policy-arn arn:aws:iam::${ACCOUNT}:policy/YaceCloudWatchRead \ --approve
不用 eksctl 时,手动建 Role,信任策略为:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT>:oidc-provider/oidc.eks.ap-southeast-1.amazonaws.com/id/<OIDC_ID>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-southeast-1.amazonaws.com/id/<OIDC_ID>:sub": "system:serviceaccount:monitoring:yace",
"oidc.eks.ap-southeast-1.amazonaws.com/id/<OIDC_ID>:aud": "sts.amazonaws.com"
}
}
}]
}
然后给 SA 打注解: eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT>:role/YaceRole 。
方式 B:EKS Pod Identity(较新,不用碰 OIDC)
aws eks create-addon --cluster-name $CLUSTER --region $REGION \ --addon-name eks-pod-identity-agent # Role 信任策略的 Principal 为 pods.eks.amazonaws.com # Action 为 sts:AssumeRole + sts:TagSession aws eks create-pod-identity-association --cluster-name $CLUSTER --region $REGION \ --namespace monitoring --service-account yace \ --role-arn arn:aws:iam::${ACCOUNT}:role/YaceRole
此方式 SA 无需任何注解, kubectl create sa yace -n monitoring 即可。
部署清单
# 确认镜像 helm upgrade --install yace \ prometheus-community/yet-another-cloudwatch-exporter \ --timeout 600s \ --dry-run=client \ --namespace monitoring --create-namespace |grep image
cat >yace.yaml <<EOF apiVersion: v1 kind: ConfigMap metadata: name: yace-config namespace: monitoring data: config.yml: | apiVersion: v1alpha1 sts-region: ap-southeast-1 discovery: exportedTagsOnMetrics: AWS/RDS: [Name, Env] AWS/ElastiCache: [Name, Env] jobs: - type: AWS/RDS regions: [ap-southeast-1] period: 300 length: 600 metrics: - { name: CPUUtilization, statistics: [Average, Maximum] } - { name: FreeableMemory, statistics: [Average, Minimum] } - { name: FreeStorageSpace, statistics: [Average, Minimum] } - { name: DatabaseConnections, statistics: [Average, Maximum] } - { name: ReadLatency, statistics: [Average, Maximum] } - { name: WriteLatency, statistics: [Average, Maximum] } - { name: ReadIOPS, statistics: [Average] } - { name: WriteIOPS, statistics: [Average] } - { name: ReplicaLag, statistics: [Average, Maximum] } - { name: BurstBalance, statistics: [Minimum] } - { name: DiskQueueDepth, statistics: [Average] } - { name: SwapUsage, statistics: [Average] } - type: AWS/ElastiCache regions: [ap-southeast-1] period: 300 length: 600 metrics: - { name: CPUUtilization, statistics: [Average, Maximum] } - { name: EngineCPUUtilization, statistics: [Average, Maximum] } - { name: DatabaseMemoryUsagePercentage, statistics: [Average, Maximum] } - { name: CurrConnections, statistics: [Average, Maximum] } - { name: ReplicationLag, statistics: [Average, Maximum] } - { name: SwapUsage, statistics: [Average] } - { name: Evictions, statistics: [Sum, Average] } - { name: CacheHits, statistics: [Sum] } - { name: CacheMisses, statistics: [Sum, Average] } - { name: FreeableMemory, statistics: [Average, Minimum] } # Amazon MQ(RabbitMQ / ActiveMQ) - type: AWS/AmazonMQ regions: [ap-southeast-1] period: 300 length: 600 metrics: - { name: CpuUtilization, statistics: [Average, Maximum] } - { name: SystemCpuUtilization, statistics: [Average] } - { name: RabbitMQMemUsed, statistics: [Average] } - { name: RabbitMQMemLimit, statistics: [Average] } - { name: RabbitMQDiskFree, statistics: [Average] } - { name: RabbitMQDiskFreeLimit, statistics: [Average] } - { name: RabbitMQFdUsed, statistics: [Average] } - { name: MessageCount, statistics: [Average] } - { name: MessageReadyCount, statistics: [Average] } - { name: MessageUnacknowledgedCount, statistics: [Average] } - { name: PublishRate, statistics: [Average] } - { name: ConfirmRate, statistics: [Average] } - { name: AckRate, statistics: [Average] } - { name: ConsumerCount, statistics: [Average] } - { name: ConnectionCount, statistics: [Average] } - { name: ChannelCount, statistics: [Average] } - { name: QueueCount, statistics: [Average] } - { name: ExchangeCount, statistics: [Average] } # 若 MQ 实为 MSK,改用 AWS/Kafka(CpuUser / KafkaDataLogsDiskUsed / SumOffsetLag ...) # 若为 SQS,改用 AWS/SQS(ApproximateNumberOfMessagesVisible / ApproximateAgeOfOldestMessage) --- apiVersion: apps/v1 kind: Deployment metadata: name: yace namespace: monitoring labels: { app: yace } spec: replicas: 1 # 必须为 1:多副本会重复调 CloudWatch API,费用翻倍 strategy: { type: Recreate } selector: matchLabels: { app: yace } template: metadata: labels: { app: yace } annotations: checksum/config: "REPLACE_ME" # 改配置后改此值触发滚动,或 kubectl rollout restart spec: serviceAccountName: yace securityContext: runAsNonRoot: true runAsUser: 65534 fsGroup: 65534 containers: - name: yace image: quay.io/prometheuscommunity/yet-another-cloudwatch-exporter:v0.62.1 args: - -config.file=/etc/yace/config.yml - -listen-address=:5000 - -scraping-interval=300 # 后台轮询 CloudWatch 的周期(秒) ports: - { name: metrics, containerPort: 5000 } volumeMounts: - { name: config, mountPath: /etc/yace, readOnly: true } resources: requests: { cpu: 50m, memory: 128Mi } limits: { cpu: 500m, memory: 512Mi } readinessProbe: httpGet: { path: /metrics, port: metrics } initialDelaySeconds: 15 periodSeconds: 20 timeoutSeconds: 10 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: { drop: ["ALL"] } volumes: - name: config configMap: { name: yace-config } --- apiVersion: v1 kind: Service metadata: name: yace namespace: monitoring labels: { app: yace } spec: selector: { app: yace } ports: - { name: metrics, port: 5000, targetPort: metrics } --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: yace namespace: monitoring labels: app: yace release: kps # 需匹配 kube-prometheus-stack 的 release 名,否则不被发现 spec: selector: matchLabels: { app: yace } endpoints: - port: metrics interval: 60s scrapeTimeout: 30s EOF kubectl -n monitoring get po kubectl -n monitoring logs deploy/yace # 验证 kubectl -n monitoring get pod -owide |grep yace curl -s http://172.31.88.138:5000/metrics |grep -c '^aws_' curl -s http://172.31.88.138:5000/metrics |grep -c '^aws_rds'
深度指标:exporter 放进 EKS
redis-exporter
cat >exporter.yaml <<EOF
apiVersion: v1
data:
sample-pwd-file.json: |-
{
"redis://redis6:6379": "",
"rediss://user@clustercfg.xxxx.cache.amazonaws.com:6379": "passwordxx"
}
kind: ConfigMap
metadata:
name: redis-pwd-config
namespace: monitoring
---
apiVersion: v1
kind: Service
metadata:
name: redis-exporter
namespace: monitoring
#annotations:
# prometheus.io/scrape: "true"
labels:
app: redis-exporter
spec:
selector:
app: redis-exporter
ports:
- name: tcp-9121
protocol: TCP
port: 9121
targetPort: 9121
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-exporter
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: redis-exporter
template:
metadata:
#annotations:
# prometheus.io/path: "/metrics"
# prometheus.io/port: "9121"
# prometheus.io/scrape: "true"
labels:
app: redis-exporter
spec:
#nodeSelector:
# type: ops-prod-prometheus
#imagePullSecrets:
#- name: docker-secret
containers:
- name: redis-exporter
image: oliver006/redis_exporter:v1.91.1
imagePullPolicy: IfNotPresent
env:
- name: REDIS_PASSWORD_FILE
value: /data/sample-pwd-file.json
resources:
limits:
cpu: 300m
memory: 300Mi
requests:
cpu: 10m
memory: 10Mi
ports:
- containerPort: 9121
volumeMounts:
- mountPath: /data
name: redis-pwd-file
volumes:
- configMap:
defaultMode: 420
items:
- key: sample-pwd-file.json
path: sample-pwd-file.json
name: redis-pwd-config
name: redis-pwd-file
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: redis-exporter
namespace: monitoring
labels:
app.kubernetes.io/name: redis-exporter
app.kubernetes.io/instance: redis-exporter
app.kubernetes.io/version: "1.91.1"
app.kubernetes.io/component: exporter
release: kps
spec:
endpoints:
- path: /metrics
port: tcp-9121
#interval: 15s
namespaceSelector:
matchNames:
- monitoring
selector:
matchLabels:
app: redis-exporter
EOF
添加 agent 静态配置
cat >prometheus-additional.yaml <<EOF #Redis - job_name: 'redis-exporter-targets' static_configs: - targets: - rediss://user@clustercfg.xxx.cache.amazonaws.com:6379 labels: dbtype: "db-redis" metrics_path: /scrape relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: redis-exporter:9121 #EC2 - job_name: 'node-exporter' static_configs: - targets: - "x.x.x.x:9100" labels: business: jp-test type: ec2 service: "ops-jmp" EOF # Update kubectl -n monitoring create secret generic additional-scrape-configs \ --from-file=prometheus-additional.yaml \ --dry-run=client -oyaml|kubectl replace -f- # helm prometheus: agentMode: true # 开启转发器。不本地存储、省内存。不能查历史 prometheusSpec: ... additionalScrapeConfigsSecret: # 静态配置 enabled: true name: additional-scrape-configs key: prometheus-additional.yaml helm upgrade --install -n monitoring kps \ prometheus-community/kube-prometheus-stack \ -f values-agent.yaml
PrometheusAlert全家桶
下载 app.conf docker-compose
# 下载默认配置。修改登录密码 mkdir prometheusalert/conf cd prometheusalert/conf wget https://raw.githubusercontent.com/feiyu563/PrometheusAlert/master/conf/app-example.conf -O app.conf prometheusalert: container_name: prometheusalert image: feiyu563/prometheus-alert:master depends_on: - prometheus user: '0' ports: - 18080:8080 environment: - TZ=Asia/Shanghai volumes: - ./prometheusalert/conf:/app/conf - ./prometheusalert/db:/app/db # 防模板丢失 - ./prometheusalert/logs:/app/logs networks: - monitor_net restart: always
配置 alertmanager 路由
cat >prometheus/alertmanager.yml <<EOF
route:
group_by: ['alertname', 'instance']
group_wait: 10s
group_interval: 10s
repeat_interval: 20m
receiver: 'default-receiver'
routes:
- receiver: 'eks-p1'
group_wait: 10s
group_interval: 30s
repeat_interval: 30m
match_re:
#severity: critical
cluster: ".*eks.*"
- receiver: 'default-receiver'
group_by: ['alertname']
match_re:
alertname: ".*"
receivers:
- name: "pt-p1"
webhook_configs:
# type:fs(飞书),tg(Telegram),webhook
# tpl:模板名。内置了内置有 prometheus-dd / prometheus-wx / prometheus-fs
# ddurl/wxurl/fsurl:覆盖 app.conf 默认值,多个用 , 分隔
# at:@ 指定手机号,多个用 , 分隔
# rr=true:多地址时随机取一个发,避免机器人限频
- url: 'http://prometheusalert:8080/prometheusalert?type=fs&tpl=prometheus-fs&fsurl=https://open.larksuite.com/open-apis/bot/v2/hook/xxxx'
send_resolved: true
- name: "default-receiver"
webhook_configs:
- url: 'http://prometheusalert:8080/prometheusalert?type=fs&tpl=prometheus-fs&fsurl=https://open.larksuite.com/open-apis/bot/v2/hook/xxx'
send_resolved: true
inhibit_rules:
- source_matchers: [severity="critical"]
target_matchers: [severity="warning"]
equal: ['alertname', 'instance']
EOF
备选方案
方案 B:跨云 VPN,保持纯 pull
阿里云 VPN 网关 ↔ AWS Site-to-Site VPN 建 IPsec 隧道(你的 10.14.0.0/16 和 172.31.0.0/16 不冲突,可以直接互通)。打通后外部 Prometheus 就能用 kubernetes_sd_configs 直抓 Pod、直连 RDS 端口。
- 优点:模型干净,指标零延迟,不用改集群。
- 缺点:跨云公网 IPsec 稳定性一般,隧道断了就整片断采;VPN 网关有固定月费;抓取是持续长连接,对隧道压力比 remote_write 大。
- 适合:你本来就需要跨云互通(比如业务侧也要访问)时顺带做。
方案 C:通过公网 apiserver 做代理抓取(小集群应急)
你的 EKS API Server 公网可达,可以让 Prometheus 通过 apiserver 的 proxy 子资源间接抓节点:
- job_name: 'eks-cadvisor-via-apiserver' scheme: https tls_config: { ca_file: /etc/prometheus/eks/ca.crt } bearer_token_file: /etc/prometheus/eks/token kubernetes_sd_configs: - role: node api_server: https://XXXXX.gr7.ap-southeast-1.eks.amazonaws.com tls_config: { ca_file: /etc/prometheus/eks/ca.crt } bearer_token_file: /etc/prometheus/eks/token relabel_configs: - action: labelmap regex: __meta_kubernetes_node_label_(.+) - target_label: __address__ replacement: XXXXX.gr7.ap-southeast-1.eks.amazonaws.com:443 - sourceLabels: [__meta_kubernetes_node_name] regex: (.+) target_label: __metrics_path__ replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
需要建一个 ServiceAccount,ClusterRole 授予 nodes/proxy、nodes/metrics、services/proxy 的 get 权限,并手动创建 kubernetes.io/service-account-token 类型的 Secret 拿长期 token(1.24+ 不再自动签发)。
- 缺点很实在:所有抓取流量都压在 apiserver 上,节点一多 apiserver CPU 会被打爆;Pod 级自定义指标也得走 service proxy,很别扭。
- 只建议用于十几个节点以内、临时过渡。另外务必把 EKS 公网 endpoint 的 Public access source allowlist 限制成 47.86.42.227/32。