19.5 클라우드 환경으로의 지속적 배포(CD) 및 오케스트레이션
성공적으로 빌드 관문(CI)을 통과한 도커(Docker) 컨테이너는 이제 넓은 세상으로 나갈 준비가 되었다. 배포의 첫 번째 타겟은 전 세계적인 사물인터넷 망의 종착지이자 두뇌인 클라우드(Cloud) 환경이다.
클라우드 배포는 단순한 파일 복사나 “서버 한 대 켜기“가 아니다. 1초에 수십만 건의 메시지를 뿌려대는 전 세계의 Zenoh 에지 기기 트래픽을 흔들림 없이 수용하기 위해서는, 라우터가 죽으면 즉시 다른 라우터가 살아나고 트래픽이 몰리면 자동으로 라우터 개수가 10대에서 100대로 우후죽순 늘어나는 마법이 필요하다.
이를 통제하는 신적인 권능을 지닌 오케스트레이션 도구가 바로 쿠버네티스(Kubernetes, K8s) 다.
1. 왜 쿠버네티스여야만 하는가?
Zenoh를 단일 리눅스 서버에 띄운다면 systemctl start zenohd 한 줄로 끝난다. 하지만 클라우드 상에서 K8s를 도입하는 이유는 단언컨대 “불멸성(Immutability)과 자가 치유(Self-Healing)” 때문이다.
- 상태 머신의 일치: “항상 5대의 Zenoh 라우터가 켜져 있어야 한다“라고 K8s에 선언(YAML)해 두면, 특정 서버(Node)의 랜선이 뽑혀 라우터 2대가 날아가더라도 K8s가 즉시 다른 서버 노드를 찾아 2대의 공유 파드(Pod)를 1초 만에 부활시켜 버린다.
- 무결점 선언적 배포 (Helm 차트): 복잡하고 거대한 백엔드 라우터 설정, 스토리지 플러그인, 시크릿(Secret) 키들을 하나의 ’패키지’로 묶어 배포해야 한다. K8s의 패키지 매니저인 헬름(Helm)을 사용하면, 수백 줄의 인프라 스펙을 단 한 줄의
helm upgrade명령어로 완전히 통제할 수 있다.
graph TD
subgraph "CI Pipeline / Image Registry"
IMG[Zenoh v2.1 Image]
end
subgraph "Kubernetes Orchestration (Cloud)"
HELM[Helm Chart<br/>zenoh-cloud-router]
HELM -->|Deploy| DPLY(Deployment)
HELM -->|Config| CM(ConfigMap<br/>zenohd.json5)
HELM -->|Traffic| SVC(Service<br/>LoadBalancer)
DPLY -->|Spawn| P1[Zenoh Pod 1]
DPLY -->|Spawn| P2[Zenoh Pod 2]
DPLY -->|Spawn| P3[Zenoh Pod 3]
CM -.->|Mount| P1
CM -.->|Mount| P2
CM -.->|Mount| P3
SVC -->|L4 NLB 7447| P1
SVC -->|L4 NLB 7447| P2
SVC -->|L4 NLB 7447| P3
end
IMG -->|Pull| DPLY
이 장에서는 수동 배포를 죄악으로 규정하고, K8s 매니페스트(Manifest) 파일과 헬름(Helm) 패키징을 바탕으로, 클라우드 환경에 수백 대의 Zenoh 라우터를 완벽한 대형으로 전개(Deploy)하고 트래픽을 로드밸런싱(Load Balancing)하는 실전 아키텍처를 상세히 뜯어본다. 클라우드는 더 이상 하드웨어가 아니라, 끝없이 변형되는 거대한 소프트웨어 덩어리다.
2. Kubernetes 클러스터 활용: 클라우드 네이티브 배포 아키텍처
클라우드 환경에 Zenoh 백엔드 시스템을 전개한다는 것은 곧 쿠버네티스(K8s)라는 거대한 체스판 위에 Zenoh 모듈이라는 말(Piece)을 올려두는 행위와 같다. 단순한 도커 컨테이너를 넘어, K8s의 심장부로 들어가 Zenoh 라우터가 완벽하게 무리(Cluster) 지어 구동할 수 있도록 하는 4가지 핵심 객체(Object)의 배치를 해부한다.
2.1 [인스펙션] K8s 매니페스트(Manifest)를 통한 아키텍처 선언
어설픈 kubectl run 명령어 타건을 당장 멈추고, 모든 인프라스트럭처를 YAML이라는 법전에 기록하여 선언하라.
2.1.1 ConfigMap (설정 파일의 분리)
컨테이너 이미지 영역에서 설정 파일(zenohd.json5)을 뽑아내어 K8s의 뇌수(etcd)에 따로 저장해야 한다. 설정 하나 바꾸겠다고 무거운 도커 이미지를 매번 새로 빌드하는 짓은 바보나 하는 짓이다.
apiVersion: v1
kind: ConfigMap
metadata:
name: zenoh-router-config
data:
zenohd.json5: |
{
mode: "router",
listen: ["tcp/0.0.0.0:7447"],
plugins: {
storage_manager: {
volumes: [
{ id: "memory_vol", backend: "memory" }
]
}
}
}
2.1.2 Deployment (불멸의 용병단 창설)
실질적으로 컨테이너 파드(Pod)들을 띄우고 라이프사이클을 관리하는 메인 심장부다. 라우터를 3대 띄워서 무결점의 교차 네트워크망을 구축하도록 지시한다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: zenoh-cloud-router
spec:
replicas: 3 # 죽어도 죽어도 계속 3대를 유지하라는 абсолют 명령
selector:
matchLabels:
app: zenoh
template:
metadata:
labels:
app: zenoh
spec:
containers:
- name: zenohd
image: my-repo.io/zenoh:v2.1
ports:
- containerPort: 7447
name: tcp-routing
volumeMounts:
- name: config-volume
mountPath: /etc/zenoh/zenohd.json5
subPath: zenohd.json5
volumes:
- name: config-volume
configMap:
name: zenoh-router-config
2.1.3 Service (트래픽의 입구 창출)
3대의 라우터 파드들은 구동될 때마다 내부 가상 IP(10.x.x.x)가 무작위로 계속 변한다. 외부 에지 단말이나 K8s 내부의 다른 마이크로서비스(Node.js 백엔드 등)가 이 라우터들에게 안정적으로 트래픽을 던지려면 영원히 변하지 않는 단일한 ’문패(Service)’를 달아 주어야 한다.
apiVersion: v1
kind: Service
metadata:
name: zenoh-gateway
annotations:
# AWS NLB(Network LoadBalancer)를 강제하여 L4 단에서 순수 패킷을 밀어 넣도록 세팅
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
type: LoadBalancer # 외부 퍼블릭망에 포트를 까발라 노출함
ports:
- port: 7447
targetPort: 7447
protocol: TCP
selector:
app: zenoh # Deployment의 라벨과 일치하는 녀석들에게만 트래픽 분산
이 3개의 덩어리 시트(YAML)를 kubectl apply -f로 K8s 컨트롤 플레인에 던지는 순간, 마법처럼 클라우드의 로드밸런서가 할당되고 그 뒤에 3대의 Zenoh 라우터가 도열하여 전 세계에서 쏟아지는 트래픽을 받아먹을 준비를 마친다. 배포 파이프라인의 임무는 새로운 코드를 찍어낼 때마다 Deployment YAML의 image: 속성 문자열만 새 버전 태그(Tag)로 쓱 밀어 넣어주는 것, 오직 그것뿐이다.
3. 클라우드 네이티브 환경을 위한 Kubernetes(K8s) 클러스터 활용
Zenoh 라우터가 죽었다고 엔지니어가 새벽 3시에 일어나서 서버를 재부팅할 것인가? 쿠버네티스(Kubernetes) 는 인간을 대신해 좀비를 살려내는 불멸의 오케스트라다.
3.0.1 [인스펙션] 불멸(Immortal) 아키텍처 전술
라우터를 일반 프로세스가 아닌 파드(Pod) 로 승격시킨다.
1. Deployment 선언과 셀프 힐링(Self-Healing)
명령어가 아닌, YAML 스크립트로 신에게 소원을 빌 듯 인프라를 선언하라.
apiVersion: apps/v1
kind: Deployment
metadata:
name: zenoh-router
spec:
replicas: 3
template:
spec:
containers:
- name: zenoh
image: eclipse/zenoh:latest
이 10줄짜리 텍스트를 쿠버네티스 마스터에 밀어 넣는 순간, 마스터 노드는 즉각 서버 3대에 zenoh-router 를 하나씩 띄운다. 만약 물리 서버 1대가 불타서 꺼져버리면(Node Failure)? K8s 는 0.1초 만에 상황을 인지하고 남은 생존 서버 쪽에 즉각 새로운 라우터 파드 하나를 다시 띄워(Reconcile) 무조건 replicas: 3 의 상태를 유지해 내는 무자비한 자가 치유력을 발휘한다.
4. Helm Charts를 통한 Zenoh 라우터 및 스토리지 백엔드 패키징
19.5.1에서 살펴본 개별 YAML 매니페스트 파일들(Deployment, Service, ConfigMap)은 강력하지만 아주 더러운 약점을 가지고 있다. 로봇 본사가 속한 “한국 클라우드 지사“와 “북미 클라우드 지사”, 그리고 “유럽 클라우드 지사” 3곳에 각각 Zenoh 라우터를 배포해야 한다면, 당신의 엔지니어 마우스를 죽도록 클릭하며 매번 IP 주소와 설정값을 하드코딩(Hard-coding)하여 복사-붙여넣기(Copy-Paste) 해야만 한다.
이렇게 파편화된 YAML 관리의 어리석음을 일격에 단절시키는 기술이 바로 쿠버네티스 패키지 매니저인 헬름(Helm) 이다.
4.1 [Runbook] Helm 기반 1타 3피 배포 템플릿화 전술
수만 줄의 YAML 매니페스트를 마치 함수처럼, 변수(Parameters)만 쓱 갈아 끼우면 다양한 환경의 클라우드로 찍어낼 수 있는 ’빵틀’로 승격시켜라.
4.1.1 Values.yaml 뼈대 설계
Helm 차트는 컨테이너 이미지가 되는 Template 영역과 고기를 채워 넣는 Values 영역으로 완벽히 통제된다.
values.yaml 에 배포 시 변경될 소지가 있는 모든 타겟 인자들을 중앙 집중화시킨다. 인간은 오직 이 파일만 조작한다.
## zenoh-router/values.yaml (유럽 리전 배포용 세팅 값 예시)
replicaCount: 5 # 유럽은 트래픽이 많으므로 5대 띄움
image:
repository: private-nexus.io/zenoh-router
tag: "v1.5.0" # 파이프라인(CI)이 배포 직전 이 숫자만 정규식으로 덮어씀
zenohConfig:
# 인프라 종속적인 라우팅 타겟 명세
peer_endpoints:
- "tcp/uk-region.zenoh.io:7447"
- "tcp/de-region.zenoh.io:7447"
storage:
enabled: true
backend: "rocksdb"
pvc_size: "100Gi" # RocksDB 디스크 저장 공간
4.1.2 Template 마법과 스토리지(Backend) 동적 결합
Zenoh 라우터에 데이터 영속성(Persistence) 부여하기 위해 외부 볼륨 스토리지를 꽂는 행위(Zenoh Storage Backend)는 Helm의 if 문기 조건식(Jinja-like) 템플릿 마법으로 동적으로 접합된다.
## zenoh-router/templates/deployment.yaml
apiVersion: apps/v1
kind: StatefulSet # PVC(Persistent Volume)를 붙이기 위해 Deployment 대신 사용
metadata:
name: {{ include "zenoh.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }} # values.yaml의 5가 주입됨
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
{{- if .Values.storage.enabled }}
# 스토리지 활성화 옵션이 켜져 있을 때만 볼륨 영역이 동적으로 생성됨!
volumeMounts:
- name: zenoh-storage-data
mountPath: /var/zenoh/data
{{- end }}
{{- if .Values.storage.enabled }}
# 고성능 RocksDB 데이터를 보존하기 위한 SSD 블록 스토리지 자동 프로비저닝 요구
volumeClaimTemplates:
- metadata:
name: zenoh-storage-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: {{ .Values.storage.pvc_size }} # values.yaml의 100Gi 할당
{{- end }}
4.1.3 무지성 타격 커맨드 라인
이제 Jenkins, GitHub Actions, ArgoCD 어떤 것이든 클라우드 배포 스텝의 명령어는 단 한 줄로 극도로 축소된다.
## 네임스페이스 'eu-central' 영역에, tag만 1.5.1로 오버라이드하여 무중단 설치/업그레이드
helm upgrade --install zenoh-cloud-eu ./zenoh-router-chart \
--namespace eu-central \
--set image.tag="v1.5.1"
코드 한 줄이 100기가바이트의 SSD 디스크를 5대 깎아내고 컨테이너를 구동시킨 뒤 라우터 간 클러스터망을 구성한다. 헬름(Helm)은 인프라 배포의 재사용성을 스크립트 수준을 넘어선, 이식 가능한 소프트웨어 패키지(Software Package) 등급으로 상향 시킨 쿠버네티스 시대 가장 완벽한 지휘자다.
5. Helm Chart를 이용한 Zenoh 라우터 및 스토리지 백엔드 패키징/배포
K8s 의 YAML 파일은 강력하지만, 개발망(Dev), 테스트망(Staging), 운영망(Prod) 마다 라우터 설정(JSON) 과 IP 를 일일이 바꿔가며 YAML 을 수십 개씩 복사/붙여넣기 할당할 것인가?
5.0.1 [Runbook] 헬름(Helm) 패키징 템플릿 전술
수만 줄의 복잡한 YAML 파일 뭉치를 단 하나의 압축 파일(Chart) 로 포장해 우아하게 배포한다.
1. 환경 변수화 (values.yaml)
라우터 포트, 플러그인 유무, 스토리지 크기 같은 “가변적인 설정 값” 을 모조리 values.yaml 에 몰아넣고, 실제 쿠버네티스 YAML 에는 {{ .Values.routerPort }} 처럼 빈칸을 뚫어놓는다(Templating).
2. 논리적 패키지(Bundle) 설치
운영 서버에 배포하고 싶다면 다음 줄 하나면 끝난다.
helm install zenoh-prod ./zenoh-chart -f values-prod.yaml
이 클릭 한 번에 Zenoh 라우터 파드, zenoh.json 이 담긴 ConfigMap, 통신을 열어주는 Service, 스토리지를 구워줄 PVC, 그리고 백엔드로 붙을 InfluxDB 까지 5구역의 인프라가 완벽하게 통일된 톱니바퀴로 맞물려 1초 만에 구동되는 기적을 맛볼 수 있다.
6. Zenoh 클라우드 노드의 고가용성(HA) 확보 및 오토스케일링(Autoscaling) 설정
새해 첫날 로봇 트래픽이 평소의 10배로 쏟아지면, 3대로 맞춰둔 라우터 파드는 메모리 부족(OOM) 으로 전멸한다. 트래픽의 파도(Wave) 에 맞춰 인프라가 스스로 덩치를 불려야 한다.
6.0.1 [인스펙션] 탄력적 확장(Elasticity) 전술
사람이 개입하지 않는 자동 스케일-아웃(HPA) 엔진 가동.
1. CPU 기반 수평적 파드 오토스케일러(HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zenoh-hpa
spec:
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
이 룰이 켜지면, K8s 는 Zenoh 라우터들의 CPU 평균 사용량을 감시한다. 70% 를 넘기는 순간, K8s 는 라우터 파드를 4개, 5개, 순식간에 최대 20개까지 늘려 파도를 분산(Load Balance) 시킨다.
2. 라우터 상호 인지(Peer Discovery) 보장
여기서 맹점은, 새로 생겨난 라우터 17대가 기존 라우터 3대와 서로 묶여야(Gossip) 한다는 점이다.
쿠버네티스의 Headless Service 를 활용하여, 오토스케일링으로 몇 대가 생겨나든 간에, 모든 라우터가 서로의 뚝배기(IP) 를 도메인 네임(DNS) 기반으로 즉시 찾아내 그물망(Mesh) 을 확장하도록 설정해야 “파편화된 라우터 섬” 현상을 막을 수 있다.
7. 클라우드 Zenoh 노드의 고가용성(High Availability) 및 오토스케일링(Autoscaling)
평상시엔 로봇들이 간헐적인 상태 정보(Telemetry)만 쏘다가, 갑자기 사고 발생이나 펌웨어 동시 업데이트 데이(Event Day)를 맞이하여 10,000대의 로봇이 초당 100MB 크기의 카메라 이미지와 덤프 데이터를 클라우드 메인 라우터로 즉각적으로 폭격하기 시작했다고 가정하자.
고정된 3대의 Zenoh 라우터 파드(Pod)로는 이 급증하는 네트워크 트래픽의 병목(Bottleneck) 현상을 처리하기 어려울 수 있으며 패킷 손실과 OOM(Out of Memory)으로 인한 라우터 연쇄 장애를 맞이하게 된다. 따라서 인프라 파이프라인은 정해진 규칙에 따라 서버 개수를 10대, 20대 단위로 동적으로 확장(Autoscaling)하는 매커니즘을 갖추어야 한다.
7.1 쿠버네티스 HPA(Horizontal Pod Autoscaler) 수평 확장 탄도학
서버 자원이 포화되는 즉시 새로운 컨테이너(가상 라우터)를 복제 소환하는 쿠버네티스의 심장 박동 조정기(HPA)를 인프라 코드에 편입시켜라.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: zenoh-cloud-scaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: zenoh-cloud-router
minReplicas: 3 # 평온할 때 지켜야 할 최소 라우터 부대원 수
maxReplicas: 30 # 트래픽 쓰나미가 왔을 때 소환 가능한 최대 한계치
metrics:
- type: Resource
resource:
name: cpu
# CPU 사용률이 70%를 넘어가는 순간, 새로운 형제(Pod)를 낳도록 설정
target:
type: Utilization
averageUtilization: 70
에지 디바이스들의 트래픽 폭주로 K8s 워커 노드의 CPU 점유율이 임계값을 돌파하는 찰나의 순간, HPA는 즉각 4번째, 5번째 Zenoh 파드를 스핀 업(Spin-up) 시킨다.
새로 탄생한 파드들은 부팅을 마친 즉시 zenohd의 피어링(Peering) 로직에 의해 자동으로 기존 라우터망과 tcp/localhost 혹은 tcp/svc-name 을 기반으로 멀티캐스트 혹은 선언적 토폴로지 융합을 단행하며 데이터 방어선의 벽을 그 즉시 거대하게 넓힌다.
7.2 안티-어피니티(Anti-Affinity)를 통한 화재 번짐 격리 전술
고가용성(High Availability, HA)이란 단순히 대수가 많은 것을 의미하지 않는다.
라우터 파드 10대를 배포했을 때, K8s 스케줄러가 이 10대를 단일 물리적 호스트 서버(EC2 Node)에 모두 배치할 경우가 발생할 수 있다. 만약 해당 서버에 장애가 발생한다면, 라우터 10대가 동시에 작동 라인에서 이탈하여 단일 장애점(Single Point of Failure)에 의한 서비스 마비를 경험하게 된다.
이를 방어하기 위해서는 안티 어피니티(Anti-Affinity) 기술을 디플로이먼트 매니페스트에 주입하여 파드들을 뿔뿔이 흩어놓아야 한다.
## Zenoh Deployment의 Spec 하단부
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- zenoh
# 절대로 동일한 K8s 워커 호스트 노드(hostname)에 나의 복제본(형제 파드)을
# 같이 위치시키지 말라는 신성한 명령이다.
topologyKey: "kubernetes.io/hostname"
이 코드를 박아 넣음으로써, Zenoh 라우터들은 마치 자기장에 밀려나듯 각기 다른 물리 노드(또는 다른 가용 영역인 AZ, Availability Zone)로 강제로 뿔뿔이 분산 배치된다. 데이터 센터 하나에 불이 나 폭발하더라도(예: AWS ap-northeast-2a 소실), 다른 가용 영역(ap-northeast-2b)에 위치한 생존 라우터들이 즉시 트래픽을 넘겨받아 망을 유지시키는 진정한 불멸의 클러스터(Indestructible Cluster) 아키텍처를 완성하는 것이다.
8. 클라우드 로드밸런서 및 외부 네트워크 Ingress 구성 시 주의사항
지상은 지옥이고 K8s 클러스터 심층부는 천국이다. 에지의 거친 데이터가 퍼블릭 인터넷 망을 뚫고 K8s 파드(Pod)라는 성역 안으로 온전하게 들어오기 위해서는 성벽의 대문, 즉 외부 네트워크 게이트웨이(Ingress / LoadBalancer) 시스템을 철저하게 해킹 및 최적화해야만 한다.
아마추어 엔지니어들은 Nginx Ingress 나 ALB(Application Load Balancer) 컨트롤러를 아무 생각 없이 프론트 단에 세운 채 Zenoh 클러스터를 연동하려 든다. 다음 날 아침, 그들은 “로봇의 패킷이 라우터에 전혀 도착하지 않습니다” 라며 밤을 새우게 될 것이다.
8.1 [인스펙션] L7 어플리케이션 성벽의 한계와 L4 포트 포워딩의 강행
Zenoh 프로토콜의 척추는 HTTP/1.1 이 아니라 순수 TCP 및 UDP(또는 QUIC)를 타고 넘나드는 독자적인 이진 규격(Binary Wire Protocol)이다.
- 재앙의 원리 (L7 Ingress의 패착): K8s의 기본 인그레스 객체(Nginx Ingress Controller 등)는 포트 80과 443으로 들어오는 트래픽이
HTTP양식에 맞게 텍스트로 적혀있으리라 맹신한다. Zenoh의 압축된 이진 헤더 패킷이 인그레스에 도착하면, Nginx 엔진은 “유효하지 않은 HTTP 요청“으로 판단하여 단 1 바이트의 데이터도 K8s 내부의 라우터로 통과시키지 않고 에러 400 문법 위반(Bad Request)으로 통신을 즉시 끊어(Drop)버린다.
따라서 우리는 웹 브라우저용 대문(L7)을 폐쇄하고 순수 포트 기반의 전용 고속도로 문맥(L4 Network Load Balancer)을 성벽 외곽에 파내야 한다.
8.1.1 AWS 환경 하드웨어 포워딩 (Network LoadBalancer)
퍼블릭 클라우드에서는 K8s Service 어노테이션(Annotation) 조작을 통해 L7 ALB 가 아닌 L4 NLB 프로비저닝을 강제 발동시켜 패킷 변형을 완전무결하게 막아낸다.
## 클라우드 로드밸런서 배포 스펙의 수정 (Service)
apiVersion: v1
kind: Service
metadata:
name: zenoh-external-gateway
annotations:
# AWS 컨트롤러에게 L4 NLB 네트워크 계층 생성을 지시
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
# 롱 폴링과 긴 구독(Subscription) 커넥션이 타임아웃으로 끊기지 않도록 여유 시간(초) 부여
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec:
type: LoadBalancer
ports:
- name: zenoh-tcp
port: 7447 # 에지가 바라보는 포트
targetPort: 7447 # 파드 안으로 던질 포트
protocol: TCP
8.1.2 사내 온프레미스 (Bare-metal) 환경에서의 NodePort 타격
여러분 회사의 자체 서버실에 K8s 클러스터 구축되어 있다면 비싼 클라우드 NLB를 사용할 수 없다. 이 경우, 모든 워커 노드의 물리적 포트 하나를 뚫어 포드를 직결시키는 짐승 같은 방식인 NodePort나 hostNetwork 브릿지를 최우선으로 투입하여 외부 방화벽 스위치 장비 라벨에 단일 IP 대역을 연결하라.
## 베어메탈 인프라 외부 트래픽 연결 스펙
spec:
type: NodePort
ports:
- port: 7447
targetPort: 7447
nodePort: 30047 # 30000~32767 사이의 물리망 포트를 뚫음
에지(로봇) 단말기 안에 있는 Zenoh 코드는 클라우드 접속 시 tcp/your-domain.com:30047 을 타겟으로 조준사격(Connect)하게 된다.
8.2 스티키(Sticky) 세션과 커넥션 무상태화 철학
Zenoh 의 통신 방식은 상태 비저장(Stateless) 라우팅을 기반으로 하기 때문에, 에지 디바이스 A가 로드 밸런서 뒤에 있는 1번 라우터 파드에 연결되든, 3번 라우터 파드에 연결되든 아무런 상관이 없다. 모든 1, 2, 3 번 라우터 파드 내부가 선언적 Gossip 통신(스카우트 스카웃, Scout)으로 묶여 데이터를 공유하고 있기(Peer Routing) 때문이다.
따라서 웹 개발에서 세션 정보 유지를 위해 사용하는 NLB의 지저분한 옵션인 “Sticky Sessions (세션 유지 기능)” 따위를 절대로 활성화하지 마라.
로드밸런서는 단순히 트래픽(Connection) 숫자만 보고 파드 간 개수를 공평하게 나누어주는 Round-Robin 또는 Least Connection 알고리즘 선에서 역할을 마무리 지어야만 시스템의 자율 생태계가 완벽히 보장된다.