19.3 코드형 인프라(IaC)를 활용한 Zenoh 네트워크 프로비저닝

19.3 코드형 인프라(IaC)를 활용한 Zenoh 네트워크 프로비저닝

분산 시스템의 규모가 로봇 10대와 클라우드 서버 1대 수준일 때는 엔지니어가 직접 SSH로 서버에 접속하여 셸 스크립트를 실행하고 설정 파일을 수정하는 ’수동 타이핑 방식(Click-Ops)’이 통할 수 있다. 하지만 그 규모가 전 세계 10개국의 공장, 10,000대의 로봇, 100대의 클라우드 라우터로 확장되는 순간, 수작업 인프라 관리는 곧바로 기업의 운영 중단(Downtime)과 극복 불가능한 기술 부채를 초래한다.

“개발망(Dev) 환경에서는 잘 돌아가던 Zenoh 통신이, 운영망(Prod)에서는 방화벽 설정 누락으로 통신이 되지 않는다” 와 같은 재난은 모두 ’인간의 손’을 거친 인프라 구성의 불규칙성에서 기인한다.

이 단원에서는 Zenoh 라우터가 올라갈 거대한 가상 머신(VM) 군단을 눈깜짝할 사이에 찍어내고, 에지 디바이스의 네트워크 환경을 100% 동일하게 복제(Clone)해 내는 코드형 인프라 (IaC, Infrastructure as Code) 철학을 인프라스트럭처에 이식하는 전술을 서술한다.

1. IaC가 Zenoh 인프라에 제공하는 사상적 가치

  1. 멱등성(Idempotency): 코드를 1번 실행하든 1,000번 실행하든 인프라의 최종 결과물 상태는 항상 동일해야 한다.
  2. 선언적 선언 (Declarative): “어떻게(How) 서버를 구성하라“가 아니라, “어떤 모습(What)으로 서버가 존재해야 한다“고 선언하라. 중간 과정은 도구가 스스로 연산한다.
  3. 버전 관리 (Version Control): 시스템 아키텍처는 단일한 머릿속이 아닌 Git 리포지토리에 저장되어야 하며, 인프라의 변경 이력(Audit) 역시 소프트웨어 코드처럼 추적 가능해야 한다.
graph TD
    subgraph "IaC 레포지토리 (Git)"
        TF[Terraform Code<br/>- K8s Nodes<br/>- VPC / Load Balancer]
        Ansible[Ansible Playbooks<br/>- Zenoh Config.json<br/>- Device Systemd]
        GitOps[Flux / ArgoCD<br/>- Helm Release<br/>- K8s Manifests]
    end

    subgraph "클라우드 프로비저닝 영역"
        TF -->|Apply| CloudProvider(AWS / GCP / Azure)
        CloudProvider --> K8sCluster[Kubernetes Cluster]
        CloudProvider --> NLB[Network Load Balancer]
    end

    subgraph "에지 프로비저닝 영역"
        Ansible -->|SSH Loop| EdgeNodes[Edge Raspberry Pi / Jetson]
        EdgeNodes --> ZRouter[Zenoh Router Daemon]
        EdgeNodes --> HostOS[IPTables / TLS Certs]
    end

    subgraph "지속적 상태 동기화"
        GitOps -.->|Observe & Pull| CloudProvider
        GitOps -.->|Config Sync| EdgeNodes
    end

우리는 위의 개념도를 골조로 삼아, 인프라 생성의 조물주격인 테라폼(Terraform), 에지 디바이스 일괄 통제의 마에스트로인 앤서블(Ansible), 그리고 인간의 개입 자체를 부정하고 기계가 스스로 인프라를 동기화하는 궁극의 사상 GitOps 까지 차례대로 전개하며 정밀 타격(Precision Strike)을 수행할 것이다.

2. Terraform을 이용한 클라우드 인프라(VM, VPC, Load Balancer) 자동 구성

클라우드 라우터(Cloud Router) 는 전체 에지 데이터를 받아내는 거대한 댐(Dam) 이다. AWS 콘솔에서 마우스를 클릭해 댐을 지으면, 나중에 그 댐이 통째로 날아갔을 때 똑같이 복구할 방도가 없다.

2.0.1 [인스펙션] 테라폼(Terraform) 조물주 전술

코드를 실행하면 AWS 인프라 전체가 1분 만에 펑, 하고 탄생한다.

1. 멀티 리전(Multi-region) 라우터 배포

resource "aws_instance" "zenoh_router" {
  count         = 3  # 라우터 3대 동시 생성
  ami           = "ami-ubuntu"
  instance_type = "t3.large"
  user_data     = file("install_zenoh.sh") # 부팅 시 Zenoh 자동 설치
}

2. 통신 방어벽(Security Group) 선언
테라폼 코드로 Zenoh 가 쓰는 TCP 7447 포트와 웹소켓용 8000 포트만 열리도록 철저하게 봉인한다.
“개발자가 실수로 22번 포트(SSH) 를 열어두었다” 따위의 재앙을 막기 위해 테라폼 코드 리뷰 시 방화벽 룰을 눈으로 확인하며 인프라의 투명성(Transparency) 을 확보한다.

