← 아티클 목록

Dockerfile ENTRYPOINT vs CMD 차이 — 헷갈리지 않게 정리

2027-07-12#docker#dockerfile#entrypoint

docker run myapp --port 8080을 실행했는데 --port 8080이 통째로 무시되거나, 반대로 의도한 실행 파일이 사라지고 인자만 남아 컨테이너가 바로 죽는 경우가 있습니다. 대부분 ENTRYPOINTCMD의 역할을 섞어 쓴 탓입니다. 이 둘은 비슷해 보이지만 "무엇이 고정이고 무엇이 바뀌는가"가 정반대입니다.

한 문장 차이

  • ENTRYPOINT: 컨테이너가 실행할 "고정된 명령" — 항상 실행됨
  • CMD: ENTRYPOINT에 넘길 "기본 인자" 또는 기본 명령 — 쉽게 덮어써짐

ENTRYPOINT는 "이 컨테이너는 무엇인가(이 도구다)"를 정하고, CMD는 "기본값은 이렇다"를 정합니다. docker run 뒤에 붙인 인자는 CMD를 대체합니다.

오버라이드 규칙

Dockerfile 설정docker run img foo bar 실행 결과
CMD ["app"]foo bar (CMD가 통째로 교체됨)
ENTRYPOINT ["app"]app foo bar (인자로 추가됨)
ENTRYPOINT ["app"] + CMD ["--help"]app foo bar (CMD만 교체)
인자 없이 위 조합으로 docker run imgapp --help (CMD가 기본값)

핵심은 CMD는 런타임 인자로 덮어써지지만, ENTRYPOINT는 그대로 남고 인자가 뒤에 붙는다는 점입니다. ENTRYPOINT를 바꾸려면 docker run --entrypoint를 명시해야 합니다.

exec 형식을 써야 하는 이유

두 가지 작성 방식이 있는데, 대괄호를 쓰는 exec 형식을 권장합니다.

Dockerfile
# exec 형식 (권장) — PID 1로 직접 실행, 시그널 전달됨
ENTRYPOINT ["nginx", "-g", "daemon off;"]

# shell 형식 — /bin/sh -c 로 감싸짐
ENTRYPOINT nginx -g "daemon off;"

shell 형식은 /bin/sh -c로 감싸지기 때문에 실제 프로세스가 PID 1이 아니라 셸이 PID 1이 됩니다. 이러면 docker stop이 보내는 SIGTERM이 앱에 제대로 전달되지 않아 종료가 느려지거나 강제 종료(SIGKILL)됩니다. exec 형식은 이 문제가 없습니다.

권장 조합 패턴

실무에서 가장 흔한 패턴은 ENTRYPOINT로 실행 파일을 고정하고 CMD로 기본 인자를 주는 것입니다.

Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY . .
ENTRYPOINT ["python", "server.py"]
CMD ["--port", "8000"]

이렇게 하면 기본은 python server.py --port 8000으로 뜨고, docker run img --port 9000으로 포트만 쉽게 바꿀 수 있습니다. 실행 파일 자체는 사용자가 실수로 날릴 수 없습니다.

확인 체크리스트

로컬 터미널
# 이미지에 박힌 ENTRYPOINT/CMD 확인
docker inspect --format='{{.Config.Entrypoint}} | {{.Config.Cmd}}' myapp

# CMD만 덮어쓰기 (인자 교체)
docker run myapp --port 9000

# ENTRYPOINT까지 갈아끼우기 (디버깅 시 셸 진입)
docker run -it --entrypoint /bin/sh myapp

docker inspect로 둘이 실제로 어떻게 박혀 있는지 먼저 확인하면, 왜 인자가 무시되는지 바로 보입니다.


ENTRYPOINT와 CMD 조합을 직접 빌드해 보며 오버라이드 동작을 확인하는 실습은 도커 트랙에서 회원가입 없이 무료로 해볼 수 있습니다.