了解 CMD 與 ENTRYPOINT

在寫 Dockerfile 或使用 docker run 時,我們使用 CMD 來執行指令。Docker 還設計了另一個類似的設定叫 ENTRYPOINT。活用這兩個設定將能讓 Docker image 使用更加靈活。

CMD 的設計

首先要強調一個重要的概念--container 就是 process。啟動 container 背後的原理就是啟動 process。

因此 Docker CMD 有個特性是,後面的設定會覆蓋前面的設定。比方說,以 alpine:3.12 的 Dockerfile 為例

FROM scratch
ADD alpine-minirootfs-3.12.0-x86_64.tar.gz /
CMD ["/bin/sh"]

這裡 CMD 的設定是 /bin/sh。FROM 它的 image 如 php:7.4-alpine

FROM alpine:3.12

# 略

CMD ["php", "-a"]

有設定新的 CMD 時,它會以後設定的為主。

docker run 是類似的情境,它是最後一關,先再看一次 docker run 的語法:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

這裡的 [COMMAND] 指的就是 CMD。在下 docker run 沒有帶 COMMAND 的時候,它會使用 Dockerfile 定義的 CMD;如果有給,它就會覆蓋 CMD 設定。

也就是,下面這幾個例子結果是一樣的:

# Alpine
docker run --rm -it alpine:3.12
docker run --rm -it alpine:3.12 /bin/sh

# Node
docker run --rm -it php:7.4-alpine
docker run --rm -it php:7.4-alpine php -a

若想在 container 上執行其他指令,只要在後面接 COMMAND 即可:

# Alpine
docker run --rm -it alpine:3.12 ls

# Node
docker run --rm -it node:14-alpine node -v

這招在過去幾天裡很常拿來用,實際的意義就是取代 CMD 設定。

ENTRYPOINT 的設計

ENTRYPOINTCMD 很像,一樣是啟動 container 的時候會用到,所以一樣也會有後面設定覆蓋的狀況。

mysql:8 為例:

# 略
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306 33060
CMD ["mysqld"]

若將啟動 container 當成是執行指令的話,那 docker runENTRYPOINT 的關係就像下面這樣

# 等於在容器內執行 docker-entrypoint.sh mysqld
docker run --rm -it mysql:8

# 等於在容器內執行 docker-entrypoint.sh bash
docker run --rm -it mysql:8 bash

這時,我們可以做一個實驗:

# 使用 bash 進入 container
docker run --rm -it mysql:8 bash

# 在 container 裡執行 ENTRYPOINT + bash
docker-entrypoint.sh bash

# 在 container 裡執行 ENTRYPOINT + mysqld
docker-entrypoint.sh mysqld

沒錯,這就跟上面直接執行 docker run 結果一樣。相信這個實驗做完,就能理解 CMD 與 ENTRYPOINT 的差異與用法了。

今日自我回顧

了解 CMD 與 ENTRYPOINT 的設計後,接著就能開始設計更多變化的 image 了。