3. Ansible을 활용한 에지 디바이스 일괄 설정 및 패키지 의존성 관리

테라폼(Terraform)이 클라우드의 조물주로서 밑바탕(AWS 계정, IP 대역)을 창조한다면, 앤서블(Ansible) 은 창조된 서버와 로봇들 안으로 파고들어 OS를 설정하고 패키지를 설치하며 데몬(Daemon)을 깨우는 정밀한 야전 사령관이다.

1,000대의 라즈베리 파이(에지 노드)가 존재할 때, 이 1,000대의 기기에 모두 똑같은 Zenoh 데몬을 띄우되, 각 기기의 zenoh.json 설정 파일 속 아이디(Locator/Locator ID)와 부여된 역할(Role) 값은 모두 미세하게 달라야 한다. 앤서블은 이 수천 가지의 미세 조정을 단일 스크립트로 묶어 일시에 병렬 타격하는 극강의 자동화 도구다.

3.1 [Runbook] 무결점 멱등성(Idempotency) 달성 런북

앤서블 스크립트(Playbook)의 제1 사상은 **“이 스크립트를 1번 실행하든 100번 실행하든 기기의 최종 결과 상태는 무조건 성공(OK)으로 동일해야 한다”**는 멱등성의 원칙이다. 파일이 없다면 생성하고, 이미 있다면 무시하며, 내용이 틀리다면 수정하라.

3.1.1 인벤토리(Inventory) 기반의 타겟 그룹 동적 분류

inventory.yaml 파일에 에지 디바이스들을 용도와 구역에 따라 명확히 조직한다.

## inventory.yaml
all:
  children:
    factory_a: # 공장 A 구역 기기들
      hosts:
        robot-a-01: { ansible_host: 192.168.1.101, node_role: "camera-sensor" }
        robot-a-02: { ansible_host: 192.168.1.102, node_role: "arm-controller" }
    factory_b: # 공장 B 구역 기기들
      hosts:
        drone-b-01: { ansible_host: 192.168.2.101, node_role: "lidar-sensor" }

3.1.2 Jinja2 템플릿 마법을 통한 설정 파일 변이 (Mutation)

모범적으로 작성된 하나의 zenohd.json.j2 빙틀(Template)을 만든 뒤, node_role과 같은 호스트별 특수 변수들을 주입하여 1,000개의 기기에 서로 다른 설정 파일을 1초 만에 박아 넣는다.

// templates/zenohd.json.j2
{
  // 인벤토리에서 주입받은 호스트명으로 고유 식별자 지정
  "node_id": "{{ inventory_hostname }}",
  "mode": "peer",
  "listen": ["tcp/0.0.0.0:7447"],
  
  // 기기 역할에 따라 연결할 엔드포인트 도메인을 분기 처리하는 템플릿 마법
  {% if node_role == 'camera-sensor' %}
  "connect": ["tcp/high-bandwidth-router.cloud:7447"],
  {% else %}
  "connect": ["tcp/low-bandwidth-router.cloud:7447"],
  {% endif %}
  
  "routing": {
    "pub_sub": true,
    "queries": true
  }
}

3.1.3 설정 변경 감지 및 데몬 자동 재부팅 (Handlers)

앤서블 플레이북에서 가장 중요한 것은 순서와 트리거(Trigger)의 설계다.
설정 파일을 배포하는 copy/template 테스크가 수행될 때, “만약 파일의 해시값(내용)이 바뀌었다면 OS 서비스 데몬에게 재시작 신호를 쏴라“라는 notify 핸들러 명세가 반드시 따라붙어야 한다.

## deploy_zenoh.yml (Playbook)
tasks:
  - name: 1. Zenoh 설정 파일(Template) 전송
    template:
      src: zenohd.json.j2
      dest: /etc/zenoh/zenohd.json
      mode: '0644'
    notify: Restart Zenoh Service # 파일이 변경되었을 때만 트리거됨

  - name: 2. Systemd 서비스 파일 등록 및 활성화 보장
    systemd:
      name: zenohd
      state: started
      enabled: yes  # 재부팅 시 자동 기동 보장

handlers:
  - name: Restart Zenoh Service
    systemd:
      name: zenohd
      state: restarted # 설정이 바뀌었으므로 프로세스를 우아하게 껐다 켬

이 플레이북을 수행하는 순간 앤서블은 1,000대의 갱도 로봇과 통신하며, 설정 파일이 어제와 똑같은 로봇들은 OK 모드로 신속하게 패스(Pass)하고, 코드가 변경된 몇몇 로봇들에게서만 파일 덮어쓰기(Changed) 후 데몬 재시작을 단행한다. 인간의 손으로 하나하나 비교하던 인프라 파일의 무결성이 기계의 논리 구조 앞에서 완벽하게 해소되는 순간이다.

