Skip to content

Exec

exec는 현재 셸 프로세스를 새 명령으로 치환(replace)하는 셸 내장 명령이다. exec 없이 명령을 실행하면 셸이 자식 프로세스를 새로 띄우고 자신은 살아남아 그 자식을 기다린다. exec를 붙이면 셸이 새 명령으로 "변신"하며, 같은 PID를 그대로 유지한다.

프로세스 트리 차이

exec 없음

 PID 100  bash (start-uvicorn)   ← 부모로 계속 살아있음
 └─ PID 101  uvicorn             ← 자식

exec 있음

 PID 100  uvicorn               ← bash가 uvicorn으로 "변신". PID 그대로 유지

실질적 차이

배포 환경(Render, Docker 등)에서 특히 중요하다.

1. 시그널 전달 (가장 중요)

Render나 Docker가 컨테이너를 종료할 때 PID 1(시작 프로세스)에 SIGTERM을 보낸다.

  • exec 없음: SIGTERM이 bash에 전달된다. bash는 기본적으로 이 시그널을 자식 uvicorn에 전달하지 않으므로, uvicorn이 graceful shutdown 기회를 얻지 못하고 타임아웃 후 SIGKILL로 강제 종료된다. 진행 중인 요청이 끊기고 연결이 정리되지 않는다.
  • exec 있음: uvicorn이 직접 그 프로세스이므로 SIGTERM을 바로 받아 정상 종료한다. 진행 중인 요청을 마무리한 뒤 종료된다.

2. 종료 코드 (exit code)

  • exec 없음: bash는 자식이 끝난 뒤 자기 종료 코드를 반환한다. 중간에 bash 로직이 끼면 uvicorn의 실제 종료 코드가 가려질 수 있다.
  • exec 있음: uvicorn의 종료 코드가 그대로 노출되어, Render의 헬스/재시작 판단이 정확해진다.

3. 프로세스 1개 절약

불필요하게 대기만 하는 bash 프로세스가 남지 않는다. 사소하지만 깔끔하다.

언제 사용하는가

상황

exec 사용 여부

셸이 마지막에 단일 서버/명령을 띄우고 더 할 일이 없음

사용 (권장)

실행 후 정리 작업, trap 핸들러로 직접 시그널 처리, 여러 프로세스 관리 필요

사용하지 않음

셸이 마지막에 단일 서버를 띄우고 더 할 일이 없는 경우 exec가 정석이다. 특히 배포 환경에서 graceful shutdown 때문에 권장된다.

반대로 uvicorn 실행 후에도 셸이 무언가 해야 한다면(예: 종료 후 정리 작업, trap 핸들러로 직접 시그널 처리, 여러 프로세스 관리) exec를 빼야 한다.

예시: start-uvicorn

exec uv run uvicorn geo_mlops.app:create_app --factory --host "$HOST" --port "$PORT"

위 스크립트에는 정리 작업이나 trap 핸들러가 없으므로 exec가 적절하다.

참고: 앞단의 read ... < <(...) (host/port 해석)은 exec 앞에 있으므로 영향받지 않는다. 그 부분이 먼저 실행되고, 마지막 uvicorn 실행 시점에만 셸이 치환된다.

See also