2026 Mac 클라우드에서 OpenClaw Docker: Exit 137/OOM, uid 1000 볼륨 권한, DNS, 그리고 가장 짧은 openclaw doctor 경로

Mac 클라우드 노드에서 OpenClaw를 Docker로 돌리는 운영자는 exit 137, 마운트된 설정에 대한 Permission denied, 호스트에서는 되는데 컨테이너 안에서만 실패하는 HTTPS 같은 증상에 자주 부딪힌 뒤 이미지를 며칠째 재설치하기도 합니다. 이 글은 누가 읽어야 하는지(SRE와 임대 Mac을 쓰는 개인 개발자), 무엇을 얻는지(2026년 공식 Compose 관행에 맞춘 순서 있는 체크리스트와 증상 매트릭스), 어떻게 구성되는지(먼저 메모리와 cgroup, 다음 uid 1000 볼륨, 그다음 Docker DNS, 이어서 openclaw doctor, 포트 18789, 컨테이너를 포기하고 네이티브 macOS로 갈 시점)를 분명히 밝힙니다.

Mac 클라우드 호스트에서 Docker로 OpenClaw 게이트웨이를 실행하는 다이어그램

이 글에서 다루는 내용

1. 준비 완료 삼각: 프로세스 가동, 포트 매핑, 볼륨 쓰기 가능

공식 Docker·Docker Compose 흐름은 OpenClaw 게이트웨이를 컨테이너로 감쌉니다. 그럼에도 RAM과 CPU 할당량의 주인은 Mac 클라우드 호스트이며, ~/.openclaw(또는 사용자 지정 설정 디렉터리)를 바인드 마운트하고, 루프백 점검이나 SSH 터널을 위해 18789나 다른 매핑 포트를 노출합니다. 책상 위 노트북과 달리 클라우드 Mac은 메모리 상한이 낮고 GUI 세션이 없는 경우가 많으며, 이미지는 흔히 루트가 아닌 node 사용자(uid/gid 1000)로 실행됩니다. 이 사실을 내면화하기 전에는 설정을 손댈 때마다 잘못된 레이어를 건드리게 됩니다.

운영자가 흔히 놓치는 것은 “컨테이너 안에서만” 보이는 파일 시스템 뷰입니다. 호스트에서 만든 디렉터리가 root 소유로 남아 있으면 게이트웨이는 로그 한 줄조차 쓰지 못하고 조용히 재시작할 수 있습니다. 반대로 워크스페이스를 넓게 잡아 두었는데 실제 마운트는 하위 경로만 읽기 전용이면, CLI는 성공한 것처럼 보이다가 백그라운드 작업에서만 터집니다. 이런 경우를 막으려면 배포 직후 한 번은 반드시 컨테이너 셸에서 touchls -la로 쓰기 경로를 검증해야 합니다.

준비 완료(readiness)를 티켓에 붙여 넣을 수 있는 세 가지 관측값으로 정의하세요. 컨테이너가 실행 중이거나 healthy이고, docker compose ps에 기대한 ports 매핑이 보이며, openclaw status나 헬스 프로브가 컨테이너 내부에서 권한 오류 없이 성공하는 상태입니다. 모델 라우팅, Slack 웹훅, Cron은 이 삼각이 초록일 때만 다룹니다. 흔한 Compose 실수로는 존재하지 않는 호스트 경로(그래서 Docker가 루트 소유 디렉터리를 만들어 버림), 게이트웨이가 상태를 써야 하는데 읽기 전용으로 마운트한 경우, 이미지 레이어와 빌드 캐시로 작은 루트 볼륨을 소진하는 경우가 있습니다. 후자는 df -h로 진실이 드러나기 전까지는 임의의 크래시처럼 보입니다.

또한 포트 게시 방식은 보안 모델과 직결됩니다. 127.0.0.1:18789:18789 형태는 로컬 루프백에서만 접근 가능하고, 0.0.0.0 바인딩은 클라우드 공급자의 보안 그룹·방화벽 규칙과 맞물립니다. “원격에서 안 열린다”는 보고가 오면 compose의 publish 목록만 보지 말고, 실제 프로세스가 어떤 인터페이스에 리슨하는지와 외부 SG를 함께 대조해야 합니다. SSH 터널을 쓰는 운영 모델이라면 터널 종단과 게이트웨이 바인드가 일치하는지도 같은 삼각의 일부로 취급하는 편이 안전합니다.

Compose 프로젝트 이름, 서비스 이름, 게시 포트, 설정·워크스페이스의 절대 호스트 경로를 기록하세요. 런북에 한 줄만 있어도 업그레이드 가이드, 웹훅 게시, 조용한 Cron 문제를 교차 참조할 때 시간을 아낍니다. 모두가 같은 토폴로지를 보게 되어 “어느 머신 말이냐”는 추측을 줄입니다.

