想學習好一點技術,最好的方法就是學習這項技術的工作原理。一直很好奇docker是如何實現互不影響的,很是神奇。
通過學習查閱一些資料,慢慢的了解了底層是如何實現的,特此記錄一下。
從本質上說,docker容器其實就是一個沙盒技術。就好像把一個應用隔離在一個盒子內,讓其運行。因為盒子有邊界的存在,應用於應用之間不會存在相互幹擾。
實現原理實現容器的核心,其實就是要生成限制應用運行時的邊界。編譯後的可執行代碼加上數據,叫做程序。
而把程序運行起來後,就變成了進程,也就是所謂的應用了。如果在啟動的時候,加上一個邊界,是不是就實現期待的沙盒了?
在Linux中,實現容器的邊界,主要有兩種技術,Cgroups 和 Namespace。
NamespaceNamespace是用於分離進程樹、網絡接口、掛載點以及進程間通信等資源的方法。通俗點講,就是將容器隔離起來,實現邊界。
在日常使用Linux 和 macOS時,並沒有必要運行多個完全分離的伺服器需要。但是如果在伺服器上啟動多個服務,這些服務其實會相互影響的。
每個服務都能看到其他服務的進程,也可以訪問宿主機上的任意文件,這是很多時候不願意看到的,我們希望他們能做到完全隔離,就像運行在不同機器上一樣。
在這種情況下,一旦伺服器上一個服務被入侵,其他服務也就被入侵了。
而Docker就是通過namespace對不同容器實現的隔離。
Cgroups通過Namespace技術,我們實現了容器和容器之間,容器和宿主機之間的隔離。
但這還不夠,想像一下一種場景。宿主機上運行著兩個容器,雖然在容器之間相互隔離,但在宿主機的視角來看,只是兩個特殊的進程。
而進程之間自然存在著競爭關係,自然可以將系統資源吃光,這是一定要避免的。
Cgroups就是Linux內核中用來為進程設置資源的一個技術。
「Linux Cgroups 全稱是 Linux Control Group,主要作用限制進程組使用的資源上限。例如CPU、磁碟I/O、內存、和網絡帶寬。還可以對進程進行優先級設置,審計,掛起和恢復等操作。
」容器的文件系統:rootfs現在知道容器的核心技術就是通過 Namespace 限制了容器看到的視野 , 通過Cgroup限制容器可訪問的資源。
在容器內,可以看到完全獨立的文件系統,而且不會收到宿主機以及其他容器的影響。
這個獨立的文件系統,叫做容器鏡像, 專業的名字叫 rootfs, rootfs中包含了一個作業系統所需要的文件,配置和目錄,但不包含系統內核。
因為在Linux中,文件和內核是分開存放的,作業系統只有在開啟啟動時才會加載指定的內核。這就說明,所有的容器都會共享宿主機上作業系統的內核。
有了rootfs之後,在鏡像內,打包的不僅僅是應用,還有所需要的依賴,都被封裝在一起。這就解決無論是在哪,應用都可以很好運行的原因。
不光是這樣,rootfs 還解決了可重用性的問題。
想像一下,你通過 rootfs 打包一個包含php的centos鏡像,別人需要安裝一個Nginx服務,是否還需要從頭開始搭建php服務呢?
docker 在解決這個問題的時候,引入一個層(layer)的概念,每次對 rootfs 的更改,都只保留增量的內容,而不是fork一個新鏡像。
層級的想法,同樣來自於 Linux,一個叫 union file system (聯合文件系統)。
它最主要的功能就是將不同位置的目錄聯合掛載到同一個目錄下。對應在 Docker 裡面,不同的環境則使用了不同的聯合文件系統。
Image、Container Layer定義Image 定義鏡像(Image)就是一堆只讀層的統一視角,也許這個定義有些難以理解,看張圖理解下。
鏡像統一視角從左邊看到多個只讀層,他們重疊在一起。除了最下面的一層,其它層都會有一個指向下一層的指針。
這些層是Docker內部的實現細節,並能在運行Docker機器的文件系統上訪問到。
統一文件系統(union file system)技術能夠將不同的層整合成一個文件系統,為這些層提供了一個統一的視角,這樣就能隱藏錯層的存在,在用戶的角度看來,只存在一個文件系統。在在右邊的圖上可以看到這種形式
Container 定義容器(container)的定義和鏡像(image)幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器最上面的那一層是可讀可寫的。
容器統一視角這裡容器的定義並沒有提及到容器是否運行,運行時的容器是怎麼樣的呢?
Running Container 定義一個運行態的容器(running container)被定義為一個可讀寫的統一文件系統加上隔離的進程空間和包含其中的進程。
運行態的容器文件系統隔離技術促使了Docker成為了一個前途無量的技術。一個容器中的進程可能會對文件進行修改、刪除、創建,這些改變都將作用於可讀可寫層(read-write layer)。
可讀可寫層修改文件Image Layer 定義為了將零星的數據整合起來,提出了鏡像層(image layer)的概念。一個鏡像層不僅僅包含文件系統的改變,他還能包含一些其他重要的信息。
image 層元數據(metadata)就是關於這個層的額外信息,他不僅能夠讓Docker獲取運行和構建時的信息,還包括父層的層次信息。需要注意的是,只讀層和讀寫層都包含元數據。
只讀層和讀寫層包含元數據除此之外,每一層都有一個指向父級層的指針,這點和mvcc中版本鏈是一樣的。如果沒有這個指針,說明他是最底層。
層次鏈Docker命令詳解docker create <image_id>docker create 命令為指定的鏡像(image)添加一個可讀可寫層,構建一個新的容器。
添加可讀可寫層這個容器此時並沒有啟動運行。
鏡像 -> 容器docker start <container_id>docker start 命令為容器文件系統創建一個進程隔離空間。注意:每一個容器只能夠有一個進程隔離空間。
創建進程隔離空間docker run <image_id>看到這個命令的時候我會產生一個疑問:run 和 start 有什麼區別?
docker run 命令解釋圖docker run 其實是兩個命令的結合體:docker create 和 docker start。
首先會使用 docker create 利用鏡像創建一個容器,然後運行這個容器。這個命令雖然很方面,但是也會隱藏一部分細節。
「題外話:git pull命令其實就是 git fetch 和 git merge 兩個命令的組合。同樣的,docker run就是docker create和docker start兩個命令的組合。
」docker ps這個命令會羅列出所有運行中的容器,隱藏了未運行的容器,如果想看到未運行的,使用 docker ps -a 則可以看到所有的容器。
docker psdocker commit <container_id>docker commit 會將可讀可寫層轉換為一個可讀層,這樣就把一個容器轉換為不可變的鏡像。
docker commitdocker builddocker build 會反覆執行多個命令。
執行過程build命令根據Dockerfile文件中的FROM指令獲取到鏡像,然後重複的。1、run(create和start);2、修改;3、commit。在循環中的每一步都會生成一個新的層,因此許多新的層會被創建。
docker 原理中涉及到很多linux相關的知識,這方面的知識還需要後續的學習補充。
總結的不對的地方,還請各位大佬多多指點。
如果我的文章對你有所幫助,還請幫忙點讚、在看、轉發一下,你的支持會激勵我繼續堅持下去,非常感謝!
你還可以把我的公眾號設為「星標」,這樣當公眾號文章更新時,你會在第一時間收到推送消息,避免錯過我的文章更新。
Namespace[1]
Layer層[2]
參考資料[1]命名空間:https://draveness.me/docker。
[2]層級:https://www.yht7.com/news/140896。