CMD 容器啟動指令

CMD 指令的格式和 RUN 相似,也是兩種格式:

  • shell 格式:CMD <指令>
  • exec 格式:CMD ["可執行檔案", "參數1", "參數2"...]
  • 參數清單格式:CMD ["參數1", "參數2"...]。在指定了 ENTRYPOINT 指令後,用 CMD 指定具體的參數。

之前介紹容器的時候曾經說過,Docker 不是虛擬機,容器就是處理序。既然是處理序,那麼在啟動容器的時候,需要指定所執行的程式及參數。CMD 指令就是用於指定 預設的容器主處理序的啟動指令的。

在執行時可以指定新的指令來替代映像檔設定中的這個 預設指令,比如,ubuntu 映像檔 預設的 CMD/bin/bash,如果我們直接 docker run -it ubuntu 的話,會直接進入 bash。我們也可以在執行時指定執行別的指令,如 docker run -it ubuntu cat /etc/os-release。這就是用 cat /etc/os-release 指令取代了 預設的 /bin/bash 指令了,輸出了系統版本資訊。

在指令格式上,一般推薦使用 exec 格式,這類格式在解析時會被解析為 JSON 陣列,因此一定要使用雙引號 ",而不要使用單引號。

如果使用 shell 格式的話,實際的指令會被包裝為 sh -c 的參數的形式進行執行。比如:

CMD echo $HOME

在實際執行中,會將其變更為:

CMD [ "sh", "-c", "echo $HOME" ]

這就是為什麼我們可以使用環境變數的原因,因為這些環境變數會被 shell 進行解析處理。

提到 CMD 就不得不提容器中應用在前台執行和後台執行的問題。這是初學者常出現的一個混淆。

Docker 不是虛擬機,容器中的應用都應該以前台執行,而不是像虛擬機、物理機裡面那樣,用 upstart/systemd 去啟動後台服務,容器內沒有後台服務的概念。

一些初學者將 CMD 寫為:

CMD service nginx start

然後發現容器執行後就立即結束了。甚至在容器內去使用 systemctl 指令結果卻發現根本執行不了。這就是因為沒有搞明白前台、後台的概念,沒有區分容器和虛擬機的差異,依舊在以傳統虛擬機的角度去理解容器。

對於容器而言,其啟動程式就是容器應用處理序,容器就是為了主處理序而存在的,主處理序結束,容器就失去了存在的意義,從而結束,其它輔助處理序不是它需要關心的東西。

而使用 service nginx start 指令,則是希望 upstart 來以後台守護處理序形式啟動 nginx 服務。而剛才說了 CMD service nginx start 會被理解為 CMD [ "sh", "-c", "service nginx start"],因此主處理序實際上是 sh。那麼當 service nginx start 指令結束後,sh 也就結束了,sh 作為主處理序結束了,自然就會令容器結束。

正確的做法是直接執行 nginx 可執行檔案,並且要求以前台形式執行。比如:

CMD ["nginx" "-g" "daemon off;"]

results matching ""

    No results matching ""