← 목록
기타 2026-06-09 9KB 읽기 11분

P4 컷오버계획 — 크라우니 게이트웨이 한선씨 무중단 포트 재바인딩

레거시 JS 게이트웨이(node bin/cli.js start, :8080 HTTP / :8443 HTTPS)를 한선씨 게이트웨이(:8080 평문 + stunnel :8443 TLS 종단)로 무중단 전환한다.

1. 전략 결정 (확정): 단계 순서교환 (Staged Handover)

SO_REUSEPORT 미사용. 단계 순서교환을 채택한다.

1.1 SO_REUSEPORT 를 쓰지 않는 근거

  • macOS 는 SO_REUSEPORT(0x0200) 를 정의한다 (/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/socket.h:137).
즉 "가용성" 자체는 있다.
  • 그러나 양쪽 리스너가 모두 SO_REUSEPORT 를 설정해야 포트를 공유할 수 있다.
라이브 리스너는 Node.js(bin/cli.js start) 이고, Node 의 net/http2 서버는 기본적으로 SO_REUSEADDR 만 설정하고 SO_REUSEPORT 는 설정하지 않는다. → 한선씨가 SO_REUSEPORT 로 8080 을 bind 해도 incumbent 가 REUSEPORT 미설정이라 EADDRINUSE.
  • 설령 REUSEPORT 가 양쪽에 켜져 있어도, 커널이 두 리스너에 연결을 로드밸런싱한다.
컷오버 중 트래픽이 구/신 게이트웨이로 무작위 분산 → "어느 쪽이 응답했는지" 불명확, 검증·롤백 경계가 무너진다. 깔끔한 단절컷오버에 부적합.
  • 현재 라이브 리스너 확인:
  lsof -nP -iTCP:8080 -iTCP:8443 -sTCP:LISTEN
  → node 59361  *:8080 (LISTEN) / *:8443 (LISTEN)   # PPID 35477 = infra watchdog
  

1.2 단계 순서교환의 무중단 보장

  • 그림자(:8081 한선씨 평문, :8444 stunnel) 에서 헬스 통과를 먼저 확인한다 (라이브 무접촉).
  • 레거시를 종료한 직후 즉시 한선씨를 8080 에 rebind 한다.
crownyc TCP_LISTENSO_REUSEADDR 를 설정하므로(crownyc.c:10528), 레거시 TIME_WAIT 잔여 상관없이 즉시 bind 성공.
  • 단절 구간은 "레거시 종료 ~ 한선씨 bind 완료" = 약 1~2초 (sleep 1 + bind).
이 구간 동안만 8080 무응답. 백엔드(9878/9907)는 무중단.

2. Watchdog 처리 (가장 중요 — 충돌 회피)

2.1 위협 구조

