博客 / 詳情

返回

Dockerfile指令介紹

Dockerfile 文件詳解

用於構建鏡像。

常用指令介紹

1、FROM

定製的鏡像都是基於 FROM 的鏡像,就是基礎鏡像。

2、RUN

用於執行後面跟着的命令行命令。

在構建過程中在鏡像中執行命令。也就是構建鏡像時運行的指令。

執行結束後, commit 這一層的修改,構成新的鏡像。

有以下倆種格式:

# 1、shell 格式:
RUN <命令行命令>
# 備註: <命令行命令> 等同於,在終端操作的 shell 命令。

# 2、exec 格式:
RUN ["可執行文件", "參數1", "參數2"]
# 例如:RUN ["./test.php", "dev", "offline"] 等價於 RUN ./test.php dev offline

簡單的例子:

FROM nginx
RUN echo '這是一個本地構建的nginx鏡像' > /usr/share/nginx/html/index.html

注意:Dockerfile 的指令每執行一次都會在 docker 上新建一層。所以過多無意義的層,會造成鏡像膨脹過大。

例如:

FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz

以上執行會創建 3 層鏡像。可簡化為以下格式:

FROM centos
RUN yum -y install wget \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && tar -xvf redis.tar.gz

如上,以 && 符號連接命令,這樣執行後,只會創建 1 層鏡像。

最簡單例子:

$ docker build -t nginx:v3 .

上下文路徑説明

上下文路徑(就是上面的點 " . "),是指 docker 在構建鏡像,有時候想要使用到本機的文件(比如複製),docker build 命令得知這個路徑後,會將路徑下的所有內容打包。

解析:由於 docker 的運行模式是 C/S。我們本機是 C,docker 引擎是 S。實際的構建過程是在 docker 引擎下完成的,所以這個時候無法用到我們本機的文件。這就需要把我們本機的指定目錄下的文件一起打包提供給 docker 引擎使用。

如果未説明最後一個參數,那麼默認上下文路徑就是 Dockerfile 所在的位置

注意:上下文路徑下不要放無用的文件,因為會一起打包發送給 docker 引擎,如果文件過多會造成過程緩慢。

3、LABEL

添加鏡像的元數據,使用鍵值對的形式。

注意

也可以使用 MAINTAINER ,指定Dockerfile的作者/維護者,但是已棄用,推薦使用 LABEL 指令。

4、CMD

指定容器創建時的默認命令。(可以被覆蓋)

類似於 RUN 指令,用於運行程序,但二者運行的時間點不同:

  • CMD 在docker run 時運行。
  • RUN 是在 docker build。

作用:為啓動的容器指定默認要運行的程序,程序運行結束,容器也就結束。CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋。

注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最後一個生效。

格式:

