RAN CN 테스트베드 구축/OAI+Free5GC (RAN-Core)

Slice의 metric 정보를 수집하는 monitoring xApp 구현하기

gksyb4235 2026. 5. 3. 17:04

E2E Slice 정보가 필요한 이유


5G 네트워크에서 Network Slice는 단순한 논리적 분리가 아니라, 서로 다른 QoS 요구사항을 갖는 서비스 단위다.
eMBB, URLLC, mMTC와 같이 요구사항이 다른 트래픽을 하나의 RAN/CN 인프라 위에서 동시에 처리하기 위해서는,
각 Slice의 상태를 정확하게 관찰하고 제어할 수 있어야 한다.

 

그러나 일반적인 Open RAN + 5GC 구성에서 Slice 관련 정보는 다음처럼 여러 계층에 분산되어 있다:

  • RAN: PRB 사용량, UE throughput, latency (KPM 기준)
  • CN: UPF bandwidth, queue 상태 (tc 기반)
  • UE: RTT, 실제 체감 성능

문제는 이 데이터들이 통합되지 않는다는 점이다.

 

예를 들어:

  • RAN에서는 PRB를 많이 쓰고 있지만
  • CN에서는 bandwidth가 제한되어 있고
  • 실제 UE에서는 latency가 증가하는 상황

이걸 한 번에 “Slice 단위로” 볼 수 있는 지점이 없다.

 

 

AI Agent 기반 Slice 제어를 하려면 필요한 데이터


AI Agent가 Slice별 정책을 결정하려면 단순 metric이 아니라 “의미 있는 상태 데이터”가 필요하다.

예를 들어:

  • throughput (DL/UL)
  • latency (RLC delay or RTT)
  • PRB usage vs allocated ratio
  • UPF bandwidth 상태
  • packet loss

이 데이터는 다음 조건을 만족해야 한다:

  1. Slice 단위로 분리되어야 한다 (S-NSSAI 기준으로 분리)
  2. 시간 축을 포함해야 한다 (trend 분석이 가능하도록)
  3. RAN + CN + UE를 모두 포함해야 한다

이걸 만족하지 못하면 AI는 다음과 같은 한계에 부딪힐 수 있다.

  • PRB를 늘렸는데 효과가 없는 이유를 모름
  • CN 병목을 RAN 문제로 오판
  • Slice 간 resource imbalance를 감지 못함

 

이 문제를 해결하기 위해 전체 구조를 다음과 같이 잡았다:

[ UE ]
   ↓ (traffic, latency)
[ gNB (RAN) ]
   ↓ (E2 interface)
[ Near-RT RIC ]
   ↓
[ xApp (KPM + RC) ]  ← 핵심
   ↓
[ SQLite DB ]        ← Slice 상태 저장
   ↓
[ AI Agent / MCP ]
   ↓
[ RAN (PRB control) + CN (tc control) ]
 
  • FlexRIC 기반 Near-RT RIC에서 xApp이 KPM을 수집한다.
  • 그리고 이 데이터를 로컬 DB(SQLite)에 저장한다. (SQLite인 이유는 추후 Core의 Promtheus와 연동하기 위함)

이 xApp은 RRU.PrbTotDl/UL, DRB.UEThpDl/UL, DRB.RlcSduDelayDl, PDCP volume 등을 수집할 수 있다.

이때 단순 수집이 아닌 S-NSSAI 기준으로 Slice filtering을 수행해야 한다.

이를 위해 실제 구현에서는 KPM subscription 시 S-NSSAI 조건을 넣고 callback에서 slice별로 분리 저장한다.

 

이후 제어를 수행하는 xApp은 다음을 수정할 수 있도록 할 것.

  • RC STYLE 2 / ACTION 6
  • Slice-level PRB quota 제어
  • min_prb / max_prb / dedicated_prb 동적 변경

 

 

DB 설계 구조


xApp은 다음 역할을 수행한다: KPM metric 수집 / 파싱 및 정규화 / Slice 단위로 변환 / SQLite 저장

DB schema 예시는 다음과 같다.

  • timestamp
  • slice_id
  • throughput_bps
  • latency_ms
  • prb_usage_percent
  • prb_ratio_allocated
  • upf_bandwidth_mbps
  • packet_loss_percent

추후, 이 DB는 단순 로그를 넘어서 AI Agent가 직접 조회하는 상태 저장소 역할을 수행하게 된다.

 

 

 

 

Slice 메트릭 수집 xApp 설계 및 구현


KPM 기반 Slice 필터링 (S-NSSAI 기준)


KPM은 기본적으로 UE 단위 metric을 제공하며, “Slice metric”이라는 개념은 직접 제공되지 않는다.

따라서 핵심은 UE metric을 Slice 기준으로 재구성하는 과정이 필요하다.

 

