DevOps/Study

AI Datacenter Network Study 4주차 - Nokia SR Linux 텔레메트리 랩(srl-telemetry-lab) Deep Dive

juyeon22 2026. 7. 12. 16:48

Nokia SR Linux 텔레메트리 랩(srl-telemetry-lab) Deep Dive — SNMP 폴링 vs gNMI 스트리밍 감지 지연 실측

개요

이 게시물은 Nokia SR Linux 기반의 srl-telemetry-lab을 WSL2 환경에서 배포하고, SNMP 폴링과 gNMI 스트리밍 텔레메트리의 감지 지연 차이를 실측한 내용을 정리한 게시글입니다.

쿠버네티스 클러스터는 node_exporter를 15초마다 스크레이프하면서, 그 클러스터가 올라가 있는 네트워크 스위치는 여전히 5분 주기 SNMP로 모니터링하는 환경이 많습니다. 이 글에서는 그 간극이 실제로 무엇을 놓치는지 컨테이너 랩에서 직접 측정합니다. Windows PC의 WSL2만으로 Nokia SR Linux 5대짜리 Clos 패브릭과 gnmic-Prometheus-Grafana 스트리밍 텔레메트리 스택을 containerlab으로 배포하고, 같은 장애 이벤트를 SNMP 폴링, gNMI sample, gNMI on-change 세 방식으로 동시에 관측하여 감지 지연을 비교합니다.

결론을 미리 요약하면, 20초 동안 지속된 링크 장애를 SNMP 폴링은 장애 전후 두 번 모두 "정상"으로 읽었습니다. 폴링 주기보다 짧은 이벤트는 늦게 감지되는 것이 아니라 기록 자체가 남지 않습니다. 이 글은 그 결과를 재현하는 과정입니다.

이 글에서 다루는 것

실습을 마치면 다음을 할 수 있게 됩니다.

  • WSL2에 containerlab 기반 네트워크 랩을 배포하고 운영
  • gnmic CLI로 gNMI Capabilities / Get / Set / Subscribe RPC를 직접 호출
  • gnmic 컬렉터 → Prometheus → Grafana 파이프라인의 각 단계 설정을 읽고 수정
  • SNMP 폴링과 gNMI 스트리밍의 감지 지연 차이를 수치로 확인
  • ECMP 패브릭에서 링크 장애를 텔레메트리로 국소화하고 로그와 상관 분석

준비물과 소요 시간

  • Windows 11 PC (RAM 16GB 권장, 디스크 여유 10GB 이상)
  • 컨테이너와 Prometheus에 대한 기본 이해
  • 실습 소요 시간: 이미지 다운로드 포함 1~2시간

네트워크 장비 CLI 경험은 없어도 따라올 수 있도록 작성했습니다.

다루지 않는 것

  • EVPN/VXLAN의 동작 원리 (랩이 사용하지만 이 글의 주제가 아닙니다)
  • 프로덕션 규모의 컬렉터 설계
  • SNMP trap 기반 이벤트 알림 체계

글의 구성

이 글은 세 단계로 진행합니다.

  1. SNMP와 gNMI가 모델 수준에서 어떻게 다른지 정리합니다 (2~3장)
  2. WSL2에 랩을 배포하고 텔레메트리 파이프라인을 해부합니다 (4~6장)
  3. 실험 세 개로 두 방식의 차이를 수치로 확인합니다 (7~9장)

1. 배경: 폴링 기반 모니터링은 무엇을 놓치는가

1.1 시나리오: 5분 폴링 사이에 사라진 장애

이런 상황을 가정해 봅시다.

  • 05:00:00 — 모니터링 시스템이 스위치를 폴링했고, 모든 지표가 정상이었습니다.
  • 05:02:10 — 백업 트래픽이 몰리면서 특정 업링크에 30초 동안 심한 혼잡이 발생했고, 그 링크를 지나던 서비스 요청들이 타임아웃으로 실패했습니다.
  • 05:02:40 — 혼잡은 저절로 해소되었습니다.
  • 05:05:00 — 다음 폴링이 실행됐고, 역시 모든 지표가 정상이었습니다.

모니터링 그래프는 평탄합니다. 남은 것은 "그 시간대에 간헐적으로 느렸다"는 고객 문의뿐입니다.

이것이 폴링 기반 모니터링의 구조적 한계입니다. 폴링은 "질의한 순간의 상태"만 기록하므로, 두 질의 사이에 발생했다 사라진 이벤트는 존재 자체가 기록되지 않습니다.

1.2 서버 모니터링과 무엇이 다른가

서버 인프라를 다뤄 온 입장에서 낯선 상황은 아닙니다. Prometheus도 pull 모델이고, 15초 스크레이프 사이의 스파이크는 똑같이 뭉개집니다. 그래서 이벤트성 정보는 메트릭이 아니라 로그와 알림으로 보완합니다.

문제는 네트워크 장비 쪽 폴링 주기가 15초가 아니라 1분, 5분 단위라는 점입니다. 장비 수가 수백 대로 늘어나면 SNMP 폴링 자체가 장비 CPU와 관리망에 부하를 주기 때문에, 폴링 주기를 공격적으로 줄이기도 어렵습니다.

1.3 관측 요구는 밀리초 단위로 내려가고 있다

한편 관측 대상의 시간 해상도 요구는 반대 방향으로 내려가고 있습니다. AI 학습 클러스터의 RoCE 패브릭에서는 수십~수백 밀리초 수준의 큐 적체나 PFC pause storm이 GPU 집단 통신을 지연시켜 학습 효율을 직접 떨어뜨립니다.

NVIDIA가 Spectrum-X 텔레메트리 사례에서 보여준 것처럼, 이런 환경에서는 "평균"이 아니라 "순간 피크"를 봐야 하고, 문제가 생긴 뒤 로그를 뒤지는 방식이 아니라 흘러드는 데이터에서 이상 징후를 즉시 잡아내는 방식이 필요합니다.

5분 폴링과 밀리초 이벤트 사이의 간극은 도구를 바꿔야 메워집니다. 그 도구가 gNMI 기반 스트리밍 텔레메트리입니다.


2. SNMP와 gNMI의 구조적 차이

2.1 동작 모델: 계속 물어보기 vs 바뀌면 말하기