CMD <shell 命令> 
CMD ["<可執行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 該寫法是為 ENTRYPOINT 指令指定的程序提供默認參數

推薦使用第二種格式,執行過程比較明確。第一種格式實際上在運行的過程中也會自動轉換成第二種格式運行,並且默認可執行文件是 sh。

5、ENTRYPOINT

設置容器創建時的主要命令。(不可被覆蓋)

運行容器時執行的shell命令。

類似於 CMD 指令,但其不會被 docker run 的命令行參數指定的指令所覆蓋,而且這些命令行參數會被當作參數送給 ENTRYPOINT 指令指定的程序。

但是, 如果運行 docker run 時使用了 --entrypoint 選項,將覆蓋 ENTRYPOINT 指令指定的程序。

優點:在執行 docker run 的時候可以指定 ENTRYPOINT 運行所需的參數。

注意:如果 Dockerfile 中如果存在多個 ENTRYPOINT 指令,僅最後一個生效。

格式:

ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可以搭配 CMD 命令使用:一般是變參才會使用 CMD ,這裏的 CMD 等於是在給 ENTRYPOINT 傳參,以下示例會提到。

示例:

假設已通過 Dockerfile 構建了 nginx:test 鏡像:

FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定參
CMD ["/etc/nginx/nginx.conf"] # 變參 
  1. 不傳參運行
$ docker run nginx:test

# 容器內會默認運行以下命令,啓動主進程。
nginx -c /etc/nginx/nginx.conf
  1. 傳參運行
$ docker run nginx:test -c /etc/nginx/new.conf

# 容器內會默認運行以下命令,啓動主進程(/etc/nginx/new.conf:假設容器內已有此文件)
nginx -c /etc/nginx/new.conf

6、EXPOSE

聲明容器運行時監聽的特定網絡端口。

僅僅只是聲明端口。

作用:

  • 幫助鏡像使用者理解這個鏡像服務的守護端口,以方便配置映射。
  • 在運行時使用隨機端口映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的端口。

格式:

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

7、ENV

在容器內部設置環境變量。

設置環境變量,定義了環境變量,那麼在後續的指令中,就可以使用這個環境變量。

格式:

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

以下示例設置 NODE_VERSION = 7.2.0 , 在後續的指令中可以通過 $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"

8、ADD

將文件、目錄或遠程URL複製到鏡像中。

拷貝文件或目錄到容器中,如果是URL或壓縮包便會自動下載或自動解壓。

ADD 指令和 COPY 的使用格類似(同樣需求下,官方推薦使用 COPY)。功能也類似,不同之處如下:

  • ADD 的優點:在執行 <源文件> 為 tar 壓縮文件的話,壓縮格式為 gzip, bzip2 以及 xz 的情況下,會自動複製並解壓到 <目標路徑>。
  • ADD 的缺點:在不解壓的前提下,無法複製 tar 壓縮文件。會令鏡像構建緩存失效,從而可能會令鏡像構建變得比較緩慢。具體是否使用,可以根據是否需要自動解壓來決定。

9、COPY

將文件或目錄複製到鏡像中。

拷貝文件或目錄到容器中,跟ADD類似,但不具備自動下載或解壓的功能。

複製指令,從上下文目錄中複製文件或者目錄到容器裏指定路徑。

格式:

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

[--chown=<user>:<group>]:可選參數,用户改變複製到容器內文件的擁有者和屬組。

<源路徑>:源文件或者源目錄,這裏可以是通配符表達式,其通配符規則要滿足 Go 的 filepath.Match 規則。例如:

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

<目標路徑>:容器內的指定路徑,該路徑不用事先建好,路徑不存在的話,會自動創建。

10、VOLUME

為容器創建掛載點或聲明卷。

指定容器掛載點到宿主機自動生成的目錄或其他容器。

定義匿名數據卷。在啓動容器時忘記掛載數據卷,會自動掛載到匿名卷。

作用:

  • 避免重要的數據,因容器重啓而丟失,這是非常致命的。
  • 避免容器不斷變大。

格式:

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

在啓動容器 docker run 的時候,我們可以通過 -v 參數修改掛載點。

11、WORKDIR

設置後續指令的工作目錄。

為 RUN、CMD、ENTRYPOINT、COPY 和 ADD 設置工作目錄,就是切換目錄。

指定工作目錄。用 WORKDIR 指定的工作目錄,會在構建鏡像的每一層中都存在。以後各層的當前目錄就被改為指定的目錄,如該目錄不存在,WORKDIR 會幫你建立目錄。

docker build 構建鏡像過程中的,每一個 RUN 命令都是新建的一層。只有通過 WORKDIR 創建的目錄才會一直存在。

格式:

WORKDIR <工作目錄路徑>

12、其它命令

USER、ARG、ONBUILD、STOPSIGNAL、HEALTHCHECK、SHELL


99、實踐總結

總結一
  • RUN

    我們在使用 Dockfile構建鏡像時,就是通過 RUN 來構建鏡像層,每執行一行 RUN 命令,就會構建一層鏡像,其命令也是在該層鏡像層中執行的。常用於安裝軟件包、下載文件等操作。

    執行時機就是 docker build 命令執行時候,比如:

    RUN yum install -y net-tools vim
  • CMD

    提供容器啓動時默認執行的命令或參數,如果運行容器時指定了其他命令,則 CMD 指定的默認命令會被覆蓋。

    記住

    • 只能有一個 CMD 指令在 Dockerfile 中生效(如果有多個,只有最後一個會起作用)。
    • CMD 可以被 docker run 命令行參數覆蓋。

    CMD 支持三種格式,但最常用的是提供一個默認的可執行文件及其參數:

    # 格式:CMD ["executable","param1","param2"]
    
    # 執行python文件
    CMD ["python", "app.py"]

    執行時機就是 docker run 命令執行時候。

    Tips

    我們知道,鏡像就是由多個鏡像層堆疊起來,就是由多個 RUN命令執行構建的。我們啓動容器就是在啓動我們自己的服務,肯定只有最後的 CMD 指令起作用,這樣才有意義。

    再者,我們再執行 docker run 時候,其實也是在構建最後一層鏡像,然後添加各種參數,比如 -p-v等等之類的。

    • ENTRYPOINT

    配置容器啓動時要運行的命令,並且這個命令不會被 docker run 命令行參數覆蓋。它更傾向於定義容器的主要任務是什麼。

    • 類似於 CMD,但是更不容易被覆蓋,旨在為容器提供一個固定的入口點。
    • 使用 --entrypoint 標誌可以在運行時覆蓋 ENTRYPOINT。

    形式:同樣支持三種格式,最常見的是使用 JSON 數組來指定命令和參數:

    ENTRYPOINT ["executable", "param1", "param2"]
    
    # 運行Java
    ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]

