docs(broker): expand PRIVATE_SERVER.md with versatility guide and create implementation_plan.md

This commit is contained in:
2026-08-20 11:59:53 +09:00
parent a9934ad104
commit 4025623958
6 changed files with 1170 additions and 45 deletions
+182 -45
View File
@@ -1,8 +1,8 @@
# 🔒 MAM 개인 전용 브로커(Private Broker) 구축 및 연동 가이드 (`PRIVATE_SERVER.md`)
- **작성일**: 2026-08-20
- **문서 목적**: MAM(Multi-Agent Mux)의 공개 브로커 의존성 및 보안 결함(A-2)을 해소하기 위한 개인 전용 브로커(NATS / Mosquitto) 구축, 운영 및 MAM 클라이언트 연동 가이드.
- **연계 문서**: [`NATS_REPORT.md`](NATS_REPORT.md), [`IMPROVEMENTS.md`](IMPROVEMENTS.md)
- **작성일**: 2026-08-20 (Rev.2)
- **문서 목적**: MAM(Multi-Agent Mux)의 공개 브로커 의존성 및 보안 결함(A-2)을 해소하기 위한 개인 전용 브로커(NATS / Mosquitto) 구축, 운영, 다능성 활용 및 MAM 클라이언트 연동 가이드.
- **연계 문서**: [`NATS_REPORT.md`](NATS_REPORT.md), [`implementation_plan.md`](implementation_plan.md), [`IMPROVEMENTS.md`](IMPROVEMENTS.md)
---
@@ -20,6 +20,8 @@
• 초저지연 (<1ms) 및 무제한 대역폭
```
MAM의 제어 평면(`run_loop.sh``wait_for_job` 파일시스템 폴링)은 브로커와 100% 독립적으로 작동하므로, 브로커는 **비동기 관측(observability) 사이드카** 역할을 수행합니다.
---
## 2. 해결 영역 매트릭스 (브로커 전환 vs 코드 패치)
@@ -46,8 +48,8 @@ MAM 클라이언트는 표준 `paho-mqtt`를 사용하므로, MQTT 3.1.1을 지
| 비교 항목 | 🏆 `nats-server` (강력 권장) | `eclipse-mosquitto` (대안) |
|---|---|---|
| **아키텍처** | Go 단일 정적 바이너리 (Zero Dependency) | C 기반 경량 오픈소스 브로커 |
| **주요 특징** | • 내장 MQTT 3.1.1 지원 (`-m 1883`)<br>• JetStream 엔진 내장 (이벤트 영속화 및 복구)<br>• NKey/JWT 기반 계정 및 Subject별 세분화된 ACL | • 가장 널리 쓰이는 표준 경량 MQTT 브로커<br>• 낮은 메모리 점유율 (~10MB) |
| **추천 용도** | 모던 인프라, 확장성, 감사 로그 영속화 | 정통 초경량 임베디드/IoT 스타일 환경 |
| **주요 특징** | • 내장 MQTT 3.1.1 리스너 (`mqtt { port: 1883 }`)<br>• JetStream 엔진 내장 (이벤트 영속화 및 복구)<br>• NKey/JWT 기반 계정 및 Subject별 세분화된 ACL<br>• WebSocket 및 NATS 네이티브 프로토콜 동시 서빙 | • 가장 널리 쓰이는 표준 경량 MQTT 브로커<br>• 낮은 메모리 점유율 (~10MB) |
| **추천 용도** | 모던 인프라, 확장성, 감사 로그 영속화, 홈랩 통합 | 정통 초경량 임베디드/단일 목적 환경 |
| **배포 난이도** | 🟢 바이너리 1개 실행 또는 Docker 1줄 | 🟢 패키지 매니저 (`apt`, `brew`) 또는 Docker |
---
@@ -56,19 +58,53 @@ MAM 클라이언트는 표준 `paho-mqtt`를 사용하므로, MQTT 3.1.1을 지
### 4.1 `nats-server` 배포 (권장)
#### 방법 A. Docker / Docker Compose (가장 간편)
`nats-server`에서 MQTT를 활성화하려면 설정 파일(`nats.conf`)에 `mqtt { port: 1883 }` 블록과 `jetstream { }` 블록이 반드시 포함되어야 합니다.
**단일 Docker 명령어 실행:**
> [!NOTE]
> `nats-server`의 `-m` 플래그는 HTTP 모니터링 포트(`--http_port`)를 지정하는 옵션이며, MQTT를 켜는 플래그가 아닙니다. MQTT 활성화는 반드시 `-c nats.conf` 설정 파일을 통해 구성해야 합니다.
#### 1) 공통 설정 파일 (`nats.conf`)
```conf
# nats.conf
server_name: mam-hub
# JetStream 영속 스토리지 (MQTT QoS 1 및 Retained 메시지 처리에 필수)
jetstream {
store_dir: "~/.local/share/nats/data" # Docker 환경에서는 "/data"로 매핑
max_file: 10G # 홈랩 디스크 상한 설정
}
# HTTP 모니터링 엔드포인트 (/varz, /jsz 대시보드)
http_port: 8222
# 평면 A: MAM MQTT 3.1.1 프로토콜 리스너
mqtt {
port: 1883
}
# 평면 B: 홈랩/웹 브라우저 대시보드용 WebSocket 리스너 (선택 사항)
websocket {
port: 8080
no_tls: true # 내부 사설망 한정
}
```
#### 2) 배포 방법 A. Docker / Docker Compose (권장)
**단일 Docker 실행:**
```bash
# 호스트에 nats.conf 생성 후 실행
docker run -d \
--name mam-nats \
--restart unless-stopped \
-p 1883:1883 \
-p 4222:4222 \
-p 8222:8222 \
-v /var/lib/nats/data:/data \
-p 8080:8080 \
-v ./nats.conf:/etc/nats/nats.conf:ro \
-v nats-data:/data \
nats:latest \
-js --sd /data -m 1883
-c /etc/nats/nats.conf
```
**Docker Compose (`docker-compose.yml`):**
@@ -80,31 +116,53 @@ services:
image: nats:latest
container_name: mam-nats
restart: unless-stopped
command: ["-js", "--sd", "/data", "-m", "1883"]
command: ["-c", "/etc/nats/nats.conf"]
ports:
- "1883:1883" # MQTT 3.1.1 포트
- "4222:4222" # NATS 기본 포트
- "8222:8222" # HTTP 모니터링 대시보드
- "1883:1883" # MQTT 3.1.1 포트 (평면 A: MAM)
- "4222:4222" # NATS 기본 포트 (평면 B)
- "8222:8222" # HTTP 모니터링 (/varz, /jsz)
- "8080:8080" # WebSocket (평면 B)
volumes:
- ./nats.conf:/etc/nats/nats.conf:ro
- nats-data:/data
volumes:
nats-data:
```
#### 방법 B. 네이티브 바이너리 설치 (Linux / macOS)
#### 3) 배포 방법 B. 네이티브 바이너리 설치 (macOS / Linux — 비루트 사용자 공간)
macOS의 sealed APFS 루트 볼륨(`/data`) 권한 문제를 방지하기 위해 사용자 홈 디렉터리(`~/.config/nats/`, `~/.local/share/nats/data`)를 기본 스토리지로 사용합니다.
```bash
# macOS (Homebrew)
brew install nats-server
nats-server -js -m 1883
# 설정 및 데이터 디렉터리 생성 (sudo 불필요)
mkdir -p ~/.config/nats ~/.local/share/nats/data
# Linux (x86_64 단일 바이너리 다운로드)
# 설정 파일 작성
cat <<'EOF' > ~/.config/nats/nats.conf
server_name: mam-hub
jetstream {
store_dir: "~/.local/share/nats/data"
max_file: 10G
}
http_port: 8222
mqtt {
port: 1883
}
websocket {
port: 8080
no_tls: true
}
EOF
# macOS (Homebrew 설치 및 실행)
brew install nats-server
nats-server -c ~/.config/nats/nats.conf &
# Linux (x86_64 단일 바이너리 설치 및 실행)
curl -L https://github.com/nats-io/nats-server/releases/download/v2.10.20/nats-server-v2.10.20-linux-amd64.tar.gz | tar xz
sudo mv nats-server-v2.10.20-linux-amd64/nats-server /usr/local/bin/
# 백그라운드 서비스 구동 (JetStream + MQTT 포트 1883 활성화)
nats-server -js --sd /var/lib/nats -m 1883 &
nats-server -c ~/.config/nats/nats.conf &
```
---
@@ -117,7 +175,7 @@ docker run -d \
--name mam-mosquitto \
--restart unless-stopped \
-p 1883:1883 \
-v /etc/mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf \
-v ./mosquitto.conf:/mosquitto/config/mosquitto.conf \
eclipse-mosquitto:latest
```
@@ -131,60 +189,139 @@ persistence_location /mosquitto/data/
---
## 5. MAM 클라이언트 연동 설정 (`.mam.env`)
## 5. 하나의 서버로 여러 프로젝트 — `nats-server` 다능성 (Versatility)
`nats-server`의 다능성은 **MAM을 네이티브 NATS로 이관할 이유가 아니라, MAM 코드를 한 줄도 바꾸지 않고도 얻을 수 있는 부가적 이득**입니다.
### 5.1 두 개의 소비 평면 (Two Consumption Planes)
`nats-server`는 단일 프로세스 내에서 여러 프로토콜 리스너를 동시에 구동하므로, MAM의 단순성과 개인 홈랩의 확장성을 완벽히 양립시킵니다.
```
┌───────────────────────────────────────────────────────────┐
│ nats-server (단일 인스턴스) │
├─────────────────────────────┬─────────────────────────────┤
│ 평면 A: MAM 워크로드 │ 평면 B: 홈랩/개인 프로젝트 │
├─────────────────────────────┼─────────────────────────────┤
프로토콜 │ MQTT 3.1.1 (포트 1883) │ NATS(4222), WebSocket(8080) │
클라이언트 │ paho-mqtt (코드 변경 0줄) │ nats-py, nats.js, CLI 등 자유 │
사용 기능 │ QoS 1, Retain, 와일드카드, TLS│ JetStream 리플레이, KV, Object│
설계 원칙 │ 초경량 동기 CLI 핫패스 보존 │ 고급 비동기 이벤트 스트리밍 │
공유 자원 │ └───── 단일 정적 바이너리 / JetStream 스토리지 / ACL ─────┘│
└───────────────────────────────────────────────────────────┘
```
### 5.2 교차 프로토콜 브리징 (Cross-Protocol Bridging)
- `nats-server`는 내부적으로 MQTT 토픽(`/`)을 NATS Subject(`.`)로 실시간 자동 변환합니다.
- MAM 에이전트가 MQTT 토픽 `python/mqtt/jobs/<job_id>/events`로 이벤트를 발행하면, 웹 브라우저나 타 프로젝트의 NATS 구독자는 NATS Subject `python.mqtt.jobs.<job_id>.events` 또는 `python.mqtt.jobs.*.events`로 즉시 실시간 수신할 수 있습니다.
- **실용적 이점**: MAM 소스 코드를 단 1줄도 수정하지 않고도 React/Vue 웹 대시보드나 터미널 모니터링 툴을 즉시 부착할 수 있습니다.
- **주의 사항**: 토픽 레벨 내에 마침표(`.`)가 포함되면 NATS 계층에서 토큰이 분리될 수 있으나, MAM의 `job_id`는 8자리 hex, 워크스페이스 지문은 12자리 hex이므로 안전합니다.
### 5.3 JetStream 이벤트 리플레이 (Event Replay)
- `python.mqtt.jobs.>` Subject를 구독하는 JetStream 스트림을 생성하면, 지난 작업의 이벤트 스트림 전체를 시점 지정(Time-based) 또는 시퀀스 지정(Sequence-based)으로 사후 리플레이할 수 있습니다.
- **주의 사항**: 이 기능은 옵트인(Opt-in)이며, MQTT QoS 1 처리를 위한 내부 시스템 스트림(`$MQTT_*`)과 별개로 관리됩니다. 디스크 용량 관리를 위해 `max_age``max_bytes` 상한을 반드시 설정해야 합니다.
### 5.4 내장 Key-Value (KV) 및 Object Store
- 홈랩 및 개인 프로젝트에서 Redis나 MinIO 같은 별도 인프라를 띄우지 않고도 `nats-server` 내장 KV 및 Object Store를 즉시 사용할 수 있습니다.
- **금지 사항 (Non-Goal)**: MAM의 로컬 레지스트리(`.mam/jobs/*.json`)를 JetStream KV로 대체해서는 안 됩니다 (`wait_for_job`의 fcntl 및 파일시스템 폴링 계약 유지).
### 5.5 멀티테넌트 계정 분리 및 보안
- 단일 서버 내에서 `MAM` 전용 계정과 `HOME` 개인 계정을 분리하여 리소스 쿼터와 권한을 완벽히 격리할 수 있습니다.
- **권고 배치**: MAM과 이를 관측하는 대시보드는 동일한 계정(`MAM`)에 배치하고, 무관한 홈랩 서비스는 별도 계정(`HOME`)에 배치합니다.
---
## 6. MAM 클라이언트 연동 설정 (`.mam.env`)
개인 서버 브로커가 구동되면, MAM 저장소 루트의 [`.mam.env`](file:///.mam.env) 파일에 개인 서버 주소를 등록합니다.
> [!NOTE]
> MAM 코드(`mqtt_common.py`)는 `MQTT_*` 접두사의 환경변수를 읽습니다. 이전 비공식 문서의 `MAM_MQTT_*` 변수는 무효하므로 반드시 아래의 표준 변수명을 사용해야 합니다.
```bash
# ==============================================================================
# MAM Private MQTT Broker Configuration
# MAM Private MQTT Broker Configuration (.mam.env)
# ==============================================================================
# 개인 서버 IP 또는 도메인
MAM_MQTT_HOST="192.168.1.100" # 예: 10.0.0.5, mqtt.my-domain.com 등
MQTT_BROKER="192.168.1.100" # 예: 10.0.0.5, mqtt.my-domain.com 등
# MQTT 기본 포트 (평문 TCP: 1883, TLS 암호화: 8883)
MAM_MQTT_PORT="1883"
MQTT_PORT=1883
# TLS 암호화 활성화 여부 (사설 내부망: false, 공인망 노출 시: true)
MAM_MQTT_TLS="false"
# TLS 암호화 활성화 여부 (0: 평문 TCP, 1: TLS 암호화)
MQTT_TLS=0
# 인증 설정 (인증 미설정 브로커는 주석 처리 또는 빈 문자열 유지)
# MAM_MQTT_USERNAME="my_agent_user"
# MAM_MQTT_PASSWORD="my_secure_password"
# 인증 설정 (익명 브로커는 주석 처리 또는 빈 문자열 유지)
# MQTT_USERNAME=my_agent_user
# MQTT_PASSWORD=my_secure_password
# TLS 인증서 경로 (MQTT_TLS=1 설정 시 사용)
# MQTT_CA_CERTS=/path/to/ca.crt
# MQTT_CERTFILE=/path/to/client.crt
# MQTT_KEYFILE=/path/to/client.key
```
*참고: OS 환경변수에 동일한 이름이 이미 `export`되어 있는 경우 OS 환경변수가 `.mam.env` 파일 설정보다 우선합니다.*
---
## 6. 연동 및 동작 검증 테스트
## 7. 연동 및 동작 검증 테스트 (4-Step Verification)
개인 서버 브로커가 정상 동작하는지 MAM 자체 도구로 즉시 검증할 수 있습니다.
개인 서버 브로커와의 연동 상태를 정확하게 검증하는 4단계 절차입니다.
### Step 1. 브로커 연결 테스트 (단일 이벤트 발행)
### Step 1. 브로커 리스너 및 JetStream 상태 확인
```bash
# 임시 테스트 이벤트 발행 (반환 코드 rc=0 단언)
.venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py \
--job test-ping-01 \
--event progress \
--detail "Private broker connection verified"
# MQTT 리스너 활성화 확인
curl -s http://192.168.1.100:8222/varz | grep -i mqtt
# JetStream 엔진 정상 구동 확인
curl -s http://192.168.1.100:8222/jsz
```
### Step 2. 전체 회귀 테스트 검증 (276건)
### Step 2. 임시 잡 등록 및 연결 검증 이벤트 발행
`publish_event.py`는 레지스트리에 등록된 잡에 대해서만 발행을 수행하므로, 임시 잡을 등록하고 발행한 후 완료 처리합니다.
```bash
# 1) 임시 잡 등록 (자동 채번된 JID 캡처)
JID=$(.venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/registry.py \
--registry-dir .mam/jobs \
register \
--prompt "Private broker connectivity test" \
--agent-session "herdr:test")
echo "registered test job: $JID"
# 2) 이벤트 발행 (상세 로그 출력 및 rc=0 단언)
.venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py \
--registry-dir .mam/jobs \
--job "$JID" \
--event progress \
--detail "Private broker connection verified" -v
# 3) 테스트 잡 종결 처리 (미종결 시 --wait-any 유령 잡 잔존 방지)
.venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/registry.py \
--registry-dir .mam/jobs status --job "$JID" --set completed
```
### Step 3. 접속 대상 브로커 IP 단언
Step 2의 `-v` 출력 로그 또는 `.mam/delegate_job_logs/$JID/events.ndjson` 파일에서 실제 접속 호스트가 개인 브로커 IP로 나타나고 `broker.hivemq.com`이 포함되지 않았는지 확인합니다.
### Step 4. 단위 회귀 테스트 검증
```bash
.venv/bin/python -m pytest tests/ -q
```
*기존 276건의 회귀 테스트 스위트가 개인 브로커 환경에서도 100% 정상 통과합니다.*
*참고: MAM의 기본 단위/컴포넌트 테스트 스위트는 모의(Mock) 객체를 사용하므로 브로커 연결 여부와 무관하게 100% 통과합니다. 실제 네트워크 연동 검증은 Step 1~3이 담당합니다.*
---
## 7. 권장 실행 순서
## 8. 권장 실행 순서
```
[Phase 1: 내결함성 확보] ──> [Phase 2: 개인 브로커 가동] ──> [Phase 3: A-2 보안 완전 종결]
Track 0 (B-14, B-15) nats-server / mosquitto 지문 토픽 및 인증 토큰 발급
Track 0 (B-14, B-15) nats-server (nats.conf) 지문 토픽 및 인증 토큰 발급
로컬 디스크 폴백 패치 .mam.env 환경변수 연동 외부 간섭 100% 차단
```
1. **Phase 1 (Track 0 선행 패치)**: `publish_event.py``job_subscriber.py`의 로컬 디스크 폴백(`B-14`, `B-15`)을 먼저 수정하여 브로커 다운 시에도 루프가 멈추지 않는 방탄 구조를 확립합니다.
2. **Phase 2 (개인 브로커 가동)**: 개인 서버에 `nats-server -js -m 1883`을 띄우고 `.mam.env` 연결합니다.
1. **Phase 1 (Track 0 선행 패치)**: `publish_event.py``job_subscriber.py`의 로컬 디스크 폴백(`B-14`, `B-15`)을 먼저 적용하여 브로커 다운 시에도 루프가 멈추지 않는 방탄 구조를 확립합니다.
2. **Phase 2 (개인 브로커 가동)**: 개인 서버에 `nats-server -c nats.conf`를 구동하고 `.mam.env``MQTT_BROKER` 연결합니다.
3. **Phase 3 (A-2 보안 완전 종결)**: 워크스페이스 지문 토픽(`mam/<sha256[:12]>/jobs/...`) 및 무조건 토큰 발급을 적용하여 공개 브로커 위험을 완전히 영구 폐기합니다.