【Docker(四)】Dockerfile命令參數詳解


【Docker(三)】通過Dockerfile定制鏡像中,描述了鏡像的分層構成邏輯,如何通過docker commit和Dockerfile定制鏡像;以及docker build命令的基本用法。

本文接着對 Dockerfile 進行探究,學習她的命令及參數;大概看一下命令的概覽,了解下,后面會進行詳細介紹。

Dockerfile格式

下面是 Dockerfile 的格式:

# Comment 注釋
INSTRUCTION arguments
  • INSTRUCTION :指令,不區分大小寫。但約定使用大寫形式,以便更容易地與參數區分開來。
  • arguments:命令行參數
  • Comment:以井號開頭的注釋

FROM 基礎鏡像

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM 指令初始化一個新的構建階段,並為后續指令設置基礎鏡像,該鏡像可以是任何有效的鏡像。舉個例子:

FROM centos:7
...

需要注意的是:

  • 一般來說,Dockerfile都是以FROM指令開始;但ARG是Dockerfile中唯一可能先於FROM的指令(具體參考下面的ARG介紹)。
  • FROM可以在一個Dockerfile中出現多次,以創建多個映像或使用一個構建階段作為另一個構建階段的依賴項。只需在每個新的FROM指令之前記錄提交的最后一個圖像ID輸出。每個FROM指令清除前面指令創建的任何狀態。
  • [AS <name>] 參數可以添加在FROM指令之后,來為新的構建階段指定一個名稱。該名稱可以在后續的FROMCOPY ——FROM =<name>指令中使用,以引用在此階段構建的映像。
  • tagdigest值是可選的。如果省略其中任何一個,構造器默認采用latest標記。如果不能找到tag值,構造器將返回一個錯誤。
  • --platform標志可用於在FROM引用多平台鏡像的情況下指定鏡像的平台。如:linux/amd64, linux/arm64, or windows/amd64
  • 除了選擇現有鏡像為基礎鏡像外,Docker 還存在一個特殊的鏡像,名為 scratch。這個鏡像是虛擬的概念,並不實際存在,它表示一個空白的鏡像。

MAINTAINER (廢棄) 設置Author

MAINTAINER <name>

MAINTAINER指令設置生成的鏡像的Author字段,已經廢棄使用。LABEL指令是一個更靈活的版本,你、應該使用它,因為它可以設置所需要的任何元數據,並且可以很容易地通過docker inspect查看。可以使用與MAINTAINER字段相對應的標簽:

LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"

在下面將會介紹LABEL指令。

RUN 執行命令

RUN指令將在當前鏡像上的新層中執行任何命令並提交結果。生成的提交鏡像將用於Dockerfile中的下一步。其格式有兩種:

  • shell 格式:RUN <命令>,就像直接在命令行中輸入的命令一樣。剛才寫的 Dockerfile 中的 RUN 指令就是這種格式。
  • exec 格式:RUN ["可執行文件", "參數1", "參數2"],這更像是函數調用中的格式。

需要注意的是,Dockerfile 中每一個指令都會建立一層,RUN 也不例外。每一個 RUN 的行為,就會新建立一層,在其上執行這些命令,執行結束后,commit 這一層的修改,構成新的鏡像。

在shell形式中,可以使用(反斜杠)來將單個RUN指令繼續到下一行。例如這兩行:

RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

等價於

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

COPY 復制文件

COPY [--chown=<user>:<group>] <源路徑>... <目標路徑> 
COPY [--chown=<user>:<group>] ["<源路徑1>",... "<目標路徑>"]

RUN 指令一樣,也有兩種格式,一種類似於命令行,一種類似於函數調用。COPY 指令將從構建上下文目錄中 <源路徑> 的文件/目錄復制到新的一層的鏡像內的 <目標路徑> 位置。比如:

COPY package.json /usr/src/app/

<源路徑> 可以是多個,甚至可以是通配符,其通配符規則要滿足 Go 的 filepath.Match 規則,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目標路徑> 可以是容器內的絕對路徑,也可以是相對於工作目錄的相對路徑(工作目錄可以用 WORKDIR 指令來指定)。目標路徑不需要事先創建,如果目錄不存在會在復制文件前先行創建缺失目錄。

