노드를 복제·붙여넣기로 늘리면 중복 launchd Label같은 포트에 붙은 유령 데몬이 남기 쉽습니다. LB 가중치만으로는 로컬 이중 등록을 고칠 수 없습니다. doctor --deep서비스 디스커버리 스캔으로 쓰세요: 먼저 대조하고, 라벨을 정리한 뒤, 포트 슬라이스로 카나리를 싣고, 마지막에 웹훅 실패 요약 브로드캐스트로 다중 노드 상태를 한 통에 맞춥니다.

멀티 AZ 카나리·스킬 슬라이스·롤링 업그레이드는 트래픽·가중치 쪽이고, 본문은 로컬에 권위 프로세스가 하나인지에 집중합니다. 병합·경계: 워크플로 카나리 HowTo·테넌트·웹훅·게이트웨이·웹훅·블로그 홈.

deep가 필요한 이유: 카나리 트래픽 글과의 분업

카나리 글은 보통 Mac당 권위 프로세스 하나를 가정하지만, CI·수동 bootstrap·남은 plist로 이중 등록이 납니다. openclaw doctor --deep(바이너리 기준)가 설정·소켓·launchd·경로를 교차해 JSON으로 내리면 "어느 plist가 거짓인지"가 정렬 목록이 됩니다.

doctor --deep: 최소 대조 필드

감사 필드: label_authority, listeners, canary_slice. 노드마다 doctor-deep-${HOST}.jsonjq로 차이 냅니다. 한 대만 중복이면 수동 복사 LaunchDaemons를 먼저 봅니다.

set -euo pipefail
/usr/local/bin/openclaw doctor --deep --json >"/var/db/openclaw/audit/doctor-deep-${HOSTNAME}-$(date -u +%Y%m%dT%H%M%SZ).json"
/usr/bin/lsof -nP -iTCP -sTCP:LISTEN | /usr/bin/grep -E 'openclaw|gateway' || true

중복 launchd 라벨 정리(최소 재현)

원칙: 같은 Label에는 권위 plist 하나만 둡니다. 나머지는 launchctl bootout/Library/LaunchDaemons(또는 팀 표준 경로)에서 콜드 스토리지로 옮기고, bootstrap은 단일 파일만 합니다. plist 지문 없이 상태를 모른 채 kickstart -k를 연속 두 번 치는 것은 금지입니다. 경쟁 상태가 "가끔 성공"으로 숨습니다.

  • ① 충돌 나열: launchctl print system | grep -A2 com.openclaw(접두사는 실제 Label로 바꿈)과 deep JSON을 나란히 둡니다.
  • ② 쓰기 동결: 병합기·자동 reload를 잠시 멈춰 라벨 정리 중 반쯤 쓰인 설정이 들어가지 않게 합니다.
  • ③ 비권위 유닛 bootout: tar 백업 경로와 실행자를 티켓에 남깁니다.
  • ④ 권위 plist bootstrap: 다시 doctor --deeplisteners가 유일한지 확인합니다.

카나리 포트 슬라이스: LB 가중치 다음 게이트

OPENCLAW_GATEWAY_PORT(예시) 등으로 카나리를 보조 포트에 붙이고, 로컬 프록시가 소비율만 넘깁니다. 이중 launchd가 있어도 슬라이스로 반경을 줄입니다. 병합 프로브에 primary_port·canary_port를 같이 넣어 오판을 막습니다.

export OPENCLAW_GATEWAY_PORT="${CANARY_PORT:-18088}"
export OPENCLAW_PROBE_TAGS="canary_slice=port:${OPENCLAW_GATEWAY_PORT}"
/usr/local/bin/openclaw doctor --deep --json | /usr/bin/tee /tmp/deep.json

웹훅 실패 요약 브로드캐스트: deep·포트 필드 포함

5분 창 요약 JSON에 duplicate_labels, canary_port, doctor_deep_ok를 넣으면 당직이 가중치 롤백 vs 호스트 plist를 빠르게 갈립니다. 상세는 JSONL.

연습 단계 체크리스트(clustervps Mac 세 대)

  1. R0: 노드마다 LaunchDaemons 디렉터리·LB/리버스 프록시 가중치 JSON을 백업하고, 현재 리스닝 표를 기록합니다.
  2. 전 노드에서 doctor --deep --json을 돌려 중복 Label·포트 충돌 목록을 모읍니다.
  3. 카나리 노드에서 bootout→콜드 스토리지→권위 plist bootstrap 순으로 진행한 뒤 deep로 listeners 유일성을 확인합니다.
  4. 포트 슬라이스와 소비율 전달을 켜고, 병합 프로브를 두 주기 돌립니다.
  5. 아웃바운드 웹훅 4xx를 한 번 의도적으로 내고, 노티파이어가 단일 요약만 브로드캐스트하며 deep·포트 필드를 포함하는지 확인합니다.
  6. 나머지 노드는 한 대씩 ②—④를 반복하고, 매번 끝에 promotions.jsonl에 지문 한 줄을 append합니다.

롤백 표(R1 / R2)

조건영향조치
카나리만 deep가 여전히 중복 Label·포트 충돌을 보고단일 노드+슬라이스 트래픽R1: 실험 plist bootout, R0 tar 복원, 슬라이스 환경 변수 해제, 가중치를 동결 스냅샷으로.
여러 노드로 확장했는데 프로브가 넓게 적색전 클러스터R2: 호스트마다 순서대로 plist 복원·bootstrap 이전 유닛·리스닝 환경 복원; "R2 시작"과 호스트 목록을 브로드캐스트.
웹훅 요약 폭주·오탐알림 채널집계기 5분 끄고, deep 전부 녹색일 때까지 "노드당 한 줄 하트비트"로 강등.
롤백 검수: R1/R2 후에는 반드시 doctor --deep를 한 번 더 돌리고 jqduplicate_labels == 0을 단언합니다. 아니면 롤백 실패로 보고 가중치를 다시 올리지 마세요.

FAQ

deep가 느려 프로브를 망가뜨리나요? deep에 하드 타임아웃·캐시 필드를 두고, 초과 시 degraded로 표시하되 중복 Label은 여전히 하드 실패로 두세요.

사용자 영역과 시스템 영역 plist가 충돌하면? 사용자 로그인 항목을 먼저 동결하고 deep 출력으로 어느 쪽이 권위인지 정합니다. 같은 Label을 양쪽에 두지 마세요.

포트 슬라이스에 방화벽 구멍이 필요한가요? 카나리 포트는 기본적으로 로컬 리버스 프록시만 붙습니다. 대외는 여전히 단일 입구이며, 경계는 게이트웨이·웹훅 글과 같습니다.

운영 가이드입니다. doctor --deep·환경 변수 이름·Label 접두사는 팀 패키지 기준으로 맞추세요. 셸·jq 조각은 패턴 예시이며 그대로 비밀에 넣지 마세요.
로그인 없이 열람

deep 스캔·포트 슬라이스를 전용 Mac 런북에 넣기

공개 페이지로 예산·절차를 맞출 수 있습니다. 요금제·도움말·사이트 홈은 콘솔 로그인 없이 팀과 링크를 공유할 수 있습니다.

Mac 요금제 보기 도움말 열기