1. 程式人生 > >如何向 Docker 容器傳遞引數

如何向 Docker 容器傳遞引數

我們在執行 docker 映象時希望能用下面的命令向容器傳遞命令列引數

docker run <image-name> <command> arg1 arg2
docker run <image-name> arg1 arg2

其實只有第一種形式,緊隨映象名後那個總是一個命令,其後才是引數。如果要向 docker 容器傳遞引數時,Dockerfile 該如何寫,這就有必要稍稍瞭解一下 Dockerfile 中  CMD 和 ENTRYPOINT 這兩個指令,並且它們有 exec 和 shell 兩種格式的寫法。詳情請見上篇 Dockerfile 中命令的兩種書寫方式的區別

對於一個 docker 映象,我們可以這麼來理解  ENTRYPOINT 與 CMD 的關係

  1. 如果沒有定義 ENTRYPOINT, CMD 將作為它的 ENTRYPOINT
  2. 定義了 ENTRYPOINT 的話,CMD 只為 ENTRYPOINT 提供引數
  3. CMD 可由 docker run <image> 後的命令覆蓋,同時覆蓋引數

對於 #1 和  #2 更精緻的理解是容器執行的最終入口由 ENTRYPOINT 和實際的 CMD 拼接而成。ENTRYPOINT 和 CMD 需轉換為實際映象中的 exec 格式來拼接,合併後的第一個元素是命令,其餘是它的引數。

舉四個例子進行說明

一, 未定義 ENTRYPOINT, 定義了 CMD

#ENTRYPOINT []
CMD ["echo", "hello"]

實際入口是它們拼接後還是 CMD 本身,["echo", "hello"]

二, 定義了 ENTRYPOINT 和 CMD

ENTRYPOINT ["echo", "hello"]
CMD ["echo", "world"]

實際入口是它們拼接起來,形成 ["echo", "hello", "echo", "world"], 執行 docker run test 顯示為 hello echo world

三, 定義了 ENTRYPOINT, CMD 由 docker run

 提供

ENTRYPOINT  ["echo", "hello"]

執行命令 docker run <image> rm -rf /, 實際入口是由 ["echo", "hello"] 與 ["rm", "-rf", "/"] 拼接而成的 ["echo", "hello", "rm", "-rf", "/"], 輸出為 hello rm -rf /。看到 rm -rf / 也不用擔心,用 ENTRYPOINT 就是讓人放心

注:ENTRYPOINT 同樣可以被覆蓋,如 docker run --entrypoint ls test -l /,將會執行 ls -l / 命令。

四, 如果 ENTRYPOINT 用 shell 格式定義的

ENTRYPOINT java -jar /app.jar
CMD ["hello", "world"]

通過 docker inspect 命令看到映象中實際的 ENTRYPOINT 是

ENTRYPOINT ["/bin/sh", "-c", "java -jar /app.jar"]

所以與 CMD 連線起來的入口就是 ["/bin/sh", "-c", "java -jar /app.jar", "hello", "world"], "bin/sh" 直接忽略掉後面的 "hello" 與 "world",這就是為什麼  shell 命令方式無法獲取引數。

有了以上幾點概念,以及四個例項作為感觀認識後,想要怎麼往容器傳遞引數應該很容易確定了。

未定義 ENTRYPOINT

沒有定義 ENTRYPOINT 的映象想怎麼來就怎麼來,docker run <image> 後面的輸入你自己作主。

有定義 ENTRYPOINT

定義了 ENTRYPOINT 的映象,則是 CMD 或 docker run <image> 後的輸入作為  ENTRYPOINT 中命令的附加引數。再次提醒 shell 格式的 ENTRYPOINT 和 CMD  務必要轉換為相應 exec  格式來理解。

shell 格式的 ENTRYPOINT

如果是複雜的 shell 命令不容易拆解出一個個引數,而希望用  shell 格式來定義 ENTRYPOINT 的話,也有辦法。shell 格式的 ENTRYPOINT 是由 "/bin/sh -c" 啟動的,而它是可以解析變數的。另一方面 CMD 或 docker run <image> 的輸入第一個元素存成了 $0,其他剩餘元素存為 [email protected], 所以 shell 格式的 ENTRYPOINT 可以這麼寫

ENTRYPOINT echo hello $0 [email protected]

注:shell 中 $0 表示命令本身,[email protected] 為所有引數

這樣執行下面 docker 命令將可獲得所有的引數輸入

$ docker run test world and China
hello world and China

如果只是按常規 shell 指令碼來對待,想當然的寫成

ENTRYPOINT echo hello [email protected]

效果將是

$ docker run test world and China
hello and China

第一個引數將被丟失,docker run <image> 後第一個輸入通常是一個命令,所以是 $0, 而 ENTRYPOINT 又希望它是一個普通引數,因此$0 [email protected] 要同時寫上。

直接用 docker inspect <container-id> 檢視

最簡單且準確的方式就是直接用 docker inspect <container-id> 檢視實際啟動的命令及引數