Docker(二):Dockerfile 使用介紹

純潔的微笑發表於2018-03-17

上一篇文章Docker(一):Docker入門教程介紹了 Docker 基本概念,其中映象、容器和 Dockerfile 。我們使用 Dockerfile 定義映象,依賴映象來執行容器,因此 Dockerfile 是映象和容器的關鍵,Dockerfile 可以非常容易的定義映象內容,同時在我們後期的微服務實踐中,Dockerfile 也是重點關注的內容,今天我們就來一起學習它。

首先通過一張圖來了解 Docker 映象、容器和 Dockerfile 三者之間的關係。

Docker(二):Dockerfile 使用介紹

通過上圖可以看出使用 Dockerfile 定義映象,執行映象啟動容器。

Dockerfile 概念

Docker 映象是一個特殊的檔案系統,除了提供容器執行時所需的程式、庫、資源、配置等檔案外,還包含了一些為執行時準備的一些配置引數(如匿名卷、環境變數、使用者等)。映象不包含任何動態資料,其內容在構建之後也不會被改變。

映象的定製實際上就是定製每一層所新增的配置、檔案。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入一個指令碼,用這個指令碼來構建、定製映象,那麼之前提及的無法重複的問題、映象構建透明性的問題、體積的問題就都會解決。這個指令碼就是 Dockerfile。

Dockerfile 是一個文字檔案,其內包含了一條條的指令(Instruction),每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。有了 Dockerfile,當我們需要定製自己額外的需求時,只需在 Dockerfile 上新增或者修改指令,重新生成 image 即可,省去了敲命令的麻煩。

Dockerfile 檔案格式

Dockerfile檔案格式如下:

##  Dockerfile檔案格式

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
 
# 1、第一行必須指定 基礎映象資訊
FROM ubuntu
 
# 2、維護者資訊
MAINTAINER docker_user docker_user@email.com
 
# 3、映象操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
 
# 4、容器啟動執行指令
CMD /usr/sbin/nginx

Dockerfile 分為四部分:基礎映象資訊、維護者資訊、映象操作指令、容器啟動執行指令。一開始必須要指明所基於的映象名稱,接下來一般會說明維護者資訊;後面則是映象操作指令,例如 RUN 指令。每執行一條RUN 指令,映象新增新的一層,並提交;最後是 CMD 指令,來指明執行容器時的操作命令。

構建映象

docker build 命令會根據 Dockerfile 檔案及上下文構建新 Docker 映象。構建上下文是指 Dockerfile 所在的本地路徑或一個URL(Git倉庫地址)。構建上下文環境會被遞迴處理,所以構建所指定的路徑還包括了子目錄,而URL還包括了其中指定的子模組。

將當前目錄做為構建上下文時,可以像下面這樣使用docker build命令構建映象:

docker build .
Sending build context to Docker daemon  6.51 MB
...

說明:構建會在 Docker 後臺守護程式(daemon)中執行,而不是CLI中。構建前,構建程式會將全部內容(遞迴)傳送到守護程式。大多情況下,應該將一個空目錄作為構建上下文環境,並將 Dockerfile 檔案放在該目錄下。

在構建上下文中使用的 Dockerfile 檔案,是一個構建指令檔案。為了提高構建效能,可以通過.dockerignore檔案排除上下文目錄下不需要的檔案和目錄。

在 Docker 構建映象的第一步,docker CLI 會先在上下文目錄中尋找.dockerignore檔案,根據.dockerignore 檔案排除上下文目錄中的部分檔案和目錄,然後把剩下的檔案和目錄傳遞給 Docker 服務。

Dockerfile 一般位於構建上下文的根目錄下,也可以通過-f指定該檔案的位置:

docker build -f /path/to/a/Dockerfile .

構建時,還可以通過-t引數指定構建成映象的倉庫、標籤。

映象標籤

docker build -t nginx/v3 .

如果存在多個倉庫下,或使用多個映象標籤,就可以使用多個-t引數:

docker build -t nginx/v3:1.0.2 -t nginx/v3:latest .

在 Docker 守護程式執行 Dockerfile 中的指令前,首先會對 Dockerfile 進行語法檢查,有語法錯誤時會返回:

docker build -t nginx/v3 .
Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD

快取

Docker 守護程式會一條一條的執行 Dockerfile 中的指令,而且會在每一步提交併生成一個新映象,最後會輸出最終映象的ID。生成完成後,Docker 守護程式會自動清理你傳送的上下文。
Dockerfile檔案中的每條指令會被獨立執行,並會建立一個新映象,RUN cd /tmp等命令不會對下條指令產生影響。
Docker 會重用已生成的中間映象,以加速docker build的構建速度。以下是一個使用了快取映象的執行過程:

$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1/4 : FROM alpine:3.2
 ---> 31f630c65071
Step 2/4 : MAINTAINER SvenDowideit@home.org.au
 ---> Using cache
 ---> 2a1c91448f5f