對比

  • RUN 是在構建階段執行命令,目的是修改鏡像。
  • CMD 提供了容器啓動時默認執行的命令,但容易被覆蓋。
  • ENTRYPOINT 設定容器的主命令,不易被覆蓋,適合明確容器的主要功能。

結合使用:可以將 ENTRYPOINTCMD 結合使用,其中 ENTRYPOINT 定義應用本身,而 CMD 則定義傳遞給該應用的默認參數。這樣,用户可以通過覆蓋 CMD 來更改參數,但不改變應用本身。

FROM ubuntu
ENTRYPOINT ["echo", "Hello"]
CMD ["world"]

運行容器時不帶額外參數會輸出 "Hello world",但如果運行時指定參數如 docker run <image> User,則輸出 "Hello User"。


總結二
  • COPY

    簡單地將文件或目錄,從主機系統複製到鏡像中指定的位置。

    • 直觀且易於理解,主要用於將本地文件複製到鏡像中。
    • 支持通配符(如 *.txt),可以方便地選擇多個文件。
    • 不會自動解壓歸檔文件(如 .tar.gz 文件)。
    • 對於簡單的文件複製任務,推薦使用 COPY
  • ADD

    除了可以完成與 COPY 相同的任務外,還提供了一些額外的功能。

    • 可以自動解壓縮本地的 .tar.tar.gz 等格式的歸檔文件到目標位置(注意:對於遠程 URL 下載的歸檔文件不會自動解壓)。
    • 支持通過 URL 添加遠程文件到鏡像中(不過通常不推薦這樣做,因為這可能引起構建不穩定的問題,例如網絡問題導致構建失敗)。
    • 如果源路徑是一個可識別的 Git 倉庫,ADD 還會嘗試克隆該倉庫而不是直接複製內容(但同樣,這不是最佳實踐,建議明確控制依賴項)。

對比

  • 功能差異COPY 更加直接,僅限於將本地文件複製到鏡像內;而 ADD 提供了更多高級功能,比如自動解壓歸檔文件和支持從遠程 URL 獲取資源。
  • 透明度和安全性:由於 ADD 的一些高級特性可能導致不可預期的行為(如自動解壓),因此對於只需簡單複製文件的情況,使用 COPY 更加透明和安全。
  • 性能考慮ADD 自動解壓歸檔文件可能會增加不必要的處理時間,尤其是在不需要解壓的情況下。

使用建議

  • 當你需要將本地文件複製到 Docker 鏡像時,除非需要 ADD 的特殊功能(如自動解壓或從 URL 加載文件),否則優先選擇 COPY。這樣可以使 Dockerfile 更加簡潔明瞭,並減少潛在的錯誤來源。
  • 如果需要添加一個 .tar.tar.gz 歸檔文件並希望它被自動解壓,或者想要從 URL 添加資源,那麼 ADD 就是更合適的選擇。