4. GitOps 기반의 인프라 상태 관리 철학 적용 (ArgoCD, FluxCD 도입)

테라폼(Terraform)과 앤서블(Ansible)은 궁극적으로 무결한 코드를 만들어 내지만, 하나의 핵심적인 맹점을 지니고 있다. 그것은 어찌 되었든 **“엔지니어나 CI 봇이 특정 버튼(Enter)을 눌러 타겟 기기에게 푸시(Push)해야만 인프라가 변한다”**는 사실이다.

만약 통신 음영 지역에 깊숙이 들어가 있는 오프로드(Off-road) 자율주행 차량이 배포가 이뤄지는 순간 파이프라인의 명령을 수신하지 못했다면? 그리고 다음날 아침 터널을 뚫고 나와 와이파이가 붙었다면 이 차량의 상태를 어떻게 업데이트할 것인가. 중앙 CI/CD 시스템이 매일 아침 끊임없이 로봇들의 접속 상태를 체크하며 푸시(Push)를 시도하는 것은 비효율의 극치다.

이를 반전시키는 극단적인 역발상의 전술이 바로 인프라 상태 관리의 정점인 GitOps(깃옵스) 다.

4.1 [인스펙션] 끊임없는 바짓가랑이 잡기(Pulling) 전술

“인프라의 물리적 상태는 반드시 Git 리포지토리(Repository)에 적힌 코드와 완벽하게 일치해야 한다”

푸시 체계가 중앙의 권한을 탈취당한 파이프라인이 지시봉을 휘두르는 제국주의 시대라면, Pull 기반의 GitOps는 노드들 스스로가 헌법(Git)을 읽고 자신의 현재 꼬락서니를 헌법에 맞게 스스로 뜯어고치는 공화정의 시대다.

4.1.1 진실의 원천 (Single Source of Truth)

관리자는 운영 클러스터에 손을 대지 않는다. 오직 사내망에 위치한 GitHub/GitLab 에 zenoh-infrastructure-repo 라는 선언용 저장소를 하나 만든다. 이곳에 K8s YAML 파일, 라우터의 Helm Chart values.yaml, 로봇 군단의 K3s 매니페스트를 모조리 텍스트 형태로 적어 둔다.

## Git 리포지토리에 저장된 zenoh-router-deployment.yaml 파일 안의 명세
spec:
  containers:
  - name: zenoh-router
    image: eclipse/zenoh:1.0.1  # 엔지니어가 이 라인을 1.0.2 로 커밋(Commit)함

4.1.2 관찰자와 집행자 (Reconciliation Loop)

클라우드 서버(K8s)와 에지 로봇(K3s) 내륙에는 첩자처럼 상주하는 에이전트 다이몬(ArgoCD 또는 FluxCD)들이 구동 중이다.
수천 대의 이 데몬들은 쉴 새 없이 외부의 Git 리포지토리 주소를 향해 핑(Ping)을 때리며 코드가 바뀌었는지 내려다본다(Pull).

누군가 오늘 새벽 2시에 GitHub 의 코드 라인을 1.0.1 에서 1.0.2로 고치고 커밋(Commit)을 찍었다고 치자.
터널 안에서 며칠간 통신이 끊어졌던 차량 한 대가 다음날 아침 지상으로 올라와 5G 통신을 물었다.
차량 내부의 FluxCD 요원은 깨어나자마자 Git 서버에 붙어 헌법 서류의 변경점을 감지한다.

“앗! 내 배 속에 도는 코드는 1.0.1인데, Git 에는 1.0.2로 적혀있군. 불일치(Drift)가 발생했다!”

FluxCD 데몬은 인간의 지시를 기다리지 않는다. 스스로 원격 레지스트리에서 1.0.2 컨테이너 이미지를 다운받아 자신의 차량 OS 안에 돌고 있던 옛날 컨테이너를 도끼로 찍어내고 새 버전을 마운트해 일치(Sync)시켜버린다.

4.1.3 재난 시의 자체 수복 (Self-Healing)

만약 술에 취한 신입 사원이 현장에 나가 로봇 안에 임의로 접속하여 K8s 파드를 지워버리거나, 라이다 매개 변수를 만졌을 때 어떻게 되는가?

ArgoCD 데몬은 매 시간 기기 내부를 스다듬으며, “Git 저장소에 정의되지 않은 이물질 파드“가 생성된 것을 감지하자마자 냉혹하게 삭제 처분해 버리며, 잃어버린 파드는 즉시 다시 재가동해 원래의 구조로 복원시킨다.

명령어 기반 관리(Imperative)에서 선언적 관리(Declarative)로 넘어오며, 우리는 네트워크 단절을 오류(Error)가 아닌 일시적인 지연(Latency) 으로 이해하는 관점을 획득수 있었다. 기계가 스스로 완전성을 지켜내는 것, 그것이 에지-클라우드 대륙 횡단 파이프라인이 도달해야 할 가장 우아한 마지막 종착지다.