1. 程式人生 > >如何理解docker鏡像build中的上下文

如何理解docker鏡像build中的上下文

text RM 容器 practice file 調用 ucc 以及 這也

參考:https://yeasy.gitbooks.io/docker_practice/content/image/build.html

理解上線文概念非常重要,不然可能碰到一些奇怪的問題。

構建鏡像

好了,讓我們再回到之前定制的 nginx 鏡像的 Dockerfile 來。現在我們明白了這個 Dockerfile 的內容,那麽讓我們來構建這個鏡像吧。

Dockerfile 文件所在目錄執行:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
 ---> e43d811ce2f4
Step 2 : RUN echo ‘<h1>Hello, Docker!</h1>‘ > /usr/share/nginx/html/index.html
 ---> Running in 9cdc27646c7b
 ---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

從命令的輸出結果中,我們可以清晰的看到鏡像的構建過程。在 Step 2 中,如同我們之前所說的那樣,RUN 指令啟動了一個容器 9cdc27646c7b,執行了所要求的命令,並最後提交了這一層 44aa4490ce2c,隨後刪除了所用到的這個容器 9cdc27646c7b

這裏我們使用了 docker build 命令進行鏡像構建。其格式為:

docker build [選項] <上下文路徑/URL/->

在這裏我們指定了最終鏡像的名稱 -t nginx:v3,構建成功後,我們可以像之前運行 nginx:v2 那樣來運行這個鏡像,其結果會和 nginx:v2 一樣。

鏡像構建上下文(Context)

如果註意,會看到 docker build 命令最後有一個 .. 表示當前目錄,而 Dockerfile 就在當前目錄,因此不少初學者以為這個路徑是在指定 Dockerfile 所在路徑,這麽理解其實是不準確的。如果對應上面的命令格式,你可能會發現,這是在指定上下文路徑。那麽什麽是上下文呢?

首先我們要理解 docker build 的工作原理。Docker 在運行時分為 Docker 引擎(也就是服務端守護進程)和客戶端工具。Docker 的引擎提供了一組 REST API,被稱為 Docker Remote API,而如 docker 命令這樣的客戶端工具,則是通過這組 API 與 Docker 引擎交互,從而完成各種功能。因此,雖然表面上我們好像是在本機執行各種 docker

功能,但實際上,一切都是使用的遠程調用形式在服務端(Docker 引擎)完成。也因為這種 C/S 設計,讓我們操作遠程服務器的 Docker 引擎變得輕而易舉。

當我們進行鏡像構建的時候,並非所有定制都會通過 RUN 指令完成,經常會需要將一些本地文件復制進鏡像,比如通過 COPY 指令、ADD 指令等。而 docker build 命令構建鏡像,其實並非在本地構建,而是在服務端,也就是 Docker 引擎中構建的。那麽在這種客戶端/服務端的架構中,如何才能讓服務端獲得本地文件呢?

這就引入了上下文的概念。當構建的時候,用戶會指定構建鏡像上下文的路徑,docker build 命令得知這個路徑後,會將路徑下的所有內容打包,然後上傳給 Docker 引擎。這樣 Docker 引擎收到這個上下文包後,展開就會獲得構建鏡像所需的一切文件。

如果在 Dockerfile 中這麽寫:

COPY ./package.json /app/

這並不是要復制執行 docker build 命令所在的目錄下的 package.json,也不是復制 Dockerfile 所在目錄下的 package.json,而是復制 上下文(context) 目錄下的 package.json

因此,COPY 這類指令中的源文件的路徑都是相對路徑。這也是初學者經常會問的為什麽 COPY ../package.json /app 或者 COPY /opt/xxxx /app 無法工作的原因,因為這些路徑已經超出了上下文的範圍,Docker 引擎無法獲得這些位置的文件。如果真的需要那些文件,應該將它們復制到上下文目錄中去。

現在就可以理解剛才的命令 docker build -t nginx:v3 . 中的這個 .,實際上是在指定上下文的目錄,docker build 命令會將該目錄下的內容打包交給 Docker 引擎以幫助構建鏡像。

如果觀察 docker build 輸出,我們其實已經看到了這個發送上下文的過程:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...

理解構建上下文對於鏡像構建是很重要的,避免犯一些不應該的錯誤。比如有些初學者在發現 COPY /opt/xxxx /app 不工作後,於是幹脆將 Dockerfile 放到了硬盤根目錄去構建,結果發現 docker build執行後,在發送一個幾十 GB 的東西,極為緩慢而且很容易構建失敗。那是因為這種做法是在讓 docker build 打包整個硬盤,這顯然是使用錯誤。

一般來說,應該會將 Dockerfile 置於一個空目錄下,或者項目根目錄下。如果該目錄下沒有所需文件,那麽應該把所需文件復制一份過來。如果目錄下有些東西確實不希望構建時傳給 Docker 引擎,那麽可以用 .gitignore 一樣的語法寫一個 .dockerignore,該文件是用於剔除不需要作為上下文傳遞給 Docker 引擎的。

那麽為什麽會有人誤以為 . 是指定 Dockerfile 所在目錄呢?這是因為在默認情況下,如果不額外指定 Dockerfile 的話,會將上下文目錄下的名為 Dockerfile 的文件作為 Dockerfile。

這只是默認行為,實際上 Dockerfile 的文件名並不要求必須為 Dockerfile,而且並不要求必須位於上下文目錄中,比如可以用 -f ../Dockerfile.php 參數指定某個文件作為 Dockerfile

當然,一般大家習慣性的會使用默認的文件名 Dockerfile,以及會將其置於鏡像構建上下文目錄中。

如何理解docker鏡像build中的上下文