Dockerfile指令詳解下

VOLUME 定義匿名卷

VOLUME指令的格式爲:nginx

  • VOLUME [ <路徑1> , <路徑2> ...]
  • VOLUME <路徑>

以前咱們說過,容器運行時應該儘可能保持容器存儲層不發生寫操做,對於數據庫類須要保存動態數據的應用,其數據庫文件應該保存於卷(volume)中。爲了防止運行時用戶忘記將動態文件所保存目錄掛載爲卷,在Dockerfile 中,咱們能夠事先指定某些目錄掛載爲匿名卷,這樣在運行時若是用戶不指定掛載,其應用也能夠正常運行,不會向容器存儲層寫入大量數據.git

VOLUMN /data

這裏的 /data 目錄就會在運行時自動掛載爲匿名卷,任何向 /data 中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。固然,運行時能夠覆蓋這個掛載設置。好比:github

docker run -d -v mydata:/data xxxx

在這行命令中,就使用了 mydata 這個命名卷掛載到了 /data 這個位置,替代了Dockerfile 中定義的匿名卷的掛載配置web

EXPOSE 聲明端口

格式爲 EXPOSE <端口1> [ <端口2> ...] 。 redis

EXPOSE 指令是聲明運行時容器提供服務端口,這只是一個聲明,在運行時並不會由於這個聲
明應用就會開啓這個端口的服務。在 Dockerfile 中寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射;另外一個用處則是在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端。docker

要將 EXPOSE 和在運行時使用 -p <宿主端口> : <容器端口> 區分開來。 -p ,是映射宿主端口和容器端口,換句話說,就是將容器的對應端口服務公開給外界訪問,而 EXPOSE 僅僅是聲明容器打算使用什麼端口而已,並不會自動在宿主進行端口映射。 shell

WORKDIR 指定工做目錄

格式爲 WORKDIR <工做目錄路徑> 數據庫

使用 WORKDIR 指令能夠來指定工做目錄(或者稱爲當前目錄),之後各層的當前目錄就被改
爲指定的目錄,如該目錄不存在, WORKDIR 會幫你創建目錄。app

下面的寫法把Dockerfile按照Shell腳原本寫,可能致使下面的錯誤curl

RUN cd /app
RUN echo "hello" > world.txt

若是將這個 Dockerfile 進行構建鏡像運行後,會發現找不到 /app/world.txt 文件,或者其內容不是 hello 。緣由其實很簡單,在 Shell 中,連續兩行是同一個進程執行環境,所以前一個命令修改的內存狀態,會直接影響後一個命令;而在 Dockerfile 中,這兩行 RUN 命令的執行環境根本不一樣,是兩個徹底不一樣的容器。這就是對 Dockerfile 構建分層存儲的概念不瞭解所致使的錯誤。以前說過每個 RUN 都是啓動一個容器、執行命令、而後提交存儲層文件變動。第一層 RUN cd /app 的執行僅僅是當前進程的工做目錄變動,一個內存上的變化而已,其結果不會形成任何文件變動。而到第二層的時候,啓動的是一個全新的容器,跟第一層的容器更徹底不要緊,天然不可能繼承前一層構建過程當中的內存變化。

所以若是須要改變之後各層的工做目錄的位置,那麼應該使用 WORKDIR 指令。

USER 指定當前用戶

格式: USER <用戶名>

USER 指令和 WORKDIR 類似,都是改變環境狀態並影響之後的層。 WORKDIR 是改變工做目錄, USER 則是改變以後層的執行 RUN , CMD 以及 ENTRYPOINT 這類命令的身份。

固然,和 WORKDIR 同樣, USER 只是幫助你切換到指定用戶而已,這個用戶必須是事先創建
好的,不然沒法切換。

若是以 root 執行的腳本,在執行期間但願改變身份,好比但願以某個已經創建好的用戶來運行某個服務進程,不要使用 su 或者 sudo ,這些都須要比較麻煩的配置,並且在 TTY 缺失的環境下常常出錯。建議使用 gosu

# 創建 redis 用戶,並使用 gosu 換另外一個用戶執行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下載 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/
    gosu-amd64" \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true
# 設置 CMD,並以另外的用戶執行
CMD [ "exec", "gosu", "redis", "redis-server" ]

HEALTHCHECK 健康檢查

HEALTHCHECK 指令是告訴 Docker 應該如何進行判斷容器的狀態是否正常,這是 Docker 1.12
引入的新指,經過該指令指定一行命令,用這行命令來判斷容器主進程的服務狀態是否還正常,從而比較真實的反應容器實際狀態。格式:

  • HEALTHCHECK [選項] CMD <命令> :設置檢查容器健康情況的命令
  • HEALTHCHECK NONE :若是基礎鏡像有健康檢查指令,使用這行能夠屏蔽掉其健康檢查指令

當在一個鏡像指定了 HEALTHCHECK 指令後,用其啓動容器,初始狀態會爲 starting ,在HEALTHCHECK 指令檢查成功後變爲 healthy ,若是連續必定次數失敗,則會變爲unhealthy

HEALTHCHECK 支持下列選項:

  • --interval= <間隔> :兩次健康檢查的間隔,默認爲 30 秒
  • --timeout= <時長> :健康檢查命令運行超時時間,若是超過這個時間,本次健康檢查就被
    視爲失敗,默認 30 秒
  • --retries= <次數> :當連續失敗指定次數後,則將容器狀態視爲 unhealthy ,默認 3次

和 CMD , ENTRYPOINT 同樣, HEALTHCHECK 只能夠出現一次,若是寫了多個,只有最後一個生效。

在 HEALTHCHECK [選項] CMD 後面的命令,格式和 ENTRYPOINT 同樣,分爲 shell 格式,和exec 格式。命令的返回值決定了該次健康檢查的成功與否: 0 :成功; 1 :失敗; 2 :保留,不要使用這個值。

假設咱們有個鏡像是個最簡單的 Web 服務,咱們但願增長健康檢查來判斷其 Web 服務是否在正常工做,咱們能夠用 curl 來幫助判斷,其 Dockerfile 的 HEALTHCHECK 能夠這麼寫:

FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1

這裏咱們設置了每 5 秒檢查一次(這裏爲了試驗因此間隔很是短,實際應該相對較長),若是健康檢查命令超過 3 秒沒響應就視爲失敗,而且使用 curl -fs http://localhost/ || exit1 做爲健康檢查命令。

使用 docker build 來構建這個鏡像:

docker build -t myweb:v1 .

構建好了後,咱們啓動一個容器:

docker run -d --name web -p 80:80 myweb:v1

當運行該鏡像後,能夠經過 docker ps 看到最初的狀態爲 (health: starting) ,在等待幾秒鐘後,再次 docker ps ,就會看到健康狀態變化爲了 (healthy) 。若是健康檢查連續失敗超過了重試次數,狀態就會變爲 (unhealthy)。

爲了幫助排障,健康檢查命令的輸出(包括 stdout 以及 stderr )都會被存儲於健康狀態裏,能夠用 docker inspect 來查看

ONBUILD 爲他人作嫁衣裳

格式: ONBUILD <其它指令>

ONBUILD 是一個特殊的指令,它後面跟的是其它指令,好比 RUN , COPY 等,而這些指令,在當前鏡像構建時並不會被執行。只有當以當前鏡像爲基礎鏡像,去構建下一級鏡像的時候纔會被執行。Dockerfile 中的其它指令都是爲了定製當前鏡像而準備的,惟有 ONBUILD 是爲了幫助別人定製本身而準備的

相關文章
相關標籤/搜索