Python上传Grafana告警组失败:连接错误问题求助
尝试通过Python脚本借助grafanalib库生成AlertGroup并上传至Grafana,期望脚本运行后告警自动同步到Grafana,但执行时出现连接错误:
File "/usr/lib/python3/dist-packages/requests/adapters.py"
line 565, in send raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPConnectionPool(host='none', port=80): Max retries exceeded with url: /api/ruler/grafana/api/v1/rules/testfolder/Production%20Alerts (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7feb3b36b910>: Failed to establish a new connection: [Errno -2] Name or service not known'))
所用代码
from grafanalib.core import AlertGroup from grafanalib._gen import DashboardEncoder, loader import json import requests from os import getenv def get_alert_json(alert: AlertGroup): ''' get_alert_json generates JSON from grafanalib AlertGroup object :param alert - AlertGroup created via grafanalib ''' return json.dumps(alert.to_json_data(), sort_keys=True, indent=4, cls=DashboardEncoder) def upload_to_grafana(alertjson, folder, server, api_key, session_cookie, verify=True): ''' upload_to_grafana tries to upload the AlertGroup to grafana and prints response WARNING: This will first delete all alerts in the AlertGroup before replacing them with the provided AlertGroup. :param alertjson - AlertGroup json generated by grafanalib :param folder - Folder to upload the AlertGroup into :param server - grafana server name :param api_key - grafana api key with read and write privileges ''' groupName = json.loads(alertjson)['name'] headers = {} if api_key: print("using bearer auth") headers['Authorization'] = f"Bearer {api_key}" if session_cookie: print("using session cookie") headers['Cookie'] = session_cookie print(f"deleting AlertGroup {groupName} in folder {folder}") r = requests.delete(f"http://{server}/api/ruler/grafana/api/v1/rules/{folder}/{groupName}", headers=headers, verify=verify) print(f"{r.status_code} - {r.content}") headers['Content-Type'] = 'application/json' print(f"ensuring folder {folder} exists") r = requests.post(f"http://{server}/api/folders", data={"title": folder}, headers=headers) print(f"{r.status_code} - {r.content}") print(f"uploading AlertGroup {groupName} to folder {folder}") r = requests.post(f"http://{server}/api/ruler/grafana/api/v1/rules/{folder}", data=alertjson, headers=headers, verify=verify) # TODO: add error handling print(f"{r.status_code} - {r.content}") grafana_api_key = getenv("eyJrIjoiS1ZjV0I2SWdlUm5lMnpYUVpzT2ZRUVpqMkM0QUVtYnIiLCJuIjoiTXlfa2V5IiwiaWQiOjF9") grafana_server = getenv("localhost:3000") grafana_cookie = getenv("GRAFANA_COOKIE") # Generate an alert from the example my_alergroup_json = get_alert_json(loader("example.alertgroup.py")) upload_to_grafana(my_alergroup_json, "testfolder", grafana_server, grafana_api_key, grafana_cookie)
example.alertgroup.py 内容
from grafanalib.core import ( AlertGroup, AlertRulev9, Target, AlertCondition, AlertExpression, GreaterThan, OP_AND, RTYPE_LAST, EXP_TYPE_CLASSIC, EXP_TYPE_REDUCE, EXP_TYPE_MATH ) # An AlertGroup is one group contained in an alert folder. alertgroup = AlertGroup( name="Production Alerts", # Each AlertRule forms a separate alert. rules=[ # Alert rule using classic condition > 3 AlertRulev9( # Each rule must have a unique title title="Alert for something 1", uid='alert1', # Several triggers can be used per alert condition='B', triggers=[ # A target refId must be assigned, and exist only once per AlertRule. Target( expr="from(bucket: \"sensors\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"remote_cpu\")\n |> filter(fn: (r) => r[\"_field\"] == \"usage_system\")\n |> filter(fn: (r) => r[\"cpu\"] == \"cpu-total\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", # Set datasource to name of your datasource datasource="influxdb", refId="A", ), AlertExpression( refId="B", expressionType=EXP_TYPE_CLASSIC, expression='A', conditions=[ AlertCondition( evaluator=GreaterThan(3), operator=OP_AND, reducerType=RTYPE_LAST ) ] ) ], annotations={ "summary": "The database is down", "runbook_url": "runbook-for-this-scenario.com/foo", }, labels={ "environment": "prod", "slack": "prod-alerts", }, evaluateFor="3m", ), # Alert rule using reduce and Math AlertRulev9( # Each rule must have a unique title title="Alert for something 2", uid='alert2', condition='C', # Several triggers can be used per alert triggers=[ # A target refId must be assigned, and exist only once per AlertRule. Target( expr="from(bucket: \"sensors\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"remote_cpu\")\n |> filter(fn: (r) => r[\"_field\"] == \"usage_system\")\n |> filter(fn: (r) => r[\"cpu\"] == \"cpu-total\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")", # Set datasource to name of your datasource datasource="influxdb", refId="A", ), AlertExpression( refId="B", expressionType=EXP_TYPE_REDUCE, expression='A', reduceFunction='mean', reduceMode='dropNN' ), AlertExpression( refId="C", expressionType=EXP_TYPE_MATH, expression='$B < 3' ) ], annotations={ "summary": "The database is down", "runbook_url": "runbook-for-this-scenario.com/foo", }, labels={ "environment": "prod", "slack": "prod-alerts", }, evaluateFor="3m", ) ] )
1. 核心问题:环境变量读取错误
错误日志显示host='none',说明grafana_server变量的值为None,根源是getenv()用法错误:
getenv("localhost:3000")是读取名为localhost:3000的环境变量,而非直接赋值字符串;getenv("eyJrIjoi...")同理,错误地将API key作为环境变量名传入,而非读取对应名称的环境变量。
修复方式
方式一:直接赋值(测试场景)
如果只是快速测试,无需依赖环境变量,直接给变量赋值:
grafana_api_key = "eyJrIjoiS1ZjV0I2SWdlUm5lMnpYUVpzT2ZRUVpqMkM0QUVtYnIiLCJuIjoiTXlfa2V5IiwiaWQiOjF9" grafana_server = "localhost:3000" grafana_cookie = getenv("GRAFANA_COOKIE") # 若该环境变量确实存在则保留,否则直接赋值或删除
方式二:正确使用环境变量
先在终端设置环境变量:
# Linux/macOS export GRAFANA_API_KEY="eyJrIjoiS1ZjV0I2SWdlUm5lMnpYUVpzT2ZRUVpqMkM0QUVtYnIiLCJuIjoiTXlfa2V5IiwiaWQiOjF9" export GRAFANA_SERVER="localhost:3000" # 可选:设置cookie export GRAFANA_COOKIE="your_cookie_value"
再在脚本中读取:
grafana_api_key = getenv("GRAFANA_API_KEY") grafana_server = getenv("GRAFANA_SERVER") grafana_cookie = getenv("GRAFANA_COOKIE")
2. 额外问题修复
(1)文件夹创建请求格式错误
Grafana创建文件夹接口要求JSON格式,但原代码用data参数传入字典,默认会以form-encoded格式发送,需改为json参数:
r = requests.post(f"http://{server}/api/folders", json={"title": folder}, headers=headers)
(2)API权限验证
确保你的API key拥有Editor或Admin权限,否则会触发权限不足错误。
(3)Grafana服务状态确认
检查Grafana服务是否正常运行,且能通过http://localhost:3000正常访问。
内容的提问来源于stack exchange,提问作者kartik Ganotra