SNMP는 1988년(SNMPv1, RFC 1067)에 설계된 프로토콜입니다. 관리 시스템(NMS)이 UDP 161 포트로 장비에 질의하면, 장비의 SNMP 에이전트가 MIB 트리에서 해당 OID 값을 찾아 응답합니다. 수집 주체는 항상 관리 시스템이고, 장비는 수동적으로 답합니다. trap/inform이라는 장비 발신 메커니즘이 있긴 하지만 별도 체계이고, UDP 기반이라 유실될 수 있으며, 전달할 수 있는 정보의 종류도 제한적입니다.

gNMI(gRPC Network Management Interface)는 OpenConfig 진영에서 만든 gRPC 기반 관리 인터페이스입니다. HTTP/2 위에서 protobuf로 인코딩된 데이터를 주고받고, 데이터 구조는 YANG 모델의 경로로 표현됩니다.

RPC는 네 개뿐입니다.

RPC 역할
Capabilities 지원 모델 조회
Get 일회성 조회
Set 설정 변경
Subscribe 구독

핵심은 Subscribe입니다. 컬렉터가 관심 있는 경로를 한 번 구독해 두면, 이후에는 장비가 그 경로의 데이터를 컬렉터로 밀어냅니다. 질문-응답 모델이 발행-구독(pub/sub) 모델로 뒤집힙니다.

용어 정리: YANG이란?
네트워크 장비의 설정과 상태 데이터를 계층 구조로 정의하는 데이터 모델링 언어입니다. gNMI는 이 YANG 모델의 경로(예: /interface[name=ethernet-1/49]/oper-state)로 데이터를 지정하므로, 숫자 OID와 달리 경로만 읽어도 무엇을 가리키는지 알 수 있습니다.

2.2 SNMP의 구조적 한계 네 가지

첫째, 폴링 주기가 감지 지연의 상한입니다. 5분 주기면 최악의 경우 이벤트 발생 후 5분 뒤에 알게 되고, 이벤트가 주기보다 짧으면 아예 감지하지 못합니다. 주기를 줄이면 되지 않느냐는 반론이 나오지만, 그다음 한계에 부딪힙니다.

둘째, 폴링 비용입니다. SNMP 질의는 장비 컨트롤 플레인 CPU에서 처리됩니다. 인터페이스 48개짜리 스위치 500대를 1분 주기로 풀 폴링하면 매 분 그만큼의 CPU 부하와 관리망 트래픽이 고정비로 발생합니다. 폴링 주기 단축과 장비 부하는 정면으로 충돌하는 요구사항입니다.

셋째, 이벤트 의미론의 부재입니다. SNMP는 특정 시점의 값을 읽을 뿐, "무엇이 언제 어느 방향으로 바뀌었는지"를 전달하지 못합니다. 인터페이스가 다운됐다가 복구된 경우, 두 번의 폴링이 모두 up을 읽으면 그 사이의 flap은 없던 일이 됩니다. 실험 A(7장)에서 이 현상을 직접 재현합니다.

넷째, 카운터 해상도입니다. 32-bit 카운터는 2^32 옥텟, 약 34.36Gbit마다 wrap됩니다. 링크 속도별 wrap 소요 시간을 계산해 보면 다음과 같습니다.

링크 속도 32-bit 옥텟 카운터 wrap 시간
10G 약 3.4초
100G 약 0.34초
400G 약 0.086초

400G 링크에서는 1초에 열 번 넘게 wrap될 수 있다는 뜻입니다. 물론 현대 장비는 64-bit 카운터(ifHC 계열)를 사용하므로 실무에서 wrap을 볼 일은 드뭅니다. 다만 "폴링 주기 사이의 변화량을 카운터 차분으로 복원한다"는 SNMP식 접근이 고속 링크에서 얼마나 취약한 가정 위에 서 있는지 보여주는 수치입니다.

2.3 gNMI 구독 모드: sample과 on-change

gNMI Subscribe에는 세 가지 모드(once, poll, stream)가 있고, 지속 수집에 사용하는 stream 모드는 다시 세 가지 방식으로 나뉩니다.

  • sample: 지정한 주기마다 값을 푸시합니다. 바이트 카운터, 큐 깊이, CPU 사용률처럼 연속적으로 변하는 값에 적합합니다.
  • on-change: 값이 실제로 바뀔 때만 즉시 푸시합니다. 인터페이스 oper-state, BGP 세션 상태처럼 이산적인 상태 값에 적합합니다. 구독 시작 시점에 현재 값을 한 번 보내고(initial sync), 그 뒤로는 변화가 있을 때만 전송합니다.
  • target-defined: 경로 특성에 따라 장비가 sample과 on-change를 알아서 선택합니다.

운영 원칙은 단순합니다. 상태는 on-change, 카운터는 sample입니다. 카운터에 on-change를 걸면 사실상 매 순간 값이 바뀌므로 의미가 없고, 상태에 sample만 걸면 주기 사이의 flap을 놓치는 SNMP식 문제가 그대로 재현됩니다.

참고: 쿠버네티스에 비유하면 SNMP 폴링은 kubectl get pod를 cron으로 돌리는 것이고, gNMI on-change 구독은 watch API입니다. 둘 중 어느 쪽으로 컨트롤러를 만들지 생각해 보면 방향은 자명합니다.

2.4 핵심 차이 요약

항목 SNMP gNMI
등장 1988 (v1), 1996 (v2c), 2002 (v3) 2016년경, OpenConfig
전송 UDP/161 gRPC (HTTP/2, TCP)
인코딩 ASN.1 BER protobuf, JSON_IETF
데이터 모델 MIB / OID (숫자 트리) YANG / 경로 (사람이 읽는 구조)
수집 모델 NMS가 폴링 (pull) 장비가 푸시 (subscribe/publish)
시간 해상도 폴링 주기 (통상 1~5분) sample 주기 (초 단위) + on-change (이벤트 즉시)
이벤트 전달 trap/inform (별도 체계, UDP 유실 가능) 구독 스트림에 통합, TCP
암호화 v2c 평문 community, v3부터 별도 보안 모델 TLS 기본

2.5 현실적인 답: 병행 후 점진 전환

SNMP를 당장 걷어내자는 이야기가 아닙니다. 기존 NMS와 용량 관리 리포트가 SNMP 위에 서 있는 조직에서 이를 한 번에 들어내는 것은 가능하지도, 바람직하지도 않습니다.

합리적인 순서는 다음과 같습니다.

  1. gNMI 지원 장비에서 상태성 센서(인터페이스, BGP 세션)는 on-change로, 성능 카운터는 sample로 구독하는 스트리밍 파이프라인을 SNMP와 병행으로 구축합니다.
  2. 신뢰가 쌓이면 SNMP 폴링 주기를 늘리거나 대상을 줄여갑니다.

