為什麼 ENTRYPOINT 沒有被執行到?
今天朋友問了一個 Docker 問題很神奇,他說:為何 ENTRYPOINT 設定好了,但它沒有被執行?
苦主的狀況如下:
Dockerfile 基於 node:14.5.0-alpine,原始碼片段如下:
ENTRYPOINT [ "docker-entrypoint.sh" ] |
docker-entrypoint.sh
內容如下:
|
苦主的問題是:他希望 docker run 的時候,一定要執行 yarn --prod
,但不知道為何,怎麼試都無法成功。
追問題過程
首先當然是要試著重現看看。上面的說明已經有 shell script 完整檔案了,Dockerfile
就得模擬一下:
FROM node:14.5.0-alpine |
然後 build and run:
docker build -t=test . |
確實 yarn --prod
沒有被執行,非常怪。
確認腳本運作正常
接著我第一步是確認 docker-entrypoint.sh
權限正常,以及進 container 可以正常使用 shell:
docker run --rm -it test bash |
腳本一切正常,但 docker run 就是不會出現 yarn --prod
。
改用 shell mode
想說會不會是環境變數的問題,於是就把 Dockerfile 改寫如下:
ENTRYPOINT docker-entrypoint.sh |
這時突然有不一樣的結果:
$ docker run --rm -it test whoami |
突然跑出 Node.js 的 REPL,咦?怎麼會是你?
這一瞬間想通了某件事,所以我把 Dockerfile 改回去,再執行一次指令試驗,終於知道為什麼了:
# 確認 docker-entrypoint.sh 檔案存在於根目錄 |
原來我完全忘了 ENTRYPOINT ["docker-entrypoint.sh"]
這樣的寫法,會依照 PATH
環境變數找執行檔的位置並執行,所以這樣的寫法會執行 Node image 的 /usr/local/bin/docker-entrypoint.sh
,而不是我的 Dockerfile 裡的 /docker-entrypoint.sh
。
會發生這個誤會的關鍵有兩個:
- 自定義的 entrypoint 剛好跟 image 的 entrypoint 撞名
- image 的 entrypoint 剛好放在
PATH
環境變數的路徑下
解法超級簡單,Dockerfile 加個斜線就好了:
ENTRYPOINT ["/docker-entrypoint.sh"] |
結論
如果了解了上面的情境,接著想像一下,如果 base image CMD 設定如下:
CMD ["mycmd", "args"] |
這時有人 FROM image 寫了下面這個 Dockerfile,而沒有覆寫 CMD 或 ENTRYPOINT:
# 另一個同名的 mycmd |
這個 Dockerfile build 出來的 image,執行就會出問題了。
然後才發現,官方 Dockerfile 不管是指令還是腳本,都會寫絕對路徑,原來就是為了避免今天遇到的這個問題。