ENTRYPOINT/CMD JSON 인자 권장

ENTRYPOINT/CMD JSON 인자 권장 (JSONArgsRecommended)

빌드 체크 규칙 중 하나로, ENTRYPOINTCMD 지시어에 셸 형식(shell form)을 쓴 경우 JSON(exec) 형식을 권장해요. 셸 형식을 쓰면 프로그램이 셸의 자식 프로세스로 실행돼 OS 신호를 받지 못할 수 있기 때문이에요.

출처: 문서

본문

ENTRYPOINTCMD 지시어는 두 가지 인자 문법을 지원해요.

  • 셸 형식: CMD my-cmd start
  • exec 형식: CMD ["my-cmd", "start"]

셸 형식을 쓰면 실행 파일이 셸의 자식 프로세스로 돌아서 신호를 전달하지 못해요. 그래서 컨테이너에서 실행되는 프로그램이 SIGTERM이나 SIGKILL 같은 OS 신호를 감지하고 올바르게 응답하지 못할 수 있어요.

출력 메시지

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

예시

❌ 나쁜 예: ENTRYPOINT 명령이 OS 신호를 받지 못해요.

FROM alpine
ENTRYPOINT my-program start
# entrypoint becomes: /bin/sh -c my-program start

실행 파일이 OS 신호를 받을 수 있게 하려면 CMDENTRYPOINT에 exec 형식을 써서, 셸 부모 프로세스 없이 컨테이너의 메인 프로세스(PID 1)로 실행되게 해요.

✅ 좋은 예: ENTRYPOINT가 OS 신호를 받아요.

FROM alpine
ENTRYPOINT ["my-program", "start"]
# entrypoint becomes: my-program start

프로그램을 PID 1로 실행하면 리눅스에서 PID 1이 가진 특별한 책임과 동작(예: 자식 프로세스 reap)을 맡게 된다는 점에 유의하세요.

해결 방법

여전히 셸 아래에서 컨테이너를 실행하고 싶은 경우가 있을 수 있어요. exec 형식을 쓰면 변수 확장, 파이프(|), 명령 연결(&&, ||, ;) 같은 셸 기능을 사용할 수 없어요. 이런 기능을 쓰려면 셸 형식이 필요합니다. 다만 이 경우에도 실행 파일은 셸의 자식 프로세스로 돌아요.

래퍼 스크립트 만들기

시작 명령을 감싸는 entrypoint 스크립트를 만들고, 그 스크립트를 JSON 형식의 ENTRYPOINT 명령으로 실행할 수 있어요.

✅ 좋은 예: ENTRYPOINT가 JSON 형식을 사용해요.

FROM alpine
RUN apk add bash
COPY --chmod=755 <<EOT /entrypoint.sh
#!/usr/bin/env bash
set -e
my-background-process &
my-program start
EOT
ENTRYPOINT ["/entrypoint.sh"]

셸을 명시적으로 지정

SHELL Dockerfile 지시어로 사용할 셸을 명시적으로 지정할 수 있어요. SHELL 지시어를 설정하면 셸 형식을 쓰는 것이 의식적인 결정임을 나타내므로 이 경고가 억제됩니다.

✅ 좋은 예: 셸이 명시적으로 정의됐어요.

FROM alpine
RUN apk add bash
SHELL ["/bin/bash", "-c"]
ENTRYPOINT echo "hello world"

더 알아보기