프레임워크/Docker

Dockerfile에서 bash 명령 사용하기 | RUN, CMD, ENTRYPOINT 차이

Julie825 2024. 12. 19. 10:45

도커는 이미지라는 틀을 만들고, 이를 사용해서 가상 머신을 만들어내는 식으로 동작한다.

이때 이미지를 정의하기 위해 Dockerfile을 문법에 맞게 적어줘야하는데, 여기서 쉘 명령어를 사용하기가 여간 까다롭지 않다.

도커파일에서 쉘 명령어를 입력하는 형식과, 사용할 수 있는 커맨드의 종류 두가지를 모두 정리해보았다.

 

1. Shell form과 exec form

Dockerfile 예시를 읽다보면 똑같은 일을 하는 명령을 두가지 방법으로 적어놓은 것을 볼 수 있다.

FROM centos:7
# 컨테이너 실행 직후 컨테이너에 running... 이라는 문구를 두번 출력한다.
CMD "echo 'Hello from shell form!'; exec bash" # shell form
CMD ["/bin/bash", "-c", "echo 'Hello from exec form!'; exec bash"] # exec form

 

대체 무슨 차이인걸까? 첫번째 방식은 shell form, 두번째는 exec form 이라고 불린다.

shell form은 기본적으로 컨테이너 쉘에 -c 옵션을 붙인 형태로 명령을 실행해주며, 괄호 표시가 필요없다.

쉘을 사용하며, $HOME, $PATH 등 환경변수를 처리해야할 때 이들을 기본적으로 상속받아오기 때문에 편리하다.

 

반면 exec form은 쉘을 거치지 않고 실행되기 때문에, "/bin/bash" 처럼 명령어를 실행 하기위한 방식을 지정해줘야한다.

일반적인 경우 커맨드 길이만 길어지고 환경변수도 상속받지 못해서 불편하지만, 아래처럼 특수한 형식으로 쓰일때는 아주 유용해진다.

 

- 볼륨 마운트 이후에 명령을 실행해야할 때

ex) 볼륨 연결해놓고 gradle build 했을 때 파일이 없다는 황당한 오류가 뜬다면 exec form을 사용하는 것 만으로도 해결된다.

- 사용자의 입력을 처리해야하는 경우.

ex) 사용자가 control + c 눌렀을 때 종료 스크립트 실행

- CMD 값을 ENTRYPOINT의 파라미터로 사용할 때

CMD와 ENTRYPOINT에 대해 읽고나면 더 잘 이해할 수 있다.

 

2. CMD, RUN, ENTRYPOINT

이제 명령어 형식은 알겠는데, 아직도 명령어가 너무 많다.

똑같이 쉘 명령어를 실행하는건데도 어디는 RUN을 쓰고 어디는 CMD를 사용하는 것이 보인다.

비슷해보이지만 명령어 사이에는 분명한 차이가 있다.

 

RUN

- 도커 이미지(= 붕어빵틀)를 빌드, 구성하는데 사용하는 명령을 내리는데에 사용된다.

- Dockerfile 하나에서 여러개의 명령어를 사용할 수 있지만, 그만큼 이미지의 레이어가 많아지고 크기도 커진다. 따라서 줄일 수 있는 경우 최소한으로 사용하는 것이 좋다.

 

ENTRYPOINT

- 컨테이너가 실행된 이후 항상 실행되어야할 명령을 내리는데에 사용된다.

- Dockerfile에서 한번만 사용할 수 있다. 이 말인즉슨  --entrypoint 옵션으로 docker run... 명령 내릴 때 치환할 수 있다는 뜻이다.

 

CMD

- 컨테이너가 실행된 이후 실행해야하는 명령을 내리는데에 사용된다.

- Dockerfile에서 한번만 사용할 수 있다. 그리고 docker run 뒤에 바꿀 커맨드를 붙이면 쉽게 치환할 수 있다.

- ENTRYPOINT의 변수로서 사용될 수 있다.

 