여기서 사용하는 기준이 S-NSSAI인데, 구조는 다음과 같다:

  • 각 Slice는 (SST, SD)로 정의됨
  • KPM subscription 시 matching condition으로 S-NSSAI를 지정
  • 해당 Slice에 속한 UE만 보고됨
  • Slice1 → SST=1, SD=000001
  • Slice2 → SST=2, SD=000002
  • Slice3 → SST=3, SD=000003

이렇게 subscription을 3개로 나누면, 각 callback이 이미 Slice 단위로 분리된 데이터를 받게 된다.

 

 

 

FlexRIC xApp 구조 설명


xApp 구조는 크게 Monitoring path (KPM)와 Control path (RC) 두 가지 흐름으로 나뉜다:

 

1) Monitoring path

  • KPM subscription 생성
  • periodic report 수신
  • callback 실행

2) Control path

  • UE 식별
  • Slice profile 매핑
  • RC CONTROL 메시지 생성
  • PRB 정책 적용

 

이때 Monitoring Path는 다음과 같은 순서로 동작하게 된다.

1) 초기화 : SQLite DB 초기화 및 RIC 연결

2) KPM 수집 : Slice별 subscription 생성 후 callback에서 metric 추출, DB 저장

 

 

KPM subscription (slice별)


이 과정에서 각 Slice마다 별도의 subscription을 생성한다.

구조:

  • report style 4 사용
  • matching condition: S-NSSAI
  • report period: 100ms

핵심 포인트는:

→ subscription 자체가 Slice-aware하게 설계됨

즉, callback은 이미:

[KPM][slice-sst1]
[KPM][slice-sst2]
[KPM][slice-sst3]
 

형태로 구분되어 들어온다.

 

 

 

SQLite 기반 Metric 저장 구조


SQLite 기반 메트릭 저장 구조는 이 xApp 설계에서 핵심적인 역할을 한다.

단순히 KPM 메트릭을 실시간으로 관찰하는 것이 아니라,

모든 Slice 상태를 DB에 축적하여 이후 분석과 제어에 활용할 수 있도록 구성했다.

 

SQLite를 선택한 이유는 xApp과 동일 VM에서 로컬로 실행되기 때문에 지연이 거의 없고, 별도의 인프라 없이도 안정적인 저장이 가능하기 때문이다. 또한 WAL 모드를 적용해 read/write 동시성을 확보했다.

 

slice_metrics 테이블은 timestamp와 slice_id를 기준으로 하는 time-series 구조로 설계되었으며, throughput, latency, PRB 사용률 및 할당 비율, UPF bandwidth, packet loss 등의 필드를 포함한다.

 

특히 KPM raw 데이터를, throughput은 kbps→bps, latency는 μs→ms로 변환하며, PRB usage와 allocation ratio를 각각 계산하여 실제 사용량과 할당량을 비교할 수 있도록 하여 이를 통해 Slice별 과소/과다 할당 상태를 판단할 수 있다.

 

 

 

 

실행 흐름과 실제 동작 (E2E)


전체 실행 흐름


전체 시스템은 다음 순서로 올라간다:

    1. Near-RT RIC 실행
    2. gNB 실행
    3. UE attach
    4. 트래픽 생성 (iperf)
    5. xApp 실행
    6. KPM 수집 → DB 저장 → RC 제어

 

 

 

1) Near-RT RIC 실행


먼저 RIC을 실행하면 다음과 같은 로그가 나온다:

cd ~/openairinterface5g/openair2/E2AP
./flexric/build/examples/ric/nearRT-RIC

 

 

핵심 로그:

[NEAR-RIC]: Loading SM ID = 3 with def = ORAN-E2SM-RC
[NEAR-RIC]: Loading SM ID = 2 with def = ORAN-E2SM-KPM
 

이 의미는 KPM → monitoring 가능 + RC → control 가능이라는 뜻이다.

이 시점에서 xApp이 사용할 수 있는 인터페이스가 준비된다

 

 

 

2) gNB / UE attach


다음으로 gNB와 UE를 실행한다.

cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --rfsim
cd ~/openairinterface5g/tools/scripts
sudo ./multi-ue.sh -c1
sudo ./multi-ue.sh -o1

sudo ip netns exec ue1 /home/user/openairinterface5g/cmake_targets/ran_build/build/nr-uesoftmodem \
  -O /home/user/openairinterface5g/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue1.conf \
  -r 106 \
  --numerology 1 \
  --band 78 \
  -C 3619200000 \
  --rfsim \
  --uicc0.imsi 208930000000001 \
  --rfsimulator.serveraddr 10.201.1.100 \
  --telnetsrv \
  --telnetsrv.listenport 9095

 

 

gNB 실행 후 UE가 attach되면 RRC 연결 완료 / NAS registration 완료 / data plane 활성화 로그가 뜬다.

 

 

 

3) xApp 실행 및 초기화


이제 xApp을 동작시킨다.

