개발 slecs

Temporal 워커 재시작 루프를 볼륨 마운트 누락으로 해결

목차

Temporal 워커가 계속 재시작 루프에 빠지던 문제를 잡아냈다.

배경: 뭔가 계속 죽고, 계속 살아나고

로컬 개발 환경에서 Temporal 서버를 띄우고 나면 워커 컨테이너가 올라갔다 죽었다를 반복하는 현상이 있었다. 로그를 보면 타임아웃이나 커넥션 에러가 찍히는데, 처음엔 DB 연결 문제인가 싶어서 packages/db/src/index.ts 쪽을 먼저 들여다봤다.

근데 DB 커넥션 자체는 멀쩡했다. 진짜 문제는 Temporal 서버가 dynamicconfig 파일을 못 읽고 있었던 것.

Temporal은 기동 시점에 dynamic config 파일을 읽어서 서버 동작을 조정하는데, 이 파일이 컨테이너 안에 마운트가 안 돼 있으면 서버가 설정 로드에 실패하거나 기본값으로 동작하다가 예상치 못한 상태로 빠진다. 특히 SQL persistence를 쓰는 환경(development-sql.yaml)에서는 DB 관련 설정값들이 dynamic config에 묶여 있는 경우가 있어서, 파일이 없으면 아예 워커 연결 자체가 실패하고 재시작 루프로 이어지는 패턴이 나온다.

# docker-compose.yml — 핵심 변경 포인트
volumes:
  - ./infra/temporal/dynamicconfig:/etc/temporal/config/dynamicconfig

이 한 줄이 없었던 게 문제였다. 파일은 레포에 있었는데 컨테이너 안에 안 들어가고 있었던 것. 전형적인 "파일은 존재하는데 마운트가 빠진" 케이스다.

파일별로 뭘 건드렸나

파일 역할 이번 변경
docker-compose.yml 컨테이너 오케스트레이션 설정 dynamicconfig 볼륨 마운트 추가
infra/temporal/dynamicconfig/development-sql.yaml SQL 환경 Temporal dynamic config 파일 내용 정비 (마운트 연결에 맞게)
packages/db/src/index.ts DB 커넥션 엔트리포인트 재시작 루프 디버깅 과정에서 확인/조정

packages/db/src/index.ts가 변경 파일에 같이 있다는 게 흥미로운 지점인데, 처음에 문제를 DB 쪽으로 보고 들어갔다가 실제 원인이 Temporal 설정임을 발견한 흔적이 그대로 남아 있는 셈이다. 삽질의 흔적이 커밋에 같이 묶이는 건 흔한 일이고, 어떤 의미에서는 "이 두 영역이 맞물려 있다"는 걸 보여주는 문맥이 되기도 한다.

재시작 루프, 어디서 자주 만나냐면

이런 류의 문제는 꽤 패턴이 있다.

  • 설정 파일이 컨테이너 안에 없어서 프로세스가 설정 로드 실패 → 크래시 → 재시작 반복
  • 볼륨 마운트 경로 오타 (컨테이너 내부 경로 vs 호스트 경로 뒤집힘)
  • 파일은 있는데 권한 문제로 읽기 실패
  • depends_on은 설정했는데 실제 readiness 체크가 없어서 의존 서비스가 준비되기 전에 연결 시도

이번 케이스는 첫 번째였고, 가장 조용하게 지나가는 유형이기도 하다. Temporal 서버 자체 로그가 "파일 없음"을 크게 알려주지 않고, 워커 쪽 로그가 커넥션 실패처럼 보여서 방향을 잘못 잡기 딱 좋다.

회고

팀 로컬 환경 세팅 문서가 있긴 한데, 이런 인프라 레벨 설정 변경이 생겼을 때 문서가 코드만큼 빠르게 따라오지 못하는 문제는 항상 있다. docker-compose.yml 변경이 생기면 "이 변경이 로컬 셋업에 영향 주는가?" 체크리스트를 PR에 붙이는 습관을 팀에 퍼뜨리는 게 다음 과제다.

그리고 재시작 루프를 만났을 때 "DB 연결인가?"로 먼저 튀어나가는 건 꽤 자연스러운 반응인데, Temporal처럼 자체 설정 레이어가 있는 미들웨어를 쓸 때는 "미들웨어 자체 설정 → 미들웨어-DB 연결 → 앱-미들웨어 연결" 순으로 레이어를 나눠서 보는 게 삽질을 줄인다. 이번에 그 순서를 거꾸로 탔다가 시간을 좀 썼음.

끝.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.