###### tags: `docker` # Dockerfile Docker會讀取`Dcokerfile`的指令自動建立image,`Dockerfile`為一文件涵蓋了所有使用者可以建立image的指令集,以下介紹各種可以用在`Dcokerfile`的指令。 ## Format 這份教學是不分大小寫的,但是為了容易辨別以下會使用大寫作為區分。。 Docker會按照順序讀取`Dockerfile`裡面的指令,一個`Dcokerfile`必須由`FROM`開頭,`FROM`指令表達了從哪一個母Image為根基,只有`ARG`能被寫在`FROM`指令前面。 Docker會將`#`視作註解符。 ## FROM `FROM`指令會初始化一個build階段並且為接下來的指令設定一個母image,一有效的`Dockerfile`會必須是由`FROM`指令為開頭,母image可以為任何有效的image。 - `ARG`是唯一一個指令可以在`FROM`前出現的。 - `FROM`在單一`Dockerfile`中可以出現好幾次用來建立多個images或是使用其中一個為另一個image的bulid stage。 - Optionally可以在`FROM`後加上`AS name`,這個name可以被使用在後續的`FROM`和`COPY --from=<name>`指令上用來標示出是哪個image要被用在建立這個stage上。 - `tag`和`digest`也是optionally,省略的話會自動以`latest`為預設 ### Understand how ARG and FROM interact `FROM`指令支援了`ARG`宣告的變數。 ```dockerfile= ARG CODE_VERSION=latest FROM base:${CODE_VERSION} CMD /code/run-app FROM extras:${CODE_VERSION} CMD /code/run-extras ``` 在`FROM`前宣告的`ARG`是屬於build stage之外的,所以它沒辦法被使用在`FROM`後面的任何指令上,為使用第一個`FROM`前的`ARG`宣告的值,可以使用一個不帶有值的`ARG`指令。 ```dockerfile= ARG VERSION=latest FROM busybox:$VERSION ARG VERSION RUN echo $VERSION > image_version ``` ## RUN RUN有兩種形式: - `RUN <command>` (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows) - `RUN ["executable", "param1", "param2"]`(exec form) `RUN`指令會在新的layer上針對目前的image執行任何指令並且遞交結果,結果會被用在`Dockerfile`的下一步。 `RUN`指令的分層以及遞交結果符合了Docker的核心概念就是階段控制是很簡單的且容器可以在images裡面的任何階段被建立,就像是版本控制一樣。 ## CMD `CMD`指令有三種格式 - `CMD ["executable","param1","param2"]` (exec form, this is the preferred form) - `CMD ["param1","param2"]`(as default parameters to ENTRYPOINT) - `CMD command param1 param2` (shell form) `Dokcerfile`只能夠有一個`CND`指令,如果使用了超過一個的`CMD`指令只有最後一個`CMD`會有作用。 `CMD`最主要的目的就是提供一個預設值給執行中的container,這些預設值可以包含可被執行的,或是省略可執行的,但就要提供一個`ENTRYPOINT`指令。如果`CMD`是用來提供`ENTRYPOINT`預設值參數的話,則兩個指令都要使用JSON array format。 如果有特別在`docker run`時特別另外寫參數,則會覆蓋掉`CMD`的預設值 :::info 別將`RUN`和`CMD`搞混了,`RUN`實際上會執行指令並且commit結果,`CMD`並不會在建立階段執行任何東西而是給了image預期的指令。 ::: ## LABEL ```dockerfile= LABEL <key>=<value> <key>=<value> <key>=<value> ... ``` `LABEL`指令為image加入metadata,`LABEL`是key-value paie的寫法,為在`LABEL`中使用空格,要使用引號或倒斜線,以下一些範例: ```dockerfile= LABEL "com.example.vendor"="ACME Incorporated" LABEL com.example.label-with-value="foo" LABEL version="1.0" LABEL description="This text illustrates \ that label-values can span multiple lines." ``` ## MAINTAINER(棄用) 官方建議使用`LABEL`來做標註image的維護者會是更有彈性的作法。例如使用`docker inspect`時,設定以下方式會與`MAINTAINER`相呼應: ```dockerfile= LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au" ``` ## EXPOSE ```dockerfile= EXPOSE <port> [<port>/<protocol>...] ``` `EXPOSE`指令指定的Docker在runtime階段要監聽哪一個網路接口,你可以設定為TCP或是UDP,預設值是TCP如果沒有特別設定的話。 `EXPOSE`指令並非真正發布這個port,其真正的作用在於文件上的形式為建立這image和使用並啟動這container的人之間說明是哪一個port會被使用,為了真正在跑container時使用這個port,在`docker run`時要使用`-p`標籤。 ## ENV ```dockerfile= ENV <key>=<value> ... ``` `ENV`指令設定了環境變數`<key>`為`<value>`的值,這個value會是接下來build stage中指令的環境變數。 ```dockerfile= ENV MY_NAME="John Doe" ENV MY_DOG=Rex\ The\ Dog ENV MY_CAT=fluffy ``` `ENV`指令允許多個`<key>=<value> ...`例如: ```dockerfile= ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \ MY_CAT=fluffy ``` 當一個container從一個被建立後的image運行後,使用`ENV`建立的環境變數會被保留,可以使用`docker inspect`來觀察,並且使用`docker run --env <key>=<value>`來變更。 永久性的環境變數可能造成一些壞處,例如設定`ENV DEBIAN_FRONTEND=noninteractive`改變了`apt-agt`的行為,並有可能混淆image的使用者。 如果一個環境變數是只有在建立階段是被需要的而非在最終的image,考慮將其放在單一指令例如下面: ```dockerfile= RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ... ``` 或是使用`ARG`,這也不會保存到最後階段的image上 ```dockerfile= ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y ... ``` ## ADD ADD 有兩種寫法 ```dockerfile= ADD [--chown=<user>:<group>] [--checksum=<checksum>] <src>... <dest> ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] ``` `ADD` 指令會從`<src>`複製新的檔案、路徑或是遠程文件並將它們加入到image的檔案系統路徑`<dest>`。 多個`<src>`是可以被設定的但如果他們是檔案或是路徑,他們的路徑會被當作這個build的內容的來源。 個別`<src>`可以是通配符例如: 將所有hom開頭的檔案加入 ```dockerfile= ADD hom* /mydir/ ``` 下面是替換單一字母 ```dockerfile= ADD hom?.txt /mydir/ ``` `<dest>`是一組絕對路徑或是`WORKDIR`的相對路徑,就是資源會被複製到的container的地方。 以下例子使用相對路徑,會將"test.txt"複製到`<WORKDIR>/relativeDir/`: ```dockerfile= ADD test.txt relativeDir/ ``` 以下是絕對路徑,會將"test.txt"複製到`/absoluteDir/` ```dockerfile= ADD test.txt /absoluteDir/ ``` ## COPY `COPY` 指令會從`<src>`複製新的檔案、路徑並將它們加入到image的檔案系統路徑`<dest>`。 ## VOLUME ```dockerfile= VOLUME ["/data"] ``` `VOLUME`指令會建立一個映照點(mount point)有自訂的名稱並標註其為存儲本地主機或其他container的外部資料。 其值可以是JSON陣列`VOLUME ["/var/log/"]`,或是字串有著多個參數例如,`VOLUME /var/log` or `VOLUME /var/log /var/db`。