總結三
  • EXPOSE

    指定容器將監聽的網絡端口。這個指令並不會實際發佈(port publish)端口。

    它只是一個聲明,告知使用者該鏡像內的服務將會監聽哪些端口。

    在運行時並不會因為這個聲明應用就會開啓這個端口的服務。

    • 可以指定TCP或UDP協議,默認為TCP。
    • 實際上暴露端口需要在運行容器時通過 -p--publish 參數來完成。
    EXPOSE 80/tcp
    EXPOSE 53/udp
  • VOLUME

    創建一個掛載點,用於綁定宿主機的一個目錄到容器內,或者指定容器內的數據卷(data volume),主要用於持久化數據或共享數據。

    比較重要

    如果你僅在 Dockerfile 中使用 VOLUME 指令而不指定宿主機上的具體路徑,Docker 會自動為該容器創建一個數據卷,並將其掛載到指定的容器目錄。這種情況下,Docker 管理這個數據卷的位置,通常位於宿主機文件系統的特定區域(例如,在 Linux 上可能是 /var/lib/docker/volumes/)。

    這種方式的好處在於簡化了數據持久化的過程,因為你不需要手動指定宿主機的路徑,同時也能確保數據獨立於容器的生命週期存在。

    VOLUME ["/data"]
    # 或者多路徑
    VOLUME ["/data1", "/data2"]
    • 數據卷允許你在容器之間共享和重用數據。
    • 宿主機上的文件或目錄可以被掛載到容器中指定的位置,從而使得數據能夠在容器重啓後仍然存在。
    • 使用 -v--volume 在運行容器時指定具體的宿主機路徑。

    比如mysql:

    VOLUME /var/lib/mysql
  • WORKDIR

    設置工作目錄。後續的 RUN, CMD, ENTRYPOINT, COPY 和 ADD 指令都會在這個目錄下執行。如果目錄不存在,WORKDIR 會幫你創建它。

    WORKDIR /path/to/workdir
    • 可以多次使用 WORKDIR,路徑是相對前面的 WORKDIR 設置進行累加的,也可以直接給出絕對路徑。
    • 改變工作目錄對於組織複雜的構建過程非常有用,並且可以讓 Dockerfile 更加清晰易讀。
    WORKDIR /app
    RUN npm install
    COPY . .
    CMD ["npm", "start"]

完畢。

模板例子

Java服務模板

FROM openjdk:8-jre
MAINTAINER wangxf <1437211236@qq.com>
VOLUME /tmp
WORKDIR /app
COPY /target/your-app-name-server.jar your-app-name-serve.jar
EXPOSE 8090
# ENTRYPOINT ["java", "-Dspring.profiles.active=test", "-jar", "your-app-name-serve.jar"]
ENTRYPOINT ["java", "-jar", "your-app-name-serve.jar"]

實際例子:

FROM dockette/jdk8
MAINTAINER wangxf <1437211236@qq.com>
VOLUME /temp # 用於存放我們的附件
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8090
ENTRYPOINT ["java", "-jar", "app.jar"]
# 把當前Dockerfile下target文件下的app複製到容器的工作目錄

上面這種是製作鏡像的流程,既是把 app.jar 做成一個鏡像(雖然上面沒有使用 RUN 命令,其實也可以使用),使用命令 docker build -t app:latest . 就可以生成鏡像,使用 docker images 查看鏡像。然後使用 docker run 去運行鏡像,運行命令:

docker run -itd -p 8090:8090 -v /opt/server/ttt:/temp --name app app
# app 是我們生成的鏡像名稱,默認版本為:app:latest

其實還有一種就是,不使用 docker build -t app:latest . 命令生成鏡像,直接運行 jdk8 的基礎鏡像,然後做目錄掛載,直接運行我們的服務,這樣就可以不用生成鏡像,一定層面上減少存儲空間的使用,這種情況就不能使用 Dockerfile 文件,必須使用 docker-compose.yml 文件,或者直接使用 docker run 命令。

  • docker-compose.yml 文件:該文件是運行多個容器服務,或者説管理容器服務,是使用鏡像,具體見 Compose介紹
  • 直接使用 docker run 命令:

    docker run -itd -p 8090:8090 -v /opt/server/ttt:/temp -v /opt/server/target:/app -w /app --name app dockette/jdk8 java -jar app.jar

    上面就是直接運行我們的 dockette/jdk8 鏡像,並不是我們做的鏡像,這種方式也可以;

    當然還是推薦使用 docker-compose.yml 文件,如果有特殊需求可以先製作鏡像(比如安裝環境),然後再使用鏡像。


完畢。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.