--chown=<user>:<group> 選項用來改變文件的所屬用戶及所屬組。

COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/

注意:如果源路徑為文件夾,復制的時候不是直接復制該文件夾,而是將文件夾中的內容復制到目標路徑。

ADD 更高級的復制文件

ADD 指令和 COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。

比如 <源路徑> 可以是一個 URL,這種情況下,Docker 引擎會試圖去下載這個鏈接的文件放到 <目標路徑> 去。下載后的文件權限自動設置為 600,如果這並不是想要的權限,那么還需要增加額外的一層 RUN 進行權限調整,另外,如果下載的是個壓縮包,需要解壓縮,也一樣還需要額外的一層 RUN 指令進行解壓縮。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下載,處理權限、解壓縮、然后清理無用文件更合理。因此,這個功能其實並不實用,而且不推薦使用。

如果 <源路徑> 為一個 tar 壓縮文件的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,ADD 指令將會自動解壓縮這個壓縮文件到 <目標路徑> 去。

在某些情況下,這個自動解壓縮的功能非常有用,比如官方鏡像 ubuntu 中:

FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
...

同樣,在使用該指令的時候還可以加上 --chown=<user>:<group> 選項來改變文件的所屬用戶及所屬組。

COPY vs ADD

在 Docker 官方的 Dockerfile 最佳實踐文檔 中要求,盡可能的使用 COPY,因為 COPY 的語義很明確,就是復制文件而已,而 ADD 則包含了更復雜的功能,其行為也不一定很清晰。最適合使用 ADD 的場合,就是所提及的需要自動解壓縮的場合。

另外需要注意的是,ADD 指令會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。

因此在 COPYADD 指令中選擇的時候,可以遵循這樣的原則,所有的文件復制均使用 COPY 指令,僅在需要自動解壓縮的場合使用 ADD

CMD 容器啟動命令

The CMD 指令的三種格式:

CMD ["executable","param1","param2"] (exec 格式, 首選推薦)
CMD ["param1","param2"] (在指定了 ENTRYPOINT 指令后,用 CMD 指定具體的參數,下面介紹ENTRYPOINT指令)
CMD command param1 param2 (shell 格式)

Docker 不是虛擬機,容器就是進程。既然是進程,那么在啟動容器的時候,需要指定所運行的程序及參數。CMD 指令就是用於指定默認的容器主進程的啟動命令的。

在運行時可以指定新的命令來替代鏡像設置中的這個默認命令,比如,ubuntu 鏡像默認的 CMD/bin/bash,如果我們直接 docker run -it ubuntu 的話,會直接進入 bash。我們也可以在運行時指定運行別的命令,如 docker run -it ubuntu cat /etc/os-release。這就是用 cat /etc/os-release 命令替換了默認的 /bin/bash 命令了,輸出了系統版本信息。

在指令格式上,一般推薦使用 exec 格式,如果使用 shell 格式的話,實際的命令會被包裝為 sh -c 的參數的形式進行執行。比如:

CMD echo $HOME

在實際執行中,會將其變更為:

CMD [ "sh", "-c", "echo $HOME" ]

注意1:容器中應用應在前台執行;原因:Docker 不是虛擬機,容器就是進程。如下面Nginx容器啟動的命令。

無效的命令(容器執行后就立即退出,原因容器主進程結束):

CMD service nginx start

有效命令(前台方式運行,不結束主進程):

CMD ["nginx", "-g", "daemon off;"]

注意2:一個Dockerfile應該僅有一個CMD指令,當存在多個,僅最后一個生效(以最后一個為准)。

ENTRYPOINT 入口點

ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式和 shell 格式。

ENTRYPOINT 的目的和 CMD 一樣,都是在指定容器啟動程序及參數。ENTRYPOINT 在運行時也可以替代,不過比 CMD 要略顯繁瑣,需要通過 docker run 的參數 --entrypoint 來指定。

當指定了 ENTRYPOINT 后,CMD 的含義就發生了改變,不再是直接的運行其命令,而是將 CMD 的內容作為參數傳給 ENTRYPOINT 指令,換句話說實際執行時,將變為:

