本文介紹了如何基于 kube-prometheus 設(shè)計(jì)一個(gè)監(jiān)控系統(tǒng), 以靈活簡單的方式對 kubernetes 上的應(yīng)用進(jìn)行指標(biāo)采集,并實(shí)現(xiàn)監(jiān)控報(bào)警功能。
本文提供了作者的應(yīng)用示例,另外還記錄了作者在學(xué)習(xí)、使用 Prometheus 過程中的一些筆記,如 arm 版鏡像獲取、一些工具的使用等。
前言
衆(zhòng)所周知,大數(shù)據(jù)産品作為底層平臺,其運(yùn)維監(jiān)控一直是生産實(shí)踐的痛點(diǎn)難點(diǎn),且在穩(wěn)定運(yùn)行的基礎(chǔ)之上,往往還需要對性能進(jìn)行評估優(yōu)化,所以其監(jiān)控系統(tǒng)的建設(shè)顯得尤為重要。
Prometheus 作為云原生時(shí)代最火的監(jiān)控軟件,很多大數(shù)據(jù)組件或原生或以第三方插件 / exporter 的形式對 Prometheus 做了支持。
我使用的大數(shù)據(jù)平臺是基于 kubernetes 運(yùn)行的,有部署靈活管理方便的優(yōu)點(diǎn),更容易與 Prometheus 進(jìn)行結(jié)合。
下面將對設(shè)計(jì)思路和技術(shù)實(shí)現(xiàn)進(jìn)行闡述探討。
設(shè)計(jì)思路
監(jiān)控系統(tǒng)的核心任務(wù)是將暴露出來的指標(biāo)數(shù)據(jù)進(jìn)行抓取,在此之上進(jìn)行分析、告警所以有以下幾個(gè)要明確的問題:
監(jiān)控對象是什么
監(jiān)控對象如何暴露指標(biāo)數(shù)據(jù)
監(jiān)控系統(tǒng)如何對指標(biāo)進(jìn)行抓取
如何實(shí)現(xiàn)告警規(guī)則動態(tài)配置、管理
監(jiān)控對象
以 pod(容器)形式運(yùn)行在 kubernetes 集群上的各個(gè)大數(shù)據(jù)組件。
指標(biāo)暴露方式
各組件根據(jù)對 Prometheus 的支持程度,可分為 3 種類型的指標(biāo)暴露方式:
直接暴露 Prometheus 指標(biāo)數(shù)據(jù) (直接,拉)
主動將指標(biāo)數(shù)據(jù)推送到 prometheus-pushGateway,由 pushGateway 暴露數(shù)據(jù)(間接,推)
自定義 exporter 將其他形式的指標(biāo)數(shù)據(jù)轉(zhuǎn)換為符合 Prometheus 標(biāo)準(zhǔn)的格式進(jìn)行暴露(exporter,直接,拉)
個(gè)別組件同時(shí)支持多種方式,如 flink 支持直接和間接方式,spark 支持直接方式而且也有第三方 exporter。大部分組件都有官方 / 第三方的 exporter,極少數(shù)需要自己開發(fā)。
一般情況下直接方式就可以了。
需要注意的是,像 flink(spark) on yarn 模式運(yùn)行的時(shí)候,flink 節(jié)點(diǎn)是跑在 yarn 容器里面的。這種情況下 Prometheus 很難對其直接進(jìn)行抓取,這種時(shí)候就只能用間接方式,主動將數(shù)據(jù)推送到 pushGateway。
另外那些短暫生命周期的組件也建議用主動 push 到 pushGateway。
指標(biāo)抓取方式
不管是 exporter 還是 pushGateway,到最后必然是由 Prometheus 主動對這些目標(biāo)進(jìn)行抓取。
?
Prometheus 主要通過 Pull 的方式來抓取目標(biāo)服務(wù)暴露出來的監(jiān)控接口,因此需要配置對應(yīng)的抓取任務(wù)來請求監(jiān)控?cái)?shù)據(jù)并寫入到 Prometheus 提供的存儲中,目前 Prometheus 服務(wù)提供了如下幾個(gè)任務(wù)的配置:
原生 Job 配置:提供 Prometheus 原生抓取 Job 的配置。
Pod Monitor:在 K8S 生態(tài)下,基于 Prometheus Operator 來抓取 Pod 上對應(yīng)的監(jiān)控?cái)?shù)據(jù)。
Service Monitor:在 K8S 生態(tài)下,基于 Prometheus Operator 來抓取 Service 對應(yīng) Endpoints 上的監(jiān)控?cái)?shù)據(jù)。
參考:https://cloud.tencent.com/document/product/1416/55995
既然都上了 kubernetes 環(huán)境了,一般當(dāng)然是推薦直接用 podMonitor。配置更簡潔易懂。podMonitorSelector 的過濾在 prometheus-prometheus.yaml 配置。
?
prometheus-prometheus.yaml 是核心配置文件,不宜頻繁修改 (會導(dǎo)致 Prometheus 重啟)。
主要配置項(xiàng)為:serviceMonitorSelector,podMonitorSelector,ruleSelector,alertmanagers。
其中 service 監(jiān)控選擇器和 pod 監(jiān)控選擇器默認(rèn)選擇所有,這里建議把 ruleSelector 也修改為選擇所有。
不過一個(gè) podMonitor 一般只對應(yīng)一種類型的 pod,在已有 pod 類型較多的情況下,還可以考慮一種更取巧的方法就是 Prometheus 的 kubernetes 服務(wù)發(fā)現(xiàn)功能。即 kubernetes_sd_config。這種屬于原生 Job 配置,建議使用 additional-scrape-config[1] 進(jìn)行配置。
kubernetes_sd_config[2] 賦予了 Prometheus 通過 kubernetes rest api 感知 kubernetes 資源的功能,利用該能力,可以使用原生 Job 配置自動發(fā)現(xiàn) pod,將其作為監(jiān)控目標(biāo)。再利用 Prometheus 的 Relabel 功能[3]可以改寫發(fā)現(xiàn)的標(biāo)簽,進(jìn)行前置處理、轉(zhuǎn)換。實(shí)現(xiàn) pod 篩選,修改抓取配置的效果。而自動發(fā)現(xiàn)的 pod 的標(biāo)簽的來源又可以是 pod 資源的 label/annotation 等。
最終實(shí)現(xiàn)的效果如下:
這是一個(gè) pushGateway 的 pod 的配置 , 則 Prometheus 會通過其 19091 端口訪問 / metrics 路徑獲取其指標(biāo)數(shù)據(jù)
annotations: prometheus.io/scrape:"true" prometheus.io/scheme:"http" prometheus.io/path:"/metrics" prometheus.io/port:"19091"
這部分的內(nèi)容主要參考:
https://godleon.github.io/blog/Prometheus/Prometheus-Relabel
https://yunlzheng.gitbook.io/prometheus-book/part-iii-prometheus-shi-zhan/readmd/service-discovery-with-kubernetes
podMonitor 是官方支持,簡潔易懂。kubernetes_sd_config+relabel 的方案較復(fù)雜,難度較高,但不用寫那么多的 podMonitor。自行抉擇就行,也可以一起用。
告警設(shè)計(jì)
告警流程
prometheus 的監(jiān)控告警基本流程是:
服務(wù)發(fā)生異常
觸發(fā) prometheus 服務(wù)器發(fā)出告警信息(alert)
alertmanager 收到告警信息
alertmanager 根據(jù)預(yù)配置的規(guī)則對告警信息進(jìn)行處理,實(shí)現(xiàn)業(yè)務(wù)邏輯,如分組、抑制、觸發(fā)短信郵箱等
當(dāng)然具體的流程沒那么簡單,有很多細(xì)節(jié)需要注意,特別是觸發(fā)告警時(shí)機(jī),是個(gè)重點(diǎn)。
這些屬于 Prometheus 的機(jī)制實(shí)現(xiàn),這里就不展開贅述,推薦閱讀以下文章:
Prometheus 一條告警的觸發(fā)流程、等待時(shí)間[4]
AlertManager 何時(shí)報(bào)警[5]
后邊會給出一個(gè)本人實(shí)際應(yīng)用測試的例子,可供參考,會直觀一些。
告警的動態(tài)配置
kube-prometheus 的告警規(guī)則分兩部分:
alertmanager: 即對告警信息的處理策略
配置參考:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/alert-manager-config
核心是 alertmanager-secret.yaml 配置文件,該文件以 secret 的形式被 Alertmanager 讀取。Alertmanager 會自動讀取 secret 中的配置進(jìn)行更新。
alertRule: 即具體的告警規(guī)則
配置參考:https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu/alert/prometheus-alert-rule
在 kubernetes 中是以 PrometheusRule 類型操作,所以管理起來跟 pod 一樣,直接使用 kubelet 增刪改即可。
接入自定義告警平臺
從個(gè)人實(shí)踐的角度來看,AlertManager 處理 web hook 之外的告警接收插件,如短信、郵箱等只適合測著玩。生産使用還是要通過 web hook 將告警信息發(fā)送到自己的告警平臺。可以根據(jù)業(yè)務(wù)需要對告警信息做高度定制化處理、記錄等。另外可以在告警信息中攜帶具體告警規(guī)則等信息指導(dǎo)告警平臺進(jìn)行處理。
這里要做個(gè)區(qū)分,AlertManager 是告警信息的前置處理,負(fù)責(zé)非業(yè)務(wù)性前置操作,如告警信息分組、平抑等。而自定義告警平臺則負(fù)責(zé)告警信息的業(yè)務(wù)處理,如記錄、去敏、發(fā)送到多終端等。
AlertManager 可能收到 1w 條告警信息,經(jīng)過處理最終只發(fā)了 1 條到自定義告警平臺。而自定義告警平臺可以將這 1 條告警信息記錄起來,修改內(nèi)容,同時(shí)使用郵箱、短信通知到多個(gè)負(fù)責(zé)人。
告警層級標(biāo)簽設(shè)計(jì)
監(jiān)控對象的粒度決定告警的層級,體現(xiàn)在配置上則是告警規(guī)則的分組。分組信息決定 alertManager 的處理方式。
alertManager 對告警信息的路由策略是樹狀的,所以可通過多個(gè)分組標(biāo)簽實(shí)現(xiàn)多層級路由處理。
具體設(shè)計(jì)應(yīng)結(jié)合業(yè)務(wù)需求,不在這里展開,感興趣的可以看我下面的實(shí)現(xiàn)舉例。
技術(shù)實(shí)現(xiàn)
技術(shù)實(shí)現(xiàn)主要分以下幾部分:
kubernetes 環(huán)境下 prometheus 的部署 (kube-prometheus)
kube-prometheus 的增強(qiáng)配置 : 即 kubernetes_sd_config+relabel 方案的實(shí)現(xiàn)
bigdata-exporter 的實(shí)現(xiàn)
告警設(shè)計(jì)實(shí)例
kubernetes 環(huán)境下 prometheus 的部署
1) kube-prometheus vs prometheus-operator
github 上 coreos 下有兩個(gè)項(xiàng)目:kube-prometheus 和 prometheus-operator,兩者都可以實(shí)現(xiàn) prometheus 的創(chuàng)建及管理。
需要注意的是,kube-prometheus 上的配置操作也是基于 prometheus-operator 的,并提供了大量的默認(rèn)配置,故這里使用的是 kube-prometheus 項(xiàng)目的配置。
另外使用前需注意 k8s 版本要求,找到對應(yīng)的 kube-prometheus 版本,弄清楚對應(yīng)的 prometheus-operator 版本。如:k8s1.14 版本最高可使用 kube-prometheus 0.3,對應(yīng)的 prometheus-operator 版本是 0.32,閱讀文檔時(shí)注意對應(yīng)版本。
2) kube-prometheus 使用前說明
kube-prometheus 使用 jsonnet 編寫配置模板文件,生成 k8s 配置清單。已提供默認(rèn)清單文件,在 manifests 文件夾下。如果需要修改默認(rèn)清單配置,需要在 go 環(huán)境下使用 jp 編譯清單。
下面都以默認(rèn)配置為例
3) 安裝教程
參考官方說明即可
1、git clone 項(xiàng)目 并切換到指定分支
2、kubectl create
清單文件中各配置已附帶 namespace 信息,故執(zhí)行時(shí)不需要指定 namespace,否則可能出錯(cuò)。
官方命令如下:
#CreatethenamespaceandCRDs,andthenwaitforthemtobeavailblebeforecreatingtheremainingresources $kubectlcreate-fmanifests/setup $untilkubectlgetservicemonitors--all-namespaces;dodate;sleep1;echo"";done $kubectlcreate-fmanifests/
kubernetes_sd_config+relabel 方案的實(shí)現(xiàn)
見:https://github.com/linshenkx/kube-prometheus-enhance
bigdata-exporter 的實(shí)現(xiàn)
hdfs、yarn、hbase、yarn 等組件都提供了 web 獲取 jmx 指標(biāo)的方式。
這里的思路是使用一個(gè) bigdata-exporter,去采集多個(gè)組件多個(gè)節(jié)點(diǎn)的指標(biāo)數(shù)據(jù),并進(jìn)行轉(zhuǎn)換,然后以 Prometheus 規(guī)定的格式對外公開。
指標(biāo)數(shù)據(jù)的轉(zhuǎn)換規(guī)則可以查看 github 上的一些項(xiàng)目,要注意版本,也可以像我一樣自己寫,更可靠。
bigdata-exporter 如何感知到采集目標(biāo)?
除了部署 ip 不同,不同組件不同角色的指標(biāo)對外端口、路徑、內(nèi)容(解析規(guī)則)也都不一樣。
這里可以參考上面 kubernetes_sd_config+relabel 的方案,做得優(yōu)雅一些:
授予 bigdata-exporter 調(diào)用 kubernetes app 的能力,
利用 label 和 annotations 進(jìn)行篩選和信息傳遞,確定捕捉目標(biāo)和途徑。
使用 role 代表解析內(nèi)容的類型,根據(jù) role 確定解析規(guī)則
labels: bigData.metrics.object:pod annotations: bigData.metrics/scrape:"true" bigData.metrics/scheme:"https" bigData.metrics/path:"/jmx" bigData.metrics/port:"29871" bigData.metrics/role:"hdfs-nn,common"
告警設(shè)計(jì)示例
這里以組和實(shí)例兩個(gè)維度為例,用 groupId 和 instanceId 表示。
1) alertManager 配置示例
以下是 alertmanager 的規(guī)則配置,有兩個(gè)接收者,其中 test.web.hook 指向自定義告警平臺。default 是空白接收者,不做處理。路由策略是根據(jù) groupId,instanceId 分組,對節(jié)點(diǎn)磁盤使用率、kafka 隊(duì)列堆積兩個(gè)組處理,instanceId 還沒有展開。
舊版本是用 secret 的 data 字段,需要將配置內(nèi)容轉(zhuǎn)成 base64 編碼格式。新版本直接用 stringData 字段。推薦用 stringData 字段配置。其實(shí)只要看一下 kube-prometheus 的 alertmanager-secret.yaml 文件就知道怎么回事了。
使用 data 字段的配置方法:寫好 config 文件,以 alertmanager.yaml 命名(不能使用其他名稱)。執(zhí)行以下命令 , 即可更新 secret。
$kubectl-nmonitoringcreatesecretgenericalertmanager-main--from-file=alertmanager.yaml--dry-run-oyaml|kubectl-n=monitoringapply-f-
global: resolve_timeout:5m receivers: -name:'default' -name:'test.web.hook' webhook_configs: -url:'http://alert-url' route: receiver:'default' group_wait:30s group_interval:5m repeat_interval:2h group_by:[groupId,instanceId] routes: -receiver:'test.web.hook' continue:true match: groupId:node-disk-usage -receiver:'test.web.hook' continue:true match: groupId:kafka-topic-highstore
2) alertRule 配置示例
組代表一個(gè)類型的所有目標(biāo):即所有節(jié)點(diǎn)。實(shí)例則代表具體的某個(gè)節(jié)點(diǎn)。
disk-usage.yaml 磁盤使用率告警配置示例如下:
?
注意:${path} 為監(jiān)控的磁盤路徑,${thresholdValue} 為使用率閾值,需自行替換。labels 中的 userIds 和 receivers 為傳遞給自定義告警平臺的參數(shù),以指導(dǎo)告警平臺如何操作。
在這個(gè)任務(wù)中,我們的目標(biāo)是組粒度的(所有節(jié)點(diǎn)),所以不需要設(shè)置 instanceId。
apiVersion:monitoring.coreos.com/v1 kind:PrometheusRule metadata: creationTimestamp:null labels: role:alert-rules name:node-disk-usage namespace:monitoring spec: groups: -name:node-disk-usage rules: -alert:node-disk-usage expr:100*(1-node_filesystem_avail_bytes{mountpoint="${path}"}/node_filesystem_size_bytes{mountpoint="${path}"})>${thresholdValue} for:1m labels: groupId:node-disk-usage userIds:super receivers:SMS annotations: title:"磁盤警告:節(jié)點(diǎn){{$labels.instance}}的${path}目錄使用率已達(dá)到{{$value}}%" content:"磁盤警告:節(jié)點(diǎn){{$labels.instance}}的${path}目錄使用率已達(dá)到{{$value}}%"
kafka-topic-highstore.yaml kafka 隊(duì)列消費(fèi)堆積告警配置示例如下:
我們只關(guān)心個(gè)別隊(duì)列的消費(fèi)情況,所以這里的粒度為 instance。
?
注意:${uniqueName} 為隊(duì)列名,${consumergroup} 為消費(fèi)組名稱,${thresholdValue} 為堆積數(shù)量閾值。
apiVersion:monitoring.coreos.com/v1 kind:PrometheusRule metadata: creationTimestamp:null labels: role:alert-rules name:kafka-topic-highstore-${uniqueName} namespace:monitoring spec: groups: -name:kafka-topic-highstore rules: -alert:kafka-topic-highstore-${uniqueName} expr:sum(kafka_consumergroup_lag{exporterType="kafka",consumergroup="${consumergroup}"})>${thresholdValue} for:1m labels: groupId:kafka-topic-highstore instanceId:${uniqueName} userIds:super receivers:SMS annotations: title:"KAFKA警告:消費(fèi)組${consumergroup}的堆積數(shù)量達(dá)到:{{$value}}" content:"KAFKA警告:消費(fèi)組${consumergroup}的堆積數(shù)量達(dá)到:{{$value}}"
其他
告警流程示例
這里以兩個(gè)節(jié)點(diǎn) node1 和 node2 配置了磁盤空間監(jiān)控為例,空間使用到達(dá)閾值則觸發(fā)告警。(測試過程中可通過生成、刪除指定體積的文件來控制空間占用)
其中配置項(xiàng)如下:
告警規(guī)則 : node-disk-usage
for 為 1m
告警中心 : alertManager
group_wait: 30s
group_interval: 5m
repeat_interval: 10m
收到的告警短信內(nèi)容及時(shí)間線如下:
1014 收到第一次警報(bào):node1 于 1044 進(jìn)入異常
1014 收到第二次警報(bào):node1 于 1044 進(jìn)入異常node2 于 1044 進(jìn)入異常
1029 收到第三次警報(bào):node1 于 1044 進(jìn)入異常node2 于 1044 進(jìn)入異常
1044 收到第四次警報(bào):node1 于 1044 進(jìn)入異常node2 于 1044 進(jìn)入異常
1044 收到第五次警報(bào):恢復(fù)告警node1 于 1044 進(jìn)入異常,并于 1044 恢復(fù)node2 于 1044 進(jìn)入異常,并于 1014 恢復(fù)
總共收到 5 次短信,第 1 次是 node1 異常,第 2 到 4 次是 node1 和 node2 都異常,因?yàn)閷儆谕瑐€(gè)分組 group,所以合并發(fā)送。第 5 次是已經(jīng)恢復(fù)正常了。
根據(jù)短信內(nèi)容和時(shí)間,整理出告警邏輯時(shí)間線如下:
node1 等待 for 1 分鐘 后警報(bào)進(jìn)入 group
node1 記錄異常時(shí)間為 1044,實(shí)際異常狀態(tài)至少在 1044 的一分鐘前
group 等待 group_wait 30s 后發(fā)送第一次告警
firing:node1
node2 等待 for 1 分鐘 后警報(bào)進(jìn)入 group
node2 記錄異常時(shí)間為 1044,實(shí)際異常狀態(tài)至少在 1044 的一分鐘前,此時(shí) group 中有兩個(gè)異常目標(biāo) node1 和 node2。
group 等待 group_interval 5m 后發(fā)送第二次告警
firing:node1,node2
注意:因?yàn)?group 發(fā)生了變化,所以這里用的是 group_interval。
group 等待 repeat_interval 10m 后發(fā)送第三次告警
firing:node1,node2
注意:因?yàn)?group 沒有變化,屬于重復(fù)告警,用的是 repeat_interval。
group 等待 repeat_interval 10m 后發(fā)送第四次告警
firing:node1,node2
同上一次。
第四次告警后的 前 5 分鐘:node2 恢復(fù)正常
第四次告警后的 后 5 分鐘:node1 恢復(fù)正常
group 等待 repeat_interval 10m 后發(fā)送第五次告警
resolved:node1,node2
注意,這里 node1,node2 都恢復(fù)正常用的也是 repeat_interval。
綜上:
for 是告警規(guī)則個(gè)體的監(jiān)控配置,用來衡量服務(wù)多久檢測不通過才算異常。
group_wait:初次發(fā)送告警的等待時(shí)間
用于 group 創(chuàng)建后的等待,這個(gè)值通常設(shè)置較小,在幾分鐘以內(nèi)。
group_interval:同一個(gè)組其他新發(fā)生的告警發(fā)送時(shí)間間隔
是 group 內(nèi)容發(fā)生變化后的告警間隔。
repeat_interval:重復(fù)發(fā)送同一個(gè)告警的時(shí)間間隔
group 內(nèi)容沒有變化且上一次發(fā)生成功時(shí)用的發(fā)生間隔。
需要注意,恢復(fù)正常不屬于 group 變化,用的是 repeat_interval。這有點(diǎn)反直覺,且個(gè)人認(rèn)為不是很合理,不知道是不是測試有問題,也沒有找到比較好的資料說明。希望知道的可以指教一下。
exporter 的位置
exporter 可以以 sidecar 的形式和原容器放在同一個(gè) pod 內(nèi)(1 對 1),也可以以獨(dú)立部署的形式存在(1 對 1/1 對多)。
這個(gè)視具體情況而定,技術(shù)上沒什么不同,sidecar 可以綁定生命周期,視為對原有組件的補(bǔ)充。獨(dú)立部署則耦合度更低,更靈活。像單節(jié)點(diǎn)的 mysql,用 sidecar 則只需要 1 個(gè) pod,不會太復(fù)雜。
而如果像多節(jié)點(diǎn)的 kafka 集群,用獨(dú)立部署則只需要一個(gè) exporter 就可以實(shí)現(xiàn)對多個(gè)節(jié)點(diǎn)的采集監(jiān)控。
這里出于減小耦合、節(jié)省資源的目的,我主要使用的是獨(dú)立部署形式。
使用 promtool 檢查指標(biāo)格式是否正確
promtool 使用方法:
#進(jìn)入pod $kubectl-n=monitoringexec-itprometheus-k8s-0sh #查看幫助 $promtool-h #檢查指標(biāo)格式 $curl-shttp://ip:9999/metrics|promtoolcheckmetrics
比方說 指標(biāo) name、labelname 不能使用小數(shù)點(diǎn)
使用 port-forward 臨時(shí)提供 Prometheus 外部訪問
#prometheus $nohupkubectlport-forward--address0.0.0.0service/prometheus-k8s19090:9090-n=monitoring& #grafana $nohupkubectlport-forward--address0.0.0.0service/grafana13000:3000-n=monitoring& #alertmanager $nohupkubectlport-forward--address0.0.0.0service/alertmanager-main9093:9093-n=monitoring&
用 jobs -l 可以查看
kube-prometheus 對 arm 的支持
目標(biāo)是找到 kube-prometheus 用到的鏡像的 arm 版本。
可以使用 https://quay.io/[6] 搜索。
以下是用到的鏡像和各自對 arm 的支持情況。注意對照自己使用的版本,很多鏡像高版本都支持 arm 了。
-
監(jiān)控系統(tǒng)
+關(guān)注
關(guān)注
21文章
3904瀏覽量
174419 -
軟件
+關(guān)注
關(guān)注
69文章
4921瀏覽量
87398 -
大數(shù)據(jù)
+關(guān)注
關(guān)注
64文章
8882瀏覽量
137397
原文標(biāo)題:kube-prometheus 監(jiān)控系統(tǒng)使用與總結(jié)
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論