AI Frontier

2026년 macOS에서 OpenClaw 게이트웨이 포트 바인딩: EADDRINUSE를 lsof·LaunchAgent로 진단하고 헬스 프로브를 실제 클라이언트에 맞춘다

MacHTML Lab2026.05.11약 30분 읽기

2026년에도 OpenClaw 배포를 가장 빨리 멈추는 오류 중 하나는 Error: listen EADDRINUSE: address already in use입니다. 팀은 “포트가 점유됨”과 “모델 API 장애”를 혼동하고 키만 돌리다가, 어제 스모크 테스트에 남은 nodeTCP 8787을 쥐고 있는 사실을 놓칩니다. macOS에서는 launchd가 이전 소켓이 완전히 닫히기 전에 작업을 다시 올리거나, 여러 프로필이 임대 Mac mini를 나눠 쓰거나, 합성 헬스가 127.0.0.1만 두드리는데 게이트웨이는 LAN에 바인딩하는 일이 흔합니다. 이 글은 lsof → plist 일치 → 프로브 정렬 순서를 고정하고 doctor 게이트웨이 진단, 첫 실행 LaunchAgent 스모크, 헬스 모니터링과 연결해 추측을 줄입니다.

포트 충돌은 용량 사고로 기록하세요. 인터페이스, PID, LaunchAgent 레이블을 남기면 사후 분석이 짧아집니다.

포트 대역 설계 없이 blue/green을 하면 주말에 같은 번호로 이중 기동이 납니다. 표로 대역을 예약하세요.

업스트림 장애처럼 보이는 증상

연결 거부, 빈 curl 응답, CPU는 한가한데 대시보드만 “오프라인”일 수 있습니다. 모델 벤더를 비난하기 전에 HTTP 서버가 LISTEN에 도달했는지 확인하세요. 시작 중 크래시면 stderr가 배너 전에 끊길 수 있습니다. StandardErrorPathlaunchctl print 상태 전환을 맞춥니다.

공유 Mac mini에서는 잊힌 npm run dev가 흔하며 메트릭이 없어 Grafana에 나타나지 않습니다.

DNS나 TLS 문제도 비슷한 구름을 만듭니다. 순서는 소켓 → TLS → 업스트림입니다.

1분 안에 답이 나오는 lsof

lsof -nP -iTCP:8787 -sTCP:LISTEN

-n은 DNS 지연을 줄이고, -P는 숫자 포트를 보여 줍니다. 낯선 node PID는 종료 전에 ps -p PID -o args=로 명령줄을 남기세요. 아무도 listen하지 않는데 클라이언트만 실패하면 HTTPS 클라이언트 vs HTTP 서버 불일치를 의심합니다.

IPv6 전용이면 lsof -nP -i6TCP:8787을 추가하세요. 듀얼 스택이면 두 줄이 나올 수 있습니다.

LaunchAgent ProgramArguments

래퍼와 템플릿에서 --port가 두 번 오면 마지막이 조용히 이깁니다. 진실은 하나: EnvironmentVariables 또는 명시 인수. 수정 후 bootout 다음 bootstrap으로 이전 소켓을 닫습니다.

WorkingDirectory가 기대한 package.json이 있는 체크아웃을 가리키는지 확인하세요. 잘못된 cwd와 npx는 기본 포트에 두 번째 리스너를 만듭니다.

127.0.0.1, 0.0.0.0, LAN IP

루프백은 단순 LAN 스캔을 줄이지만 다른 호스트의 헬스를 깨뜨립니다. 0.0.0.0은 방화벽 규율이 필요합니다. 고정 사무실 IP는 DHCP 갱신에 깨집니다. Runbook과 openclaw doctor 기대치를 맞추세요.

거짓말하는 헬스 프로브

curl http://127.0.0.1:8787/readyz만 초록인데 원격 사용자는 10.0.40.12:8787에서 split VPN 때문에 실패할 수 있습니다. 프로브 소스 IP를 실제 사용자 경로에 맞추거나 같은 배스천으로 터널링하세요. 시작 시 gateway_bind_interface 메트릭을보내 Grafana에서 환경 드리프트를 보세요.