Step 3/4 : RUN apk update &&      apk add socat &&        rm -r /var/cache/
 ---> Using cache
 ---> 21ed6e7fbb73
Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
 ---> Using cache
 ---> 7ea8aef582cc
Successfully built 7ea8aef582cc

構建快取僅會使用本地父生成鏈上的映象,如果不想使用本地快取的映象,也可以通過--cache-from指定快取。指定後將不再使用本地生成的映象鏈,而是從映象倉庫中下載。

尋找快取的邏輯

Docker 尋找快取的邏輯其實就是樹型結構根據 Dockerfile 指令遍歷子節點的過程。下圖可以說明這個邏輯。

     FROM base_image:version           Dockerfile:
           +----------+                FROM base_image:version
           |base image|                RUN cmd1  --> use cache because we found base image
           +-----X----+                RUN cmd11 --> use cache because we found cmd1
                / \
               /   \
       RUN cmd1     RUN cmd2           Dockerfile:
       +------+     +------+           FROM base_image:version
       |image1|     |image2|           RUN cmd2  --> use cache because we found base image
       +---X--+     +------+           RUN cmd21 --> not use cache because there's no child node
          / \                                        running cmd21, so we build a new image here
         /   \
RUN cmd11     RUN cmd12
+-------+     +-------+
|image11|     |image12|
+-------+     +-------+

大部分指令可以根據上述邏輯去尋找快取,除了 ADD 和 COPY 。這兩個指令會複製檔案內容到映象內,除了指令相同以外,Docker 還會檢查每個檔案內容校驗(不包括最後修改時間和最後訪問時間),如果校驗不一致,則不會使用快取。

除了這兩個命令,Docker 並不會去檢查容器內的檔案內容,比如 RUN apt-get -y update,每次執行時檔案可能都不一樣,但是 Docker 認為命令一致,會繼續使用快取。這樣一來,以後構建時都不會再重新執行apt-get -y update

如果 Docker 沒有找到當前指令的快取,則會構建一個新的映象,並且之後的所有指令都不會再去尋找快取。

簡單示例

接下來用一個簡單的示例來感受一下 Dockerfile 是如何用來構建映象啟動容器。我們以定製 nginx 映象為例,在一個空白目錄中,建立一個文字檔案,並命名為 Dockerfile:

mkdir mynginx
cd mynginx
vi Dockerfile

構建一個 Dockerfile 檔案內容為:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
vi Dockerfile

這個 Dockerfile 很簡單,一共就兩行涉及到了兩條指令:FROM 和 RUN,FROM 表示獲取指定基礎映象,RUN 執行命令,在執行的過程中重寫了 nginx 的預設頁面資訊,將資訊替換為:Hello, Docker!。

在 Dockerfile 檔案所在目錄執行:

docker build -t nginx:v1 .

命令最後有一個. 表示當前目錄

構建完成之後,使用 docker images 命令檢視所有映象,如果存在 REPOSITORY 為 nginx 和 TAG 是 v1 的資訊,就表示構建成功。

docker images
REPOSITORY                      TAG                 IMAGE ID            CREATED             SIZE
nginx                           v1                  8c92471de2cc        6 minutes ago       108.6 MB

接下來使用 docker run 命令來啟動容器

docker run  --name docker_nginx_v1   -d -p 80:80 nginx:v1

這條命令會用 nginx 映象啟動一個容器,命名為docker_nginx_v1,並且對映了 80 埠,這樣我們可以用瀏覽器去訪問這個 nginx 伺服器:http://192.168.0.54/,頁面返回資訊:

Docker(二):Dockerfile 使用介紹

這樣一個簡單使用 Dockerfile 構建映象,執行容器的示例就完成了!

修改容器內容

容器啟動後,需要對容器內的檔案進行進一步的完善,可以使用docker exec -it xx bash命令再次進行修改,以上面的示例為基礎,修改 nginx 啟動頁面內容:

docker exec -it docker_nginx_v1   bash
root@3729b97e8226:/# echo '<h1>Hello, Docker neo!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit

以互動式終端方式進入 docker_nginx_v1 容器,並執行了 bash 命令,也就是獲得一個可操作的 Shell。然後,我們用<h1>Hello, Docker neo!</h1>覆蓋了 /usr/share/nginx/html/index.html 的內容。

再次重新整理瀏覽器,會發現內容被改變。

Docker(二):Dockerfile 使用介紹

修改了容器的檔案,也就是改動了容器的儲存層,可以通過 docker diff 命令看到具體的改動。

docker diff docker_nginx_v1 
... 

這樣 Dockerfile 使用方式就為大家介紹完了,下期為大家介紹 Dockerfile 命令的詳細使用。

參考

Dockerfile reference
使用Dockerfile構建Docker映象
Docker映象構建檔案Dockerfile及相關命令介紹
深入Dockerfile(一): 語法指南
Docker — 從入門到實踐

相關文章