Docker之構建上下文詳解


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

Docker鏡像構建

簡單說下構建鏡像步驟:

  1. cd Dockerfile 所在目錄;

  2. 執行 docker build 構建命令:

    docker build -t imageName:imageTag .

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

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

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

Docker架構

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

https://docs.docker.com/engine/images/architecture.svg

docker build 的工作原理

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

鏡像構建上下文(Context)

當我們進行鏡像構建的時候,並非所有定制都會通過 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 引擎無法獲得這些位置的文件。如果真的需要那些文件,應該將它們復制到上下文目錄中去。

示例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 文件;

理解構建上下文對於鏡像構建是很重要的,可以避免犯一些不應該的錯誤。比如有些初學者在發現 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,以及會將其置於鏡像構建上下文目錄中。

示例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參數指定某個文件作為 Dockerfile。


免責聲明!

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



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