Docker 學(xué)習(xí)篇(四)
1、Docker 鏡像(image)
(1)、鏡像是什么?
鏡像是一種輕量級、可執(zhí)行的獨(dú)立軟件包,用來打包軟件運(yùn)行環(huán)境和基于運(yùn)行環(huán)境開發(fā)的軟件,它包含運(yùn)行某個軟件所需的所有內(nèi)容,包括代碼、運(yùn)行時(shí)所需的環(huán)境、庫和配置文件。
(2)、UnionFS(聯(lián)合文件系統(tǒng),Union File System)
聯(lián)合文件系統(tǒng)是一種分層、輕量級并且高性能的文件系統(tǒng),聯(lián)合文件系統(tǒng)的內(nèi)部,是一個文件套著另一個文件,很像俄羅斯套娃,如圖1所示。

(3)、鏡像的分層結(jié)構(gòu)
Docker 鏡像的內(nèi)部結(jié)構(gòu)其實(shí)就是一個聯(lián)合文件系統(tǒng),是一層一層的。首先我們講解一下 Docker 中的 base 鏡像,base 鏡像最內(nèi)層的是 bootfs(boot file system),稍外層的是 rootfs(root file system),見圖2。
bootfs:在 linux/unix 操作系統(tǒng)中,bootfs 主要包含 boot loader(boot加載器) 和 kernel(操作系統(tǒng)內(nèi)核),boot loader 主要是負(fù)責(zé)加載 kernel。linux 剛啟動時(shí)會加載 bootfs 文件系統(tǒng),bootfs 中的 boot loader 將整個 kernel 加載到內(nèi)存,此時(shí)內(nèi)存的使用權(quán)由 bootfs 轉(zhuǎn)交給 kernel,之后 bootfs 會被卸載掉,從而釋放出它占用的內(nèi)存。
rootfs:rootfs 包含著典型 linux 系統(tǒng)的目錄結(jié)構(gòu),比如 /dev, /proc, /bin, /etc, /lib, /usr, /tmp 等標(biāo)準(zhǔn)目錄和文件。

從上面的講解可以看出,base 鏡像其實(shí)就是一個最小安裝的 Linux 發(fā)行版,對于 base 鏡像來說,底層直接用宿主機(jī)的 kernel 就行(上篇文章講過,Docker 和宿主機(jī)共用內(nèi)核和內(nèi)存),只需要根據(jù) Linux 操作系統(tǒng)的不同提供不同的 rootfs 即可。比如 Ubuntu 和 CentOS 的 Linux kernel 差別不大,但是它們的 rootfs 卻并不相同,Ubuntu 有它自己的 rootfs, CentOS 也有它自己的rootfs,見圖3。

(4)、為什么 Docker 鏡像要采用這種分層結(jié)構(gòu)呢?
Docker 鏡像采用分層結(jié)構(gòu),最大的好處就是能夠?qū)崿F(xiàn)資源共享,節(jié)省資源開銷。
有多個鏡像都是從 base 鏡像構(gòu)建而來,那么宿主機(jī)磁盤上只需保存一份 base 鏡像,同時(shí)內(nèi)存也只需加載一份 base 鏡像,就可以為所有容器服務(wù)了。
舉個栗子:比如我現(xiàn)在用 CentOS 操作系統(tǒng)的 ISO 做了一個 rootfs,然后又在里面安裝了Java環(huán)境(JDK),用來部署我的應(yīng)用 A。此時(shí),我的另一個同事在發(fā)布他的應(yīng)用 B 時(shí),希望能夠直接使用我安裝過 Java 環(huán)境的rootfs,而不是重復(fù)這個流程,鏡像的分層結(jié)構(gòu)就能夠?qū)崿F(xiàn)這個需求。

(5)、使用幾個例子來驗(yàn)證鏡像是分層的
(a)docker pull redis,見圖 5 的下載過程,我們可以看到鏡像是一層一層下載的。

(b)平時(shí)我們在 windows 上安裝的 centos 都是好幾個G,為什么我們通過docker pull centos 從阿里云上拉取的 centos 鏡像才200多兆(見圖6)?

原因如下,對于不同的 linux 發(fā)行版本,kernel 差別不大,可以共用,主要區(qū)別是 rootfs。我們是在 centos 虛擬機(jī)上做的演示,本地已經(jīng)安裝過 centos,即本地已經(jīng)有 kernel 了。通過 docker pull centos 下載鏡像時(shí),centos 鏡像可以和本地的 centos 共用 kernel,所以我們不需要下載 bootfs,只下載 rootfs 就可以了。即通過 docker pull centos 下載鏡像時(shí),實(shí)際上只下載 rootfs,因此 docker 下載的 centos 鏡像只有200M。

當(dāng)我們下載 ubuntu鏡像,也是此理,因?yàn)?ubuntu 的 kernel 和 centos 類似,ubuntu 鏡像的 bootfs 可以使用本地 centos 的 kernel。

(c)平時(shí)我們在windows環(huán)境安裝tomcat只有幾十兆,但是我們通過docker pull tomcat拉去下來的tomcat鏡像足足有幾百兆,這是怎么回事?
原因如下,tomcat 運(yùn)行時(shí)需要 jdk,所以 tomcat 鏡像里面一定要包括 jdk,此外 tomcat 有自己的內(nèi)核,不能和本機(jī)的 centos 共用 kernel(對于不同的鏡像,bootfs 基本是一致的,注意這是基本,不要以為 docker 中所有的鏡像都是和宿主機(jī)共用內(nèi)核的,也有 tomcat 這種例外,無法共用,必須自己生成自己的內(nèi)核),所以 tomcat 鏡像里除了 rootfs,還需要包括 bootfs (包含 kernel ),所以才這么大。

2、Docker 容器(container)
容器(container)的定義和鏡像(image)幾乎一模一樣,也是一堆層的統(tǒng)一視角,唯一區(qū)別在于容器的最上面那一層是可讀可寫的。
容器 = 鏡像(可讀) + 可讀可寫層,綜合圖10和圖11,從里層到外層分別是 base image,其他 image,最外層加上一個可讀可寫層,組成最終的 container。


3、docker commit,根據(jù)容器創(chuàng)建鏡像
(1)docker commit 命令
根據(jù)容器創(chuàng)建鏡像,并提交至倉庫,具體命令如下:docker commit -m="提交的描述信息" -a="作者" 容器ID [REPOSITORY[:TAG]]。
比如我們從倉庫下載了 tomcat 鏡像,把 tomcat 鏡像運(yùn)行起來,運(yùn)行起來的 tomcat 就是一個容器,我們在當(dāng)前這個容器上做一些自定義和調(diào)整,然后使用上述命令對這個容器執(zhí)行 commit 操作,就會形成一個新的鏡像,并提交至倉庫。
類似于你買了一輛車(原始的tomcat鏡像),這輛車原來并沒有導(dǎo)航雷達(dá),我們回來對車進(jìn)行了重新組裝,安上了導(dǎo)航雷達(dá),形成一輛新的車(新的tomcat鏡像)。
(2)演示 docker commit 操作
(a)docker pull tomcat,從倉庫下載 tomcat 鏡像。

(b)運(yùn)行 tomcat 鏡像,生成 tomcat 容器,docker run -it -p 8888(docker對外暴漏的端口):8080(tomcat端口) tomcat。
我們看 tomcat 啟動日志,出現(xiàn)如圖14的 startup 日志,表示 tomcat 啟動成功。


或者在本機(jī)的centos上訪問 localhost:8888,出現(xiàn) tomcat 那只貓,也表示 tomcat 啟動成功,見圖15。

(c)接下來我們修改上一步生成的 tomcat 容器。我們刪除 tomcat 容器里面的官方說明文檔(即 tomcat 目錄中的 docs 文件夾,也即 tomcat運(yùn)行界面中的 Documentation 模塊,見圖16),生成一個新的容器。

具體步驟如下所示:
第一步:獲取生成的 tomcat 容器的ID,docker ps,見圖 15,我們可以知道 tomcat 容器的容器 ID 為 1f19ddcbddf6。
第二步:進(jìn)入到這個 tomcat 容器中,并刪除 docs 文件夾。
1、docker exec -it 1f19ddcbddf6 /bin/bash,docker exec -it 容器ID baseShell,使用該命令可以進(jìn)入正在運(yùn)行的容器。
2、pwd,顯示/usr/local/tomcat。
3、ls -l,顯示tomcat目錄里的各種文件夾,包括webapps。

4、cd webapps.dist,ls -l,cd 到 webapps.dist 目錄,ls 發(fā)現(xiàn) webapps.dist 目錄中有一個 docs 文件夾,我們把它刪除掉。
5、rm -rf docs,刪除掉 docs 文件夾。

此時(shí)我們訪問 localhost:8888,點(diǎn)擊 Documentation,報(bào)錯 404 Not Found(如果第一次沒有變得話,說明有緩存,我們把緩存清一下,再點(diǎn)擊)。

(d)當(dāng)前的 tomcat 容器是一個沒有文檔內(nèi)容的容器,以它為模板 commit 一個沒有 docs 的tomcat 新鏡像 zhouxuejiao/tomcat02。
docker commit -a="zhouxuejiao" -m="del tomcat docs" 1f19ddcbddf6 zhouxuejiao/tomcat02:1.2,使用這個命令,根據(jù)我們修改后的容器,會生成一個新的鏡像。

docker images,使用這個命令查看所有鏡像,發(fā)現(xiàn)有我們要生成的新鏡像 zhouxuejiao/tomcat02。

(e)驗(yàn)證新鏡像是否創(chuàng)建成功。
當(dāng)我們運(yùn)行新的 tomcat 鏡像,并且在 tomcat 運(yùn)行界面中點(diǎn)擊 Documentation 時(shí)會報(bào) 404 錯誤的話,說明我們創(chuàng)建的這個新鏡像成功了。
docker run -it -p 7777:8080 453122812ee3(新鏡像的鏡像ID),運(yùn)行新的鏡像。


訪問ocalhost:7777,點(diǎn)擊 Documentation,報(bào)錯 404 Not Found,說明我們的新鏡像創(chuàng)建成功啦,見圖23、24。


今天中秋,祝大家中秋快樂啊!記得吃月餅哦 