<ENTRYPOINT> "<CMD>"

那么有了 CMD 后,為什么還要有 ENTRYPOINT 呢?這種 <ENTRYPOINT> "<CMD>" 到底是什么意思,有什么好處?

舉個例子🌰:

假設我們需要一個得知自己當前公網 IP 的鏡像,那么可以先用 CMD 來實現:

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
CMD [ "curl", "-s", "http://myip.ipip.net" ]

使用 docker build -t myip . 來構建鏡像。 然后啟動容器:

$ docker run myip
當前 IP:219.142.100.68  來自於:中國 北京 北京  電信

從上面的 CMD 中可以看到實質的命令是 curl,那么如果我們希望顯示 HTTP 頭信息,就需要加上 -i 參數,結果如下:

$ docker run myip -i
docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".

顯然curl -s http://myip.ipip.net被替換成了-i ,並不存在這樣的命令於是報錯了。我們需要執行下面的命令才能正確的執行:

$ docker run myip curl -s http://myip.ipip.net -i
HTTP/1.1 200 OK
...

當前 IP:219.142.100.68  來自於:中國 北京 北京  電信

這里便用完整的 curl -s http://myip.ipip.net -i 替換了curl -s http://myip.ipip.net從而實現了HTTP頭信息的顯示。但這顯然不是很好的解決方案,而使用 ENTRYPOINT 就可以解決這個問題。現在我們重新用 ENTRYPOINT 來實現這個鏡像:

FROM ubuntu:18.04
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://myip.ipip.net" ]

這次我們再來嘗試直接使用 docker run myip -i

$ docker run myip
當前 IP:219.142.100.68  來自於:中國 北京 北京  電信

$ docker run myip -i
HTTP/1.1 200 OK
Date: Wed, 22 Sep 2021 09:18:21 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 69
Connection: keep-alive
X-Shadow-Status: 200
X-Via-JSL: 1d9bd9a,4f293cc,-
Set-Cookie: __jsluid_h=9e709c5148e18188831d1230155ff4d4; max-age=31536000; path=/; HttpOnly
X-Cache: bypass

當前 IP:219.142.100.68  來自於:中國 北京 北京  電信

由此可見,當存在 ENTRYPOINT 后,CMD 的內容將會作為參數傳給 ENTRYPOINT,而這里 -i 就是新的 CMD,因此會作為參數傳給 curl,從而達到了我們預期的效果。

ENV 設置環境變量

ENV指令用於設置環境變量,無論是后面的其它指令,如 RUN,還是運行時的應用,都可以直接使用這里定義的環境變量。

格式有兩種:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

示例1:

如何換行,以及對含有空格的值用雙引號括起來的辦法,這和 Shell 下的行為是一致的。

ENV VERSION=1.0 DEBUG=on \
    NAME="Happy Feet"

示例2:

定義環境變量,在后續的指令中使用這個環境變量。比如在官方 node 鏡像 Dockerfile 中,就有類似這樣的代碼。這里先定義了環境變量 NODE_VERSION,其后的 RUN 這層里,多次使用 $NODE_VERSION 來進行操作定制。

ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
  && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
  && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
  && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
  && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
  && ln -s /usr/local/bin/node /usr/local/bin/nodejs

可以看到,將來升級鏡像構建版本的時候,只需要更新 7.2.0 即可,Dockerfile 構建維護變得更輕松了。

下列指令都支持環境變量: ADDCOPYENVEXPOSEFROMLABELUSERWORKDIRVOLUMESTOPSIGNALONBUILDRUN

可以看到,環境變量可以使用的地方很多,很強大。通過環境變量,可以讓一份 Dockerfile 制作更多的鏡像,只需使用不同的環境變量即可。

ARG 構建參數

ARG <參數名>[=<默認值>]

構建參數和 ENV 的效果一樣,都是設置環境變量。所不同的是,ARG 所設置的構建環境的環境變量,在將來容器運行時是不會存在這些環境變量的。但是不要因此就使用 ARG 保存密碼之類的信息,因為 docker history 還是可以看到所有值的。

