在我們搜尋 5G 核心網路的時候,時常會看到以下關鍵字:
其實這些技術不僅是應用在核心網路,只要是任何大型的網路服務系統,那麼它們的 system design 也都跟這些技術脫不了關係。
當我們將 Network Function 虛擬化跳脫專用硬體的限制後,我們可以讓核心網路運作在筆電、桌機、工作站,或是電腦中的 Container、公有雲、私有雲等等。
讓核心網路運作在公有雲或是私有雲平台其實也就實踐了 Cloud Native 的設計概念:
雲端原生架構和技術是設計、建構和操作工作負載的方法,這些工作負載是在雲端中建置,並充分利用雲端運算模型。
對於一個 Cloud Native 應用來說,如何搜集運作在不同節點上的系統資訊、錯誤偵測與告警系統的建置就會是一個挑戰。幸運的是,CNCF(Cloud Native Computing Foundation)啟動了孵化器的計畫,在 CNCF 孵化器下也產出了很多知名的專案,如:Argo、Helm、Prometheus... 等等,本章的重點就會圍繞在 Prometheus 進行介紹。
只要點開 prometheus 的官網就可以看到一句大大的標語:
Power your metrics and alerting with the leading
open-source monitoring solution.
這段標語清楚的告訴我們 prometheus 是一個開源的 monitoring solution。
此外,prometheus 不止作為一個監控系統,它還是一個時序性的資料庫:
Exporter 用於匯出應用程式採集的 metrics,我們會將這些 exporter 新增到 prometheus 的設定檔案,讓 prometheus 定期的向這些 exporter 取得資料。
常見的 exporter 有:
此外,我們也可以通過 prometheus 提供的函式庫在應用程式中客製化自己的 exporter,以 Network Function 為例,我們可以在它啟動時使用一個 goroutine 進行資料的採集(Collector),並且利用函式庫實作(Exporter)。這樣一來,我們就能夠讓 prometheus 採集到來自 Network Function 提供的訊息。
而 PromQL 則是 prometheus 提供給使用者查詢資料庫內容的語法,我們可以利用 http request 呼叫 prometheus 的 web api 得到我們想要查詢的資料。
有了這些資料以後,我們可以自己撰寫後台應用,或是使用成熟的開源專案將 prometheus 採集到的資訊圖表化。
完整程式碼可以參考:
本節將示範如何在現有的 free5GC 專案上將 Prometheus 整合進去,為了方便環境的模擬,這是的實驗一樣使用了 docker compose 進行。
在這次的實驗中,我希望在 AMF 內部計算 init registration 的成功率,為了達到目的,我們需要考慮以下事項:
1. 實作 monitor package:
+++ b/internal/monitor/monitor.go
@@ -0,0 +1,23 @@
+package monitor
+
+import "github.com/free5gc/amf/internal/context"
+
+func GetAmountOfSuccessRegistration() float64 {
+ return context.AMF_Self().RegisSuccess
+}
+
+func GetAmountOfReceivedRegistration() float64 {
+ return context.AMF_Self().RegisTry
+}
+
+func IncAmountOfSuccessRegistration() {
+ context.AMF_Self().Lock.Lock()
+ context.AMF_Self().RegisSuccess++
+ context.AMF_Self().Lock.Unlock()
+}
+
+func IncAmountOfReceivedRegistration() {
+ context.AMF_Self().Lock.Lock()
+ context.AMF_Self().RegisTry++
+ context.AMF_Self().Lock.Unlock()
+}
+++ b/internal/monitor/exporter.go
@@ -0,0 +1,38 @@
+package monitor
+
+import (
+ "net/http"
+
+ "github.com/free5gc/amf/internal/logger"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+type regisCollector struct {
+ regisDetail *prometheus.Desc
+}
+
+func NewRegistrationCollector() *regisCollector {
+ return ®isCollector{
+ regisDetail: prometheus.NewDesc("registration_metric",
+ "Amount of successful registration & received regis req",
+ nil, nil),
+ }
+}
+
+func (collector *regisCollector) Describe(ch chan<- *prometheus.Desc) {
+ ch <- collector.regisDetail
+}
+
+func (collector *regisCollector) Collect(ch chan<- prometheus.Metric) {
+ regisSuccess := GetAmountOfSuccessRegistration()
+ regisTry := GetAmountOfReceivedRegistration()
+ ch <- prometheus.MustNewConstMetric(collector.regisDetail, prometheus.CounterValue, regisSuccess/regisTry)
+}
+
+func (collector *regisCollector) Start() {
+ prometheus.MustRegister(collector)
+ http.Handle("/metrics", promhttp.Handler())
+ logger.HttpLog.Infoln("Monitor service is listening on 3000 port")
+ logger.HttpLog.Errorln(http.ListenAndServe(":3000", nil))
+}
這邊簡單講解一下程式碼:
prometheus.NewDesc()
用於新增一個 metric descriptor,如果這個 metric 有 label,也需要在新增的時候帶上。prometheus.MustRegister(collector)
用於註冊 metric。Collect()
用於搜集 metric,所以我們可以看到這邊將 prometheus.MustNewConstMetric()
建立的資訊傳進 ch 當中。http.Handle("/metrics", promhttp.Handler())
用來註冊 prometheu 的處理函式。2. 修改 init registration handler:
+++ b/internal/gmm/handler.go
@@ -17,6 +17,7 @@ import (
"github.com/free5gc/amf/internal/context"
gmm_message "github.com/free5gc/amf/internal/gmm/message"
+ "github.com/free5gc/amf/internal/monitor"
ngap_message "github.com/free5gc/amf/internal/ngap/message"
"github.com/free5gc/amf/internal/sbi/consumer"
"github.com/free5gc/amf/internal/sbi/producer/callback"
@@ -571,6 +572,7 @@ func IdentityVerification(ue *context.AmfUe) bool {
func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) error {
ue.GmmLog.Infoln("Handle InitialRegistration")
+ monitor.IncAmountOfReceivedRegistration()
amfSelf := context.AMF_Self()
@@ -715,6 +717,7 @@ func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) erro
if anType == models.AccessType__3_GPP_ACCESS {
gmm_message.SendRegistrationAccept(ue, anType, nil, nil, nil, nil, nil)
+ monitor.IncAmountOfSuccessRegistration()
} else {
// TS 23.502 4.12.2.2 10a ~ 13: if non-3gpp, AMF should send initial context setup request to N3IWF first,
// and send registration accept after receiving initial context setup response
3. 啟動 monitor service:
+++ b/cmd/main.go
@@ -10,6 +10,7 @@ import (
"github.com/urfave/cli"
"github.com/free5gc/amf/internal/logger"
+ "github.com/free5gc/amf/internal/monitor"
"github.com/free5gc/amf/internal/util"
"github.com/free5gc/amf/pkg/service"
"github.com/free5gc/util/version"
@@ -60,6 +61,8 @@ func action(c *cli.Context) error {
logger.AppLog.Infoln(c.App.Name)
logger.AppLog.Infoln("AMF version: ", version.GetVersion())
+ regisMetric := monitor.NewRegistrationCollector()
+ go regisMetric.Start()
AMF.Start()
1. 於專案內新增 prometheus.yaml
:
+global:
+ scrape_interval: 5s
+ external_labels:
+ monitor: 'demo-monitor'
+scrape_configs:
+ - job_name: 'prometheus'
+ static_configs:
+ - targets: ['localhost:9090']
+ - job_name: 'api_monitor'
+ scrape_interval: 5s
+ static_configs:
+ - targets: ['prom.free5gc.org']
2. 在 docker-compose.yaml
內新增 prometheus 服務:
+ prometheus:
+ image: prom/prometheus:v2.1.0
+ volumes:
+ - ./prometheus.yaml:/etc/prometheus/prometheus.yaml
+ command:
+ - '--config.file=/etc/prometheus/prometheus.yaml'
+ ports:
+ - '9090:9090'
+ networks:
+ privnet:
+ aliases:
+ - prom.free5gc.org
讓 amf 的 3000 port 對應到本機的 3000 port:
command: ./amf -c ./config/amfcfg.yaml
expose:
- "8000"
+ ports:
+ - '3000:3000'
volumes:
- ./config/amfcfg.yaml:/free5gc/config/amfcfg.yaml
3. 修改 base 的 docker image:
RUN cd $GOPATH/src \
&& git clone --recursive -b v3.2.1 -j `nproc` https://github.com/free5gc/free5gc.git
+# prometheus branch of AMF
+RUN cd $GOPATH/src/free5gc/NFs/amf \
+ && git remote add ian https://github.com/ianchen0119/amf.git \
+ && git fetch ian \
+ && git checkout ian/feat/prom
這個步驟的目的是將 amf 的 branch 切換到剛剛我們修改過的版本上。
首先必須確定我們已經將 subscriber data 新增到核網端:
接著,一樣打開 ueransim container 的終端機進行註冊:
$ docker exec -it <container_id> bash
$ ./nr-ue -c config/uecfg.yaml
當修改過的 free5gc-compose 成功啟動後,我們可以使用瀏覽器訪問 Prometheus 提供的頁面:
http://127.0.0.1:9090/graph
並且找到我們註冊的 registration_metric:
訪問 http://127.0.0.1:3000/metrics 同樣可以看到 AMF collector 回傳的 metrics 資訊。
本篇文章帶大家在 AMF 上實作一個簡易的 prometheus collector,並且利用先前介紹過的 free5gc-compose 專案將整個核心網路與 prometheus 運作在 container 上,如果筆者有興趣還可以繼續深入實作更多資訊的 monitor 並且結合 Grafana 實作一個核心網路的監控系統!