Dockerfile 介紹
容器docker
可以通過讀取dockerfile
中的指令來自動構建鏡像。Dockerfile是一個文本文檔,它包含用戶可以在命令行上調用的所有命令,並組裝一個映像。
使用docker build
,用戶可以創建一個連續執行多個命令行指令的自動化構建的鏡像。
Docker鏡像是分層結構,由只讀層組成,每個只讀層表示Dockerfile指令。這些層被堆疊起來,每一層都是前一層變化的增量。例如:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
上面沒條指令的含義:
- FROM :創建一個基礎鏡像層(基於ubuntu:18.04),之后的指令都是基於該層的。因此一個 Dockerfile 中 FROM 是必備的指令,並且必須是第一條指令。
- COPY :將docker客戶端當前目錄中的文件全部 copy 到容器中的/app目錄中。
- RUN :使用make構建應用程序,RUN 指令是用來執行命令行命令的。
- CMD :指定在容器中運行什么命令。
當您運行一個鏡像像並生成一個容器時,將在底層層上添加一個新的可寫層(“容器層”)。對正在運行的容器所做的所有更改,如寫入新文件、修改現有文件和刪除文件,都被寫入這個可寫的容器層。
理解docker構建上下文
我們在構建鏡像是,其格式為:
docker build [選項] <上下文路徑/URL/->
從上面指令可以看到 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 引擎收到這個上下文包后,展開就會獲得構建鏡像所需的一切文件。
發出docker構建命令時,當前工作目錄便是構建上下文。默認情況下,Dockerfile位於此處,但是您可以使用file標志(-f)指定一個不同的位置。無論Dockerfile實際位於何處,當前目錄中的文件和目錄的所有遞歸內容都將作為構建上下文發送到Docker守護進程。
例如,如果在 Dockerfile 中這么寫:
COPY ./app.yml /app
這並不是要復制執行 docker build 命令所在的目錄下的 app.yml,也不是復制 Dockerfile 所在目錄下的 app.yml,而是復制 上下文(context) 目錄下的 app.yml。
docker build /opt/myapp/ -f ~/Dockerfile
上文問目錄為“/opt/myapp/”, 而Dockerfile文件為“~/Dockerfile”。
鏡像的構建方式
基於Dockerfile構建
我們創建一個myproject的目錄,並cd進入到該目錄。創建一個hello文件,內容為hello。然后我們創建一個Dockerfile,用來查看hello文件的內容。
mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox:latest\nCOPY hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .
然后我們將Dockerfile移動到其他的目錄,通過-f
參數來制定Dockerfile文件的位置,並構建鏡像:
mkdir -p dockerfiles context
mv Docekerfile dockerfiles && mv hello context
docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context
注意:
我們在docker build
的時候,應該盡可能地避免引入不必要的文件或目錄,這樣會導致鏡像體積增加;一個龐大體積的鏡像在pull,push
的過程中會大大增加時間,以及容器運行的體積。要想查看你容器有多大,在build的過程中會看到這樣的提示:
Sending build context to Docker daemon 2.607kB
基於管道和標准輸入構建
Docker能夠通過使用本地或遠程構建上下文,通過stdin管道傳輸Dockerfile來構建映像。
通過stdin管道傳輸Dockerfile對於執行一次性構建
非常有用,不需要將Dockerfile寫入磁盤,或者在生成Dockerfile的情況下,不應該在以后持久化Dockerfile。
例如:
echo -e 'FROM busybox\nRUN echo "hello, world~"' | docker build -
或者
docker build - <<EOF
FROM busybox
RUN echo "hello, world~"
EOF
說明:
上面的例子中,我們在構建的使用,使用了連接符-
占據了路徑的位置,指示Docker從stdin而不是從目錄中讀取構建上下文(只包含Dockerfile),這種情況不會將任何文件作為構建上下文發送給守護進程。
使用git repo進行構建
docker build
支持直接從url進行構建:
$ docker build https://github.com/username/project.git#version
這行命令指定了構建所需的 Git repo,並且指定默認的 master 分支,構建目錄為 /version/(例如版本是1.1,那么構建目錄便是/1.1/),然后 Docker 就會自己去 git clone 這個項目、切換到指定分支、並進入到指定目錄后開始構建。
通過給定tar壓縮包構建
$ docker build http://server/context.tar.gz
如果所給出的 URL 不是個 Git repo,而是個 tar 壓縮包,那么 Docker 引擎會下載這個包,並自動解壓縮,以其作為上下文,開始構建。