背景:
目的是監(jiān)控tomcat的cpu和內(nèi)存的,本來(lái)是打算是使用zabbix自發(fā)現(xiàn)去做,但感覺(jué)又要寫模板,又要寫腳本,還要用自動(dòng)化工具推自發(fā)現(xiàn)腳本,而且還擔(dān)心性能也不是很好。所以就打算換種新的監(jiān)控工具,最終選擇了prometheus.
實(shí)施:
1. 第一步就是要安裝prometheus了,我這邊為了保持可通用性和簡(jiǎn)潔,不污染機(jī)器環(huán)境,能用docker安裝的都用docker進(jìn)行安裝(其他的工具也是這樣的)。另外docker安裝比較方便和省心。先上premoethus的docker-compose,安裝docker-compose的可以移步docker-compose安裝
version: '2'
services:
prometheus:
image: prom/prometheus:v2.0.0
ports:
- "9090:9090"
volumes:
- /data/compose_data/prometheus/prometheus:/prometheus #prometheus數(shù)據(jù)目錄
- /data/compose_data/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml #prometheus配置文件
- /data/compose_data/prometheus/first_rules.yml:/etc/prometheus/first_rules.yml #報(bào)警配置文件
command: --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus --web.console.libraries=/usr/share/prometheus/console_libraries --web.console.templates=/usr/share/prometheus/consoles --web.external-url=http://{ip或者域名}:9090 #重寫了啟動(dòng)的配置參數(shù),其中web.external-url配置問(wèn)prometheus地址是為了在報(bào)警郵件里面點(diǎn)擊直接到prometheus的web界面
這里需要注意我的配置文件都寫好了,所以直接進(jìn)行volumes映射的。如果是第一次創(chuàng)建容器環(huán)境,請(qǐng)先啟動(dòng)沒(méi)有映射的容器將配置文件取出來(lái),配置好進(jìn)行映射。下面列出配置文件的內(nèi)容:
prometheus.yml:
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
alerting:
alertmanagers:
- static_configs:
- targets:
- altermanager:9093 #設(shè)置altermanager的地址,后文會(huì)寫到安裝altermanager
rule_files:
- "first_rules.yml" # 設(shè)置報(bào)警規(guī)則
# - "second_rules.yml"
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090'] #這個(gè)自帶的默認(rèn)監(jiān)控prometheus所在機(jī)器的prometheus狀態(tài)
# - job_name: 'localhost'
# static_configs:
# - targets: ['192.168.98.73:9101'] #這部分是監(jiān)控機(jī)器的狀態(tài),需要在機(jī)器節(jié)點(diǎn)啟動(dòng)[node_exporter](https://github.com/prometheus/node_exporter),需要監(jiān)控機(jī)器的可以移步查看
# labels:
# instance: localhost
- job_name: "uat-apps-status" # 自己定義的監(jiān)控的job_name
static_configs:
- targets: ['192.168.98.73:9091'] # 指向pushgateway. 我在每臺(tái)機(jī)器上使用的是推的方式到pushgateway,所以采取了此種方式。
labels:
instance: uat #新添加的標(biāo)簽,可以自定義
scrape_interval: 60s
這里需要說(shuō)明的是也可以使用metrics的方式讓premetheus去各個(gè)節(jié)點(diǎn)去拉數(shù)據(jù),因?yàn)檫@樣我就需要在監(jiān)控的每個(gè)節(jié)點(diǎn)運(yùn)行web服務(wù)端,所以就改成了推到pushgateway的方式。
first_rules.yml:
groups:
- name: example #報(bào)警規(guī)則的名字
rules:
# Alert for any instance that is unreachable for >5 minutes.
- alert: InstanceDown #檢測(cè)job的狀態(tài),持續(xù)1分鐘metrices不能訪問(wèn)會(huì)發(fā)給altermanager進(jìn)行報(bào)警
expr: up == 0
for: 1m #持續(xù)時(shí)間
labels:
serverity: page
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
- alert: "it's has problem" #報(bào)警的名字
expr: "test_tomcat{exported_instance="uat",exported_job="uat-app-status",host="test",instance="uat",job="uat-apps-status"} - test_tomcat{exported_instance="uat",exported_job="uat-app-status",host="test",instance="uat",job="uat-apps-status"} offset 1w > 5" # 這個(gè)意思是監(jiān)控該表達(dá)式查詢出來(lái)的值與一周前的值進(jìn)行比較,大于5且持續(xù)10m鐘就發(fā)送給altermanager進(jìn)行報(bào)警
for: 1m #持續(xù)時(shí)間
labels:
serverity: warning
annotations:
summary: "{{ $labels.type }}趨勢(shì)增高"
description: "機(jī)器:{{ $labels.host }} tomcat_id:{{ $labels.id }} 類型:{{ $labels.type }} 與一周前的差值大于5,當(dāng)前的差值為:{{ $value }}" #自定義的報(bào)警內(nèi)容
這些是自定義的基本報(bào)警內(nèi)容,具體還可以使用模塊功能,構(gòu)建更詳細(xì)的報(bào)警頁(yè)面,具體可以參考模板使用方法,讀者可以基于自己環(huán)境的情況進(jìn)行配置。
2. 然后就需要安裝上面配置文件用到了altermanger和pushgateway了,這邊同樣使用docker來(lái)安裝。
altermanger的docker-compose:
version: '2'
services:
altermanager:
image: prom/alertmanager:master
volumes:
- /data/compose_data/prometheus_altermanager/conf/config.yml:/etc/alertmanager/config.yml #altermanager配置文件
- /data/compose_data/prometheus_altermanager/data:/altermanager #altermanager數(shù)據(jù)目錄
ports:
- "9093:9093"
command: -config.file=/etc/alertmanager/config.yml -storage.path=/alertmanager -web.external-url=http://{ip或者域名}:9093 #重寫了啟動(dòng)方式,添加了web.external參數(shù),使報(bào)警郵件點(diǎn)擊可以直接到altermanager web頁(yè)面
這里的配置文件同樣的需要從容器里面導(dǎo)出來(lái)配置好,放到對(duì)應(yīng)的映射目錄的。
confg.yml:
global:
# The smarthost and SMTP sender used for mail notifications.
smtp_smarthost: 'smtp.exmail.qq.com:465' #這里是指smtp的服務(wù)器
smtp_from: 'test@qq.com' # 郵箱from地址,一般寫郵箱的用戶名
smtp_auth_username: 'test@qq.com' #郵箱的用戶名
smtp_auth_password: '******' #郵箱的密碼
smtp_require_tls: false # 這個(gè)配置了true導(dǎo)致沒(méi)有報(bào)錯(cuò),最后我設(shè)置成了false正常了
# The auth token for Hipchat.
#hipchat_auth_token: '1234556789'
# Alternative host for Hipchat.
#hipchat_api_url: 'https://hipchat.foobar.org/' #這是其他的報(bào)警方式
route:
group_by: ['host','id','type'] #可以機(jī)器標(biāo)簽進(jìn)行報(bào)警的分組
group_wait: 30s #分組等待時(shí)間
group_interval: 30s #分組的時(shí)間間隔
repeat_interval: 1h #重復(fù)報(bào)警的時(shí)間間隔
receiver: 'test-mails' #發(fā)給定義的name
receivers:
- name: 'test-mails'
email_configs:
- to: "test@qq.com" #收件人地址 想發(fā)送多個(gè)人可以這樣寫test1@qq.com,test2@qq.com
上面只是介紹簡(jiǎn)單的報(bào)警配置,具體可以依據(jù)分組做靜默,分類發(fā)送,按級(jí)別發(fā)送等等,具體配置可以看看docker容器默認(rèn)的配置文件配置方法。
3.接下來(lái)就是pushgateway的docker-compose:
version: '2'
services:
pushgateway:
image: prom/pushgateway:master
ports:
- "9091:9091"
這個(gè)比較簡(jiǎn)單,我們只是用這個(gè)做監(jiān)控?cái)?shù)據(jù)的中轉(zhuǎn)。
4.配置完成之后依次啟動(dòng)這些容器:
docker-compose -f /data/compose/prometheus_pushgateway/docker-compose.yml up -d
docker-compose -f /data/compose/prometheus_altermanager/docker-compose.yml up -d
docker-compose -f /data/compose/prometheus/docker-compose.yml up -d
如果啟動(dòng)失敗可以使用docker-compose -f 文件 logs 查看錯(cuò)誤詳情進(jìn)行更正
5.啟動(dòng)完成后可以訪問(wèn)相應(yīng)的web頁(yè)面進(jìn)行查看:
premetheus web頁(yè)面:
訪問(wèn) http://{premetheus_ip}:9090

