Docker之構建上下文詳解

昨天寫了使用 Dockerfile 定制鏡像。其中構建上下文這一塊沒有寫,今天把這一塊單獨拿出來寫一下。

Docker鏡像構建

簡單說下構建鏡像步驟:

  1. cd Dockerfile 所在目錄;

  2. 執(zhí)行 docker build 構建命令:

    docker build -t <imageName:imageTag> .

通過上面的工作流,很容易形成這樣的理解誤區(qū):

  1. docker build 后面的 . 為 Dockerfile 所在的目錄;
  2. Dockerfile 文件名 必須為 Dockerfile;

其實上面這種理解是錯誤的,要想準確理解其含義,首先我們需要先了解下 Docker 的架構和 docker build 的工作原理。

Docker架構

Docker 使用C/S (客戶端/服務器)體系的架構,Docker 客戶端與 Docker 守護進程通信,Docker 守護進程負責構建,運行和分發(fā) Docker 容器。Docker 客戶端和守護進程可以在同一個系統(tǒng)上運行,也可以將 Docker 客戶端連接到遠程 Docker 守護進程。Docker 客戶端和守護進程使用 REST API 通過UNIX套接字或網(wǎng)絡接口進行通信。

docker-structrue.png

docker build 的工作原理

  1. Client端執(zhí)行 docker build . 命令 ;
  2. Docker 客戶端會將構建命令后面指定的路徑(.)下的所有文件打包發(fā)送給 Docker 服務端;
  3. Docker 服務端收到客戶端發(fā)送的包,然后解壓,根據(jù) Dockerfile 里面的指令進行鏡像的分層構建;

鏡像構建上下文(Context)

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

這就引入了上下文的概念。當構建的時候,用戶會指定構建鏡像上下文的路徑,docker build 命令得知這個路徑后,會將路徑下的所有內(nèi)容打包,然后上傳給 Docker 引擎。這樣 Docker 引擎收到這個上下文包后,展開就會獲得構建鏡像所需的一切文件。如果在 Dockerfile 中這么寫:

COPY ./package.json /app/

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

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

示例1 :

[root@192 test]# ls
Dockerfile
[root@192 test]# cat Dockerfile
FROM alpine:latest
ADD  /root/mydocker/apache-tomcat-9.0.27.tar.gz /data/soft
[root@192 test]# ls /root/mydocker/apache-tomcat-9.0.27.tar.gz
/root/mydocker/apache-tomcat-9.0.27.tar.gz
[root@192 test]# docker build  .
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM alpine:latest
 ---> 965ea09ff2eb
Step 2/2 : ADD /root/mydocker/apache-tomcat-9.0.27.tar.gz /data/soft
ADD failed: stat /var/lib/docker/tmp/docker-builder904012777/root/mydocker/apache-tomcat-9.0.27.tar.gz: no such file or directory

可以看出:

  1. 鏡像構建上下文路徑并不是 Dockerfile文件所在的路徑;
  2. Dockerfile 中指令的工作目錄是服務端解壓客戶端傳輸包的路徑,因為 ADD 指令失敗了,意味著當前目錄并沒有 apache-tomcat 文件;

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

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

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

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

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

示例2

[root@192 test]# docker images
REPOSITORY                         TAG                         IMAGE ID            CREATED             SIZE
tomcat                             jdk8-adoptopenjdk-hotspot   1a2bfb3e6eee        15 hours ago        318MB
openjdk                            8-jre-slim                  d0cfe439ce3d        13 days ago         184MB
btnguyen2k/oraclejdk8_jre-alpine   latest                      fab475620d00        4 years ago         208MB
[root@192 test]# cat ../mynginx/test
FROM alpine:latest
[root@192 test]# ls
apache-tomcat-9.0.27.tar.gz
[root@192 test]#  docker build -f ../mynginx/test  -t test:v1 .
Sending build context to Docker daemon  10.99MB
Step 1/1 : FROM alpine:latest
latest: Pulling from library/alpine
89d9c30c1d48: Already exists
Digest: sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a
Status: Downloaded newer image for alpine:latest
 ---> 965ea09ff2eb
Successfully built 965ea09ff2eb
Successfully tagged test:v1
[root@192 test]# docker images
REPOSITORY                         TAG                         IMAGE ID            CREATED             SIZE
tomcat                             jdk8-adoptopenjdk-hotspot   1a2bfb3e6eee        15 hours ago        318MB
alpine                             latest                      965ea09ff2eb        11 days ago         5.55MB
test                               v1                          965ea09ff2eb        11 days ago         5.55MB
openjdk                            8-jre-slim                  d0cfe439ce3d        13 days ago         184MB
btnguyen2k/oraclejdk8_jre-alpine   latest                      fab475620d00        4 years ago         208MB     

可以看出:

Dockerfile 的文件名并不要求必須為 Dockerfile,而且并不要求必須位于上下文目錄中,比如可以用-f ../mynginx/test參數(shù)指定某個文件作為 Dockerfile。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容