Dockerfile 中的 ARG 指令是定義參數名稱,以及定義其默認值。該默認值可以在構建命令 docker build 中用 --build-arg <參數名>=<值> 來覆蓋。

靈活的使用 ARG 指令,能夠在不修改 Dockerfile 的情況下,構建出不同的鏡像。

ARG 指令有生效范圍,如果在 FROM 指令之前指定,那么只能用於 FROM 指令中。

ARG DOCKER_USERNAME=library

FROM ${DOCKER_USERNAME}/alpine

RUN set -x ; echo ${DOCKER_USERNAME}

使用上述 Dockerfile 會發現無法輸出 ${DOCKER_USERNAME} 變量的值,要想正常輸出,必須在 FROM 之后再次指定 ARG

# 只在 FROM 中生效
ARG DOCKER_USERNAME=library

FROM ${DOCKER_USERNAME}/alpine

# 要想在 FROM 之后使用,必須再次指定
ARG DOCKER_USERNAME=library

RUN set -x ; echo ${DOCKER_USERNAME}

VOLUME 定義匿名卷

容器運行時應該盡量保持容器存儲層不發生寫操作,對於數據庫類需要保存動態數據的應用,其數據庫文件應該保存於卷(volume)中(后面進一步介紹 Docker 卷的概念 待完善)

為了防止運行時用戶忘記將動態文件所保存目錄掛載為卷,在 Dockerfile 中,我們可以事先指定某些目錄掛載為匿名卷,這樣在運行時如果用戶不指定掛載,其應用也可以正常運行,不會向容器存儲層寫入大量數據。

VOLUME /data

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

$ docker run -d -v mydata:/data xxxx

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

EXPOSE 暴露端口

EXPOSE <端口1> [<端口2>...]

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

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

WORKDIR 指定工作目錄

WORKDIR <工作目錄路徑>

使用 WORKDIR 指令可以來指定工作目錄(或者稱為當前目錄),以后各層的當前目錄就被改為指定的目錄,如該目錄不存在,WORKDIR 會幫你建立目錄。

示例1

Dockerfile 進行構建鏡像運行后,找不到 /app/world.txt 文件的寫法:

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

在 Shell 中,連續兩行是同一個進程執行環境,因此前一個命令修改的內存狀態,會直接影響后一個命令;而在 Dockerfile 中,這兩行 RUN 命令的執行環境根本不同,是兩個完全不同的容器。

正確找到 /app/world.txt 文件的寫法:

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

示例2

WORKDIR 指令使用的相對路徑,那么所切換的路徑與之前的 WORKDIR 有關:

WORKDIR /a
WORKDIR b
WORKDIR c

RUN pwd

RUN pwd 的工作目錄為 /a/b/c

USER 指定當前用戶

USER <用戶名>[:<用戶組>]

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

注意,USER 只是幫助你切換到指定用戶而已,這個用戶必須是事先建立好的,否則無法切換。

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]

如果以 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.12/gosu-amd64" \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true
# 設置 CMD,並以另外的用戶執行
CMD [ "exec", "gosu", "redis", "redis-server" ]

HEALTHCHECK 健康檢查

格式:

  • HEALTHCHECK [選項] CMD <命令>:設置檢查容器健康狀況的命令
  • HEALTHCHECK NONE:如果基礎鏡像有健康檢查指令,使用這行可以屏蔽掉其健康檢查指令

HEALTHCHECK 指令是告訴 Docker 應該如何進行判斷容器的狀態是否正常,這是 Docker 1.12 引入的新指令。

當在一個鏡像指定了 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 來幫助判斷,其 DockerfileHEALTHCHECK 可以這么寫:

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/ || exit 1 作為健康檢查命令。

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

$ docker build -t myweb:v1 .

構建好了后,我們啟動一個容器:

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

當運行該鏡像后,可以通過 docker container ls 看到最初的狀態為 (health: starting)

$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS               NAMES
03e28eb00bd0        myweb:v1            "nginx -g 'daemon off"   3 seconds ago       Up 2 seconds (health: starting)   80/tcp, 443/tcp     web

