動機

這裡是研究(自己的觀察與猜測)dockerfile的語意啦

docker的container就是應用程式 只不過每個container都有自己的

  1. 網路空間
  2. 儲存空間

ENTRYPOINT & CMD

一般在網路上會看到ENTRYPOINT & CMD在不同form下會有不同的效果 在官方文件上有以下的表格

| | No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | | ************************** | ************************** | ****************************** | ********************************************** | | No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry | | CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd | | CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_cmd | | CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |

來觀察一下在不同的form下會有什麼差別

exec form: 沒有/bin/sh -c shell form: 有/bin/sh -c

所以shell form就是一種macro會自己加上/bin/sh -c

故想像成在terminal打指令 exec form: 直接打在terminal shell form: 先加上/bin/sh -c,在打到terminal

那再回來看ENTRYPOINT & CMD 其實ENTRYPOINT & CMD就是

分別展開ENTRYPOINT與CMD的指令再append起來再送到terminal執行

ARG & ENV

再來看ARG與ENV的scope

ARG是build的參數,所以只能在RUN的時候看到 ENTRYPOINT與CMD都看不到

ENV就都看的到

exec form & shell form

那執行期時ARG與ENV在exec form與shell form有什麼不同嗎? 直接跑跑看!!

Sending build context to Docker daemon  2.048kB
Step 1/9 : FROM alpine
latest: Pulling from library/alpine
df20fa9351a1: Pull complete
Digest: sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321
Status: Downloaded newer image for alpine:latest
 ---> a24bb4013296
Step 2/9 : ARG hi=1
 ---> Running in 81dac0cde5c5
Removing intermediate container 81dac0cde5c5
 ---> 0a224d4f46ce
Step 3/9 : ENV wow=2
 ---> Running in 6c54e59b4201
Removing intermediate container 6c54e59b4201
 ---> 6a008ef31f84
Step 4/9 : RUN echo $hi
 ---> Running in 69eec389ae0d
1
Removing intermediate container 69eec389ae0d
 ---> a025a3789730
Step 5/9 : RUN ["echo", "$hi"]
 ---> Running in ecf7cc563edf
$hi
Removing intermediate container ecf7cc563edf
 ---> c3f8c7e3d843
Step 6/9 : RUN echo $wow
 ---> Running in 0a47051aeaa0
2
Removing intermediate container 0a47051aeaa0
 ---> f0ec803da2d4
Step 7/9 : RUN ["echo", "$wow"]
 ---> Running in 7b443431db18
$wow
Removing intermediate container 7b443431db18
 ---> 91902d5a74b1
Step 8/9 : RUN ["/bin/sh", "-c", "echo", "$hi"]
 ---> Running in 865729798cb8

Removing intermediate container 865729798cb8
 ---> e709f7ba52dd
Step 9/9 : RUN ["/bin/sh", "-c", "echo", "$wow"]
 ---> Running in 9897c8732ca2

Removing intermediate container 9897c8732ca2
 ---> aaa72c558553
Successfully built aaa72c558553

如果用exec form,不會置換, shell form會置換

所以只有shell form才會看到Dockerfile的變數 但exec form根本看不到

所以可以對shell form與exec form下一個結論 shell form是統一跑在Dockerfile自己開的shell(像/bin/sh) exec form就是直接用像是execl之類的東西直接去跑,把所有東西當成字串

那麼應該可以猜shell的展開大概會像

envs = ""
for (k,v) in ENVS: # env的所有pair,與arg的pair(如果可以的話)
    envs += "{}={} ".format(k,v)

return "/bin/sh -c {} {CMD}".format(envs,cmd)

exec的話就是

system(' '.join(input_array))

等等,那為什麼CMD可以執行指令,在沒有ENTRYPOINT的時候

如果沒有ENTRYPOINT就可以看成

"" + <expanded CMD>

自然就有CMD作為指令執行的效果

等等,那為什麼ENTRYPOINT的shell form會忽略CMD??

如果兩邊都是shell form,兩邊展開就是

/bin/sh -c ... /bin/sh -c ...

但是實際上CMD不會執行指令,他只是展開與ENTRYPOINT接在一起 這樣不符合shell form會執行指令的預期就擋掉了

2 phases

前面看了這麼多,可以看出Dockerfile有兩個階段

  1. build phase: 生成Image的時候,ARG+RUN+ENV
  2. run phase: 跑container的時候,ENTRYPOINT+CMD+ENV

Ref

官方文件