作者:莫善
某網際網路公司高階 DBA。
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
一、背景介紹
運維工作中可能會遇到這麼一個痛點,因線上機器基本都是單機多例項,有時候會出現因為某個例項而影響了整個機器的效能。因缺少程式級別的監控,事後想分析是哪個例項跑滿了系統資源往往比較困難。為了解決這一痛點,迫切希望實現程式級別的監控。
程式級別的資源監控,包括但是不限於CPU,記憶體,磁碟IO,網路流量。
二、前期準備
經瞭解,有一個 process_exporter 可以實現程式監控,但是在實際調研及測試發現,該工具有些不足:
process_exporter https://github.com/ncabatoff/...
- 監控的物件必須預先配置
我們線上單臺機器可能部署有20個例項,要麼是將20個例項的配置放在一個 process_export ,要麼是單個例項一個 process_export ,不管哪種方式部署 process_export 可能都有些麻煩,另外新加一個想監控的物件也需要重新維護一下 process_exporter 。
我希望是新增待監控機器後能自動發現所有活躍的程式。
- 不能監控程式的網路情況
測試 process_exporter 發現只有 io、記憶體、cpu等使用情況,沒找到網路監控的指標。
我們線上機器很多還是千兆網路卡,監控網路使用情況的需求更大。
- 額外的需求
我們的環境可能會有一些臨時程式(不是常駐程式)。
三、需求實現
1、監控採集
最開始的思路很簡單,就想著使用一些系統工具直接讀結果進行解析。但是領導覺得讀取它們的採集結果可能稍微重了一點,可能效率不高,達不到小粒度採集,所以想讓我研究一下直接抓取【/proc/pid/】下面執行態的資料,這種方式效率應該是最高的。但是在實際測試過程中發現,想要通過【/proc/pid/】來實現程式監控的方案真是困難重重,以至於後來暫時放棄該方案了,不過還是想簡單聊一下這個的測試歷程。
[root process-exporter]# ls /proc/1
attr auxv clear_refs comm cpuset environ fd gid_map limits map_files mem mounts net numa_maps oom_score pagemap personality root schedstat setgroups stack statm syscall timers wchan
autogroup cgroup cmdline coredump_filter cwd exe fdinfo io loginuid maps mountinfo mountstats ns oom_adj oom_score_adj patch_state projid_map sched sessionid smaps stat status task uid_map
[root process-exporter]#
這個連線比較詳細的介紹了【/proc/pid】下面的檔案/目錄 https://github.com/NanXiao/gn...
(1)CPU狀態抓取
直接翻車,在【/proc/pid/】下面沒找到CPU相關的狀態資料。
有知道的大佬請指導一下。
(2)MEM狀態抓取
記憶體可以通過【/proc/pid/status】檔案進行抓取。
$ grep "VmRSS:" /proc/3948/status
VmRSS: 19797780 kB
$
(3)io狀態抓取
同理,io也可以通過【/proc/pid/io】檔案進行抓取。
$ grep "bytes" /proc/3948/io
read_bytes: 7808071458816
write_bytes: 8270093250560
(4)網路狀態抓取
這個也直接翻車,在【/proc/pid/】下面倒是找到了跟網路相關的資訊,比如【/proc/pid/net/dev】,還有【/proc/pid/netstat】。
起初以為dev這個檔案是儲存了程式級網路傳輸資料,但是發現這個檔案記錄的網路流量是整個網路卡的,就是說【/proc/pid/net/dev】和【/proc/net/dev】這兩個檔案記錄的網路流量位元組數基本一樣大。具體測試如下:
首先模擬兩個網路傳輸的程式,因為我測試的機器是有NFS,所以直接從NFS拷貝到本地,就模擬了網路傳輸。
$ ps -ef|grep -- "cp -i -r"|grep -v grep
root 66218 111973 12 17:14 pts/1 00:00:11 cp -i -r Backup_For_TiDB/15101/2022-06-20 /work
root 67099 122467 10 17:14 pts/2 00:00:09 cp -i -r Backup_For_TiDB/15001/2022-06-20 /work
程式號分別是 66218 67099
然後通過同時列印兩個pid對應的【/proc/pid/net/dev】檔案及系統的【/proc/net/dev】檔案做對比
$ cat /proc/66218/net/dev /proc/67099/net/dev /proc/net/dev |grep eth0 && sleep 1 && echo "------------------------" && cat /proc/66218/net/dev /proc/67099/net/dev /proc/net/dev|grep eth0
eth0: 364616462197417 249383778845 0 0 0 0 0 0 77471452119287 170038153309 0 0 0 0 0 0
eth0: 364616462197417 249383778845 0 0 0 0 0 0 77471452119287 170038153309 0 0 0 0 0 0
eth0: 364616462197417 249383778845 0 0 0 0 0 0 77471452119287 170038153309 0 0 0 0 0 0
------------------------
eth0: 364616675318586 249383924598 0 0 0 0 0 0 77471456448161 170038229547 0 0 0 0 0 0
eth0: 364616675318586 249383924598 0 0 0 0 0 0 77471456449457 170038229571 0 0 0 0 0 0
eth0: 364616675318586 249383924598 0 0 0 0 0 0 77471456449835 170038229578 0 0 0 0 0 0
$
可以看到,這三個【/proc/66218/net/dev】【/proc/67099/net/dev】 【/proc/net/dev】的eth0網路卡的流量是一樣的,就是說【/proc/pid/net/dev】實際也是系統的流量開銷,而不是單個程式對應的流量開銷。
【/proc/pid/net/dev】這個不行就懷疑【/proc/pid/net/netstat】這個檔案是我需要的,但是很難受,幾乎看不懂裡面的資訊,找了很多資料才大致弄清楚裡面的資料,最終發現也不是需要的資料。
/proc/pid/net/netstat的詳情可以參考這裡 https://github.com/moooofly/M...
最終還是妥協了,老老實實使用現成的工具進行採集吧。
top free ps iotop iftop等工具
2、資料分析
確定了採集方式後就是資料的分析了,下面準備挨個分析一下。
(1)CPU
下面這幾個是整機的情況
$ lscpu|grep 'NUMA node0 CPU(s)'|awk '{print $NF}'|awk -F'-' '{print $2+1}' #機器CPU核心數
$ uptime|awk -F'average: ' '{print $2}'|awk -F, '{print int($1)}' #機器當前負載情況
$ top -b -n 1|grep '%Cpu(s):' |awk '{print int($8)}' #idle
$ top -b -n 1|grep '%Cpu(s):' |awk '{print int($10)}' # iowait
這部分比較簡單,直接記錄即可。
下面是針對程式抓取CPU使用情況
$ top -b -n 1|grep -P "^[ 0-9]* "|awk 'NF==12 {
if($9 > 200 || $10 > 10) {
for (i=1;i<=NF;i++)
printf $i"@@@";
print "";
}
}' #程式使用CPU百分比,記憶體百分比,僅記錄使用到cpu和記憶體的程式
這部分稍微有點複雜,結果會儲存到top_dic字典。
這個操作的目的是想著記錄程式的cpu記憶體使用情況,但是會發現top的詳情裡面並沒有程式資訊,所以還需要結合ps輔助一下,具體如下:
ps -ef|awk '{printf $2"@@@" ;for(i=8;i<=NF;i++) {printf $i" "}print ""}'
這部分結果會儲存到ps_dic字典。只需要記錄pid和程式詳情即可,所以對ps做了分析後最後的結果就是【pid@@@process_info】,最終top_dic和ps_dic通過pid關聯
(2)MEM
下面是整機的情況
$ free |grep '^Mem:'|awk '{print int($2/1024/1024),int($3/1024/1024),int(($2-$3)/1024/1024)}'
下面是針對程式抓取MEM使用情況
#程式使用MEM百分比在cpu部分就已經採集
記憶體這塊是為了方便並沒有使用proc下面的執行態資料,如果從proc下面採集需要遍歷所有pid,感覺比較麻煩,還不如直接通過top採集一次來的方便(還是順便採集)。但是也有一個弊端,最終計算程式使用記憶體大小會多一個操作,即需要根據MEM百分比對其進行計算換成具體位元組數。
(3)磁碟
下面是整機的磁碟使用情況
$ df|grep ' " + part + "'|awk '$2 > 1024 * 1024 * 50 && /^\//{print $1,int($2/1024/1024),int($3/1024/1024),int($4/1024/1024)}' #磁碟使用情況
需要資料盤的掛載點,如果沒有配置掛載點會記錄整個機器的所有掛載點(大於50GB的掛載點)的使用情況。
下面是針對程式抓取io使用情況
$ iotop -d 1 -k -o -t -P -qq -b -n 1|awk -F' % ' '
NR>2{
OFS="@@@";
split($1,a," ");
if(a[5] > 10240 || a[7] > 10240 ) {
print a[1],a[2],a[5]a[6],a[7]a[8],$NF;
}
}
NR<3{
print $0;
}'|awk '
{
if(NR==1){
print $1,$2,$6,$13;
} else if(NR==2) {
print $1,$2,$5,$11;
} else {
print $0;
}
}'
這個採集也稍微有點複雜,結果會儲存到iotop_dic字典,通過pid和top_dic和ps_dic這兩個字典關聯。需要注意的是,在實際測試過程中發現,部分程式的詳情非常長,所以為了避免資料冗餘,程式資訊會記錄到單獨的表【tb_monitor_process_info】,並記錄該串的md5值且將md5作為唯一鍵,這樣可以避免空間的浪費。在展示的時候僅需要通過md5值作為關聯條件即可。
我們需要對結果做分析並加工成我們需要的,我覺得有用的就是【時間】【pid】【讀io】【寫io】【程式資訊】,以及對於io訪問量少的程式直接過濾掉。
綜上,程式級別的cpu,記憶體,io使用情況的採集資料上報給server端大概是下面這樣:
{
"19991":{
"cpu":"50.0",
"mem":"12.5",
"io_r":"145",
"io_w":"14012",
"md5":"2932fb739fbfed7175c196b42021877b",
"remarks":"/opt/soft/mysql57/bin/mysqld --defaults-file=//work/mysql23736/etc/my23736.cnf"
},
"58163":{
"cpu":"38.9",
"mem":"13.1",
"io_r":"16510",
"io_w":"1245",
"md5":"c9e1804bcf8a9a2f7c4d5ef6a2ff1b62",
"remarks":"/opt/soft/mysql57/bin/mysqld --defaults-file=//work/mysql23758/etc/my23758.cnf"
}
}
(4)網路
網路的監控有點難受,沒法基於pid做分析,只能通過ip:port分析來回的流量。
下面是整機的網路使用情況
$ iftop -t -n -B -P -s 1 2>/dev/null|grep Total |awk '
NR < 3 {
a = $4;
if ($4 ~ /MB/) {
a = ($4 ~ /MB/) ? 1024 * int($4) "KB" : $4;
} else if ($4 ~ /GB/) {
a = ($4 ~ /GB/) ? 1024 * 1024 * int($4) "KB" : $4;
}
a = (a ~ /KB/) ? int(a) : 0
print $2, a;
}
NR == 3 {
b = $6;
if ($6 ~ /MB/) {
b = ($6 ~ /MB/) ? 1024 * int($6) "KB" : $6;
} else if ($6 ~ /GB/) {
b = ($6 ~ /GB/) ? 1024 * 1024 * int($6) "KB" : $6;
}
b = (b ~ /KB/) ? int(b) : 0
print $1, b;
}'
下面是程式級別的網路使用情況
$ iftop -t -n -B -P -s 2 -L 200 2>/dev/null|grep -P '(<=|=>)'|sed 'N;s/\\n/,/g'|awk 'NF==13{
if($4 ~ /(K|M|G)B/ || $10 ~ /(K|M|G)B/) {
if(($4 ~ /KB/ && int($4) > 10240) ||
($10 ~ /KB/ && int($10) > 10240) ||
($4 ~ /MB/ && int($4) > 10240) ||
($10 ~ /MB/ && int($10) > 10240) ||
($4 ~ /GB/ || $10 ~ /GB/)) {
print $2,$4,$8,$10
}
}
}'
這部分比較麻煩的是單位換算及計算。這部分會將結果儲存到iftop_dic。
這個採集也稍微有一點複雜,需要對結果做分析並加工成我們所需要的。我認為有用的就是【出ip:port】【出口流量】【回ip:port】【入口流量】。最後程式網路使用情況的採集資料上報給server端大概是下面這樣子。
{
"net":{
"speed":"1000",
"send":"7168",
"receive":"8192",
"Total":"16384",
"time":"2022-06-29 20:16:20",
"iftop" : {
"192.168.168.11:55746":[
{
"remote":"192.168.168.13:18059",
"out":"7.94KB",
"in":"307KB"
}
],
"192.168.168.11:60090":[
{
"remote":"192.168.168.13:18053",
"out":"6.73KB",
"in":"307KB"
}
]
}
}
}
至此,所有采集項的監控資料都已經拿到了,下面就是將資料入庫了。
3、資料入庫
監控資料分析了以後,就是將其記錄下來,本專案採用 MySQL 來儲存資料,其中不免涉及一些二次分析,以及注意事項,這裡不準備介紹,放在注意事項部分進行介紹。
4、資料展示
完成了資料分析,資料記錄,最後的工作就是將資料展示出來,供運維人員在需要的時候隨時檢視分析,本專案採用 grafana 將資料展示出來,這個部分有些注意事項,這裡也不準備介紹,放在注意事項進行介紹。可以先來看個效果圖:
四、注意事項
使用 python3 實現程式碼部分,所有注意事項的解決方案也是僅針對 python3 語法來實現的。
1、ssh環境
資料的採集是通過rpc實現,但是 server 端對 client 的管理都是依賴 ssh ,所以必須保證 server 到所有的 client 都能 ssh 免密登入。
2、長連線
跟 MySQL 的通訊,建議使用長連線。尤其是需要監控的機器個數比較多,如果是短連線會頻繁跟 MySQL 進行建立連線釋放連線,存在一定的不必要開銷。
def f_connect_mysql(): #建立連線
"""
建立連線
"""
state = 0
try :
db = pymysql.connect(monitor_host, monitor_user, monitor_pass, monitor_db, monitor_port, read_timeout = 2, write_timeout = 5) #連線mysql
except Exception as e :
f_write_log(log_opt = "ERROR", log = "[ 建立連線失敗 ] [ " + str(e) + " ]", log_file = log_file)
db = None
return db
def f_test_connection(db):
"""
測試連線
"""
try:
db.ping()
except:
f_connect_mysql()
return db
def f_close_connection(db):
"""
關閉連線
"""
try:
db.close()
except:
db = None
return db
需要注意,如果是多執行緒的話建議每個執行緒維護一個連線,或者新增互斥鎖,這樣可以避免部分異常。
3、做減法
因為我們是基於機器去做的程式監控,難免會出現很多被監控的物件(一臺機器上千個服務也不是不可能),在測試過程中沒有發現這個問題影響很大,但是在實際上線後發現,如果不優化這個部分會導致 metrics 太多,grafana 的渲染很慢,所以對於不必要的採集記錄,可以在採集的時候就過濾掉,能一定程度避免 client 到 server 端的網路開銷,也能減少磁碟空間的開銷,還提升 grafana 的出圖效率。
優化後,在配置檔案提供了配置項,只有當程式使用系統資源滿足閾值才會被採集。
4、超時機制
(1)操作 MySQL 的超時
既為了程式碼的健壯性,也是為了程式的持續、穩定性都建議加上超時引數,可以避免因為一些極端場景導致讀數或者寫數卡住。
(2)採集資料的超時
生產環境的複雜性,什麼都可能發生,即便是一條簡單到不能再簡單的命令也可能出現卡住,所以加上超時機制吧。這裡需要注意,在新增超時機制的時候發現有些問題,具體測試如下:
Python 3.7.4 (default, Sep 3 2019, 19:29:53)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime,subprocess
>>> s_time = datetime.datetime.now()
>>> res = subprocess.run("echo $(sleep 10)|awk '{print $1}'",shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8",timeout=2)
省略了很多錯誤輸出
subprocess.TimeoutExpired: Command 'echo $(sleep 10)|awk '{print $1}'' timed out after 2 seconds
>>> e_time = datetime.datetime.now();
>>>
>>> print(s_time)
2022-06-23 13:05:37.886864
>>> print(e_time)
2022-06-23 13:05:48.353889
>>> print(res)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'res' is not defined
>>>
可以看到【subprocess.run】設定了兩秒超時,但是兩秒後沒有丟擲異常並結束執行,從執行時間看s_time跟e_time相差11秒,但是整個執行結果是異常的(res都沒有結果),就是說並沒有起到預想的超時效果(預想的效果是到超時閾值後終止執行,返回異常)。
如果操作命令是簡單的命令就沒事,比如將【echo $(sleep 10)|awk '{print $1}'】改成【sleep 10】,這個超時機制就正常。
針對這個超時機制可能會出現失效的情況,在程式碼裡面直接用系統命令timeout代替了。
5、返回值
如果操作命令帶管道等複雜命令,返回值可能並不可信。具體測試如下
>>> res = subprocess.run("echoa|awk '{print $1}'",shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8",timeout=2)
>>> res.returncode
0
>>>
【echoa|awk '{print $1}'】管道左邊的操作是錯誤的,所以整個返回結果應該是非零(預期是這樣),但是這裡返回了0。原因是在管道操作的場景,bash預設情況是僅獲取最後一個管道的執行返回狀態碼,例如【comm1|comm2|comm3】,如果comm1執行成功,comm2執行失敗,但是comm3執行成功,那整個返回狀態就是執行成功。
解決方案如下
>>> res = subprocess.run("set -o pipefail;echoa|awk '{print $1}'",shell=True,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8",timeout=2)
>>> res.returncode
127
>>>
執行命令前先加一個set -o pipefail,關於set -o的解釋可以參考下面的描述。
-o If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all com‐mands in the pipeline exit successfully. This option is disabled by default.
五、工作原理
1、server
- 執行緒1
這個執行緒會做三個事情:
(1)在server重啟的時候會去讀【tb_monitor_version】表,判斷當前版本號跟MySQL記錄的版本號是否一致,如果不一致就會去更新MySQL記錄的版本號。然後將【tb_monitor_host_config】表所有istate=2的節點更新為istate=1。
(2)管理client上線下線,每30s去讀一次【tb_monitor_host_config】表,將需要上線的節點或者需要下線的節點進行維護。istate=1表示需要上線,就會去部署監控指令碼(升級就更新程式碼),並更新為istate=2,istate=0表示需要下線,會去下線該client節點並更新為istate=-1。
(3)管理client狀態,每30s去讀一次【tb_monitor_host_config,tb_monitor_alert_info,tb_monitor_host_info】表(三表關聯),將最近兩分鐘沒有上報的client且最近5min沒有被告警的節點統計出來並告警。
- 執行緒2
這個執行緒做兩個事:
(1)等待client上報監控資料,然後進行二次分析並寫到MySQL中。
(2)返回當前版本號給client。
2、client
client端會做三個事情
(1)六執行緒並行去採集【機器cpu】【機器記憶體】【機器磁碟】【機器網路】【程式網路】【程式io,程式cpu,程式記憶體】。採集完畢後,主執行緒會進行分析並上報給server端。
(2)在上報過程中如果遇到連續三次server都是異常狀態就會將server異常記錄(避免多個client同時告警)到【tb_monitor_alert_info】表傳送告警。
(3)上報完成後會判斷自己的版本號跟server端返回的版本號是否一致,如果不一致就會退出程式,等待crontab拉起,以此完成升級。
server端完成程式碼更新,在重啟server的時候會將新程式碼同步到各個client。
3、MySQL
MySQL的作用是存版本資訊,client ip配置,監控資料,以及告警狀態等。
4、grafana
grafana的作用是從MySQL讀取監控資料並展示出來。
5、alert
採用企業微信的機器人作為告警通道。
六、使用限制
1、系統環境
(1)作業系統版本及核心。
$ uname -a
Linux 3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
其他版本沒有測試過,不確定是否能用。
(2)系統工具
監控資料採集依賴於作業系統工具,主要依賴如下:
awk,grep,sed,tr,md5sum
top,iftop,iotop
df,free,lscpu,uptime
ip,netstat
rsync,python3
cd,ssh,timeout
server端到client要有免密登入。
2、軟體環境
軟體版本可能存在相容性問題,所以其他版本不確定是否能用,請各自測試除錯。
(1)Python環境
3.7.4
(2)MySQL版本
5.7.26
(3)grafana版本
8.3.1 建議小版本也要一致。 https://dl.grafana.com/enterp...
七、使用介紹
1、部署server
(1)clone專案
mkdir -p /opt/soft/git
cd /opt/soft/git
git clone https://gitee.com/mo-shan/rpc_for_process_monitor.git
依賴Python3環境,建議3.7.4,要求python3在PATH裡面,安裝過程略。
(2)部署server
cp -r /opt/soft/git/rpc_for_process_monitor /opt/soft/rpc_for_monitor #注意這裡的目錄是有區別的, 主要是希望開發環境跟實際部署的目錄不一樣, 避免失誤
cd /opt/soft/rpc_for_monitor
$ tree -L 2
.
├── conf
│ └── config.ini #配置檔案
├── img #忽略
│ ├── all-info.png
│ ├── cpu-info.png
│ ├── disk-info.png
│ ├── grafana-data-source-1.png
│ ├── grafana-data-source-2.png
│ ├── grafana-data-source-3.png
│ ├── grafana-data-source-4.png
│ ├── grafana-data-source-5.png
│ ├── grafana-data-source-6.png
│ ├── grafana-data-source-7.png
│ ├── mem-info.png
│ ├── net-info.png
│ └── process-info.png
├── init #初始化檔案
│ ├── grafana.json #grafana配置模板
│ ├── init.sql #mysql建表語句
│ └── requirements.txt #python3依賴的模組
├── lib #庫檔案
│ ├── Config.py #解析config.ini
│ ├── ConnectMySQL.py #連線並操作mysql
│ ├── globalVar.py #全域性變數
│ ├── Public.py #公共函式
│ └── __pycache__
├── LICENSE
├── logs #日誌目錄
│ └── info.log #日誌檔案
├── py37env #虛擬環境,要求在/opt/soft/rpc_for_monitor/py37env下才能使用(activate等檔案的路徑寫死了)
│ ├── bin
│ ├── include
│ ├── lib
│ └── pip-selfcheck.json
├── README.md #幫助文件
├── rpc.py #主程式
├── start_server.sh #server端的啟動指令碼
└── state #忽略
└── state.log
11 directories, 28 files
(3)配置server
vim conf/config.ini #根據實際情況進行編輯
如果需要變更專案目錄,需要將【lib/Config.py】檔案的變數【config_file】也改一下。
[global]
version = 1.1 #版本號, 通過這個變數控制server和client的程式碼,如果server發現這個配置跟表裡儲存的版本不一致就認為程式碼進行了變更,就會將新程式碼傳到client,client如果發現自己的版本和server版本不一樣會進行重啟,以此達到升級效果。
interval_time = 30 #監控採集粒度單位是秒,即30秒一次,這個不是完全精確的30s一次
retention_day = 30 #監控資料保留天數,即30天
log_file = /opt/soft/rpc_for_monitor/logs/info.log #日誌檔案
script_dir = /opt/soft/rpc_for_monitor #指令碼目錄,不建議變更
mount_part = /work #資料盤掛載點, 也可以不配置,置為空,但是不能刪除這個配置項
log_size = 20 #日誌檔案大小(MB)限制,超過這個值就會刪除歷史日誌
[RULE]
cpu = 200 #採集的閾值,200表示某個程式使用cpu大於等於200%才會被採集
mem = 10 #採集的閾值,10表示某個程式使用記憶體大於等於10GB才會被採集
io = 10240 #採集的閾值,10240表示某個程式使用io(讀寫有一個就算)大於等於10MB才會被採集
net = 10240 #採集的閾值,10240表示某個程式使用網路(進出有一個就算)大於等於10MB才會被採集
[CLIENT]
path = xxxx #預定義一下作業系統的path,因為client會維護一個cront任務,所以避免因為環境變數問題導致指令碼執行報錯,需要定義一下path
python3 = /usr/local/python3 #python3安裝目錄
py3env = /opt/soft/rpc_for_monitor/py37env #python3虛擬環境目錄,工程自帶了一個虛擬環境,可以直接用(前提是指令碼目錄沒有變更)
[MSM]
wx_url = xxxx #企業微信報警url,告警功能需要使用者自己修改一下並測試(如果是告警機器人url+key,可以直接配上就能用,本例就是通過企業微信機器人傳送告警)
[Monitor] #存放監控資料的MySQL的配置
mysql_host = xxxx
mysql_port = xxxx
mysql_user = xxxx
mysql_pass = xxxx
省略部分不建議變更的配置
所有目錄不建議修改, 要不然需要變更的地方太多,容易出錯。
2、部署 MySQL
安裝MySQL略,建議的版本:5.7
(1)新建必要的賬戶
用MySQL管理員使用者登入並操作。
create user 'monitor_ro'@'192.%' identified by 'pass1'; #密碼請根據實際情況變更
grant select on dbzz_monitor.* to 'monitor_ro'@'192.%';
create user 'monitor_rw'@'192.%' identified by 'pass2';
grant select,insert,update,delete on dbzz_monitor.* to 'monitor_rw'@'192.%';
monitor_ro使用者給grafana使用, monitor_rw使用者是給程式寫入監控資料的(server端寫資料,client上報給server)。所以注意的是,monitor_ro使用者要給grafana機器授權,monitor_rw使用者要給所有監控物件授權,這個目的是用來控制當server失聯了,第一個發現的client就會向表裡寫一條告警記錄並告警,避免其他client重複操作。
(2)初始化MySQL
用MySQL管理員使用者登入並操作。
cd /opt/soft/rpc_for_monitor
mysql < init/init.sql
所有表放在dbzz_monitor庫下
(dba:3306)@[dbzz_monitor]>show tables;
+----------------------------+
| Tables_in_dbzz_monitor |
+----------------------------+
| tb_monitor_alert_info | # 告警表, 觸發告警就會在裡面寫入一條記錄, 避免同一時間多次告警。
| tb_monitor_disk_info | # 磁碟資訊表,多個盤會記錄多條記錄
| tb_monitor_host_config | # client配置表,需要採集監控的機器配置到這裡面就行
| tb_monitor_host_info | # 系統層面的監控記錄到這裡面
| tb_monitor_port_net_info | # 埠級別的網路監控會記錄到這裡面
| tb_monitor_process_info | # 這裡面是記錄了程式資訊,全域性的
| tb_monitor_process_io_info | # 這裡是記錄的程式的io監控資料
| tb_monitor_version | # 記錄版本號,及版本號變更時間
+----------------------------+
6 rows in set (0.00 sec)
(dba:3306)@[dbzz_monitor]>
所有表都有詳細的註釋,請看錶的建表註釋。
3、配置client
配置客戶端很簡單,只需要往MySQL表裡面寫入一條記錄。
use dbzz_monitor;
insert into tb_monitor_host_config(rshost,istate) select '192.168.168.11',1;
#多個機器就寫多條記錄,server端會有後臺執行緒定時掃描tb_monitor_host_config
#如果有待新增的client就會進行部署
#如果需要下線監控節點直接將istate狀態改成0即可
這裡有個限制條件,這個client端已經有python3環境,否則會報錯。
4、部署grafana
安裝略。
grafana版本:8.3.1,建議小版本也要一致。 https://dl.grafana.com/enterp...
這部分涉及到grafana的配置,所有的配置都已經導成json檔案,使用者直接匯入即可。
具體的操作如下。
(1)新建DataSource
新建一個資料來源
需要選擇MySQL資料來源
資料來源的名稱要求寫【dba_process_monitor】,如果跟grafana配置不一致可能會有影響。
(2)匯入json配置
$ ll init/grafana.json
-rw-r--r-- 1 root root 47875 Jun 23 14:28 init/grafana.json
本配置是在grafana 8.3.1 版本下生成的。需要注意一下版本,不同版本可能不相容。如果版本不一致匯入會導致出圖失敗,需要使用者自己重新配置grafana,出圖的sql可以參考一下grafana配置檔案的rawSql的配置【grep rawSql init/grafana.json】。
- 建議1:【Lagend】的配置都改成【as table】,要不然如果指標太多顯示出來的會很亂
- 建議2:選擇單位的時候對於不希望進行轉換的可以選【Custom unit】屬性
- 建議3:【Stacking and null value】屬性建議設定為【null as zero】
5、啟動server
將server端的啟動指令碼配置到crontab中,可以起到守護程式的作用。
echo "*/1 * * * * bash /opt/soft/rpc_for_monitor/start_server.sh" >> /var/spool/cron/root
client端不用管,server啟動以後會自動去管理client。
配置完成後,等待一分鐘檢視日誌【/opt/soft/rpc_for_monitor/logs/info.log】,可以看到類似下面的日誌。
[ 2022-06-30 15:13:01 ] [ INFO ] [ V1.1 Listening for '0.0.0.0:9300' ]
[ 2022-06-30 15:13:04 ] [ INFO ] [ 新加監控節點成功 ] [ 192.168.168.11 ]
[ 2022-06-30 15:13:11 ] [ INFO ] [ 監控資料上報成功 ] [ 192.168.168.11 ]
埠預設是9300,可以通過修改【/opt/soft/rpc_for_monitor/start_server.sh】這個檔案就行變更監聽埠。
6、效果圖
(1)主頁面
總共有五個ROW,前面四個是機器級別的監控圖,process是程式的監控圖。
(2)CPU頁面
整個機器的CPU使用情況。
(3)記憶體頁面
整個機器的記憶體使用情況。
(4)磁碟頁面
整個機器的磁碟使用情況,如果沒有定義具體的掛載點,會採集所有的掛載點。
(5)網路頁面
整個機器的網路使用情況。
(6)程式頁面
會看到具體的程式對系統資源的使用情況。需要注意,因在採集的時候做了過濾,所以監控資料並不一定是連續的,所以建議配置grafana的【null as zero】,這樣展示監控圖的時候是連續的,而不是很多點。另外某個指標可能為空,這也是正常現象。
八、注意事項
- server,client端一定要有python3環境。
- 如果有多個server,在啟動server的時候就要指定多個(用逗號隔開),要不然在部署client的時候只會配上單個server,配置多個的好處就是如果第一個當機/異常,client會上報給其他的server。
- server在執行過程中可以新增client,只需要在【tb_monitor_host_config】表新增istate為1的記錄即可。同理,如果下線的話就更新istate為0即可,執行中的istate為2,下線後的istate為-1。
- 該工具有告警功能(如果配置),server掛了(client連續三次都連不上server),會由第一個發現的client記錄到MySQL裡面,併傳送告警,如果client掛了,server會發現並告警(超過兩分鐘未上報告警資料)。
- 如果需要升級程式碼,只需要測試好新程式碼,確認無誤後,更新到server的部署指令碼目錄,然後kill掉server程式即可,等待crontab拉起就行了,client端的程式碼不用人為進行更新。需要注意,新程式碼一定要記得修改配置檔案的版本號,要不然server端不會發現版本不一致,也就不會下發相關任務去更新client的程式碼。
- 如果需要修改部署目錄請根據實際情況修改【conf/config.ini】【lib/Config.py】,注意這時候自帶的虛擬環境將不能使用了。強烈不建議變更目錄結構或者目錄名。
- 因考慮到MySQL效能問題及grafana渲染效能問題,所以增加了採集閾值功能,所以部分皮膚的監控資料可能會沒有(該時間段的程式沒有滿足採集閾值的資料)。
九、寫在最後
本文所有內容僅供參考,因各自環境不同,在使用文中程式碼時可能碰上未知的問題。如有線上環境操作需求,請在測試環境充分測試。