IP Infusion이 OcNOS 환경에서 제안하는 전환 시나리오도 같은 구조입니다.

전제 조건은 하나입니다. 장비가 gNMI와 YANG 모델을 얼마나 제대로 지원하는가입니다. 이 글에서 Nokia SR Linux를 실습 대상으로 선택한 이유이기도 합니다. SR Linux는 상태와 설정 데이터 전체가 YANG으로 모델링되어 있어(Nokia가 100% coverage로 내세우는 지점입니다) "이 값은 gNMI로 조회되지 않는다" 같은 예외를 만날 일이 없고, 컨테이너 이미지가 계정 등록 없이 공개되어 있어 랩을 구성하기도 쉽습니다.


3. 실습 환경 구성: WSL2 + containerlab

3.1 랩 토폴로지: 패브릭 하나, 파이프라인 둘

srl-labs/srl-telemetry-lab을 사용합니다. containerlab 토폴로지 파일 하나로 다음이 전부 배포됩니다.

                    ┌─ 패브릭 (Clos) ─────────────────────────┐
                    │       spine1         spine2             │
                    │      /   |   \      /   |   \           │
                    │  leaf1  leaf2  leaf3  (e1-49 → spine1,  │
                    │    |      |      |     e1-50 → spine2)  │
                    │ client1 client2 client3                 │
                    └─────────────────────────────────────────┘

 텔레메트리 파이프라인 (메트릭)
   SR Linux x5 ──(gNMI subscribe, 5s sample push)──► gnmic ──:9273/metrics──► Prometheus ◄── Grafana
                                                                  (5s scrape, pull)

 로깅 파이프라인 (로그)
   SR Linux x5 ──(syslog push)──► Alloy ──► Loki ◄── Grafana

노드 구성은 다음과 같습니다.

  • SR Linux 스위치 5대 (spine 2, leaf 3)
  • 리눅스 클라이언트 3대 (각 leaf에 하나씩, iperf3 내장)
  • 메트릭용 텔레메트리 파이프라인: gnmic, Prometheus, Grafana
  • 로그용 로깅 파이프라인: Alloy, Loki

인터페이스 표기는 이후 e1-49처럼 줄여서 사용합니다. ethernet-1/49의 축약이며, containerlab과 대시보드 라벨이 이 표기를 사용합니다.

패브릭은 underlay eBGP, overlay iBGP EVPN으로 leaf 간 L2 서비스를 제공합니다. 다만 이 글에서는 "클라이언트끼리 같은 서브넷(172.17.0.0/24)으로 통신하게 해주는 배경"으로만 취급합니다.

3.2 짚고 갈 점: 스트리밍인데 Prometheus는 pull이다

파이프라인 그림에서 한 가지를 짚고 가겠습니다. Prometheus를 써 본 분이라면 바로 걸리는 지점이 있을 텐데, 스트리밍이라고 했지만 Prometheus는 여전히 pull입니다.

맞습니다. 이 구조에서 push는 장비→gnmic 구간입니다. gnmic은 gNMI 구독으로 받은 데이터를 Prometheus exporter 형식(:9273/metrics)으로 노출하고, Prometheus가 이를 5초마다 스크레이프합니다. 즉 gnmic이 스트리밍 세계와 pull 세계 사이의 어댑터입니다. 이 구분은 실험 C(9장)에서 파이프라인의 유효 해상도를 따질 때 다시 등장합니다.

3.3 랩 배포

# containerlab 설치
echo "deb [trusted=yes] https://netdevops.fury.site/apt/ /" | \
sudo tee -a /etc/apt/sources.list.d/netdevops.list

sudo apt update && sudo apt install containerlab


git clone https://github.com/srl-labs/srl-telemetry-lab.git
cd srl-telemetry-lab
sudo containerlab deploy --reconfigure

첫 배포는 이미지 다운로드 때문에 수 분 소요됩니다. SR Linux 이미지(ghcr.io/nokia/srlinux)가 압축 해제 후 3GB가량이고, 나머지 스택 이미지까지 합치면 디스크 10GB 정도는 확보해 두어야 합니다.

계정 등록이나 라이선스 절차는 없습니다. 이 지점이 상용 NOS 이미지(예: Arista cEOS는 arista.com 계정으로 수동 다운로드 필요)와 대비되는 SR Linux 랩의 실질적 장점입니다.

이 글의 실습은 다음 환경에서 검증했습니다: [실측 기입: wsl --version / containerlab 버전 / SR Linux 이미지 태그 / gnmic·Prometheus·Grafana 버전 / 랩 저장소 커밋 해시]. 랩 저장소는 계속 갱신되므로, 본문과 출력이 다르게 보이면 버전 차이부터 의심하는 것이 순서입니다.

배포가 끝나면 13개 노드가 표로 출력됩니다. 다시 확인하려면 sudo containerlab inspect --name st를 실행합니다.

╭────────────┬───────────────────────────────────────┬─────────┬────────────────╮
│    Name    │               Kind/Image              │  State  │ IPv4/6 Address │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ leaf1      │ nokia_srlinux                         │ running │ 172.80.80.11   │
│ leaf2      │ nokia_srlinux                         │ running │ 172.80.80.12   │
│ leaf3      │ nokia_srlinux                         │ running │ 172.80.80.13   │
│ spine1     │ nokia_srlinux                         │ running │ 172.80.80.21   │
│ spine2     │ nokia_srlinux                         │ running │ 172.80.80.22   │
│ client1~3  │ linux (network-multitool)             │ running │ 172.80.80.31~33│
│ gnmic      │ linux                                 │ running │ 172.80.80.41   │
│ prometheus │ linux                                 │ running │ 172.80.80.42   │
│ grafana    │ linux                                 │ running │ 172.80.80.43   │
│ alloy      │ linux                                 │ running │ 172.80.80.45   │
│ loki       │ linux                                 │ running │ 172.80.80.46   │
╰────────────┴───────────────────────────────────────┴─────────┴────────────────╯

containerlab이 WSL의 /etc/hosts에 노드 이름을 등록해 주므로 leaf1, spine1 같은 이름으로 바로 접근할 수 있습니다. Prometheus(9090)와 Grafana(3000)는 포트가 호스트로 공개되어 있고, WSL2의 localhost 포워딩 덕분에 Windows 브라우저에서 http://localhost:9090, http://localhost:3000으로 바로 열립니다.

Grafana는 익명 접속이 Admin 권한으로 열려 있어 로그인 없이 대시보드가 보입니다. 랩 전용 설정이므로 프로덕션에서는 절대 따라 하면 안 됩니다.

