Docker 利用commit理解構建鏡像
鏡像是容器的基礎,每次執行 docker run 的時候都會指定哪個鏡像作為容器運行的基礎。當我們使用Docker Hub的鏡像無法滿足我們的需求時,我們就需要自己定制鏡像來滿足我們的需求。
鏡像是多層存儲,每一層是在前一層的基礎上進行的修改;而容器同樣也是多層存儲是在以鏡像為基礎層,在基礎層上加一層作為容器運行時的存儲層。
示例說明鏡像是如何構建的。
[root@server ~]# docker run --name webServer -d -p 80:80 nginx:1.11 #啟動一個容器,基於docker hub上面的nginx:1.11鏡像
這條命令會用 nginx:1.11 鏡像啟動一個容器,命名為 webServer,並且映射了80 端口,這樣便可以去訪問這個 nginx 服務器。然后我們直接訪問宿主機的IP可以看到Nginx的歡迎頁面
現在,假設我們不喜歡這個歡迎頁面,我們喜歡改成別的文字,我們可以使用docker exec 命令進入到容器,修改其內容給
root@714830c04e5e:/# echo '<h1>Hello Docker Nginx Server</h1>' >/usr/share/nginx/html/index.html #修改默認首頁的內容 root@714830c04e5e:/# exit exit
已交互式終端方式進入 webServer 容器,並執行了 bash命令, 也就是獲得了一個可操作的shell。然后覆蓋了index.html內容,再次刷新瀏覽器,會發現內容被改變了。
修改了容器的文件,也就是改動了容器的存儲器,可以通過 docker diff 命令查看具體的改動
[root@server ~]# docker diff webServer #查看webServer容器改動的內容 C /var C /var/cache C /var/cache/nginx A /var/cache/nginx/scgi_temp A /var/cache/nginx/uwsgi_temp A /var/cache/nginx/client_temp A /var/cache/nginx/fastcgi_temp A /var/cache/nginx/proxy_temp C /root A /root/.bash_history C /usr C /usr/share C /usr/share/nginx C /usr/share/nginx/html C /usr/share/nginx/html/index.html C /run A /run/nginx.pid
現在我們定制好了變化,我們希望能將其保存下來形成鏡像。要知道,當我們運行一個容器的時候(如果不使用卷的話),我們做的任何文件修改都會被記錄於容器存儲器里。而Docker提供了一個 docker commit 命令,可以將容器的存儲層保存下來稱為鏡像。也就是說在原有鏡像的基礎上,再疊加上容器的存儲層,並構成新的鏡像。以后我們在運行這個新鏡像的時候,就會擁有原有容器最后的文件變化。
docker commit 語法格式:
docker commit [選項] <容器ID或容器名> [<倉庫名>[:<標簽>]]
示例將上面更改首頁的webServer 容器保存為鏡像:
[root@server ~]# docker commit \ --author "Bu Ji <381347268@qq.com>" \ --message "修復了默認首頁" \ webServer \ nginx:v1 [root@server ~]# docker images nginx #查看制作完成的nginx鏡像 REPOSITORY TAG IMAGE ID CREATED SIZE nginx v1 b639fbcc5ec4 2 minutes ago 183MB nginx 1.11 5766334bdaa0 21 months ago 183MB
其中 --author 是指定修改的作者,而 --message 則是記錄本次修改的內容。這點和 git 版本控制器相似。還可以使用 docker history 具體查看鏡像內的歷史記錄
[root@server ~]# docker history nginx:v1 #查看nginx:v1鏡像的歷史記錄 IMAGE CREATED CREATED BY SIZE COMMENT b639fbcc5ec4 5 minutes ago nginx -g daemon off; 157B 修復了默認首頁 5766334bdaa0 21 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B <missing> 21 months ago /bin/sh -c #(nop) EXPOSE 443/tcp 80/tcp 0B <missing> 21 months ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B <missing> 21 months ago /bin/sh -c echo "deb http://nginx.org/packag… 59.1MB <missing> 21 months ago /bin/sh -c set -e; NGINX_GPGKEY=573BFD6B3D8… 4.9kB <missing> 21 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.11.13… 0B <missing> 21 months ago /bin/sh -c #(nop) MAINTAINER NGINX Docker M… 0B <missing> 21 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 21 months ago /bin/sh -c #(nop) ADD file:4eedf861fb567fffb… 123MB
新的鏡像定制好后,我們來運行這個鏡像
[root@server ~]# docker run --name web1 -d -p 81:80 nginx:v1 #基於上面新建的nginx:v1啟動一個名字為web1的容器
當我們訪問宿主機IP:81時候,其內容和之前修改后的 webServer一樣
至此,完成了一個定制鏡像,使用的是 docker commit 命令,手動給舊的鏡像添加了新的一層,形成了新的鏡像,對鏡像多層存儲應該有了很直觀的感受。
慎用 docker commit
使用 docker commit 命令雖然可以比較直觀的幫助理解鏡像分層存儲的概念,但是實際環境中很少這樣使用。
首先, 從上面的 docker diff webServer 的結果中,可以發現除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由於命令的執行,還有很多文件被改動或添加了。這還只是最簡單的操作,如果安裝軟件包、編譯構建,那么有大量的無關內容被添加進來,如果不小心清理,將會導致鏡像為臃腫。
此外,使用docker commit 意味着所有對鏡像的操作都是黑箱操作,生成的鏡像也被稱為黑箱鏡像,換句話說,就是除了制作鏡像的人知道執行過什么命令、怎么生成的鏡像,別人根本無法從知。雖熱docker diff 或許可以告訴得到一些線索,但是遠遠不到可以確保生成一致鏡像的地步。這種黑箱鏡像的維護工作是非常痛苦的。
而且,除當前層外,之前的每一層都是不會發生改變的,也就是說,任何修改的結果僅僅是在當前層進行標記、添加、修改,而不會改動上一層。如果使用 docker commit 制作鏡像,以及后期修改的話,每次一次修改都會讓鏡像更加臃腫一次,所刪除的上一層的東西並不會丟失,會一直如影隨形的跟着這個鏡像,即使根本無法訪問到。這會讓鏡像更加臃腫。