예시를 통해 보자.

FROM centos:7
# 1. 이미지 빌드시에 실행
RUN echo "⭐️ build... 1 ⭐️"
RUN echo "⭐️ build... 2 ⭐️"
# 2. 컨테이너 실행 후 실행
ENTRYPOINT ["echo", "=> 컨테이너 빌드완료\n"]
CMD ["=> 첫번째 CMD 실행"]
CMD ["=> 두번째 CMD 실행"]

 

이 이미지를 빌드하면 RUN은 모두 출력되지만, ENTRYPOINT와 CMD는 마지막 하나만 인식해서 아래처럼 출력된다.

# 이미지 빌드 명령 : docker build 실행위치 -f 도커파일위치 -t 이미지이름
kim@Julieui-MacBookAir api % docker build . -t echoimage -f ./Dokerfile --no-cache
[+] Building 1.1s (7/7) FINISHED                                           docker:desktop-linux
 => [internal] load build definition from Dokerfile                                        0.0s
 => => transferring dockerfile: 790B                                                       0.0s
 => [internal] load metadata for docker.io/library/centos:7                                0.8s
 => [internal] load .dockerignore                                                          0.0s
 => => transferring context: 2B                                                            0.0s
 => CACHED [1/3] FROM docker.io/library/centos:7@sha256:be65f488b7764ad3638f236b7b515b367  0.0s
 => [2/3] RUN echo "⭐️ build... 1 ⭐️"                                                            0.1s
 => [3/3] RUN echo "⭐️ build... 2 ⭐️"                                                            0.1s
 => exporting to image                                                                     0.0s
 => => exporting layers                                                                    0.0s
 => => writing image sha256:9fff2af34473c77734f67de0a543b584703cda78d0c66fe95f1cae0d2bfd3  0.0s
 => => naming to docker.io/library/echoimage                                               0.0s

What`s next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview 

# 컨테이너 생성 명령 : docker run 이미지이름 바꾸고싶은CMD
kim@Julieui-MacBookAir api % docker run echoimage                                 
=> 컨테이너 빌드완료
 => 두번째 CMD 실행
kim@Julieui-MacBookAir api % docker run echoimage 입력받은 CMD 실행
=> 컨테이너 빌드완료
 입력받은 CMD 실행

 

3. CMD와 ENTRYPOINT의 차이

CMD랑 ENTRYPOINT는 둘다 컨테이너 생성 직후에 호출되지만, 자주 교체되는 파라미터에 CMD를 사용하는 경우가 많다.

FROM centos:7
ENTRYPOINT ["echo"]
CMD ["i'm running ... 😀"]


위와 같은 도커파일이 있다고 할 때, exec 형태인 두 명령은 결합되어서 아래처럼 실행된다.

kim@Julieui-MacBookAir api % docker build . -t echoimage -f ./Dokerfile
kim@Julieui-MacBookAir api % docker run echoimage
i'm running ... 😀


ENTRYPOINT의 "echo"가 실행명령으로, CMD의 "i'm running... 😀" 이 파라미터로 들어간 것을 볼 수 있다.

 

이게 좋은 점은, 컨테이너를 만들 때 파라미터를 바꿀 수 있다는 점이다.

예를들어 i'm running 대신 i'm happy를 쓰고싶다고 해보자. 도커파일을 직접 수정하는 대신 아래와 같이 실행해주면 된다.

# docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
kim@Julieui-MacBookAir api % docker run echoimage  "im happy... 😆"
im happy... 😆

배포 환경별로 도커파일을 새로 만드는 경우도 흔한데, 이렇게 파라미터를 바꿀 수 있게되면 환경별로 다른 스크립트 파일을 실행시키는 것으로 비효율을 줄일 수 있다. 보통 이미지를 만드는 것이 병목구간이어서 이는 아주 유용하다.

 

 

참고한 자료

- Docker 모범 사례: RUN, CMD, ENTRYPOINT 중에서 선택

- ENTRYPOINT, CMD 차이