在等待幾秒鍾后,再次 docker container ls,就會看到健康狀態變化為了 (healthy)

$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
03e28eb00bd0        myweb:v1            "nginx -g 'daemon off"   18 seconds ago      Up 16 seconds (healthy)   80/tcp, 443/tcp     web

如果健康檢查連續失敗超過了重試次數,狀態就會變為 (unhealthy)

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

$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
    "FailingStreak": 0,
    "Log": [
        {
            "End": "2016-11-25T14:35:37.940957051Z",
            "ExitCode": 0,
            "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
            "Start": "2016-11-25T14:35:37.780192565Z"
        }
    ],
    "Status": "healthy"
}

ONBUILD 為他人作嫁衣

格式:ONBUILD <其它指令>

ONBUILD 是一個特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而這些指令,在當前鏡像構建時並不會被執行。只有當以當前鏡像為基礎鏡像,去構建下一級鏡像的時候才會被執行。

Dockerfile 中的其它指令都是為了定制當前鏡像而准備的,唯有 ONBUILD 是為了幫助別人定制自己而准備的。

舉個例子🌰:

假設我們要制作 Node.js 所寫的應用的鏡像。我們都知道 Node.js 使用 npm 進行包管理,所有依賴、配置、啟動信息等會放到 package.json 文件里。在拿到程序代碼后,需要先進行 npm install 才可以獲得所有需要的依賴。然后就可以通過 npm start 來啟動應用。我們可以先做一個基礎鏡像:

FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]

然后在多個子項目中:

FROM my-node

是的,只有這么一行。當在各個項目目錄中,用這個只有一行的 Dockerfile 構建鏡像時,之前基礎鏡像的那三行 ONBUILD 就會開始執行,成功的將當前項目的代碼復制進鏡像、並且針對本項目執行 npm install,生成應用鏡像。

LABEL 為鏡像添加元數據

LABEL 指令用來給鏡像以鍵值對的形式添加一些元數據(metadata)。

LABEL <key>=<value> <key>=<value> <key>=<value> ...

我們還可以用一些標簽來申明鏡像的作者、文檔地址等:

LABEL org.opencontainers.image.authors="yeasy"
LABEL org.opencontainers.image.documentation="https://yeasy.gitbooks.io"

具體可以參考 https://github.com/opencontainers/image-spec/blob/master/annotations.md

STOPSIGNAL 容器平滑退出

STOPSIGNAL signal

官方文檔

The STOPSIGNAL instruction sets the system call signal that will be sent to the container to exit. This signal can be a valid unsigned number that matches a position in the kernel’s syscall table, for instance 9, or a signal name in the format SIGNAME, for instance SIGKILL.

STOPSIGNAL 指令設置將發送到容器的系統調用信號以退出。此信號可以是與內核的系統調用表中的位置匹配的有效無符號數,例如 9,或 SIGNAME 格式的信號名,例如 SIGKILL

默認的stop-signal是SIGTERM,在docker stop的時候會給容器內PID為1的進程發送這個signal,通過--stop-signal可以設置自己需要的signal,主要的目的是為了讓容器內的應用程序在接收到signal之后可以先做一些事情,實現容器的平滑退出,如果不做任何處理,容器將在一段時間之后強制退出,會造成業務的強制中斷,這個時間默認是10s。

SHELL 指令

格式:SHELL ["executable", "parameters"]

SHELL 指令可以指定 RUN ENTRYPOINT CMD 指令的 shell,Linux 中默認為 ["/bin/sh", "-c"]

SHELL ["/bin/sh", "-c"]
RUN lll ; ls

SHELL ["/bin/sh", "-cex"]
RUN lll ; ls

兩個 RUN 運行同一命令,第二個 RUN 運行的命令會打印出每條命令並當遇到錯誤時退出。

ENTRYPOINT CMD 以 shell 格式指定時,SHELL 指令所指定的 shell 也會成為這兩個指令的 shell

SHELL ["/bin/sh", "-cex"]

# /bin/sh -cex "nginx"
ENTRYPOINT nginx
SHELL ["/bin/sh", "-cex"]

# /bin/sh -cex "nginx"
CMD nginx

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM