鏡像是容器的基礎,每次執行 docker run 的時候都會指定哪一個鏡像做爲容器運行的基礎。當咱們使用Docker Hub的鏡像沒法知足咱們的需求時,咱們就須要本身定製鏡像來知足咱們的需求。html
鏡像是多層存儲,每一層是在前一層的基礎上進行的修改;而容器一樣也是多層存儲是在以鏡像爲基礎層,在基礎層上加一層做爲容器運行時的存儲層。nginx
示例說明鏡像是如何構建的。git
[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的歡迎頁面web
如今,假設咱們不喜歡這個歡迎頁面,咱們喜歡改爲別的文字,咱們可使用docker exec 命令進入到容器,修改其內容給docker
root@714830c04e5e:/# echo '<h1>Hello Docker Nginx Server</h1>' >/usr/share/nginx/html/index.html #修改默認首頁的內容 root@714830c04e5e:/# exit exit
已交互式終端方式進入 webServer 容器,並執行了 bash命令, 也就是得到了一個可操做的shell。而後覆蓋了index.html內容,再次刷新瀏覽器,會發現內容被改變了。shell
修改了容器的文件,也就是改動了容器的存儲器,能夠經過 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 命令,能夠將容器的存儲層保存下來稱爲鏡像。也就是說在原有鏡像的基礎上,再疊加上容器的存儲層,並構成新的鏡像。之後咱們在運行這個新鏡像的時候,就會擁有原有容器最後的文件變化。bash
docker commit 語法格式:服務器
docker commit [選項] <容器ID或容器名> [<倉庫名>[:<標籤>]]
示例將上面更改首頁的webServer 容器保存爲鏡像:tcp
[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 diff webServer 的結果中,能夠發現除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,因爲命令的執行,還有不少文件被改動或添加了。這還只是最簡單的操做,若是安裝軟件包、編譯構建,那麼有大量的無關內容被添加進來,若是不當心清理,將會致使鏡像爲臃腫。
此外,使用docker commit 意味着全部對鏡像的操做都是黑箱操做,生成的鏡像也被稱爲黑箱鏡像,換句話說,就是除了製做鏡像的人知道執行過什麼命令、怎麼生成的鏡像,別人根本沒法從知。雖熱docker diff 或許能夠告訴獲得一些線索,可是遠遠不到能夠確保生成一致鏡像的地步。這種黑箱鏡像的維護工做是很是痛苦的。
並且,除當前層外,以前的每一層都是不會發生改變的,也就是說,任何修改的結果僅僅是在當前層進行標記、添加、修改,而不會改動上一層。若是使用 docker commit 製做鏡像,以及後期修改的話,每次一次修改都會讓鏡像更加臃腫一次,所刪除的上一層的東西並不會丟失,會一直如影隨形的跟着這個鏡像,即便根本沒法訪問到。這會讓鏡像更加臃腫。