2. 고통 포인트 분해(번호 매김)

  1. Exit 137과 OOM: Docker는 Linux cgroup 메모리 한도를 워크로드에 매핑합니다. 커널 OOM 킬러가 동작하면 Docker는 종종 종료 코드 137(128 + SIGKILL 9)을 보여 줍니다. 이미지 pull·빌드는 정상 가동 중 게이트웨이 트래픽보다 RAM을 훨씬 많이 씁니다. “몇 시간은 멀쩡한” 노드가 docker compose build 몇 분 만에 죽을 수 있습니다. 137이 빌드 단계인지 실행 단계인지 항상 메모해 두세요. 대응이 달라집니다.
  2. 볼륨 소유권 대 uid 1000: 호스트에서 ~/.openclaw를 루트나 개인 계정으로 만들면 컨테이너의 node 프로세스는 openclaw.json, 로그, 워크스페이스 파일에 쓸 수 없습니다. 실패가 조용한 시작 루프로만 보이고 스택 트레이스가 없을 수 있으며, “릴리스 노트가 나쁘다”는 오진으로 자주 넘어갑니다.
  3. Docker DNS와 기업 출구: 호스트에서 curl https://api.anthropic.com은 되는데 docker exec … curl만 실패하면 거의 항상 컨테이너에 올바른 DNS 서버, HTTP(S)_PROXY 변수, 또는 TLS 가로채기 프록시를 통한 신뢰 저장소 가시성이 없다는 뜻입니다. 네트워크 경로를 고치기 전에 API 키만 돌리면 시간과 쿼터만 낭비합니다.
  4. 헬스 체크와 의존성 경쟁: 넉넉한 start_period 없이 공격적인 depends_on만 두면 connection refused 로그가 범람해 애플리케이션 버그처럼 보입니다. 다운스트림이 두들기기 전에 게이트웨이가 리슨하는지 증명하세요.

이 네 패턴이 Mac 클라우드 테넌트에서 보는 프로덕션형 사고 대부분을 설명합니다. 아래 표는 이를 첫 조치로 압축합니다.

3. 증상–근본 원인 매트릭스

사고 중 의사결정 시트로 표를 쓰세요. 가장 가까운 증상을 고르고, 보조 가설로 넘어가기 전에 첫 조치 열을 순서대로 실행합니다.

관측 신호유력한 근본 원인첫 조치(순서 있음)
재시작 루프, OOMKilled, exit 137cgroup 또는 호스트 메모리 압박Docker Desktop/Engine 메모리 또는 compose mem_limit 상향; RAM 많이 쓰는 작업 중지; 병렬 레이어를 줄여 빌드 재시도
설정·워크스페이스 경로에서 Permission denied잘못된 uid의 바인드 마운트호스트에서 sudo chown -R 1000:1000 /path/to/mount; compose에서 RW 확인; docker exec -u node … touch로 테스트 파일
컨테이너 안에서만 HTTPS 또는 DNS 실패컨테이너 리졸버 또는 프록시 env 누락docker execcat /etc/resolv.conf, curl -v; compose에 dns: 또는 daemon.json; 기업 프록시는 --env-file로 전달
프로세스는 떴는데 원격에서 포트 도달 불가포트 매핑, 바인드 주소, 클라우드 보안 그룹ports:를 의도한 0.0.0.0127.0.0.1과 비교; SG 규칙 개방; 보강 가이드대로 SSH 터널 선호
openclaw doctor가 자격 증명 누락을 보고하는데 compose는 “설정했다”고 함환경 변수가 컨테이너 프로세스에 주입되지 않음docker exec … env | grep OPENCLAW와 compose environment 대조; 인용·상속 수정

4. 순서 있는 복구 6단계(가볍게 순서 바꾸지 말 것)

