Docker / 深入理解的容器和镜像
這篇文章希望能夠幫助讀者深入理解 docker 的命令,還有容器(container)和鏡像(image)之間的區(qū)別,并深入探討容器和運行中的容器之間的區(qū)別。
零、概覽
當我對 docker 技術還是一知半解的時候,我發(fā)現理解 docker 的命令非常困難。于是,我花了幾周的時間來學習 docker 的工作原理,更確切地說,是關于 docker統(tǒng)一文件系統(tǒng)(the union file system)的知識,然后回過頭來再看 docker 的命令,一切變得順理成章,簡單極了。
題外話:就我個人而言,掌握一門技術并合理使用它的最好辦法就是深入理解這項技術背后的工作原理。通常情況下,一項新技術的誕生常常會伴隨著媒體的大肆宣傳和炒作,這使得用戶很難看清技術的本質。更確切地說,新技術總是會發(fā)明一些新的術語或者隱喻詞來幫助宣傳,這在初期是非常有幫助的,但是這給技術的原理蒙上了一層砂紙,不利于用戶在后期掌握技術的真諦。
git 就是一個很好的例子。我之前不能夠很好的使用 git,于是我花了一段時間去學習 git 的原理,直到這時,我才真正明白了 git 的用法。我堅信只有真正理解 git 內部原理的人才能夠掌握這個工具。
一、Image Definition
鏡像(Image)就是一堆只讀層(read-only layer)的統(tǒng)一視角。
上述這個定義有些難以理解,下面的這張圖能夠幫助讀者理解鏡像的定義。
鏡像從左邊我們看到了多個只讀層,它們重疊在一起。除了最下面一層,其它層都會有一個指針指向下一層。這些層是 docker 內部的實現細節(jié),并且能夠在主機(譯者注:運行 docker 的機器)的文件系統(tǒng)上訪問到。
統(tǒng)一文件系統(tǒng)(union file system)技術能夠將不同的層整合成一個文件系統(tǒng),為這些層提供了一個統(tǒng)一的視角,這樣就隱藏了多層的存在,在用戶的角度看來,只存在一個文件系統(tǒng)。我們可以在圖片的右邊看到這個視角的形式。
你可以在你的主機文件系統(tǒng)上找到有關這些層的文件。需要注意的是,在一個運行中的容器內部,這些層是不可見的。在我的主機上,我發(fā)現它們存在于/var/lib/docker目錄下。
xcl@XCL-ThinkPad:/var/lib/docker$ sudo tree -L 1 . ├── builder ├── buildkit ├── containers ├── image ├── network ├── overlay2 ├── plugins ├── runtimes ├── swarm ├── tmp ├── trust └── volumes12 directories, 0 files二、Container Definition
容器(container)的定義和鏡像(image)幾乎一模一樣,也是一堆層的統(tǒng)一視角,唯一區(qū)別在于容器的最上面那一層是可讀可寫的。
容器細心的讀者可能會發(fā)現,容器的定義并沒有提及容器是否在運行,沒錯,這是故意的。正是這個發(fā)現幫助我理解了很多困惑。
要點:容器 = 鏡像 + 可讀寫層,并且容器的定義并沒有提及是否要運行容器。
接下來,我們將會討論運行態(tài)容器。
三、Running Container Definition
一個運行態(tài)容器(running container)被定義為一個可讀寫的統(tǒng)一文件系統(tǒng)加上隔離的進程空間和包含其中的進程。下面這張圖片展示了一個運行中的容器。
運行中的容器正是文件系統(tǒng)隔離技術使得 Docker 成為了一個前途無量的技術。一個容器中的進程可能會對文件進行修改、刪除、創(chuàng)建,這些改變都將作用于可讀寫層(read-write layer)。
下面這張圖展示了這個行為。
對讀寫層進行讀寫我們可以通過運行以下命令來驗證我們上面所說的:
sudo docker run ubuntu touch happiness.txt
即便是這個ubuntu容器不再運行,我們依舊能夠在主機的文件系統(tǒng)上找到這個新文件。
sudo find / -name happiness.txt
/var/lib/docker/aufs/diff/860a7b...889/happiness.txt
四、Image Layer Definition
為了將零星的數據整合起來,我們提出了鏡像層(image layer)這個概念。下面的這張圖描述了一個鏡像層,通過圖片我們能夠發(fā)現一個層并不僅僅包含文件系統(tǒng)的改變,它還能包含了其他重要信息。
鏡像層元數據(metadata)就是關于這個層的額外信息,它不僅能夠讓 docker 獲取運行和構建時的信息,還包括父層的層次信息。需要注意,只讀層和讀寫層都包含元數據。
元數據除此之外,每一層都包括了一個指向父層的指針。如果一個層沒有這個指針,說明它處于最底層。
指針五、Metadata Location
我發(fā)現在我自己的主機上,鏡像層(image layer)的元數據被保存在名為“json”的文件中,比如說:
/ var / lib / docker / graph / e809f156dc985.../ json
e809f156dc985...就是這層的 id 。
一個容器的元數據好像是被分成了很多文件,但或多或少能夠在 /var/lib/docker/containers/<id> 目錄下找到,<id>就是一個可讀層的id。這個目錄下的文件大多是運行時的數據,比如說網絡,日志等等。
六、全局理解(Tying It All Together)
現在,讓我們結合上面提到的實現細節(jié)來理解 Docker 的命令。
1、docker create [ image-id ]
docker create imageid 輸入和輸出docker create 命令為指定的鏡像(image)添加了一個可讀寫層,構成了一個新的容器。注意,這個容器并沒有運行。
鏡像和容器2、docker start [ container-id ]
docker start containerid 輸入和輸出docker start 命令為容器文件系統(tǒng)創(chuàng)建了一個進程隔離空間。注意,每一個容器只能夠有一個進程隔離空間。
3、docker run [ image-id ]
docker run imageid 輸入和輸出看到這個命令,讀者通常會有一個疑問:docker start 和 docker run 命令有什么區(qū)別。
docker start 和 docker run 的區(qū)別從圖片可以看出,docker run 命令先是利用鏡像創(chuàng)建了一個容器,然后運行這個容器。這個命令非常的方便,并且隱藏了兩個命令的細節(jié),但從另一方面來看,這容易讓用戶產生誤解。
題外話:繼續(xù)我們之前有關于 Git 的話題,我認為 docker run 命令類似于 git pull 命令。git pull命令就是 git fetch 和 git merge 兩個命令的組合,同樣的,docker run 就是 docker create 和 docker start 兩個命令的組合。
4、docker ps
docker ps 輸入和輸出docker ps 命令會列出所有運行中的容器。這隱藏了非運行態(tài)容器的存在,如果想要找出這些容器,我們需要使用下面這個命令。
5、docker ps –a
docker ps -a 輸入和輸出docker ps –a命令會列出所有的容器,不管是運行的,還是停止的。
6、docker images
docker images 輸入和輸出docker images 命令會列出了所有頂層(top-level)鏡像。
實際上,在這里我們沒有辦法區(qū)分一個鏡像和一個只讀層,所以我們提出了 top-level 鏡像。只有創(chuàng)建容器時使用的鏡像或者是直接 pull 下來的鏡像能被稱為頂層(top-level)鏡像,并且每一個頂層鏡像下面都隱藏了多個鏡像層。
7、docker images –a
docker images -a 輸入和輸出docker images –a 命令列出了所有的鏡像,也可以說是列出了所有的可讀層。如果你想要查看某一個 image-id 下的所有層,可以使用 docker history 來查看。
8、docker stop [ container-id ]
docker stop 輸入和輸出docker stop 命令會向運行中的容器發(fā)送一個 SIGTERM 的信號,然后停止所有的進程。
9、docker kill [ container-id ]
docker kill 輸入和輸出docker kill 命令向所有運行在容器中的進程發(fā)送了一個不友好的 SIGKILL 信號。
10、docker pause [ container-id ]
docker stop 和 docker kill 命令會發(fā)送 UNIX 的信號給運行中的進程,docker pause 命令則不一樣,它利用了cgroups 的特性將運行中的進程空間暫停。
具體的內部原理你可以在這里找到:https://www.kernel.org/doc/Doc ... m.txt,但是這種方式的不足之處在于發(fā)送一個 SIGTSTP 信號對于進程來說不夠簡單易懂,以至于不能夠讓所有進程暫停。
11、docker rm [ container-id]
docker rm 命令會移除構成容器的可讀寫層。注意,這個命令只能對非運行態(tài)容器執(zhí)行。
12、docker rmi [ image-id ]
docker rmi 命令會移除構成鏡像的一個只讀層。你只能夠使用 docker rmi 來移除最頂層(top level layer)(也可以說是鏡像),你也可以使用-f參數來強制刪除中間的只讀層。
13、docker commit [ container-id ]
docker commit命令將容器的可讀寫層轉換為一個只讀層,這樣就把一個容器轉換成了不可變的鏡像。
14、docker build
docker build 命令非常有趣,它會反復的執(zhí)行多個命令。
?
我們從上圖可以看到,build 命令根據 dockerfile 文件中的 FROM 指令獲取到鏡像,然后重復地
- run(create和start)
- 修改
- commit
??在循環(huán)中的每一步都會生成一個新的層,因此許多新的層會被創(chuàng)建。
15、docker exec [ running-container-id ]
docker exec 命令會在運行中的容器執(zhí)行一個新進程。
16、docker inspect [ container-id ] or [ image-id ]
docker inspect 命令會提取出容器或者鏡像最頂層的元數據。
17、docker save [image-id]
docker save 命令會創(chuàng)建一個鏡像的壓縮文件,這個文件能夠在另外一個主機的 docker 上使用。和export命令不同,這個命令為每一個層都保存了它們的元數據。這個命令只能對鏡像生效。
18、docker export [ container-id ]
docker export 命令創(chuàng)建一個tar文件,并且移除了元數據和不必要的層,將多個層整合成了一個層,只保存了當前統(tǒng)一視角看到的內容(譯者注:expoxt后的容器再import到docker中,通過docker images –tree命令只能看到一個鏡像;而save后的鏡像則不同,它能夠看到這個鏡像的歷史鏡像)。
19、docker history [ image-id ]
docker history 命令遞歸地輸出指定鏡像的歷史鏡像。
七、結論
我希望你們能喜歡這篇文章。還有其他許多的命令(pull,search,restart,attach等)我沒有提及,但是我相信通過閱讀這篇文章,大部分的docker命令都能夠被很好理解。我僅僅學習了docker兩個星期,因此,如果我有什么地方說的不好,歡迎大家指出。
?
(SAW:Game Over!)
總結
以上是生活随笔為你收集整理的Docker / 深入理解的容器和镜像的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenLDAP / ubuntu 18
- 下一篇: 密码学 / PKI 体系概述