일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 앱테크
- 하나머니
- AWS
- built-in
- Container
- mininet
- 커피머니불리기
- Linux
- 후기
- docker
- MongoDB
- VPC
- python3
- 토스카드
- Python
- 실사용
- 리뷰
- aws codecommit
- clone
- 포인트앱
- DocumentDB
- 재테크
- codecommit
- MongoEngine
- 도커
- S3
- 리워드앱
- network
- namespace
- docker network
- Today
- Total
ㅍㅍㅋㄷ
Dockerfile Entrypoint 와 CMD의 올바른 사용 방법 본문
ENTRYPOINT 와 CMD 는 무엇인가
ENTRYPOINT 와 CMD는 해당 컨테이너가 수행하게 될 실행 명령을 정의하는 선언문이다.
즉, 컨테이너가 무슨 일을 하는지 결정하는 최종 단계를 정의하는 명령이라고 생각하면 된다. 그렇기 때문에 Dockerfile 의 가장 마지막 부분 쯤에 Entrypoint 또는 CMD 를 선언하게 된다.
그렇다면, ENTRYPOINT 와 CMD는 어떤 차이가 있고 어떻게 사용해야 좋을까.
ENTRYPOINT 와 CMD 는 무엇이 다른가?
ENTRYPOINT 와 CMD 의 가장 큰 차이점은 바로 컨테이너 시작시 실행 명령에 대한 Default 지정 여부이다.
만약 ENTRYPOINT 를 사용하여 컨테이너 수행 명령을 정의한 경우,
해당 컨테이너가 수행될 때 반드시 ENTRYPOINT 에서 지정한 명령을 수행되도록 지정 된다.
하지만, CMD를 사용하여 수행 명령을 경우에는,
컨테이너를 실행할때 인자값을 주게 되면 Dockerfile 에 지정된 CMD 값을 대신 하여 지정한 인자값으로 변경하여 실행되게 된다.
말이 어려운데 실제로는 심플한 동작 방식이다.
아래 Dockerfile 예제를 보자.
# Dockerfile
FROM ubuntu
CMD ["/bin/df", "-h"]
위에서 정의된 Dockerfile 은 CMD 를 사용하여 df -h 명령을 한번 수행하고 종료되는 이미지를 만드는 것이다.
먼저 테스트를 위해 위 Dockerfile 을 사용해 jhsong/df 라는 이름을 가진 이미지를 빌드해보자.
❯ docker build -t jhsong/df .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM ubuntu
---> 94e814e2efa8
Step 2/2 : CMD ["/bin/df", "-h"]
---> Running in c5f57fca1068
Removing intermediate container c5f57fca1068
---> 80eeec0ef7c0
Successfully built 80eeec0ef7c0
Successfully tagged jhsong/df:latest
빌드된 jhsong/df 이미지를 컨테이너로 동작시켜 보면, Dockerfile 에서 정의된 대로 df -h 명령을 실행하고 종료되게 된다.
❯ docker run --name jhsong-df jhsong/df
Filesystem Size Used Avail Use% Mounted on
overlay 59G 5.6G 50G 11% /
tmpfs 64M 0 64M 0% /dev
tmpfs 1000M 0 1000M 0% /sys/fs/cgroup
/dev/sda1 59G 5.6G 50G 11% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 1000M 0 1000M 0% /proc/acpi
tmpfs 1000M 0 1000M 0% /sys/firmware
이번에는, 컨테이너 실행시 추가 인자 값을 줘서 컨테이너가 수행할 명령을 바꿔보자.
docker run 으로 컨테이너 실행시 마지막에 ps 명령을 추가 인자를 주고 실행해 보면 아래와 같은 결과를 볼 수 있다.
❯ docker run --name jhsong-df jhsong/df ps -aef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 15:19 ? 00:00:00 ps -aef
즉, CMD 로 지정한 내용 대신 컨테이너 실행시 받은 인자로 대체하여 실행됨을 볼 수 있다.
docker inspect 명령을 통해 컨테이너 설정 내용을 자세히 보자.
❯ docker inspect jhsong-df
....
"Cmd": [
"ps",
"-aef"
],
...
위와 같이 컨테이너 설정에 Cmd 값이 인자 값으로 대체된 것이 확인 가능하다.
이번에는 ENTRYPOINT 를 사용하여 컨테이너 이미지를 만들어보자.
내용은 이전에 정의했던 것과 같으며, 단지 CMD를 ENTRYPOINT 로 대신한 것 뿐이다.
# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/df", "-h"]
이번엔 jhsong/df:entry 라는 태그를 추가하여 이미지를 빌드 하고,
❯ docker build -t jhsong/df:entry .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM ubuntu
---> 94e814e2efa8
Step 2/2 : ENTRYPOINT ["/bin/df", "-h"]
---> Running in 61f6f8ad4f61
Removing intermediate container 61f6f8ad4f61
---> cc23a8719b6e
Successfully built cc23a8719b6e
Successfully tagged jhsong/df:entry
빌드된 jhsong/df:entry 이미지로 컨테이너를 실행해 본다.
❯ docker run --name jhsong-df jhsong/df:entry
Filesystem Size Used Avail Use% Mounted on
overlay 59G 5.6G 50G 11% /
tmpfs 64M 0 64M 0% /dev
tmpfs 1000M 0 1000M 0% /sys/fs/cgroup
/dev/sda1 59G 5.6G 50G 11% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 1000M 0 1000M 0% /proc/acpi
tmpfs 1000M 0 1000M 0% /sys/firmware
실행된 결과는 이전 CMD 와는 다른게 없다.
하지만, docker inspect 로 자세히 살펴보면 약간의 다른점을 볼 수 있다.
❯ docker inspect jhsong-df
...
"Cmd": null,
...
"Entrypoint": [
"/bin/df",
"-h"
],
...
Entrypoint 항목에 실행된 명령 정보가 있고, CMD는 null 로 비워져 있는것을 볼 수 있다.
이제, 위에서 했던 작업과 같이 docker run 으로 수행시 인자를 추가로 넣어 컨테이너를 실행해 보면
ENTRYPOINT 와 CMD의 확실한 차이를 볼 수 있다.
❯ docker run --name jhsong-df jhsong/df:entry ps -aef
/bin/df: invalid option -- 'e'
Try '/bin/df --help' for more information.
위와 같이 에러를 출력하며 원하는 동작이 실행되지 않았음을 볼 수 있다.
왜 그런지는 docker inspect 를 통해 살펴보면 알 수 있을 것이다.
❯ docker inspect jhsong-df
...
"Cmd": [
"ps",
"-aef"
],
...
"Image": "jhsong/df:entry",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/bin/df",
"-h"
],
...
즉, 컨테이너 실행시 /bin/df 명령은 유지하고, 추가 인자를 CMD로 받아 처리한 것을 볼 수 있다.
결국, 컨테이너 시작시 아래와 같은 명령어를 수행한 것과 같은 것이며, 이는 적절한 명령이 아니었으므로 에러로 끝난 것임을 알 수 있다.
> df -h ps -aef # ENTRYPOINT : df -h / CMD : ps -aef
df: invalid option -- 'e'
Try 'df --help' for more information.
ENTRYPOINT 와 CMD의 올바른 사용 방법
그렇다면 ENTRYPOINT와 CMD 는 어떻게 사용하는게 좋을까.
첫째로는, 컨테이너가 수행될 때 변경되지 않을 실행 명령은 CMD 보다는 ENTRYPOINT 로 정의하는게 좋다.
컨테이너를 만들때 아마 대부분은 해당 컨테이너가 실행될 목적이 분명할 것이다.
웹서버(nginx)가 될 수도 있고, App서버(node)가 될 수도 있으며 DB(mysql) 가 될 수도 있다.
즉, 이미지를 만들때는 이러한 실행 목적이 분명하므로 nginx / node / mysql 같은 메인 프로세스가 될 명령의 경우는 ENTRYPOINT 로 정의하는게 명확할 것이다.
두번째로는, 메인 명령어가 실행시 default option 인자 값은 CMD로 정의해 주는게 좋다.
CMD 는 ENTRYPOINT 와 함께 사용시 추가 인자 값으로 활용 된다.
그러므로, 메인 프로세스에 대한 default 옵션값을 CMD 로 정의해주면 좋을 것이다.
추가로 ENTRYPOINT 와 CMD는 리스트 포맷 ( ["args1", "args2",...] )으로 정의해 주는게 좋다.
보통 ENTRYPOINT 와 CMD 를 작성할때는 대부분 List 형태로 작성하지만,
아래와 같이 일반적인 shell 형태로도 작성 가능하다.
# Dockerfile
FROM ubuntu
Add loop.sh /usr/local/bin/loop.sh
ENTRYPOINT /usr/local/bin/loop.sh 1 # Shell format
하지만, 이런 방식으로 이미지를 빌드 한 후 컨테이너를 실행하게 되면
아래와 같이 컨테이너 실행시 몇가지 다른 방식으로 프로세스를 구동되게 됨을 알수 있다.
아래는 loop 를 돌며 ps 명령을 통해 컨테이너의 프로세스를 확인하는 쉘 스크립트이다.
#!/bin/bash
INTERVAL=$1
while true;
do
ps x;
sleep $INTERVAL;
done
이걸 사용하여 컨테이너가 구동시 실제 컨테이너 내부의 프로세스를 확인해 보자.
❯ docker run --name jhsong-loop jhsong/loop
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/sh -c /usr/local/bin/loop.sh 1
6 ? S 0:00 /bin/bash /usr/local/bin/loop.sh 1
43 ? R 0:00 ps x
....
컨테이너 내부 프로세스를 보니,
먼저 /bin/sh -c 명령으로 loop.sh 실행 명령을 string 으로 읽은 후에,
loop.sh 을 /bin/bash 명령으로 실행하는 것을 볼 수 있다.
그렇다면 동일한 내용을 ENTRYPOINT를 List 포맷으로 실행하면 어떨까.
# Dockerfile
FROM ubuntu
Add loop.sh /usr/local/bin/loop.sh
ENTRYPOINT ["/usr/local/bin/loop.sh"] # exec form
CMD ["1"]
이미지를 다시 빌드 후 docker run 으로 실행해 보면 아래와 같은 결과를 볼 수 있다.
❯ docker run --name jhsong-loop2 jhsong/loop:2
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/bash /usr/local/bin/loop.sh 1
7 ? R 0:00 ps x
...
아까와는 다르게 /bin/sh -c 를 거치지 않고 바로 script 를 실행한 것을 볼 수 있다.
별다른 차이는 아니지만, sh 를 거쳐 실행하는 것보다는 바로 script 를 실행하는 것이 clear 할 것이다.
그래서 Docker 공식 Documentation 을 보면, List 형태로 작성하는 것을 추천하고 있다.
[Reference]
'IT > Docker' 카테고리의 다른 글
Docker Network 구조(4) - container link 구조 (9) | 2016.07.11 |
---|---|
Docker Network 구조(3) - container 외부 통신 구조 (15) | 2016.07.06 |
Docker Network 구조(2) - container network 방식 4가지 (1) | 2016.05.20 |
Docker container IP 확인 (4) | 2016.05.18 |
docker container에 접속하기 (2) | 2015.07.21 |