一条备忘录,我试图将数据从IBM Cloud Private中包含的Prometheus用Python导出到csv。
假设您要在一个好的旧系统上创建并报告性能信息报告,例如Excel中每个吊舱的CPU使用率。
已通过ICP v3.1.0确认。由于该命令在Mac上执行,因此
普罗米修斯查询
本文不介绍Prometheus查询,但是最好看一下下面的链接。
- 普罗米修斯查询道场
例如,以下查询用于获取命名空间中每个Pod的CPU使用率。
1 | sum(rate(container_cpu_usage_seconds_total{namespace="default"}[5m])) by (pod_name) * 100 |
该查询顺序如下。尽管数据类型和rate()函数令人困惑。
-
container_cpu_usage_seconds_total 是容器使用的CPU时间的积分值,此查询返回带有多个容器的时间戳数据(即时矢量类型数据)。 -
可以通过
container_cpu_usage_seconds_total{namespace="default"} 这样的标签缩小数据范围(即时矢量类型数据) -
通过设置
container_cpu_usage_seconds_total{namespace="default"}[5m] ,可以指定数据周期,并返回跨越多个容器的多个时间戳的数据(范围向量类型数据)。 -
通过设置
rate(container_cpu_usage_seconds_total{namespace="default"}[5m]) ,可以计算指定时间段内每秒的平均增量(即时矢量类型数据)。- 如果CPU使用时间每秒增加0.5秒,则CPU使用率将为0.5(50%)。
-
使用
sum(rate(container_cpu_usage_seconds_total{namespace="default"}[5m])) by (pod_name) 为每个Pod汇总多个数据(即时向量类型数据)- 由于吊舱中可能有多个容器,因此每个吊舱均使用该总数。
从Grafana
导出
从Grafana仪表板,可以使用GUI以面板单位导出csv。在仪表板上,指定数据周期和根据仪表板设置的变量(在下面的示例中,interval和Namespace)在显示数据之后,单击面板的标题部分。与csv。似乎浏览器上的JavaScript已将已经下载到浏览器的json数据转换为csv。
导出时可能会进行某些设置。
curl
接下来,尝试使用
认证
您需要令牌才能访问Prometheus API。
- 准备执行组件API命令或管理API命令
- 普罗米修斯API
请注意,您需要ID令牌,但需要访问令牌而不是ID令牌才能访问Prometheus API。
如果
令牌在本地已经是
1 | ACCESS_TOKEN=$(LANG=C cloudctl tokens | grep "Access token:" | awk '{print ($4)}') |
您还可以通过传递
或用户名和密码从API中获取它。
1 2 3 4 5 | USERNAME="admin" PASSWORD="admin" ACCESS_TOKEN=$(curl -s -k -H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" \ -d "grant_type=password&username=${USERNAME}&password=${PASSWORD}&scope=openid" \ https://mycluster.icp:8443/idprovider/v1/auth/identitytoken | jq -r '.access_token') |
检查查询
通过单击
Grafana面板标题中的"编辑",可以查看正在使用的Prometheus查询。
您还可以通过打开查询检查器来检查您拥有哪种HTTP请求。
您需要传递
运行卷曲
如下向
1 2 3 4 5 6 | curl -k -s -G -H "Authorization:Bearer $ACCESS_TOKEN" \ https://mycluster.icp:8443/prometheus/api/v1/query_range \ --data-urlencode 'query=sum(rate(container_cpu_usage_seconds_total{namespace="default"}[5m])) by (pod_name) * 100' \ --data-urlencode "start=1547517120" \ --data-urlencode "end=1547527950" \ --data-urlencode "step=30" | jq . |
如果
参数设置为变量并放在前面,则如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 | NAMESPACE="default" INTERVAL="5m" QUERY="sum(rate(container_cpu_usage_seconds_total{namespace="${NAMESPACE}"}[${INTERVAL}])) by (pod_name) * 100" START=$(date -v -1d +%s) # 1日前の時刻をエポックタイムで取得 END=$(date +%s) # 今の時刻をエポックタイムで取得 STEP=30 curl -k -s -G -H "Authorization:Bearer $ACCESS_TOKEN" \ https://mycluster.icp:8443/prometheus/api/v1/query_range \ --data-urlencode "query=${QUERY}" \ --data-urlencode "start=${START}" \ --data-urlencode "end=${END}" \ --data-urlencode "step=${STEP}" | jq . |
执行后,将以json形式返回,如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | $ curl -k -s -G -H "Authorization:Bearer $ACCESS_TOKEN" \ > https://mycluster.icp:8443/prometheus/api/v1/query_range \ > --data-urlencode "query=${QUERY}" \ > --data-urlencode "start=${START}" \ > --data-urlencode "end=${END}" \ > --data-urlencode "step=${STEP}" | jq . { "status": "success", "data": { "resultType": "matrix", "result": [ { "metric": { "pod_name": "infra-test-nodeport-cust-0" }, "values": [ [ 1547537972, "2.5491993487228775" ], [ 1547538002, "2.300661640484626" ], (省略) |
的Python
使用shell脚本使
json csv有点困难,所以让我们尝试使用Python。
代码创建
创建以下过程代码。我仍然不知道如何编写好的代码,所以这个想法是让Python做无聊的事情。
pod_cpu_exporter.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | import argparse import collections import csv import datetime import logging import re import subprocess import pprint import requests import urllib3 formatter = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s' logging.basicConfig(level=logging.WARNING, format=formatter) logger = logging.getLogger(__name__) # 警告を非表示にする urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # コマンド引数の処理 parser = argparse.ArgumentParser(description='PodのCPU使用率をcsvに出力します。') parser.add_argument('-f', '--filename', action='store', type=str, help='出力先のファイル名を指定します') parser.add_argument('-n', '--namespace', action='store', type=str, default='default', help='Namespaceを指定します') parser.add_argument('--interval', action='store', type=str, default='5m', help='CPU使用率計算に使用するデータの間隔を指定します(例)1h、5m') parser.add_argument('--start', action='store', type=str, help='データの開始時間を指定します(例)20190101-1000') parser.add_argument('--end', action='store', type=str, help='データの終了時間を指定します(例)20190102-1000') parser.add_argument('--step', action='store', type=int, help='データポイントの間隔(秒)を指定します') args = parser.parse_args() filepath = args.filename namespace = args.namespace interval = args.interval step = args.step logger.debug('filepath: {}'.format(filepath)) logger.debug('interval: {}'.format(interval)) logger.debug('step: {}'.format(step)) # 引数の開始時間と終了時間をUNIX時刻に変換 start_str = args.start end_str = args.end start_dt = datetime.datetime.strptime(start_str, '%Y%m%d-%H%M') end_dt = datetime.datetime.strptime(end_str, '%Y%m%d-%H%M') start_unix = start_dt.timestamp() end_unix = end_dt.timestamp() logger.debug('start_dt: {}'.format(start_dt)) logger.debug('end_dt: {}'.format(end_dt)) logger.debug('start_unix: {}'.format(start_unix)) logger.debug('end_unix: {}'.format(end_unix)) # サブプロセスでコマンドを実行し、結果からアクセストークンを抽出 completed_process = subprocess.run(['cloudctl', 'tokens'], stdout=subprocess.PIPE) result_str = completed_process.stdout.decode('utf-8') match = re.search(r'(.*)\s+Bearer\s+(.*)', result_str) access_token = (match.group(2)) logger.debug('access_token: {}'.format(access_token)) # Prometheusクエリー # 指定のNamespaceの、指定のintervalで算出したPod毎のCPU使用率を取得する # sum(rate(container_cpu_usage_seconds_total{namespace="$namespace"}[$interval])) by (pod_name) * 100 query = 'sum(rate(container_cpu_usage_seconds_total{{namespace="{}"}}[{}])) ' \ 'by (pod_name) * 100'.format(namespace, interval) logger.debug('query: {}'.format(query)) # リクエスト url = 'https://mycluster.icp:8443/prometheus/api/v1/query_range' headers = {'Authorization': 'Bearer {}'.format(access_token)} params = {'query': query, 'start': start_unix, 'end': end_unix, 'step': step} logger.debug('url: {}'.format(url)) logger.debug('headers: {}'.format(headers)) logger.debug('params: {}'.format(params)) # リクエストを実行 response = requests.get(url, verify=False, headers=headers, params=params) response.raise_for_status() logger.debug('response: {}'.format(response)) # レスポンスは以下のようなデータ # pprint.pprint(response.json()) # {'data': {'result': [{'metric': {'pod_name': 'infra-test-nodeport-cust-0'}, # 'values': [[1547528400, '2.64939279124293'], # [1547532000, '2.5820633706497045'], # [1547535600, '2.562417181158173'], # [1547539200, '2.4563804665536724'], # 意味のない部分を取り除いて中のリストを取り出す results = response.json()['data']['result'] # 取り出したのは以下のようなデータ # pprint.pprint(results) # [{'metric': {'pod_name': 'infra-test-nodeport-cust-0'}, # 'values': [[1547528400, '2.64939279124293'], # [1547532000, '2.5820633706497045'], # [1547535600, '2.562417181158173'], # [1547539200, '2.4563804665536724'], # # このデータを時刻をキーにして以下のような辞書にまとめる # # {1547464889.632: {'infra-test-nodeport-cust-0': '3.1518179124293577', # 'infra-test-nodeport-cust-1': '1.530811175762711', # 'infra-test-nodeport2-cus-0': '3.0063879859887037', # 'infra-test-nodeport2-cus-1': '1.5241500936723127'}, # 1547468489.632: {'infra-test-nodeport-cust-0': '3.161739384943495', # 'infra-test-nodeport-cust-1': '1.5393470943785368', # 'infra-test-nodeport2-cus-0': '2.8831145322598943', # 'infra-test-nodeport2-cus-1': '1.578976048757047'}, # 時刻毎のデータの辞書を用意する time_series = collections.defaultdict(dict) # Pod名のSetを用意する pod_names = set() for result in results: # Pod名を取り出してSetに入れておく pod_name = result['metric']['pod_name'] pod_names.add(pod_name) for value in result['values']: # timestampを辞書のキーにすることで同じtimestampのデータをまとめる # defaultdictを使うことでキーがなくてもKeyErrorにならない time_series[value[0]][pod_name] = value[1] # pprint.pprint(time_series) # csvのヘッダーは時刻とPod名にする fieldnames = ['timestamp'] fieldnames.extend(pod_names) # csvファイルに保存する with open(filepath, 'w') as csv_file: writer = csv.DictWriter(csv_file, fieldnames=fieldnames) writer.writeheader() # 辞書から時間毎のデータを取り出してループする for timestamp, values in time_series.items(): # 行に時間の列を追加 row = {'timestamp': datetime.datetime.fromtimestamp(timestamp)} # valuesは以下のような辞書 # {'infra-test-nodeport-cust-0': '2.467225521553685', # 'infra-test-nodeport-cust-1': '1.5932590068361583', # 'infra-test-nodeport2-cus-0': '2.2811341803954917', # 'infra-test-nodeport2-cus-1': '1.6517850743220521'}, # 事前に格納したPod名のリストの方でループする for pod_name in pod_names: try: row[pod_name] = values[pod_name] except KeyError: # valuesにこのPodのデータがないときはKeyErrorが発生するので空データを入れる row[pod_name] = '' writer.writerow(row) |
执行示例
1 | pip install requests |
由于令牌是通过在
子进程中执行
如果执行
,则可以按以下方式获取csv。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | $ python export_pod_cpu.py --help usage: export_pod_cpu.py [-h] [-f FILENAME] [-n NAMESPACE] [--interval INTERVAL] [--start START] [--end END] [--step STEP] PodのCPU使用率をcsvに出力します。 optional arguments: -h, --help show this help message and exit -f FILENAME, --filename FILENAME 出力先のファイル名を指定します -n NAMESPACE, --namespace NAMESPACE Namespaceを指定します --interval INTERVAL CPU使用率計算に使用するデータの間隔を指定します(例)1h、5m --start START データの開始時間を指定します(例)20190101-1000 --end END データの終了時間を指定します(例)20190102-1000 --step STEP データポイントの間隔(秒)を指定します $ python export_pod_cpu.py -f test.csv -n default --interval 5m --start 20190115-1600 --end 20190116-1600 --step 600 $ cat test.csv timestamp,infra-test-nodeport-cust-1,infra-test-nodeport2-cus-1,infra-test-nodeport2-cus-0,infra-test-nodeport-cust-0 2019-01-15 16:00:00,1.7765650066665255,1.5227466104164478,2.4642478804166026,2.7171226995831903 2019-01-15 16:10:00,1.4900129837500724,1.4548672362502657,2.3264455262498513,2.7998607379167124 2019-01-15 16:20:00,1.7629794254168016,1.6523557370834396,2.231762218333415,2.040916205000182 2019-01-15 16:30:00,1.87828389291667,1.8159218674998103,1.7364743683333472,2.4042730716670726 2019-01-15 16:40:00,1.6692312762499266,1.7964510670833533,2.264148609108464,2.300661640484626 2019-01-15 16:50:00,1.7293341049999826,1.7319731500000066,1.8862790512499334,2.091235875833111 (省略) 2019-01-16 15:00:00,1.4205530700000204,1.450829464166835,2.229421241249838,2.6200906879167483 2019-01-16 15:10:00,1.207286078750182,1.4032802437501837,1.8004293182339135,2.120460568511992 2019-01-16 15:20:00,1.2547518466667875,1.5286131908334255,1.8340893729164995,2.258688518749826 2019-01-16 15:30:00,1.2122690649999868,1.3929129920832868,2.2814286763049063,2.8766292608645045 2019-01-16 15:40:00,1.4557575473232511,1.2478161367336864,1.8957547083334705,2.535229864166316 2019-01-16 15:50:00,1.0475173487499962,1.2800040254167773,1.750940527916403,2.7946370483334704 2019-01-16 16:00:00,1.4323884024997824,1.3961431675001752,2.2708808758333516,2.626497514166885 $ |