docker教程——利用 commit 理解鏡像構成(五)

 

        docker commit 命令除了學習之外,還有一些特殊的應用場合,比如被入侵後保存現場等。但是,不要使用 docker commit 定製鏡像,定製鏡像應該使用 Dockerfile 來完成。

        鏡像是容器的基礎,每次執行 docker run 的時候都會指定哪個鏡像作爲容器運行的基礎。在之前的例子中,我們所使用的都是來自於 Docker Hub 的鏡像。直接使用這些鏡像是可以滿足一定的需求,而當這些鏡像無法直接滿足需求時,我們就需要定製這些鏡像。

        鏡像是多層存儲,每一層是在前一層的基礎上進行的修改;而容器同樣也是多層存儲,是在以鏡像爲基礎層,在其基礎上加一層作爲容器運行時的存儲層。

        現在讓我們以定製一個 Web 服務器爲例子,來講解鏡像是如何構建的。

$ docker run --name webserver -d -p 80:80 nginx

這條命令會用 nginx 鏡像啓動一個容器,命名爲 webserver,並且映射了 80 端口,這樣我們可以用瀏覽器去訪問這個 nginx 服務器。直接用瀏覽器訪問的話,我們會看到默認的 Nginx 歡迎頁面。

 

現在,假設我們非常不喜歡這個歡迎頁面,我們希望改成歡迎 Docker 的文字,我們可以使用 docker exec 命令進入容器,修改其內容。

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

我們以交互式終端方式進入 webserver 容器,並執行了 bash 命令,也就是獲得一個可操作的 Shell。然後,我們用 <h1>Hello, Docker!</h1> 覆蓋了 /usr/share/nginx/html/index.html 的內容。現在我們再刷新瀏覽器的話,會發現內容被改變了。

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

現在我們定製好了變化,我們希望能將其保存下來形成鏡像。

 

 

        要知道,當我們運行一個容器的時候(如果不使用卷的話),我們做的任何文件修改都會被記錄於容器存儲層裏。而 Docker 提供了一個 docker commit 命令,可以將容器的存儲層保存下來成爲鏡像。換句話說,就是在原有鏡像的基礎上,再疊加上容器的存儲層,並構成新的鏡像。以後我們運行這個新鏡像的時候,就會擁有原有容器最後的文件變化。docker commit 的語法格式爲:

docker commit [選項] <容器ID或容器名> [<倉庫名>[:<標籤>]]

我們可以用下面的命令將容器保存爲鏡像:

$ docker commit \
    --author "Tao Wang <[email protected]>" \
    --message "修改了默認網頁" \
    webserver \
    nginx:v2
sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214

其中 --author 是指定修改的作者,而 --message 則是記錄本次修改的內容。這點和 git 版本控制相似,不過這裏這些信息可以省略留空。我們可以在 docker image ls 中看到這個新定製的鏡像:

我們還可以用 docker history 具體查看鏡像內的歷史記錄,如果比較 nginx:latest 的歷史記錄,我們會發現新增了我們剛剛提交的這一層。

$ docker history nginx:v2

新的鏡像定製好後,我們可以來運行這個鏡像。

docker run --name web2 -d -p 81:80 nginx:v2

這裏我們命名爲新的服務爲 web2,並且映射到 81 端口。如果是 Docker for Mac/Windows 或 Linux 桌面的話,我們就可以直接訪問 http://localhost:81 看到結果,其內容應該和之前修改後的 webserver 一樣。

        至此,我們第一次完成了定製鏡像,使用的是 docker commit 命令,手動操作給舊的鏡像添加了新的一層,形成新的鏡像,對鏡像多層存儲應該有了更直觀的感覺。

 

 

慎用 docker commit

        使用 docker commit 命令雖然可以比較直觀的幫助理解鏡像分層存儲的概念,但是實際環境中並不會這樣使用。

        首先,如果仔細觀察之前的 docker diff webserver 的結果,你會發現除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由於命令的執行,還有很多文件被改動或添加了。這還僅僅是最簡單的操作,如果是安裝軟件包、編譯構建,那會有大量的無關內容被添加進來,如果不小心清理,將會導致鏡像極爲臃腫。

        此外,使用 docker commit 意味着所有對鏡像的操作都是黑箱操作,生成的鏡像也被稱爲黑箱鏡像,換句話說,就是除了製作鏡像的人知道執行過什麼命令、怎麼生成的鏡像,別人根本無從得知。而且,即使是這個製作鏡像的人,過一段時間後也無法記清具體在操作的。雖然 docker diff 或許可以告訴得到一些線索,但是遠遠不到可以確保生成一致鏡像的地步。這種黑箱鏡像的維護工作是非常痛苦的。

        而且,回顧之前提及的鏡像所使用的分層存儲的概念,除當前層外,之前的每一層都是不會發生改變的,換句話說,任何修改的結果僅僅是在當前層進行標記、添加、修改,而不會改動上一層。如果使用 docker commit 製作鏡像,以及後期修改的話,每一次修改都會讓鏡像更加臃腫一次,所刪除的上一層的東西並不會丟失,會一直如影隨形的跟着這個鏡像,即使根本無法訪問到。這會讓鏡像更加臃腫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章