Quick Comparison
FROM python:3.12-slim
WORKDIR /app
COPY . .
ENTRYPOINT ["python", "-m", "http.server"]
CMD ["8000"]# CMD만 덮어쓰기 — ENTRYPOINT는 그대로 유지됨
docker run my-http 9000
# ENTRYPOINT까지 교체
docker run --rm --entrypoint sh my-http -c 'ls /app'ENTRYPOINT와 CMD를 exec form으로 함께 쓰면 실행 파일은 고정하고 기본 인자만 바꿀 수 있습니다. 명령 전체를 교체해야 하면 CMD만 쓰거나 --entrypoint를 명시합니다.
CMD와 ENTRYPOINT
ENTRYPOINT가 컨테이너의 기본 실행 파일을 정의하는 이유
exec form ENTRYPOINT는 이 컨테이너가 "어떤 프로그램으로 동작하는가"를 기본으로 정한다. docker run의 추가 인자는 CMD를 바꾸고 ENTRYPOINT 뒤에 붙는다. 다만 docker run --entrypoint로 실행 파일 자체를 명시적으로 교체할 수 있다. CLI 도구나 서버처럼 "하나의 역할"이 명확한 이미지에 적합하다.
# 이미지가 항상 gunicorn으로 기동
ENTRYPOINT ["gunicorn", "app:create_app()"]
CMD ["--workers=4", "--bind=0.0.0.0:8000"]CMD가 ENTRYPOINT의 기본 인자로 동작하는 패턴
CMD는 단독으로 쓰면 기본 실행 명령이 되고, ENTRYPOINT와 함께 쓰면 기본 인자 역할을 한다. docker run에 추가 인자를 주면 CMD만 교체되고 ENTRYPOINT는 유지된다.
# CMD ["8000"] 이 기본값 → 9000으로 교체
docker run my-http 9000
# CMD 전체 무시, ENTRYPOINT에 sh 추가 인자 전달
docker run --entrypoint sh my-http -c "ls /app"PID 1과 종료 신호
exec form(["python", ...])은 실행 파일이 PID 1로 직접 시작된다. shell form(python ...)은 /bin/sh -c를 시작하므로 실제 프로세스는 보통 그 자식이 된다. 이때 shell이나 entrypoint script가 exec로 자신을 교체하거나 신호를 전달하지 않으면 docker stop의 신호가 애플리케이션까지 가지 않아 timeout 뒤 강제 종료될 수 있다. shell form 자체가 항상 종료 실패를 뜻하는 것은 아니지만, 장기 실행 프로세스에는 exec form 또는 스크립트의 exec가 안전한 기본값이다.
# exec form: SIGTERM이 python 프로세스에 직접 전달됨 (권장)
ENTRYPOINT ["python", "-m", "http.server"]
# shell form: shell이 PID 1이다. script라면 마지막에 exec를 사용한다.
ENTRYPOINT exec python -m http.serverCMD만 쓸지 ENTRYPOINT + CMD를 쓸지는 "무엇을 고정할지"로 고른다
이미지를 사실상 하나의 서버나 CLI처럼 고정된 역할로 만들고 싶다면 ENTRYPOINT + CMD가 맞습니다. 반대로 실행 명령 자체를 쉽게 바꿔야 하는 범용 이미지라면 CMD만 쓰는 편이 낫습니다. 실행 파일은 고정하고 인자만 바꾸고 싶은지, 명령 전체를 자주 바꾸는지가 핵심 기준입니다.
shell form의 신호 전달을 자동으로 가정하지 마세요. 장기 실행 프로세스에는 exec form을 우선하고, shell script를 써야 하면 마지막 프로세스를 exec로 시작하거나 signal forwarding을 직접 구현합니다. graceful shutdown은 애플리케이션의 SIGTERM 처리도 필요합니다.
참고 링크
2 sources