13.4 선언적 파이프라인 구성: YAML 디스크립터(Descriptor)
엔지니어는 파이프라인을 구축할 때 절대로 제어 코드 (C++ 의 main() 함수 등) 안에 네트워크 연결 로직을 하드코딩해서는 안 된다.
Zenoh-Flow 의 거대한 분산 공장은 오직 한 장의 설계도, YAML 로 작성된 선언적 디스크립터 (Declarative Descriptor) 에 의해서만 통제된다. 이 챕터는 코딩이 아닌, 아키텍트로서의 “지휘” 런북이다.
1. Zenoh-Flow 디스크립터 파일의 문법과 구조 이해하기
야믈(YAML) 파일 하나가 로봇 수십 대의 스레드를 들었다 놨다 한다. 기본 뼈대를 체화하라.
1.0.1 [인스펙션] Dataflow 그래프의 지배 구조
디스크립터의 최상단은 항상 flow 라는 선언으로 시작된다.
flow: "robot_vision_pipeline"
## 1. 어떤 블록(부품)을 이 공장에 들여올 것인가?
sources:
- id: "camera_src"
uri: "file://modules/libcamera_capture.so"
operators:
- id: "face_detect"
uri: "file://modules/libface_infer.so"
sinks:
- id: "display_out"
uri: "file://modules/libdisplay.so"
## 2. 그 블록들을 어떻게 파이프로 연결(Link) 할 것인가?
links:
- from: "camera_src"
to: "face_detect"
- from: "face_detect"
to: "display_out"
이 짧은 15줄의 텍스트가, 데몬에게 제출되는 순간 동적으로 3개의 스레드(워커)를 찍어내고, 그 사이를 메모리 큐나 TCP 소켓으로 용접해 버리는 기적을 일으킨다. 당신은 노드를 그저 레고 블록(URI) 처럼 선언(Declare) 하기만 하면 된다.
2. 노드(Node) 인스턴스화 및 런타임 매개변수(Parameter) 전달 방법
코드(C/Rust) 안에 변숫값을 고정하지 마라. 카메라 해상도나 포트 번호는 실행 시(Runtime) 에 YAML 을 통해 동적으로 주입되어야 한다.
2.0.1 [Runbook] 동적 임베딩을 위한 파라미터 주사 전술
노드 선언부 안에 configuration 블록을 뚫어 환경 변수를 밀어 넣는다.
sources:
- id: "main_camera"
uri: "file://plugins/camera.so"
# 요기서 노드 코드로 JSON 형태의 변수값을 주입한다!
configuration:
width: 1920
height: 1080
fps: 30
device: "/dev/video0"
노드 코드를 짠 개발자(Rust/Python) 는 런타임에 초기화(Initialize) 될 때 이 configuration 블록을 딕셔너리(Dictionary) 형태로 넘겨받아 자기 자신을 세팅하게 된다.
즉, 똑같은 camera.so 파일이라도, main_camera 와 rear_camera 2개의 인스턴스로 복제하여 YAMl 상에서 해상도만 다르게 주어 멀티 스레드로 가동하는 “오브젝트 팩토리(Factory)” 패턴의 극한을 보여준다.
3. 입력-출력 포트(Port) 정의 및 데이터 타입 매핑
한 필터(Operator) 가 입력을 2개 받아야 하는 경우(예: 센서 퓨전), 파이프를 어디다 꽂을지 포트의 “이름표” 가 필요하다.
3.0.1 [인스펙션] 멀티 쓰레드 타게팅 매핑
1. 노드의 포트 선언 (코드 레벨)
(개발자가 노드를 짤 때 포트에 이름을 둔다.)
Input Port 1:"image"Input Port 2:"lidar"Output Port:"fusion_result"
2. YAML 에서의 멀티 파이프 조립 (Link 맵핑)
포트 이름이 여러 개인 노드에 파이프를 꽂을 때는 반드시 node_id:port_name 포맷을 써서 정확히 타격(Targeting) 해야 한다.
links:
# 카메라 데이터는 fusion_center 의 "image" 구멍으로!
- from: "camera_src"
to: "fusion_center:image"
# 라이다 데이터는 fusion_center 의 "lidar" 구멍으로!
- from: "lidar_src"
to: "fusion_center:lidar"
포트의 데이터 타입(Data Type)은 현재 Zenoh-Flow 가 강제 검증(Validation) 을 엄격히 안 하긴 하지만, 보내는 쪽과 받는 쪽의 크기(Byte) 나 스키마(Protobuf) 가 다르면 코어 단에서 패닉(Segfault) 이 날 수 있으므로 아키텍트의 메뉴얼적인 방어 지휘가 절실하다.
4. 링크 매핑을 통한 방향성 비순환 그래프(DAG) 파이프라인 정의
물은 흐르다가 뒤로 역류하면 파이프가 터진다.
4.0.1 [인스펙션] 순환 참조의 독약 방어
Zenoh-Flow 는 아키텍처 구조상 “방향성 비순환 그래프 (DAG - Directed Acyclic Graph)” 만을 허용한다.
1. 사다리타기(Fork) 와 합치기(Join) 는 무제한 허용
- 1개의 카메라 영상(Source) 이 3개의 서로 다른 오퍼레이터(안면인식, 객체추적, 녹화) 로 동시에 쫙 갈라지는 건 0.01ms 의 오버헤드도 없이 아주 잘 돌아간다. (포인터 복사).
2. 역류(Cycle) 의 금제
## ❌ 절대 금지된 행위 (죽음의 사이클)
links:
- from: "A" to: "B"
- from: "B" to: "C"
- from: "C" to: "A" # A로 다시 돌아간다?
순환 고리가 생기는 순간 파이프라인 내부에 무한 루프 핑퐁이 쳐지면서 시스템 데몬 전체가 메모리 포화 락(Deadlock) 에 빠져 죽는다.
과거의 상태를 기억하고 싶다면, 파이프의 역류가 아닌 오퍼레이터 본체 안의 “자체 로컬 변수(State Context)” 저장 런북을 사용해야 한다. (13.2.3 장 참조).
5. 배치(Placement) 규칙 작성: 특정 노드를 지정된 에지나 클라우드 장비에 할당하기
이 책 전체를 관통하는 Zenoh-Flow 의 궁극기(Ultimate) 다. 데이터가 어디서 연산 될지를 물리적 장비 단위로 찢어발기는 지배의 영역.
5.0.1 [Runbook] 지리 공간적(Geo-Spatial) 강제 할당 전술
수백 대의 데몬이 퍼져 있는 광활한 네트워크에서, 무거운 인공지능 추론 필터를 로봇이 아닌 거대한 AWS GPU 서버에 심어버린다.
## 클라우드 추론 오퍼레이터 선언
operators:
- id: "heavy_ai_inference"
uri: "file://plugins/ai_model.so"
# 이 노드가 실행될 위치(기계)를 물리적으로 강제한다!
mapping: "aws_gpu_server_01"
[아키텍트의 승리 선언]
당신이 YAML 에서 mapping 한 줄을 수정한 것만으로, 다음과 같은 1만 줄짜리 인프라 코딩이 생략되었다.
- 개발자가 로봇에서 AWS 로 소켓(Socket) 을 열 필요가 없어졌다.
- IP 주소가 바뀌어도 Zenoh 라우터가
aws_gpu_server_01이라는 데몬 ID 를 추적해 다시 연결한다. - 데이터플로우 스케줄러가 로봇 단말단의 램(RAM) 과 CPU 연산을 완벽하게 우회하여, 거대한 연산량을 클라우드로 오프로딩(Off-loading) 해버린다.
에지와 클라우드의 경계선이 완전히 붕괴되는 클라우드 네이티브의 끝판왕이다.