docker是一個輕量級容器,屬於操作系統層面的虛擬化技術,封裝了文件系統(AUFS)以及網絡互聯,進程隔離等特性。
傳統虛擬化架構:
docker虛擬化架構:
可以看出,docker是沒有Guest OS一層的,它屬於宿主機上面開啟的一個單獨進程,共享宿主機的內核和硬件。 傳統虛擬化技術每個實例都要虛擬出一套OS的硬件支持,當一台宿主機開啟多個虛擬機的時候,這些硬件虛擬無疑是重復的,且占用了大量宿主機的資源。 因此,docker這種輕量級的容器技術會更高效地使用宿主機的內核和硬件資源。同時由於沒有完整OS的啟動,docker的啟動也很快,如同你在win10里面開啟一個瀏覽器一樣,可以類比出,docker的級別與OS的application是一層的。
docker庫的架構: docker registry -> docker repository -> docker image
docker registry相當於一個郵輪,repository則是郵輪上的集裝箱,image就是集裝箱內部的系統鏡像。
docker的最小單元為image,docker image是只讀的,不支持修改,因此當你看上去修改了一個docker image,哪怕是只改了它的名字,docker的真實操作卻是復制出來一份列在下面,原來的仍舊被保留。同時為便於文件管理,docker image 可以通過命令被export為tar包存在本地任何位置,也可以import tar包成為本地的image。
注意:docker只是提供了一個環境,這個概念是獨特的,其他虛擬機是一個完全隔離開的封閉的就像一個完整的物理機那樣,而docker的用戶獲得的是一個基於通用OS上面的一個相同的環境,只要使用相同的image,docker能運行起來,那么環境就是一致的。目前docker已經可以支持幾乎所有流行的OS,它並不限制OS的種類、版本,這就使docker變成了跨平台技術,避免了用戶環境變化導致的Bug。
docker container 是 docker image 的實例化。
docker提供的這個環境,可以達到如一個純凈OS的效果,可對其進行具體的操作,如安裝新的包,文件變更等。而上文提到了,docker的image是只讀,不支持修改image本身,那么這個環境變更真的不是對image進行操作嗎?
答:確實不是的,docker的文件系統是AUFS,當我們對image進行修改的時候,表面上是進入那個container容器內部,然后做安裝命令,文件變更等修改,其實這些變更並不是發生在像傳統虛擬系統那樣在容器內部,而是在外部!docker container並不是一個封閉獨立的空間。
這些變更是存在於本地文件路徑/var/lib/docker/aufs/下,每次與原image不同的修改都會放在該目錄中。然而docker也提供了修改這個默認目錄的方式,就是在啟動容器的時候,使用-v選項設置這個本地目錄的映射參數,啟動后每一次修改都是針對映射的這個本地目錄,但是要注意以后的每次啟動都要帶着這個-v參數和這個本地目錄,否則又指向了默認的目錄相當於啟動一個基於原始image的新container了。
我們來深究一下上面的操作,進入docker container以后,會發現終端的用戶名和主機名,包括該用戶的root目錄(user@dockermachine:~/)都與進入之前不同了,好像真的進入了一個傳統虛擬機,這其實是linux的change root命令的效果,利用chroot命令將當前目錄映射為該用戶的root目錄。當exit的時候,又恢復為原真正的用戶root目錄,其對應的container也就被退了出來。
如果想提交這個最新的變更后的環境,讓更多同事使用,實際也是提交一個本地的image,通常是使用commit和dockerfile的方式。commit方式每次都要提交具體的變更細節,深入docker的實現原理,它是將每次的commit的差異文件存在AUFS文件系統的diff文件夾中,每次部署環境的時候,如果要檢查其中某次提交的文件變更,就要捋順一遍這個commit列表,這實在是不方便。因此dockerfile的方式被廣泛應用,dockerfile會記錄以初始的基礎image為原點的每次對環境的變更,例如安裝了某個新的命令,它像一個描述文件有着自己的語法格式,每當以dockerfile啟動的時候,會先啟動其依賴的基礎image,然后再按照dockerfile中的命令順序一條條去執行,最終會獲得一個變更后的統一環境。這樣,部署人員只需要維護dockerfile中的代碼即可。所有docker的用戶只需要本地留一個基礎image,然后按照業務需要去get相應的dockerfile,啟動的時候就會獲得最新的環境,與同組其他同事獲得的能夠保持一致。
總結一下,docker是如此輕巧靈活僅相當於一個普通app開啟一個宿主機的進程,image是只讀,dockerfile用來描述變更,container修改其實是映射到本地卷。
補充:
上面多次提到了docker的aufs文件系統,它非常強大,對應的路徑是在/var/lib/docker/aufs,它可以分塊(Blob)存儲一個image,由於image是只讀的,分塊存儲可加快地讀取。如果不做-v修改本地映射卷的話,它還可以存儲用戶在container中修改的變更文件。還可以存儲commit時與基礎image的差異文件。
提到分塊存儲image的特性,就要說一下linux的mount命令,它可以將兩個具體路徑映射成一個目錄,合並包含兩個具體路徑下的內部文件。例如,mount -t aufs -o dirs="/tmp/a;/tmp/b" none /mnt。執行以后,原/tmp/a和/tmp/b中的內容都會出現在/mnt中。
在docker run一個image的時候,如果不加任何參數,則默認是在https://hub.docker.com/ 下載,這就如同maven,有一個public registry,也應該有一個nexus服務於局域網。如果我想給team也建立一個局域網的“郵輪”,提高傳輸(上傳下載,push和pull)的效率,方便內部管理,就要自己創建一個registry服務端。
registry創建也非常簡單。registry服務本身在docker hub中仍舊是以一個image的形式存在的,所以直接在服務器上docker run registry就ok了。
客戶端container可以指定服務端的IP加端口去pull image,也可以將本地的image tag成服務端的IP加端口下的image,然后push到服務端去。