ENTRYPOINT ["java", "-jar", "/app.jar"] # 항상 실행되는 것
CMD ["--spring.profiles.active=prod"] # 기본 인자 (덮어쓸 수 있다)
docker run myapp → java -jar /app.jar --spring...=prod
docker run myapp --debug → java -jar /app.jar --debug (CMD 만 교체)
docker run --entrypoint sh myapp → sh (ENTRYPOINT 를 바꾼다)
-
ENTRYPOINT — 이 컨테이너가 '무엇인가' 를 정한다
-
CMD — 기본 인자. 사용자가 덮어쓸 것을 전제로 한다
-
CMD 만 쓰면 — docker run myapp bash 로 통째로 교체된다 (디버깅엔 편하다)
-
ENTRYPOINT 만 — 인자를 덧붙이는 형태가 된다
exec form 과 shell form — 시그널이 갈린다
CMD java -jar app.jar # shell form → /bin/sh -c "java -jar app.jar"
CMD ["java", "-jar", "app.jar"] # exec form → 직접 실행
shell form 이면 PID 1 이 sh 가 된다
- 컨테이너 정지 시 SIGTERM 을 sh 가 받는다
- sh 는 그것을 자식에게 전달하지 않는다
- 애플리케이션이 graceful shutdown 을 못 한다 → 강제 종료
배포마다 요청이 잘리는 원인 1순위다 반드시 exec form(대괄호 표기)을 쓴다
변수 치환이 필요할 때
# exec form 은 셸을 안 거치므로 $VAR 가 치환되지 않는다
CMD ["java", "-Xmx$MEM", "-jar", "app.jar"] # ✗ 문자열 그대로 넘어간다
# 방법 ① 셸을 명시하되 exec 로 교체해 PID 1 을 넘긴다
CMD ["sh", "-c", "exec java -Xmx$MEM -jar app.jar"]
# 방법 ② 엔트리포인트 스크립트에서 exec
# entrypoint.sh 마지막 줄: exec "$@"
핵심은 exec 다 — 셸이 자기를 애플리케이션으로 '대체' 하므로 PID 1 이 애플리케이션이 된다 → 시그널이 직접 간다
엔트리포인트 스크립트를 쓴다면 마지막이 exec "$@" 인지 반드시 확인한다
컨테이너를 실행 파일처럼 만든다
ENTRYPOINT ["/usr/local/bin/mytool"]
CMD ["--help"]
-
docker run mytool — → --help 가 나온다
-
docker run mytool scan /src — → 도구처럼 인자를 받는다
-
CLI 도구를 배포할 때의 관용적 형태다
엔트리포인트 스크립트 패턴
#!/bin/sh
set -e
# 실행 전 준비 — 설정 렌더링 · 마이그레이션 대기 등
envsubst < /app/config.tmpl > /app/config.yml
exec "$@" # ← 반드시 exec. 이것이 PID 1 을 넘긴다
ENTRYPOINT ["/entrypoint.sh"]
CMD ["java", "-jar", "/app.jar"]
스크립트가 준비를 끝내고 자기를 애플리케이션으로 '대체' 한다 exec 를 빠뜨리면 스크립트가 PID 1 로 남아 시그널이 애플리케이션에 안 간다
기존 이미지의 엔트리포인트 스크립트를 볼 때 마지막 줄이 exec 인지 확인하는 것이 습관이 돼야 한다
실행 권한을 잊지 않는다
-
COPY entrypoint.sh /entrypoint.sh
-
실행 비트가 없으면 종료 코드 126 (permission denied)
- COPY --chmod=755 entrypoint.sh /entrypoint.sh
- 또는 RUN chmod +x /entrypoint.sh
윈도우에서 만든 스크립트는 줄바꿈(CRLF)도 문제가 된다
- "exec /entrypoint.sh: no such file or directory"
- 셔뱅 뒤의 \r 때문이다. .gitattributes 로 LF 를 강제한다
면접 함정
- ❌ "CMD와 ENTRYPOINT는 골라 쓰면 된다" → 함께 쓰는 것이 기본형이다.
- ❌ "shell form이 읽기 편하니 괜찮다" → PID 1이 sh가 되어 무중단 배포가 깨진다.