순서가 중요한 이유는 뒤 단계가 앞 단계에서 실패 범주 전체를 배제했다고 가정하기 때문입니다. 바로 이미지 태그만 바꾸면 증거가 사라집니다.

  1. 상태 포착: docker compose ps -a를 실행한 뒤 docker logs <service> --tail 200을 실행합니다. exit 137이 이미지 pull, 빌드, 안정 실행 중 어느 단계였는지 저장합니다.
  2. 메모리 여유 검증: 빌드를 위해 Docker에 대략 4~8GB 이상을 임시로 할당합니다(제공사 문서에 맞게 조정). 호스트에서는 memory_pressure나 Activity Monitor에 해당하는 도구로 확인합니다. 스왑이 심하고 게이트웨이 부하는 SIGKILL 불안정의 재료입니다.
  3. 볼륨 소유권 정규화: 컨테이너가 쓰는 모든 바인드 경로에 chown을 적용합니다. OpenClaw를 다시 건드리기 전에 컨테이너 안에서 uid 1000으로 사소한 쓰기 테스트를 다시 합니다.
  4. 컨테이너 네트워크 증명: 내부에서 LLM 제공자에 curl -sI로 치거나 TLS가 의심되면 openssl s_client를 씁니다. 기업 MITM이 있으면 호스트와 동일한 신뢰 자료나 프록시 변수를 미러링합니다.
  5. 진단 CLI 실행: openclaw doctor, openclaw status, openclaw models status(이름은 현재 CLI 기준)를 실행합니다. 위를 모두 한 뒤에도 설치 수준 오류로 doctor가 빨강일 때만 재설치나 이미지 태그 변경을 고려합니다.
  6. 포트 18789 수락 테스트: 호스트에서 curl -sI http://127.0.0.1:18789(또는 매핑 포트); 원격 관리자는 보안 글에 적힌 SSH 터널이나 리버스 프록시 경로를 검증합니다.

실무에서는 위 순서를 그대로 스크립트에 옮기기보다, 각 단계의 “완료 조건”을 체크박스로 적어 두는 편이 팀 합의를 빠르게 만듭니다. 예를 들어 2단계는 “Docker 할당 RAM과 호스트 여유가 문서 권장 범위 안인가”, 4단계는 “컨테이너 내부에서 동일한 SNI로 핸드셰이크가 끝나는가”처럼 관측 가능한 문장으로 쓰면 재발 시에도 같은 증거를 남깁니다.

docker compose ps -a docker logs openclaw-gateway-1 --tail 200 docker exec -it openclaw-gateway-1 sh -lc "id && ls -la /home/node/.openclaw && openclaw doctor"
주의: 사고가 열려 있는 동안 공격적인 docker system prune -a는 피하세요. 레이어 캐시와 로그 맥락을 지워 평균 복구 시간만 늘립니다.

5. 인용 가능한 기술 사실(최소 세 가지, 여기서는 일곱 가지)

6. 맺음말: Docker 편의와 VPSMAC 네이티브 macOS

Docker는 재현 가능한 데모와 멀티 테넌트 격리에는 뛰어나지만, 자정 호출이 올 때마다 네임스페이스와 cgroup은 홉이 하나 더 붙습니다. DNS 설정 이중화, 볼륨 uid 곡예, 호스트 스왑 압박과 컨테이너 죽음의 상관을 맞추기 어려움—대가로 치릅니다. 팀이 마운트를 고치는 대신 이미지만 반복해서 갈아끼우면 아무도 건드리기 두려운 “눈송이” compose 파일이 쌓입니다. 7×24 에이전트 게이트웨이에 바라는 결과와는 거리가 멉니다.

베어메탈 Mac 클라우드 노드에 네이티브로 설치하면—SSH로 들어가 공식 비컨테이너 경로를 실행하고 launchd로 감독하면—실패 영역이 여럿 접힙니다. ssh에서 보는 uid가 설정을 쓰는 uid이고, 리졸버 설정이 셸과 같으며, openclaw doctor가 이미 신뢰하는 파일시스템을 읽습니다. 일회성 실험실이 아니라 프로덕션 서비스처럼 움직이는 OpenClaw 워크로드에는 움직이는 부품을 줄이는 편이 보통 또 다른 커스텀 이미지를 짜내는 것보다 낫습니다. Docker 전용 스택은 지속적인 트러블슈팅 부담과 부하 지속 시 성능 분산을 더하고, 전용 물리 Mac 호스트는 둘 다 완화하는 경향이 있습니다.

따라서 준비 완료 삼각이 안정을 거부할 때 현실적인 다음 단계는 VPSMAC Mac 노드를 임대하는 것입니다. Apple 실리콘 호환성과 24시간 전원을 유지하면서 클라우드 쿼터 위에 컨테이너 인체공학까지 싸우지 않아도 됩니다. 부트스트랩은 5분 배포 글과 짝을 이루게 하고, 노출은 프로덕션 보강 체크리스트와 맞추세요. 같은 도구 체인, 추상화 세금은 줄입니다. 이 글이 권하는 것은 위 매트릭스를 정직하게 소진한 뒤의 선택이며, 슬로건이 아니라 반복되는 137과 권한 루프의 증거가 레이어 수를 가리킬 뿐 모델 품질 문제를 뜻하지 않기 때문입니다.