╭────────────┬───────────────────────────────────────┬─────────┬────────────────╮
│    Name    │               Kind/Image              │  State  │ IPv4/6 Address │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ alloy      │ linux                                 │ running │ 172.80.80.45   │
│            │ grafana/alloy:v1.16.0                 │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ client1    │ linux                                 │ running │ 172.80.80.31   │
│            │ ghcr.io/srl-labs/network-multitool    │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ client2    │ linux                                 │ running │ 172.80.80.32   │
│            │ ghcr.io/srl-labs/network-multitool    │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ client3    │ linux                                 │ running │ 172.80.80.33   │
│            │ ghcr.io/srl-labs/network-multitool    │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ gnmic      │ linux                                 │ running │ 172.80.80.41   │
│            │ ghcr.io/openconfig/gnmic:0.45.0       │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ grafana    │ linux                                 │ running │ 172.80.80.43   │
│            │ grafana/grafana:13.0.1                │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ leaf1      │ nokia_srlinux                         │ running │ 172.80.80.11   │
│            │ ghcr.io/nokia/srlinux:25.10           │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ leaf2      │ nokia_srlinux                         │ running │ 172.80.80.12   │
│            │ ghcr.io/nokia/srlinux:25.10           │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ leaf3      │ nokia_srlinux                         │ running │ 172.80.80.13   │
│            │ ghcr.io/nokia/srlinux:25.10           │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ loki       │ linux                                 │ running │ 172.80.80.46   │
│            │ grafana/loki:3.7.1                    │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ prometheus │ linux                                 │ running │ 172.80.80.42   │
│            │ quay.io/prometheus/prometheus:v3.11.3 │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ spine1     │ nokia_srlinux                         │ running │ 172.80.80.21   │
│            │ ghcr.io/nokia/srlinux:25.10           │         │ N/A            │
├────────────┼───────────────────────────────────────┼─────────┼────────────────┤
│ spine2     │ nokia_srlinux                         │ running │ 172.80.80.22   │
│            │ ghcr.io/nokia/srlinux:25.10           │         │ N/A            │

3.5 배포 검증: 세 가지 확인

실험 결과를 신뢰하려면 관측 대상이 정상 상태인지부터 확인해야 합니다. 세 가지만 확인합니다.

3.5.1 패브릭 BGP 세션

스위치에 SSH로 접속합니다. 계정은 admin, 암호는 NokiaSrl1!입니다.

ssh admin@leaf1

이 랩은 startup config가 자동 적용되므로, 수동 설정 없이 underlay eBGP 2세션(spine 방향), overlay iBGP 2세션(EVPN)이 모두 established여야 정상입니다.

A:admin@leaf1# show network-instance default protocols bgp neighbor
+----------+--------------+--------------+-------+----------+-------------+---------------+----------------------+
| Net-Inst |     Peer     |    Group     | Flags | Peer-AS  |    State    |    Uptime     | AFI/SAFI [Rx/Act/Tx] |
+==========+==============+==============+=======+==========+=============+===============+======================+
| default  | 10.0.2.1     | iBGP-overlay | S     | 100      | established | 0d:0h:27m:36s | evpn [2/2/1]         |
| default  | 10.0.2.2     | iBGP-overlay | S     | 100      | established | 0d:0h:27m:35s | evpn [2/0/1]         |
| default  | 192.168.11.1 | eBGP         | S     | 201      | established | 0d:0h:27m:41s | ipv4-unicast [3/3/2] |
| default  | 192.168.12.1 | eBGP         | S     | 202      | established | 0d:0h:27m:41s | ipv4-unicast [3/3/4] |
+----------+--------------+--------------+-------+----------+-------------+---------------+----------------------+
4 configured neighbors, 4 configured sessions are established, 0 disabled peers

3.5.2 클라이언트 간 통신

세 클라이언트는 EVPN 덕분에 같은 L2 세그먼트(172.17.0.0/24)에 있는 것처럼 동작합니다.

docker exec -it client1 ping -c 3 172.17.0.3
# PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
# 64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=7.85 ms
# 64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=1.31 ms
# 64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=1.23 ms


3.5.3 텔레메트리 스택

Windows 브라우저에서 http://localhost:3000에서 Grafana 대시보드에 5대 스위치의 메트릭이 유입되고 있는지 확인합니다. 아직 클라이언트 트래픽이 없으므로 트래픽 그래프가 0에 붙어 있는 것이 정상입니다.

Figure 3.3 Grafana 기본 대시보드

여기까지 확인되면 관측 인프라는 준비된 것입니다.


4. gNMI 직접 호출하기

대시보드로 바로 넘어가기 전에, gNMI가 실제로 어떻게 동작하는지 CLI로 먼저 확인합니다. 파이프라인이 자동으로 수행하는 일을 한 번 수동으로 해보면, 이후 설정 파일이 훨씬 잘 읽히기 시작합니다.

4.1 gnmic 설치

WSL 호스트에 gnmic을 설치합니다. 랩의 gnmic 컨테이너는 컬렉터로 이미 동작 중이고, 지금 설치하는 것은 실험용 CLI입니다.

bash -c "$(curl -sL https://get-gnmic.openconfig.net)"

4.2 Capabilities와 Get

장비가 어떤 YANG 모델과 인코딩을 지원하는지 질의합니다. SR Linux의 gNMI 포트는 57400이고, containerlab이 생성한 자체 서명 인증서를 사용하므로 --skip-verify를 붙입니다.

gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify capabilities

수백 개의 모델 목록과 지원 인코딩(JSON_IETF 등)이 출력됩니다.

다음은 Get입니다. leaf1의 spine1 방향 업링크(ethernet-1/49) 상태를 조회합니다.

gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  get --path /interface[name=ethernet-1/49]/oper-state
[
  {
    "source": "leaf1:57400",
    "timestamp": 1783840106806426564,
    "time": "2026-07-12T16:08:26.806426564+09:00",
    "updates": [
      {
        "Path": "srl_nokia-interfaces:interface[name=ethernet-1/49]/oper-state",
        "values": {
          "srl_nokia-interfaces:interface/oper-state": "up"
        }
      }
    ]
  }
]

경로를 눈여겨볼 필요가 있습니다. /interface[name=ethernet-1/49]/oper-state — 어떤 인터페이스의 무슨 값인지 경로만 읽어도 알 수 있습니다. 잠시 뒤 SNMP로 같은 값을 조회할 때 이 가독성이 어떤 의미인지 체감하게 됩니다.