L4 헬스가 초록이어도 L7 인증이 실패할 수 있으니 최소 인증 HTTP를 함께 두세요.

결정 표

시나리오선호 바인딩주의
단일 노트북 랩127.0.0.1원격 데모 전에 전환
사내 방화벽 뒤 공유 Mac miniLAN IP + 화이트리스트DHCP 예약과 조율
공개 엣지 + 리버스 프록시loopback + nginx게이트웨이를 공개 인터페이스에 직접 노출하지 않기

TIME_WAIT와 빠른 재시작

30초마다 재시작하는 스크립트는 약 60초까지 TIME_WAIT를 남기거나 임시 포트를 고갈시킵니다. bootoutbootstrap 사이에 약 5초를 두거나 검증 중에는 관리 포트 +1을 쓰세요.

애플리케이션 방화벽 함정

macOS는 새 node 바이너리 경로마다 수신 허용을 묻습니다. 거부하면 로컬 바인드는 성공하지만 원격 SYN이 타임아웃되어 포트 충돌처럼 보입니다. 경로와 서명 프로세스를 표준화하세요.

한 Mac에 여러 게이트웨이

Blue/green은 8787/8788처럼 포트 분리와 고유 레이블이 필요합니다. 8700–8799는 OpenClaw, 8800–8899는 목업처럼 대역을 문서화하지 않으면 외주가 임의로 고릅니다.

한 호스트에서 staging과 production을 병행하면 사용자나 로그를 분리하지 않으면 lsof가 읽기 어렵습니다.

launchctl kickstart

포트를 비운 뒤 launchctl kickstart -k gui/$UID/com.example.openclaw를 선호해 launchd가 고집 센 자식에 SIGKILL을 내도록 하세요. -k 없이는 미들웨어 스레드가 FD를 쥔 채 “shutdown complete”만 남을 수 있습니다. 전후 launchctl printrunningnot running을 증명하세요.

임시 클라이언트 포트

도구 fan-out은 수천 개의 아웃바운드를 열고 macOS는 임시 범위를 고갈시킬 수 있지만 inbound LISTEN은 정상처럼 보입니다. 클라이언트 측 EADDRINUSE를 리스너 충돌과 혼동하지 마세요. 동일 호스트 CI 러너에서는 sysctl net.inet.ip.portrange.hifirst를 확인하세요.

바인드 실패 구조화 로그

JSON에 event="bind_failed", errno, host, port, argv 해시를 넣으세요. errno 48(EADDRINUSE)와 같은 줄에 lsof 명령을 두어 복사-붙여넣기 진단이 되게 합니다.

Linux CI가 macOS 바인드 경쟁을 놓치는 이유

컨테이너는 GUI launchd 세션과 네임스페이스가 다릅니다. Linux CI는 컴파일 검증으로 두고 plist 변경은 Apple 하드웨어에서 스모크 바인드를 하세요. 하루 Mac mini 임대(~16.9 USD)는 엔지니어 1시간 비용과 비슷해 격차를 메웁니다.

FAQ

macOS는 포트를 즉시 재사용하나요?

빈번한 재시작 후에는 잠시 기다리거나 포트를 바꾸세요.

헬스는 초록인데 사용자는 실패?

프로브 경로와 사용자 경로가 다릅니다.

0.0.0.0이 루프백보다 안전한가요?

아니요. 노출이 넓습니다.

언제 Mac mini를 임대하나요?

노트북 밖에서 프로덕션과 같은 macOS 소켓 동작이 필요할 때입니다.

포트 싸움은 지루하지만 비쌉니다. MacHTML에서 Apple Silicon Mac mini를 하루 약 16.9 USD에 빌리면 배송 없이 프로덕션과 같은 launchd 수명주기와 방화벽 프롬프트를 재현합니다. 릴리스 주에만 켜고 lsof 증거를 모은 뒤 끄면 됩니다.

저소음은 긴 SSH 바인드 테스트에도 도움이 됩니다.

실제 macOS에서 OpenClaw 바인드 문제 재현

클라우드 Mac mini로 포트, LaunchAgent plist, 헬스 프로브를 macOS에 맞는 소켓 동작으로 검증하세요.

게이트웨이 포트 QA
약 $16.9/일부터