其中:
- alters可以查看當(dāng)前報(bào)警的狀態(tài)
- status->rules可以查看配置的報(bào)警規(guī)則.
- status->targets可以查看配置的job及狀態(tài)。
altermanger頁(yè)面:
訪問(wèn):http://altermanager:9093

這里可以基于label設(shè)置告警的靜默期,查看當(dāng)前報(bào)警的內(nèi)容等。郵件里面點(diǎn)擊的連接就是到達(dá)這里。
pushgateway頁(yè)面:
訪問(wèn): http://pushgateway:9091

這里可以看到pushgateway的對(duì)應(yīng)的job,已經(jīng)對(duì)應(yīng)job的key及上次收到數(shù)據(jù)的時(shí)間,也可以刪除job的數(shù)據(jù)重新生成。
6.上述步驟都完成后接下來(lái)就需要寫腳本取數(shù)據(jù)到pushgateway了。我這邊給出個(gè)實(shí)例腳本,大家可以根據(jù)此進(jìn)行更改以監(jiān)控自己想要監(jiān)控的數(shù)據(jù)
import requests,time
from get_application_status import get_app_status
# 這個(gè)get_app_status的模塊是自己寫的通過(guò)命令取得tomcat的cpu和內(nèi)存,并返回字典。
def _submit_wrapper(url, job_name, value):
headers = {'X-Requested-With': 'Python requests', 'Content-type': 'text/xml'}
requests.post('http://%s/metrics/job/%s' % (url, job_name),
data='%s\n' % (value), headers=headers)
def push_metrics(job_name,hostid,instance,url):
all_app_status = get_app_status()
tomcat_status = all_app_status.tomcat_status()
metrice_name = ""
for tomcat in tomcat_status:
metrice_name += '%s_tomcat{id="%s",host="%s",type="mem",instance="%s",job="%s"} %s\n'%(hostid,tomcat,hostid,instance,job_name,tomcat_status[tomcat]['mem'])
metrice_name += '%s_tomcat{id="%s",host="%s",type="cpu",instance="%s",job="%s"} %s\n' % (hostid,tomcat,hostid,instance,job_name,tomcat_status[tomcat]['cpu'])
#重點(diǎn)是這塊將取到的值組成一個(gè)字符串,字符串的格式要符合metrics的標(biāo)準(zhǔn),可以選擇target的一個(gè)metrics進(jìn)行格式查看。這里給出個(gè)實(shí)例:
# uat_tomcat{id="tomcat_1018",host="uat",type="mem",instance="uat",job="uat-app-status"} 3.2
# uat_tomcat{id="tomcat_1018",host="uat",type="cpu",instance="uat",job="uat-app-status"} 2.4
_submit_wrapper(url=url,job_name=job_name,value=metrice_name)
if __name__ == "__main__":
job_name = "{{ job_name }}" #我這里使用的是ansible批量推的形式運(yùn)行該腳本,所有用了jinja的變量,如果不需要可以直接加此設(shè)置成對(duì)應(yīng)的值運(yùn)行。
hostid = "{{ hostid }}".replace("-","_") #這里我發(fā)現(xiàn)type標(biāo)簽的值不支持-,所以就替換成_
instance = "{{ instance }}"
url = "{{ url }}" #這里的地址填寫的是altermanger的地址(algermanger:9091)
while True:
push_metrics(job_name=job_name,hostid=hostid,instance=instance,url=url)
time.sleep(60) #這里用的是死循環(huán)不斷的取數(shù)據(jù),其實(shí)也可以使用計(jì)劃任務(wù)。
7.接下來(lái)運(yùn)行上面的腳本推到gateway,prometheus就可以取到數(shù)據(jù)了。我這邊再補(bǔ)充下用ansible推的大致yml:
我用的anisible的roles功能:
tasks/main.yml:
---
# tasks file for premethous_client
- name: test dir is exits
file: path=/root/scripts state=directory
- name: copy service_promethous
copy: src=service_promethous.sh dest=/root/scripts/service_promethous.sh #這是自己寫的簡(jiǎn)單啟動(dòng)關(guān)閉該腳本的文件
- name: copy get_application_status
template: src=get_application_status.py dest=/root/scripts/get_application_status.py #這是那個(gè)模塊,這個(gè)根據(jù)大家寫的腳本內(nèi)容,可用可不用的。這里就不說(shuō)明了
- name: copy single_gateway
template: src=single_gateway.py dest=/root/scripts/single_gateway.py #這是推gateway的腳本,我寫的是以死循環(huán)的方式運(yùn)行,其實(shí)可以用計(jì)劃任務(wù)
notify:
- restart prometheus
- name: start prometheus
shell: sh /root/scripts/service_promethous.sh start
ignore_errors: True
files/service_promethous.sh:
#!/bin/bash
start(){
ps aux |grep single_gateway.py | grep python | grep -v grep > /dev/null
if [ $? -eq 0 ];then
echo "already start"
exit 0
else
nohup python /root/scripts/single_gateway.py > /dev/null 2>&1 &
fi
}
stop(){
num=`ps aux |grep single_gateway.py | grep -v grep | awk '{print $2}'`
if [ $num ];then
kill $num
echo "stop success..."
else
echo "no starting..."
fi
}
status(){
ps aux |grep single_gateway.py | grep python | grep -v grep > /dev/null
if [ $? -eq 0 ];then
echo "starting...."
else
echo "stoping......"
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
sleep 2
start
;;
status)
status
;;
*)
echo "only suport start|stop|restart|status"
esac
# 偷懶寫的簡(jiǎn)單的啟動(dòng)關(guān)閉文件,大家看下就好了。
handlers/main.yml:
---
# handlers file for premethous_client
#
- name: restart prometheus
shell: sh /root/scripts/service_promethous.sh restart #文件變化重啟腳本的handles
premethous_client.yml:
- hosts: uat_env
gather_facts: True
roles:
- premethous_client
vars:
job_name: uat-app-status
hostid: "{{ ansible_hostname }}"
instance: uat
url: altermanager:9091
tags:
- uat-premethous
這是執(zhí)行role的playbook,主要就是定義了全局變量替換role的templates。做到不同的機(jī)器推到pushgateway的值的key不一樣。
8.配置完ansible
ansible-playbook premethous_client.yml
指定的機(jī)器就可以就行數(shù)據(jù)的推送了。
注:ansible只是為了方便才做的,其實(shí)可以不做。
9.現(xiàn)在其實(shí)prometheus的監(jiān)控報(bào)警已經(jīng)完成了,這邊我再擴(kuò)展下grafana結(jié)合prometheus展示的使用方法。
按照慣例依然使用docker-compose部署
version: '2'
services:
grafana:
image: grafana/grafana:4.6.3
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=123456 #設(shè)置管理員的密碼
volumes:
- /data/compose_data/grafana:/var/lib/grafana #設(shè)置數(shù)據(jù)目錄
command: cfg:default.smtp.enabled=true cfg:default.smtp.host=smtp.exmail.qq.com:25 cfg:default.smtp.user=test@qq.com cfg:default.smtp.password=***** cfg:default.smtp.from_address=test@qq.com cfg:default.server.root_url=http://{grafna_url或者域名}:3000
#重寫了grafana啟動(dòng)的配置,添加了郵件發(fā)送的功能
啟動(dòng)grafna:
docker-compose -f /data/compose/grafana/docker-compose.yml up -d
訪問(wèn):http://granfana:3000 輸入用戶名密碼登錄