4.3 Subscribe: sample 모드

traffic-rate를 2초 주기 sample로 구독합니다.

gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  subscribe --path /interface[name=ethernet-1/49]/traffic-rate \
  --stream-mode sample --sample-interval 2s


# {
#   "source": "leaf1:57400",
#   "subscription-name": "default-1783840141",
#   "timestamp": 1783840175498433013,
#   "time": "2026-07-12T16:09:35.498433013+09:00",
#   "updates": [
#     {
#       "Path": "srl_nokia-interfaces:interface[name=ethernet-1/49]/traffic-rate",
#       "values": {
#         "srl_nokia-interfaces:interface/traffic-rate": {
#           "in-bps": "146",
#           "out-bps": "0"
#         }
#       }
#     }
#   ]
# }

2초마다 in-bps/out-bps 값이 수신됩니다. 값이 변하든 변하지 않든 무조건 전송되며, Ctrl+C로 끊기 전까지 계속됩니다. 이것이 sample 모드입니다.

폴링과 비슷해 보이지만 차이가 있습니다. 질의-응답 왕복이 매번 발생하는 것이 아니라, 구독 한 번에 장비가 알아서 계속 보냅니다. 장비 입장에서 질의 처리보다 훨씬 저렴한 작업이고, 그래서 주기를 초 단위 이하로 내릴 수 있습니다.

4.4 Subscribe: on-change 모드

이번엔 oper-state를 on-change로 구독합니다.

gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  subscribe --path "/interface[name=ethernet-1/*]/oper-state" \
  --stream-mode on-change

구독 직후 전체 인터페이스의 현재 상태가 한 번 출력되고(initial sync), 그 뒤로는 아무것도 오지 않습니다. 값이 바뀌지 않았기 때문에 아무것도 오지 않는 것이 정상 동작입니다.

이 터미널은 열어 둔 채로 둡니다. 7장의 실험 A에서 이 침묵이 깨지는 순간을 관측합니다.

4.5 대조: 같은 값을 SNMP로 읽기

비교를 위해 같은 인터페이스 상태를 SNMP로 조회해 봅니다. containerlab의 SR Linux 노드는 SNMPv2c 서버가 기본 활성화되어 있고, community는 public입니다. WSL 호스트에 net-snmp 도구를 설치합니다.

sudo apt install -y snmp

인터페이스 상태는 IF-MIB의 ifOperStatus에 있습니다. 그런데 ifOperStatus는 ifIndex라는 숫자로 인터페이스를 식별하므로, 먼저 ethernet-1/49가 몇 번인지부터 찾아야 합니다.

# ifDescr(1.3.6.1.2.1.2.2.1.2)를 걸어 인터페이스 이름-인덱스 매핑부터 확인
snmpwalk -v2c -c public leaf1 1.3.6.1.2.1.2.2.1.2
# iso.3.6.1.2.1.2.2.1.2.16386 = STRING: "ethernet-1/49"   ← 이런 식의 출력에서 인덱스를 찾는다

# 찾은 인덱스로 oper-status 조회 (1=up, 2=down)
snmpwalk -v2c -c public leaf1 1.3.6.1.2.1.2.2.1.8.<ifIndex>

조회 자체는 동작합니다. 다만 방금 수행한 과정을 되짚어 보면, "ethernet-1/49의 상태"라는 질문 하나를 위해 숫자 OID 트리에 snmpwalk를 두 번 실행했고, 결과도 1이나 2 같은 정수로 반환됩니다. gNMI에서는 /interface[name=ethernet-1/49]/oper-state 경로 하나로 끝났습니다. 데이터 모델의 차이는 취향 문제가 아니라 자동화 코드의 유지보수성 문제입니다.

하나 더 있습니다. SR Linux의 SNMP 에이전트는 읽기 전용이고, 노출하는 범위도 표준 MIB 일부에 그칩니다. gNMI로는 YANG으로 모델링된 상태 전체가 보이는 반면 SNMP로는 이 장비의 극히 일부만 보인다는 뜻입니다. 두 인터페이스의 커버리지 격차는 이 장비에서 이미 구조적입니다.


5. 텔레메트리 파이프라인 해부: gnmic → Prometheus → Grafana

4장에서 수동으로 실행한 구독을, 랩에서는 gnmic 컨테이너가 설정 파일 기반으로 상시 수행합니다. 설정 파일 세 개를 순서대로 읽으면 파이프라인 전체가 이해됩니다.

5.1 gnmic-config.yml: 구독의 세 요소

configs/gnmic/gnmic-config.yml의 뼈대는 세 부분입니다. 누구에게서(targets), 무엇을(subscriptions), 어디로(outputs).

targets:            # 5대 스위치 전부, 같은 구독 세트를 적용
  leaf1: *common_srl_subs
  leaf2: *common_srl_subs
  # ... spine2까지