라이브 게이트웨이를 자동 재기동하는 주체는 crowny-infra watchdog (PID 35477) 이다. (node /Users/ef/crowny-infra/bin/cli.js watchdog, PPID=1, launchd KeepAlive)

  • crowny-stack.yamlcrowny-gateway:
  • command: /opt/homebrew/bin/node bin/cli.js start
  • port: 8080, health: http://127.0.0.1:9100/health
  • watchdog 동작(lib/watchdog.js):
  • 10초 간격으로 :9100/health 프로브 (interval=10000).
  • 3회 연속 다운(약 30초) 시 _restart() 발동.
  • _restart()lsof -ti :8080SIGTERM, 2초 후 SIGKILLnode bin/cli.js start 재기동.
  • 즉 컷오버 후 :9100/health 가 죽으면(레거시 admin 포트), watchdog 가 약 30초 뒤
  • 우리 한선씨 게이트웨이(8080 점유자)를 죽이고 레거시 JS 를 되살린다.

    2.2 처리 방안 (채택): SIGSTOP → 감시대상 교체 → SIGCONT

    1. 컷오버 직전 watchdog 를 SIGSTOP 으로 동결한다(종료 아님, 프로세스 트리 보존):
       kill -STOP 35477
       
    STOP 상태에서는 setInterval 타이머가 진행되지 않아 프로브·재기동이 모두 멈춘다. 30초 카운트다운 자체가 동결되므로 안전.
    1. 컷오버 성공 확인 후 crowny-stack.yamlcrowny-gateway 를 교체:
    - command: cd /Users/ef/crowny-gateway && ./crownyc run 한선게이트웨이/게이트웨이라이브.toau (+ stunnel 한선게이트웨이/stunnel-live.conf) - health : http://127.0.0.1:8080/임시. :9100 admin 은 한선씨에 아직 미구현. (후속 과제: 한선씨 게이트웨이에 :9100/health admin 엔드포인트 구현 → 그때 health 원복)
    1. watchdog 재개:
       kill -CONT 35477
       
    이제 watchdog 는 한선씨 게이트웨이를 :8080 루트로 감시하고, 죽으면 한선씨를 되살린다.

    2.3 대안 (미채택, 참고)

    • A. health 프로브만 :8080 으로 바꾸고 STOP 생략: watchdog 가 30초 창 안에 8080 다운을
    감지해 SIGKILL 할 위험. STOP 이 더 안전.
    • B. 한선씨에 :9100/health 구현 후 stack.yaml 무변경: 이상적이지만 :9100 구현은 별도 과제.
    컷오버 시점에는 STOP+health임시변경이 최소 변경.
    • 게이트웨이 자체 scripts/watchdog.shgateway:9150(CGWCS 관제 포트)만 감시하고
    8080/8443/cli.js 를 재기동하지 않으므로 컷오버와 무관(건드릴 필요 없음).

    3. 라이브 런북 (운영자 수동 트리거 — 단계별 명령)

    드라이런: cd /Users/ef/CrownyOS/crownyc && ./hanseonc_high /Users/ef/crowny-gateway/한선게이트웨이/컷오버.한선 > /tmp/컷오버.toau 2>/dev/null && ./crownyc run /tmp/컷오버.toau 아래는 실제 적용. 순서 엄수. 각 단계 검증 실패 시 즉시 §4 롤백.

    bash# ── 사전 빌드 (1회) ──
    cd /Users/ef/CrownyOS/crownyc
    ./hanseonc_high /Users/ef/crowny-gateway/한선게이트웨이/게이트웨이라이브.한선 \
      > /Users/ef/crowny-gateway/한선게이트웨이/게이트웨이라이브.toau 2>/dev/null
    
    # ── 단계0: 사전체크 (라이브 무접촉) ──
    curl -sf -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8081/    # 그림자 한선씨 (200 기대)
    curl -sf -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9878/    # tiomta  (200)
    curl -sf -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9907/    # mpti    (200)
    #  하나라도 실패면 컷오버 미진입.
    
    # ── 단계1: 워치독 동결 (레거시 자동 재기동 차단) ──
    kill -STOP 35477
    ps -p 35477 -o pid,stat=    # STAT 에 T(stopped) 확인
    
    # ── 단계2: 레거시 종료 (bin/cli.js start 패턴만) ──
    for p in $(lsof -ti :8080 2>/dev/null); do ps -p $p -o command= 2>/dev/null | grep -q 'bin/cli.js start' && kill -TERM $p 2>/dev/null; done
    for p in $(lsof -ti :8443 2>/dev/null); do ps -p $p -o command= 2>/dev/null | grep -q 'bin/cli.js start' && kill -TERM $p 2>/dev/null; done
    sleep 1
    lsof -nP -iTCP:8080 -sTCP:LISTEN    # 비어야 함
    
    # ── 단계3: 한선씨 8080 재바인딩 + stunnel 8443 종단 ──
    cd /Users/ef/crowny-gateway
    ./crownyc run 한선게이트웨이/게이트웨이라이브.toau > /tmp/한선게이트웨이-라이브.log 2>&1 &
    sleep 2
    stunnel 한선게이트웨이/stunnel-live.conf &     # foreground=no → 데몬화
    sleep 1
    
    # ── 단계4: 승격 헬스 재확인 ──
    curl -sf -o /dev/null -w 'http8080=%{http_code}\n' http://127.0.0.1:8080/
    curl -sk --resolve tiomta.com:8443:127.0.0.1     https://tiomta.com:8443/     -o /dev/null -w 'tiomta8443=%{http_code}\n'
    curl -sk --resolve mpti.tiomta.com:8443:127.0.0.1 https://mpti.tiomta.com:8443/ -o /dev/null -w 'mpti8443=%{http_code}\n'
    #  정상코드(200/301 등)면 컷오버 확정 → 단계5. 실패면 §4 롤백.
    
    # ── 단계5: 워치독 감시대상 교체 + 재개 ──
    #  crowny-stack.yaml 의 crowny-gateway 를 아래로 편집:
    #    command: cd /Users/ef/crowny-gateway && ./crownyc run 한선게이트웨이/게이트웨이라이브.toau
    #    health : http://127.0.0.1:8080/
    kill -CONT 35477
    ps -p 35477 -o pid,stat=    # STAT 에 S/R (running) 확인
    

    4. 롤백 (어느 단계든 Ta 시)

    bash# 한선씨 라이브 인스턴스 종료
    for p in $(lsof -ti :8080 2>/dev/null); do ps -p $p -o command= 2>/dev/null | grep -qE '한선게이트웨이|crownyc' && kill -TERM $p 2>/dev/null; done
    # stunnel 8443 종료
    for p in $(lsof -ti :8443 2>/dev/null); do ps -p $p -o command= 2>/dev/null | grep -q 'stunnel' && kill -TERM $p 2>/dev/null; done
    # 레거시 재기동
    cd /Users/ef/crowny-gateway && nohup /opt/homebrew/bin/node bin/cli.js start > /tmp/gw-rollback.log 2>&1 &
    sleep 2
    curl -sf -o /dev/null -w 'legacy9100=%{http_code}\n' http://127.0.0.1:9100/health
    # stack.yaml 을 원복(편집했다면) 후 워치독 재개
    kill -CONT 35477
    

    레거시가 :8080/:8443/:9100 으로 복귀하면 운영 영향 최소화.

    5. 산출 파일

    파일역할
    한선게이트웨이/컷오버.한선오케스트레이터 (드라이런 기본, 단계0~5 + 롤백) — 컴파일·드라이런 검증됨
    한선게이트웨이/게이트웨이라이브.한선컷오버 후 라이브 8080 엔트리 (게이트웨이메인의 8080 변형) — 컴파일 검증됨
    한선게이트웨이/stunnel-live.conf라이브 8443 TLS 종단 → 8080 포워드 (SNI: ext + abti)
    한선게이트웨이/컷오버계획.md본 문서 — 전략·watchdog·런북·롤백

    6. 잔여 이슈

    • :9100 admin/health 한선씨 미구현 → 컷오버 후 watchdog health 를 임시로 :8080 루트로 둠.
    한선씨 게이트웨이에 :9100/health(status/services/trident/shield) 구현 시 stack.yaml health 원복.
    • stunnel SNI 분기는 ext(기본) + abti(crownybus.com) 2개만. 추가 SAN별 cert 필요 도메인은
    인증서선택(도메인) 산출로 stunnel conf 동적 생성(P2 연동) 후속.
    • 게이트웨이라이브.toau 는 실행 시 TCP accept 루프로 블록 → 반드시 백그라운드(&) 기동.