cd ~/openairinterface5g/openair2/E2AP/flexric
XAPP_DURATION=60 ./build/examples/xApp/c/kpm_rc/xapp_kpm_rc_prb

 

xApp 실행 시 가장 먼저 하는 작업은 다음과 같다.

 

(1) PRB 정책 로드

load_prb_from_env(&g_profile_sst1, "SLICE1");
 

(2) SQLite 초기화

sqlite_init_db();
[SQLITE] Database opened: /tmp/slice_metrics.db
 

이 시점에서 모든 metric이 저장될 준비가 완료된 상태가 된다.

 

 

 

4) KPM Subscription (Slice별)


xApp은 각 Slice에 대해 subscription을 생성한다:

report_sm_xapp_api(..., sm_cb_kpm_sst1);
report_sm_xapp_api(..., sm_cb_kpm_sst2);
report_sm_xapp_api(..., sm_cb_kpm_sst3);
 

 

[RIC-XAPP][KPM] subscribe slice1 snssai=(1,...)
[RIC-XAPP][KPM] subscribe slice2 snssai=(2,...)
[RIC-XAPP][KPM] subscribe slice3 snssai=(3,...)
 

이 시점부터 Slice별 metric stream이 분리되어 들어온다

 

 

 

 

5) KPM 수집 → 파싱 → DB 저장


실제 핵심 동작은 callback에서 발생한다:

handle_kpm_with_profile(...)
 

(1) 로그 출력

[KPM][slice-sst1] latency = 12345 [us]
RRU.PrbTotDl = 80
DRB.UEThpDl = 12000 kbps
 

(2) metric 추출

extract_kpm_metrics(...)
 

(3) DB 저장

sqlite_insert_metrics(...)
 

결과적으로 DB에는 다음과 같은 row가 쌓인다:

timestamp slice_id throughput_bps latency_ms prb_usage_percentage
... slice_1 12000000 12.3 75.4

 

이 흐름이 100ms 주기로 반복된다.

 

 

6) Slice 트래픽 생성 및 DB 확인


xApp이 정상적으로 실행된 상태에서 실제 Slice metric이 DB에 저장되는지 확인하기 위해 UE에서 트래픽을 발생시킨다.

먼저 Free5GC VM의 UPF 컨테이너 안에서 iperf3 서버를 실행한다.

docker exec -it upf iperf3 -s -p 5201
 

 

이후 xApp이 KPM subscription과 SQLite 초기화를 마친 뒤, UE namespace에서 UPF 방향으로 트래픽을 흘려준다. 예를 들어 Slice 1에 해당하는 ue1에서 40초 동안 5 Mbps 트래픽을 발생시키면 다음과 같다.

sudo ip netns exec ue1 iperf3 -c 10.100.200.11 -p 5201 -t 40 -b 5M
 

 

이때 xApp은 KPM callback을 통해 DRB.UEThpDl, RRU.PrbTotDl, DRB.RlcSduDelayDl 등의 값을 주기적으로 수집하고, 이를 slice_metrics 테이블에 저장한다. xApp이 종료된 뒤에는 SQLite DB를 조회하여 실제 metric이 저장되었는지 확인할 수 있다.

 

 

sqlite3 -header -column /tmp/slice_metrics.db \
"SELECT timestamp, throughput_bps, prb_usage_percent, prb_ratio_allocated, latency_ms \
 FROM slice_metrics \
 WHERE slice_id='slice_1' AND throughput_bps > 0 \
 ORDER BY id;"
 

 

이 조회 결과를 통해 Slice 1에 대해 트래픽이 흐른 구간의 throughput, PRB 사용률, PRB 할당 비율, latency가 시간 순서대로 저장되었는지 확인할 수 있다. 즉, iperf3로 실제 트래픽을 만들고, xApp이 이를 KPM metric으로 수집한 뒤, SQLite에 Slice 단위 time-series 데이터로 남기는 E2E 모니터링 흐름이 검증된다.

 

위 로그가 3초간 수집된 DB의 양이다.

추후 이 부분을 적절하게 Aggregate해서 AI Agent로 전달하는 부분을 구현해야 할 듯.

추가로 prb_usage_percentage도 100이 넘는 값이 찍히는 경우가 있다. (정확하게 어떻게 수집되는지 확인 필요)

 

이때 DB의 latency_ms는 ping이 아니라 DRB.RlcSduDelayDl에서 온다.

// xapp_kpm_rc_prb.c의 sqlite_insert_metrics()
sqlite3_bind_double(stmt, 4, metrics->rlc_sdu_delay_dl / 1000.0);
// DRB.RlcSduDelayDl (μs) → ms 변환

 

즉 xApp의 latency_ms는 RLC delay로 채워지고 있고, 이건 DL 트래픽이 없으면 0이 나오는 이유이기도 하다.

이 부분은 추후 ping 기반 ICMP RTT를 통해 UE→UPF 간 정확한 latency를 구현해야 할 듯.