subscriptions:
  srl-if-stats:     # 인터페이스 센서: 상태, 카운터, 트래픽 레이트
    mode: stream
    stream-mode: sample
    sample-interval: 5s
    paths:
      - /interface[name=ethernet-1/*]/oper-state
      - /interface[name=ethernet-1/*]/statistics
      - /interface[name=ethernet-1/*]/traffic-rate
  srl-bgp:          # 이외에 BGP, CPU/메모리, 라우트 테이블 등 7개 구독이 같은 패턴
    ...

outputs:
  prom-output:
    type: prometheus
    listen: :9273   # 이 포트를 Prometheus가 스크레이프
    event-processors:
      - oper-state-to-int   # "up"/"down" 문자열을 1/0으로 변환
      - rename-srl-interface # ethernet-1/49 → e1-49 라벨 정리

읽으면서 두 가지를 짚어둘 필요가 있습니다.

첫째, 모든 구독이 sample 5초입니다. oper-state 같은 상태 값까지도 그렇습니다. 즉 이 랩의 기본 설정은 "5초 해상도의 폴링형 스트리밍"이고, on-change의 장점은 아직 활용하지 않고 있는 셈입니다. 5.4절에서 직접 수정합니다.

둘째, event-processor입니다. Grafana에서 상태를 그래프로 그리려면 숫자가 필요한데, YANG의 oper-state는 "up"/"down" 문자열입니다. gnmic이 중간에서 1/0으로 변환해 줍니다. 컬렉터가 단순 전달자가 아니라 변환 계층이라는 것을 보여주는 대목입니다.

5.2 Prometheus: 저장과 질의

configs/prometheus/prometheus.yml은 gnmic의 :9273을 5초 간격으로 스크레이프하는 것이 거의 전부입니다. http://localhost:9090에서 몇 가지 질의를 실행해 봅니다. 메트릭 이름은 YANG 경로에서 파생되므로, 자동완성에 interface_를 입력하면 목록이 나옵니다.

curl -s 'http://localhost:9090/api/v1/label/__name__/values' | jq -r '.data[]' | grep -i interface
# interface_oper_state
# interface_statistics_carrier_transitions
# interface_statistics_in_broadcast_packets
# interface_statistics_in_discarded_packets
# interface_statistics_in_error_packets
# interface_statistics_in_fcs_error_packets
# interface_statistics_in_multicast_packets
# interface_statistics_in_octets
# interface_statistics_in_packets
# interface_statistics_in_unicast_packets
# interface_statistics_out_broadcast_packets
# interface_statistics_out_discarded_packets
# interface_statistics_out_error_packets
# interface_statistics_out_mirror_octets
# interface_statistics_out_mirror_packets
# interface_statistics_out_multicast_packets
# interface_statistics_out_octets
# interface_statistics_out_packets
# interface_statistics_out_unicast_packets
# interface_traffic_rate_in_bps
# interface_traffic_rate_out_bps
curl -s 'http://localhost:9090/api/v1/label/source/values' | jq
# {
#   "status": "success",
#   "data": [
#     "leaf1",
#     "leaf2",
#     "leaf3",
#     "spine1",
#     "spine2"
#   ]
# }

5.3 Grafana: Flow 대시보드

http://localhost:3000의 기본 제공 대시보드는 Flow 패널 플러그인으로 만든 토폴로지 지도(일명 weather map)를 포함합니다. SVG로 그린 패브릭 다이어그램 위에 실시간 메트릭이 오버레이되어, 링크별 사용률과 포트 상태가 색과 숫자로 표시됩니다. 8장의 실험에서 장애를 발생시키면 이 지도에서 어느 링크가 죽었는지 즉시 확인할 수 있습니다.

5.4 개조: oper-state를 on-change로 분리

기본 설정의 약점을 직접 수정해 봅니다. srl-if-stats 구독에서 oper-state를 분리하여 on-change 구독으로 옮깁니다.

subscriptions:
  srl-if-oper-state:          # 신설: 상태 변화는 즉시 push
    mode: stream
    stream-mode: on-change
    paths:
      - /interface[name=ethernet-1/*]/oper-state

  srl-if-stats:               # 기존: 카운터는 5초 sample 유지
    mode: stream
    stream-mode: sample
    sample-interval: 5s
    paths:
      - /interface[name=ethernet-1/*]/statistics
      - /interface[name=ethernet-1/*]/traffic-rate

common_srl_subs 앵커의 구독 목록에 srl-if-oper-state를 추가하는 것도 잊지 않아야 합니다. 수정 후 gnmic 컨테이너를 재시작합니다.

docker restart gnmic
docker logs -f gnmic

# 2026/07/12 07:21:26.576065 [gnmic] sending gNMI SubscribeRequest: subscribe='subscribe:{subscription:{path:{elem:{name:"network-instance"  key:{key:"name"  value:"*"}}  elem:{name:"protocols"}  elem:{name:"bgp"}  elem:{name:"statistics"}}  mode:SAMPLE  sample_interval:5000000000}}', mode='STREAM', encoding='JSON', to leaf1

랩 토폴로지가 gnmic을 로그 출력을 켠 채로 기동하도록 정의해 두었기 때문에, 구독으로 수신한 이벤트가 컨테이너 로그에 그대로 남습니다. 이제 인터페이스 상태 변화는 5초를 기다리지 않고 발생 즉시 gnmic에 도착합니다.

단, Prometheus 스크레이프가 여전히 5초 주기이므로 대시보드에 반영되는 최종 지연은 스크레이프 주기의 지배를 받습니다. 파이프라인의 유효 해상도는 가장 주기가 긴 구간이 결정합니다. 이 문장이 이 절의 결론이고, 9장에서 수치로 다시 확인합니다.

참고: on-change의 즉시성을 끝까지 살리려면 gnmic에서 Prometheus remote-write나 Kafka/NATS 같은 push형 출력을 사용하는 선택지도 있습니다. 다만 이 랩의 범위를 넘어가므로 이 글에서는 다루지 않습니다.

같은 원리로 BGP 세션 상태(/network-instance[name=*]/protocols/bgp/neighbor[peer-address=*]/session-state)를 on-change로 분리하면 BGP flap을 실시간 감지할 수 있습니다. 상태는 on-change, 카운터는 sample이라는 2.3절의 원칙이 설정 파일에서 이렇게 구현됩니다.


6. 실험 A: 같은 이벤트, 세 가지 시선

여기까지가 준비 과정이었고, 지금부터가 이 글의 본론입니다. 2~3장에서 서술로 정리한 주장들을 측정으로 확인합니다.

첫 번째 실험의 질문은 다음과 같습니다.

  • 같은 링크 다운 이벤트를 SNMP 폴링, gNMI sample, gNMI on-change가 각각 언제 감지하는가?
  • 폴링 주기보다 짧은 flap은 어떻게 되는가?

6.1 실험 설계: 관측 창 네 개

Windows Terminal 창을 네 개로 분할합니다.

창 1 — SNMP 폴링 역할. 60초 주기로 leaf1 ethernet-1/49의 oper-status를 조회하는 루프를 실행합니다. 4.5절에서 찾아둔 ifIndex를 넣습니다. 현장의 5분 주기를 그대로 사용하면 대기 시간만 길어지므로 60초로 줄였습니다. 주기가 5분이든 60초든 아래 결론의 구조는 같습니다.

while true; do
  echo "--- poll at $(date +%T)"
  snmpwalk -v2c -c public leaf1 1.3.6.1.2.1.2.2.1.8.<ifIndex>
  sleep 60
done
# --- poll at 16:38:19
# iso.3.6.1.2.1.2.2.1.8.1589246 = INTEGER: 2

창 2 — gNMI sample 구독. 5초 주기로, 랩 파이프라인과 같은 조건입니다.

gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  subscribe --path /interface[name=ethernet-1/49]/oper-state \
  --stream-mode sample --sample-interval 5s
#   "source": "leaf1:57400",
#   "subscription-name": "default-1783841843",
#   "timestamp": 1783841901735601421,
#   "time": "2026-07-12T16:38:21.735601421+09:00",
#   "updates": [
#     {
#       "Path": "srl_nokia-interfaces:interface[name=ethernet-1/49]",
#       "values": {
#         "srl_nokia-interfaces:interface": {
#           "oper-state": "down"
#         }
#       }
#     }
#   ]
# }

창 3 — gNMI on-change 구독.

gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  subscribe --path /interface[name=ethernet-1/49]/oper-state \
  --stream-mode on-change

#   "source": "leaf1:57400",
#   "subscription-name": "default-1783841848",
#   "timestamp": 1783841880166036920,
#   "time": "2026-07-12T16:38:00.16603692+09:00",
#   "updates": [
#     {
#       "Path": "srl_nokia-interfaces:interface[name=ethernet-1/49]",
#       "values": {
#         "srl_nokia-interfaces:interface": {
#           "oper-state": "down"
#         }
#       }
#     }
#   ]
# }

창 4 — 이벤트 트리거. 장애 주입도 gNMI로 수행합니다. Set RPC를 실습해 볼 기회이기도 합니다. 명령 앞에 date를 붙여 주입 시각을 밀리초 단위로 기록해 둡니다.

6.2 시나리오 1: 지속 다운

창 4에서 인터페이스를 내리고, 세 관측 창이 각각 언제 반응하는지 기록합니다.

date +%T.%3N && gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  set --update-path /interface[name=ethernet-1/49]/admin-state --update-value disable

결과는 다음과 같은 표로 정리됩니다.

관측 방식 감지 시각 주입 시점부터 지연 알 수 있는 것
SNMP 폴링 (60s) 16:39:23 최대 60초 (다음 폴링까지) 현재 값이 down이라는 사실
gNMI sample (5s) 16:39:25 최대 5초 현재 값이 down이라는 사실
gNMI on-change 16:39:15 1초 미만 down으로 "바뀌었다"는 이벤트 + 장비 타임스탬프

6.3 시나리오 2: 폴링 주기보다 짧은 flap

인터페이스를 복구(enable)해 상태를 정리한 뒤, 창 1에서 폴링이 막 지나간 직후에 다음을 실행합니다.

date +%T.%3N && gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  set --update-path /interface[name=ethernet-1/49]/admin-state --update-value disable
sleep 20
date +%T.%3N && gnmic -a leaf1:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  set --update-path /interface[name=ethernet-1/49]/admin-state --update-value enable

20초짜리 다운 이벤트입니다. 결과를 예상해 봅시다.

  • 창 1 (SNMP): flap 이전과 이후에 폴링했으므로 두 번 모두 up(1)을 읽습니다. 폴링 로그만 보면 이 장애는 발생한 적이 없습니다.
  • 창 2 (sample): 5초 주기이므로 down 상태를 서너 번의 샘플로 포착합니다.
  • 창 3 (on-change): down 전환과 up 전환을 각각 즉시, 장비가 찍은 타임스탬프와 함께 수신합니다.

6.4 결과 해석: 관측이 아니라 기록의 문제

이 실험이 보여주는 것은 감지 "지연"의 문제만이 아닙니다. 폴링 주기보다 짧은 이벤트는 늦게 아는 것이 아니라 영원히 알 수 없습니다. 관측의 문제가 아니라 기록의 문제입니다. 1장의 가상 시나리오("고객 문의만 남았다")가 여기서 20초 만에 재현됩니다.

참고로 e1-49를 내리면 leaf1-spine1 간 eBGP 세션도 함께 끊어지므로, 5.4절에서 BGP session-state를 on-change로 구독해 두었다면 링크 이벤트와 BGP 이벤트가 연쇄로 도착하는 것도 관찰할 수 있습니다.

실험이 끝나면 인터페이스를 enable로 되돌리고 BGP 세션이 재수립되는지 확인합니다.


7. 실험 B: ECMP 패브릭에서 장애 링크 국소화

두 번째 실험의 질문입니다. 트래픽이 흐르는 패브릭에서 링크 하나가 죽으면, 텔레메트리 스택만으로 "어디가" 문제인지 얼마나 빨리 좁힐 수 있는가.

NVIDIA가 Spectrum-X 텔레메트리 사례로 소개한 흐름(LLM 학습 대역폭 급락 → SuperNIC의 RoCE 재전송 지표 확인 → 특정 spine 포트의 symbol error 특정 → 포트 차단 → 대역폭 회복)의 미니어처를 이 랩 규모에서 재현합니다.

7.1 배경 트래픽과 기준선

client1은 토폴로지 배포 시점부터 iperf3 서버(5201, 5202)가 실행 중이므로, client3에서 트래픽을 보내기만 하면 됩니다.

docker exec -d client3 iperf3 -c 172.17.0.1 -p 5201 -t 600 -P 8 -b 20M

스트림 8개로 나눠 보내는 이유는 ECMP가 플로우 단위 해시로 경로를 결정하기 때문입니다. 플로우가 하나뿐이면 업링크 두 개 중 하나로만 흐릅니다.

Grafana의 토폴로지 대시보드에서 leaf3의 두 업링크(e1-49→spine1, e1-50→spine2)에 트래픽이 나뉘어 흐르는 것을 확인합니다. 이것이 정상 상태의 기준선입니다. NVIDIA 사례가 짚듯, 균형 잡힌 분산 자체가 "패브릭 설정이 건강하다"는 신호이기도 합니다.

7.2 장애 주입과 관측 포인트 네 곳

leaf3의 spine1 방향 업링크를 내립니다.

gnmic -a leaf3:57400 -u admin -p 'NokiaSrl1!' --skip-verify -e json_ietf \
  set --update-path /interface[name=ethernet-1/49]/admin-state --update-value disable

관측 포인트는 네 곳입니다.

  1. on-change 구독 (5.4절을 적용했다면 docker logs -f gnmic): 링크 다운 이벤트가 즉시 도착
  2. 토폴로지 대시보드: e1-49가 다운으로 표시되고, 흐르던 트래픽이 e1-50 하나로 합쳐짐
  3. 트래픽 그래프: 절체(failover) 순간의 일시적 요동 후 총량 회복 (TCP가 재전송으로 흡수)
  4. 로그 대시보드 (Alloy → Loki 파이프라인): 같은 시각 leaf3과 spine1 양쪽에서 인터페이스 다운, BGP 세션 다운 syslog가 도착

여기서 중요한 것은 4번입니다. 메트릭은 "무엇이 이상한지"를, 로그는 "왜 그런지"를 말해 줍니다. 대역폭 요동(증상)에서 시작해 링크별 메트릭으로 구간을 좁히고(국소화), 같은 타임스탬프의 로그로 원인을 확정하는(상관) 세 단계가, 규모만 다를 뿐 NVIDIA 사례의 골격 그대로입니다.

확인이 끝나면 e1-49를 enable로 되돌리고 트래픽이 다시 분산되는지 확인합니다.

7.3 심화: 살아 있는 척하는 불량 링크

실무에서 정말 어려운 것은 깔끔하게 죽은 링크가 아니라 살아 있는 척하는 불량 링크입니다. 죽은 링크는 라우팅에서 빠져 트래픽이 우회하지만, 패킷을 조금씩 깨뜨리는 링크는 경로에 남아 계속 피해를 만듭니다. containerlab의 netem 도구로 이 상황을 모사할 수 있습니다.

sudo containerlab tools netem set -n leaf3 -i e1-49 --loss 10

링크는 up인데 유효 대역폭만 떨어지는 상태가 됩니다. 토폴로지 지도에는 장애가 보이지 않고, 트래픽 그래프와 iperf3의 재전송 카운트만 이상 신호를 냅니다. "모든 것이 up인데 느리다"는 상황을 텔레메트리로 어떻게 추적할지 생각해 보는 재료로 좋습니다.

다만 두 가지 한계를 밝혀둡니다.

  • WSL2 커널에 sch_netem 모듈이 없으면 이 명령은 실패합니다. 그 경우 이 변형 실험은 건너뜁니다.
  • veth에 주입한 loss는 실장비의 symbol error 같은 물리 계층 카운터로는 잡히지 않으므로 개념 재현에 그칩니다.

실험이 끝나면 --loss 0으로 되돌립니다.


8. 정리

실험 세 개의 결론을 세 문장으로 요약하면 다음과 같습니다.

  1. 폴링의 감지 지연 상한은 폴링 주기이고, 주기보다 짧은 이벤트는 늦게 아는 것이 아니라 기록조차 되지 않는다. (실험 A)
  2. 스트리밍 텔레메트리는 이벤트를 타임스탬프가 있는 기록으로 만들고, 메트릭-로그 상관과 결합하면 장애 국소화 속도가 달라진다. (실험 B)
  3. 유효 해상도는 파이프라인에서 가장 느린 구간이 결정하며 공짜가 아니므로, 상태는 on-change로, 카운터는 필요한 만큼의 sample로 나누는 설계가 출발점이다. (실험 C)

이 랩이 보여주지 않는 것도 분명히 해둡니다. 스위치 5대와 수백 대는 다른 문제입니다. 컬렉터 이중화와 샤딩, TSDB 카디널리티 관리, 구독 폭주 시 장비 보호 같은 운영 설계는 이 글의 범위 밖입니다. 벤더 편차도 큽니다. SR Linux는 상태 전체가 YANG으로 모델링된 이상적인 케이스이고, 실제 장비에서는 지원 경로와 on-change 가능 여부를 Capabilities로 일일이 확인해야 합니다. 다만 그 확인 작업 자체는 이 글에서 익힌 gnmic 사용법 그대로입니다.

Arista 장비를 사용하는 환경이라면 이 글의 스택이 거의 그대로 적용됩니다. gnmic도 Prometheus도 Grafana도 벤더 중립이고, cEOS 컨테이너로 같은 구조의 랩(arista-clab-telemetry-lab)을 구성할 수 있습니다. 차이는 cEOS 이미지를 arista.com 계정으로 직접 받아 docker import 해야 한다는 점, 그리고 구독 경로로 OpenConfig 모델을 사용한다는 점 정도입니다. 이것이 gNMI 생태계의 실질적인 가치입니다. 장비가 바뀌어도 수집 스택과 운영 지식이 이식됩니다.

랩을 정리하려면 다음을 실행합니다.

sudo containerlab destroy --cleanup
wsl --shutdown        # PowerShell에서 실행. WSL이 점유한 메모리 반환

다음 글에서는 이번에 간략히 언급한 로깅 파이프라인(Alloy-Loki)을 자세히 살펴보고, syslog 이벤트와 on-change 메트릭을 하나의 타임라인에서 상관 분석하는 구성을 다룰 예정입니다.


10. 트러블슈팅 FAQ

WSL 환경에서 Docker Desktop을 사용하는경우

15:52:58 WARN failed gleaning v4 and/or v6 addresses from bridge via netlink, falling back to docker network inspect data

   ERROR

  Failed to lookup link "br-5a41eaac12a9": Link not found.

해당 에러가 발생하고, Docker Desktop을 종료한 뒤에
sudo curl -fsSL https://get.docker.com | sudo sh 명령어로 WSL에 Docker 설치합니다.

노드 일부가 exited 상태로 뜬다.
거의 항상 메모리 부족입니다. WSL 안에서 free -h로 확인하고 .wslconfig의 memory를 상향한 뒤 wsl --shutdown 후 재배포합니다.

gnmic subscribe의 타임스탬프가 이상하다.
Windows 절전/최대절전 후 WSL2의 시계가 실제 시각과 어긋나는 알려진 문제가 있습니다. 감지 지연을 측정하는 실험 A가 특히 영향을 받습니다. wsl --shutdown 후 재시작이 가장 확실하고, 급하면 WSL 안에서 sudo hwclock -s로 재동기화합니다. 실험 전에 WSL의 date와 Windows 시계가 일치하는지 확인하는 습관을 권장합니다.

snmpwalk가 응답이 없다.
버전(v2c)과 community(public)를 확인하고, 대상은 노드 이름(leaf1) 또는 mgmt IP(172.80.80.x)여야 합니다. 그래도 값이 나오지 않으면 조회하려는 OID가 노출 범위 밖일 수 있습니다. 4.5절에서 확인한 대로 SR Linux의 SNMP는 읽기 전용이고 노출 범위도 제한적입니다.

containerlab tools netem이 실패한다.
WSL2 기본 커널에 sch_netem 모듈이 없는 경우입니다. 실험 B의 심화 변형만 건너뛰면 나머지 실습에는 지장이 없습니다.

Grafana가 로그인을 요구한다.
이 랩은 익명 접속이 Admin으로 열려 있어 로그인이 필요 없습니다. 로그인 화면이 보인다면 grafana 컨테이너가 정상 기동했는지(docker logs grafana) 확인합니다.

이미지 pull이 실패한다.
ghcr.io와 quay.io 접근이 가능한지 확인합니다. 회사 프록시 환경이라면 WSL 안의 docker에 프록시 설정이 필요할 수 있습니다.