這里有幾個(gè)步驟,添加數(shù)據(jù)源及添加用戶,我就不說(shuō)了,根據(jù)提示可以很輕松的完成。我這里主要介紹dashboard templates的使用,點(diǎn)擊到dashboard然后點(diǎn)擊new,然后點(diǎn)擊齒輪形狀的圖標(biāo),點(diǎn)擊templating,新建variable

這里需要注意的是從取到的數(shù)據(jù)顧慮出來(lái)單個(gè)的值要用()包括才能取所有的key值里面取到想要的appid,這里讀者可以嘗試用括號(hào)包裹不同的內(nèi)容進(jìn)行測(cè)試。然后還可以新建variables一個(gè)取相應(yīng)的host。這里根據(jù)實(shí)際環(huán)境做適合自己的配置。我這邊配置的整體效果是這個(gè)樣子的。

注:在創(chuàng)建graph圖形的時(shí)候,如果要引用templating設(shè)定的值,可以使用這樣的格式[[]],比如引用appid就使用[[appid]],在legend format使用label的值可以使用{{}}將labels括起來(lái).
下面給出大致配置的截圖:
config.png
結(jié)語(yǔ):
這里也就是用到了prometheus自定義監(jiān)控的基本功能,實(shí)際上prometheus還支持其他好幾種數(shù)據(jù)類型,各種豐富的算術(shù)表達(dá)式,報(bào)警聚合和抑制,以及自動(dòng)發(fā)現(xiàn)等,這些需要大家慢慢發(fā)掘與學(xué)習(xí)了,真正業(yè)務(wù)有需求了就會(huì)驅(qū)動(dòng)技術(shù)的進(jìn)步。
這里有1個(gè)問(wèn)題,就是推送端的監(jiān)控問(wèn)題,如果腳本意外出錯(cuò)停止,數(shù)據(jù)就不會(huì)更新了,這邊我用的是zabbix監(jiān)控了機(jī)器的這個(gè)進(jìn)程,當(dāng)然還有更好的方法,大家可以嘗試嘗試。
