從剛才的 docker commit 的學習中,我們可以了解到,映象的定製實際上就是定製每一層所新增的配置、檔案。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入乙個指令碼,用這個指令碼來構建、定製映象,那麼之前提及的無法重複的問題、映象構建透明性的問題、體積的問題就都會解決。這個指令碼就是 dockerfile。
dockerfile 是乙個文字檔案,其內包含了一條條的指令(instruction),每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。
mkdir mynginx
cd mynginx
touch dockerfile
dockerfire的內容:
from nginx
run echo
'' /usr/share/nginx/html/index.html
所謂定製映象,那一定是以乙個映象為基礎,在其上進行定製。就像我們之前執行了乙個 nginx 映象的容器,再進行修改一樣,基礎映象是必須指定的。而 from 就是指定基礎映象,因此乙個 dockerfile 中 from 是必備的指令,並且必須是第一條指令。在 docker store 上有非常多的高質量的官方映象,有可以直接拿來使用的服務類的映象,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便開發、構建、執行各種語言應用的映象,如 node、openjdk、python、ruby、golang 等。可以在其中尋找乙個最符合我們最終目標的映象為基礎映象進行定製。
如果沒有找到對應服務的映象,官方映象中還提供了一些更為基礎的作業系統映象,如 ubuntu、debian、centos、fedora、alpine 等,這些作業系統的軟體庫為我們提供了更廣闊的擴充套件空間。
除了選擇現有映象為基礎映象外,docker 還存在乙個特殊的映象,名為 scratch。這個映象是虛擬的概念,並不實際存在,它表示乙個空白的映象。
run 指令是用來執行命令列命令的。由於命令列的強大能力,run 指令在定製映象時是最常用的指令之一。其格式有兩種:
shell 格式:run 《命令》,就像直接在命令列中輸入的命令一樣。剛才寫的 dockerfile 中的 run 指令就是這種格式。
run echo
'' /usr/share/nginx/html/index.html
exec 格式:run [「可執行檔案」, 「引數1」, 「引數2」],這更像是函式呼叫中的格式。既然 run 就像 shell 指令碼一樣可以執行命令,那麼我們是否就可以像 shell 指令碼一樣把每個命令對應乙個 run 呢?比如這樣:
之前說過,dockerfile 中每乙個指令都會建立一層,run 也不例外。每乙個 run 的行為,就和剛才我們手工建立映象的過程一樣:新建立一層,在其上執行這些命令,執行結束後,commit 這一層的修改,構成新的映象。
而上面的這種寫法,建立了 7 層映象。這是完全沒有意義的,而且很多執行時不需要的東西,都被裝進了映象裡,比如編譯環境、更新的軟體包等等。結果就是產生非常臃腫、非常多層的映象,不僅僅增加了構建部署的時間,也很容易出錯。 這是很多初學 docker 的人常犯的乙個錯誤。
union fs 是有最大層數限制的,比如 aufs,曾經是最大不得超過 42 層,現在是不得超過 127 層。
上面的 dockerfile 正確的寫法應該是這樣:
首先,之前所有的命令只有乙個目的,就是編譯、安裝 redis 可執行檔案。因此沒有必要建立很多層,這只是一層的事情。因此,這裡沒有使用很多個 run 對一一對應不同的命令,而是僅僅使用乙個 run 指令,並使用 && 將各個所需命令串聯起來。將之前的 7 層,簡化為了 1 層。在撰寫 dockerfile 的時候,要經常提醒自己,這並不是在寫 shell 指令碼,而是在定義每一層該如何構建。好了,讓我們再回到之前定製的 nginx 映象的 dockerfile 來。現在我們明白了這個 dockerfile 的內容,那麼讓我們來構建這個映象吧。
在 dockerfile 檔案所在目錄執行:
從命令的輸出結果中,我們可以清晰的看到映象的構建過程。在 step 2 中,如同我們之前所說的那樣,run 指令啟動了乙個容器 9cdc27646c7b,執行了所要求的命令,並最後提交了這一層 44aa4490ce2c,隨後刪除了所用到的這個容器 9cdc27646c7b。
這裡我們使用了 docker build 命令進行映象構建。其格式為:
在這裡我們指定了最終映象的名稱 -t nginx:v3,構建成功後,我們可以像之前執行 nginx:v2 那樣來執行這個映象,其結果會和 nginx:v2 一樣。
如果注意,會看到 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 中這麼寫:
一般來說,應該會將 dockerfile 置於乙個空目錄下,或者專案根目錄下。如果該目錄下沒有所需檔案,那麼應該把所需檔案複製乙份過來。如果目錄下有些東西確實不希望構建時傳給 docker 引擎,那麼可以用 .gitignore 一樣的語法寫乙個 .dockerignore,該檔案是用於剔除不需要作為上下文傳遞給 docker 引擎的。
那麼為什麼會有人誤以為 . 是指定 dockerfile 所在目錄呢?這是因為在預設情況下,如果不額外指定 dockerfile 的話,會將上下文目錄下的名為 dockerfile 的檔案作為 dockerfile。
這只是預設行為,實際上 dockerfile 的檔名並不要求必須為 dockerfile,而且並不要求必須位於上下文目錄中,比如可以用 -f …/dockerfile.php 引數指定某個檔案作為 dockerfile。
當然,一般大家習慣性的會使用預設的檔名 dockerfile,以及會將其置於映象構建上下文目錄中。
Dockerfile 定製映象
docker 是乙個文字檔案,包含一條條指令 以構建nginx映象為例,這次我們用docker去構建定製映象 建立目錄並建立dockerfile mkdir mynginx cd mynginx touch dockerfile其內容有 from nginx run echo usr share n...
Dockerfile定製映象
映象定製實質就是定製每一層所新增的配置 檔案。dockerfile就是乙個指令碼來構建和定製映象,把每一層的修改 安裝 構建 操作都寫入指令碼。以此來解決體積 映象構建透明等問題。dockerfile是乙個文字檔案,包含一條條指令 instruction 每一條指令構建一層,每一條指令的內容,就是描...
dockerfile建立映象
dockerfile用來快速建立自定義映象 dockerfile分為部分 基礎映象資訊,維護者資訊,映象操作指令,容器啟動時執行指令 一開始必須指明所基於的映象名稱 維護者資訊 映象操作指令 run 執行容器時的操作命令 cmd from 功能為指定基礎映象,並且必須是第一條指令。如果不以任何映象為...