❶ Docker鏡像存儲格式分析
新版本的docker鏡像存儲其實是很繞的,各種ID和目錄定義較多,不是很直觀,本文較詳細的分析一下鏡像本地存儲和在registry存儲的格式。測試用的docker版本是20.10.9,存儲引擎overlay2。
為了方便分析鏡像層級結構,我們基於ubuntu加兩個文件,使用 docker build -t myubuntu . 創建一個新鏡像myubuntu。
鏡像元數據存儲在 /var/lib/docker/image/overlay2 :
imagedb目錄存儲的是鏡像元數據。
鏡像ID是從Image Json文件得到的,即 sha256sum(ImageJson)。
Layer DiffID是沒有壓縮的對應層的tar文件的sha256sum值。當然,打包和截包文件的適合得保證是可以可重復操作的,不然會導致Layer的DiffID出錯(可以使用tar-split保存tar headers)。比如上面例子中ubuntu鏡像只有一層,diff_ids列表只有 (history裡面的CMD不佔磁碟空間) "sha256:"。每一層的tar文件我們可以通過 docker save ubuntu -o ubuntu.tar 得到,解壓ubuntu.tar後可以得到對應的層級的打包後的layer.tar文件,如下可以驗證DiffID的值計算原理。
ChainID用於標識鏡像層級棧,它的值由DiffID計算得來。ChainID對應的layer目錄是 /var/lib/docker/image/overlay2/layerdb/sha256 ,這下面的目錄就是ChainID,其中內容存儲了鏡像的層級棧關系。比如這一層的parent是什麼,以及對應的鏡像數據存儲目錄的cache_id。本身layerdb只是存儲layer的元數據信息,並不存儲實際鏡像數據。
即最底層的ChainID跟DiffID一樣,而其他層的ChainID則是通過計算從最底層到這層的Digest得到。
查看Image Json文件可以看到myubuntu相比ubuntu的diff_ids加了兩層,imagedb目錄下面也多了兩個鏡像元數據信息文件,對應新增加的兩層鏡像。
除了之前的350f...對應ubuntu,其中7c7e是one.txt那層,而a58f則是two.txt那層。
我們可以看下ChainID目錄下的內容,可以看到除了ubuntu的基礎層,其他層都有一個文件parent,值就是父層的diff_id,diff是diff_id值,cache-id則是鏡像實際存儲目錄,位於 /var/lib/docker/overlay2/{cache-id} ,size是這一層實際增加文件的大小,tar-split.json.gz是打包這層鏡像的配置(參考 https://github.com/vbatts/tar-split )。
CacheID是一個uuid值,每次都不一樣。它對應的目錄 /var/lib/docker/overlay2/${CacheID} ,該目錄存儲了鏡像每層文件和下一層的鏈接等。驗證一下:
其中diff目錄下面便是這一層的文件。link是對應的diff目錄的短鏈接,lower則是下一層diff目錄的短鏈接。
在layerdb目錄的size文件可以看到每一層的鏡像大小,但是加起來跟 docker images 顯示的大小會有點差距,比如myubuntu鏡像每一層計算加起來是 4 + 65593591 + 4 = 65593599 / 1024 / 1024 = 62.55MiB ,實際顯示是 65.6MB,這是因為 docker images 裡面計算是按 65593599/1000/1000=65.59 計算的。
另外,因為鏡像每層記錄的是相對前一層的文件變化,即便刪除了文件和軟體包,新鏡像大小也不會變小。除非使用 docker export 重新導出一個新鏡像。
這個目錄存儲的是layer diffid和digest的關系,其中digest是鏡像倉庫裡面的目錄ID。
其中 v2metadata-by-diff存儲了layer diffid對應在鏡像倉庫的信息,包括digest,sourcerepository等。
diffid-by-digest則是反過來的,文件名是倉庫裡面的layer digest,內容是layer diffid。因為我們新加的鏡像myubuntu並沒有push到倉庫,所以這個目錄下面沒有信息。
我們創建一個本地的registry,然後對myubuntu另外打個tag, docker tag myubuntu 127.0.0.1:5000/myubuntu ,則respositories.json會多一條新的記錄。此時,distribution目錄還沒有變化。
當我們執行 docker push 127.0.0.1:5000/myubuntu ,則會發現distribution的兩個目錄分別多了兩條記錄,對應的是myubuntu的 7c7e... 和 a58f... 兩個diffid。此時repository.json裡面 127.0.0.1:5000/myubuntu 會多一條帶digest的記錄,這就是鏡像倉庫裡面對應的digest。其中push時顯示的 digest:sha256:bb3c... 對應的是registry的manifest文件的digest,下一節分析。
registry中存儲的鏡像文件是經過gzip壓縮,比如myubuntu本地大小是65.6MB,推到registry壓縮後大小約27MB,壓縮比還是不錯的。registry存儲目錄如下,主要分為blobs和repositories兩個目錄。
repositories的一級子目錄是鏡像名,這里是myubuntu。下面對應三個子目錄 _manifests,_layers, _uploads。
blobs存儲的內容除了各鏡像layer的壓縮後的文件,還包括Manifest Json和Image Json文件(未壓縮)。
選一個layer的壓縮文件data解壓看一下內容:
另外需要注意的是, docker pull 時前面顯示的值是對應層在registry的壓縮文件的digest值,並不是layer diffid,size也是registry存儲的壓縮文件大小。
❷ Docker的主要作用是什麼
目前來看,Docker至少有以下應用場景:
1)測試:Docker 很適合用於測試發布,將 Docker 封裝後可以直接提供給測試人員進行運行,不再需要測試人員與運維、開發進行配合,進行環境搭建與部署。
2)測試數據分離:在測試中,經常由於測試場景變換,需要修改依賴的資料庫數據或者清空變動 memcache、Redis 中的緩存數據。Docker 相較於傳統的虛擬機,更輕量與方便。可以很容易的將這些數據分離到不同的鏡像中,根據不同需要隨時進行切換。
3)開發:開發人員共同使用同一個 Docker 鏡像,同時修改的源代碼都被掛載到本地磁碟。不再因為環境的不同而造成的不同程序行為而傷透腦筋,同時新人到崗時也能迅速建立開發、編譯環境。
4)PaaS 雲服務:Docker 可以支持命令行封裝與編程,通過自動載入與服務自發現,可以很方便的將封裝於 Docker 鏡像中的服務擴展成雲服務。類似像 Doc 轉換預覽這樣的服務封裝於鏡像中,根據業務請求的情況隨時增加和減少容器的運行數量,隨需應變。
具體到Docker技術在測試領域的應用,可以體現在:
1)快速搭建兼容性測試環境
從Docker的鏡像與容器技術特點可以預見,當被測應用要求在各類Web伺服器、中間件、資料庫的組合環境中得到充分驗證時,可以快速地利用基礎Docker鏡像創建各類容器,裝載相應的技術組件並快速啟動運行,測試人員省去了大量花在測試環境搭建上的時間。
2)快速搭建復雜分布式測試環境
Docker的輕量虛擬化特點決定了它可以在一台機器上(甚至是測試人員的一台筆記本電腦上)輕松搭建出成百上千個分布式節點的容器環境,從而模擬以前需要耗費大量時間和機器資源才能搭建出來的分布式復雜測試環境。
3)持續集成
Docker可以快速創建和撤銷容器,在持續集成的環境中,可以頻繁和快速地進行部署和驗證工作。
❸ docker的容器常規操作
解釋:創建一個容器,但容器處於停止狀態
解釋:啟動容器
解釋:
-t : 讓Docker分配一個偽終端並綁定到容器的標准輸入上
-i: 讓容器的標准輸入保持打開
-d: 讓Docker容器在後台以守護態運行
解釋:
-details:列印詳細信息
-f,-follow:保持持續輸出
-since string:輸出從某個時間開始的日誌
-tail string:輸出最近的若干日誌
-t,-timestamps:輸出時間戳信息
-until string:輸出某個時間之前的日誌
解釋:暫停容器,暫停後,容器處於paused狀態
解釋:將paused狀態的容器恢復至運行狀態
解釋: 終止一個運行狀態的容器,但是這個終止不是立即執行的。該命令會先向容器發送一個SIGTERM信號,等待一段超時時間(默認為10s)後,再發送SIGKILL信號終止容器,而終止容器的關鍵就在於SIGKILL信號。
解釋: 終止一個運行狀態的容器,但是這個終止是立即執行的。該命令直接發送SIGKILL信號強行終止容器。
解釋: 重啟容器
解釋:進入容器。使用這個命令有時候並不方便,當多個窗口同時attach到同一個容器的時候,所有窗口都會同步顯示;當某個窗口因命令阻塞時,其他窗口也無法執行操作;默認使用CTRL+q或者CTRL+p退出容器
解釋:
-d,--detach:在容器中後台執行命令
--detach-keys="":指定將容器切回後台的案件
-e,--env=[]:指定環境變數列表
-i,--interactive=true|false:打開標准輸入接收用戶輸入命令,默認值為false
--privileged=true|false:是否給執行命令以高許可權,默認值為false
-t,--try=true|false:分配偽終端,默認值為false
-u,--user="":執行命令的用戶名或ID
解釋:
刪除處於退出或者終止狀態的容器
-f,--force=false:是否強行終止並刪除一個處於運行狀態的容器
-l,--link=false:刪除容器的鏈接,但保留容器
-v,--volumns=false:刪除容器掛載的數據卷
解釋:導出容器。導出一個已經創建的容器到文件,不管此時這個容器是否處於運行狀態。在這里如果把export換成save,再把af18換成鏡像名稱:tag,就是導出鏡像的命令了。但是這兩個命令的區別在於:docker export命令丟棄了原容器的歷史信息及元數據,而docker save命令會保留原文件的歷史信息。
解釋:將導出的文件變成鏡像。當然,這個也可以用docker load命令來執行。
解釋:查看容器具體信息,比如:容器ID,創建時間,路徑,狀態,鏡像,配置等
解釋: 查看容器內進程信息
解釋:查看容器的內存、CPU、存儲、網路等使用情況
-a,-all:輸出所有容器統計信息
-format string: 格式化輸出信息
-no-stream:不持續輸出,默認會自動更新持續實時結果
-no-trunc:不截斷輸出信息
解釋:第一行命令為將test容器的temp文件夾復制到主機的data文件夾下
第二行的命令為將主機的data文件夾復制到test容器的tmp文件夾下
解釋:查看容器內的數據修改
解釋:查看test容器的埠映射情況
解釋:修改容器的一些運行時配置。主要是一些資源限制配額。
❹ Dock基本原理
這篇文章希望能夠幫助讀者深入理解Docker的命令,還有容器(container)和鏡像(image)之間的區別,並深入探討容器和運行中的容器之間的區別。
當我對Docker技術還是一知半解的時候,我發現理解Docker的命令非常困難。於是,我花了幾周的時間來學習Docker的工作原理,更確 切地說,是關於Docker統一文件系統(the union file system)的知識,然後回過頭來再看Docker的命令,一切變得順理成章,簡單極了。
題外話:就我個人而言,掌握一門技術並合理使用它的最好辦法就是深入理解這項技術背後的工作原理。通常情況 下,一項新技術的誕生常常會伴隨著媒體的大肆宣傳和炒作,這使得用戶很難看清技術的本質。更確切地說,新技術總是會發明一些新的術語或者隱喻詞來幫助宣 傳,這在初期是非常有幫助的,但是這給技術的原理蒙上了一層砂紙,不利於用戶在後期掌握技術的真諦。
Git就是一個很好的例子。我之前不能夠很好的使用Git,於是我花了一段時間去學習Git的原理,直到這時,我才真正明白了Git的用法。我堅信只有真正理解Git內部原理的人才能夠掌握這個工具。
鏡像(Image)就是一堆只讀層(read-only layer)的統一視角,也許這個定義有些難以理解,下面的這張圖能夠幫助讀者理解鏡像的定義。
從左邊我們看到了多個只讀層,它們重疊在一起。除了最下面一層,其它層都會有一個指針指向下一層。這些層是Docker內部的實現細節,並且能夠 在主機(譯者註:運行Docker的機器)的文件系統上訪問到。統一文件系統(union file system)技術能夠將不同的層整合成一個文件系統,為這些層提供了一個統一的視角,這樣就隱藏了多層的存在,在用戶的角度看來,只存在一個文件系統。 我們可以在圖片的右邊看到這個視角的形式。
你可以在你的主機文件系統上找到有關這些層的文件。需要注意的是,在一個運行中的容器內部,這些層是不可見的。在我的主機上,我發現它們存在於/var/lib/docker/aufs目錄下。
<pre class="prettyprint" style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; color: rgb(57, 57, 57); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); text-decoration-style: initial; text-decoration-color: initial;">/var/lib/docker/
├── aufs
├── containers
├── graph
├── init
├── linkgraph.db
├── repositories-aufs
├── tmp
├── trust
└── volumes
7 directories, 2 files</pre>
容器(container)的定義和鏡像(image)幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
細心的讀者可能會發現,容器的定義並沒有提及容器是否在運行,沒錯,這是故意的。正是這個發現幫助我理解了很多困惑。
要點:容器 = 鏡像 + 可讀層。並且容器的定義並沒有提及是否要運行容器。
接下來,我們將會討論運行態容器。
一個運行態容器(running container)被定義為一個可讀寫的統一文件系統加上隔離的進程空間和包含其中的進程。下面這張圖片展示了一個運行中的容器。
正是文件系統隔離技術使得Docker成為了一個前途無量的技術。一個容器中的進程可能會對文件進行修改、刪除、創建,這些改變都將作用於可讀寫層(read-write layer)。下面這張圖展示了這個行為。
我們可以通過運行以下命令來驗證我們上面所說的:
<pre class="prettyprint" style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; color: rgb(57, 57, 57); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); text-decoration-style: initial; text-decoration-color: initial;">docker run ubuntu touch happiness.txt</pre>
即便是這個ubuntu容器不再運行,我們依舊能夠在主機的文件系統上找到這個新文件。
<pre class="prettyprint" style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; color: rgb(57, 57, 57); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); text-decoration-style: initial; text-decoration-color: initial;">/var/lib/docker/aufs/diff/860a7b...889/happiness.txt</pre>
為了將零星的數據整合起來,我們提出了鏡像層(image layer)這個概念。下面的這張圖描述了一個鏡像層,通過圖片我們能夠發現一個層並不僅僅包含文件系統的改變,它還能包含了其他重要信息。
元數據(metadata)就是關於這個層的額外信息,它不僅能夠讓Docker獲取運行和構建時的信息,還包括父層的層次信息。需要注意,只讀層和讀寫層都包含元數據。
除此之外,每一層都包括了一個指向父層的指針。如果一個層沒有這個指針,說明它處於最底層。
Metadata Location:
我發現在我自己的主機上,鏡像層(image layer)的元數據被保存在名為」json」的文件中,比如說:
<pre class="prettyprint" style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; color: rgb(57, 57, 57); font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(250, 247, 239); text-decoration-style: initial; text-decoration-color: initial;">/var/lib/docker/graph/e809f156dc985.../json</pre>
e809f156dc985...就是這層的id
一個容器的元數據好像是被分成了很多文件,但或多或少能夠在/var/lib/docker/containers/<id>目錄下找到,<id>就是一個可讀層的id。這個目錄下的文件大多是運行時的數據,比如說網路,日誌等等。
現在,讓我們結合上面提到的實現細節來理解Docker的命令。
docker create 命令為指定的鏡像(image)添加了一個可讀寫層,構成了一個新的容器。注意,這個容器並沒有運行。
Docker start命令為容器文件系統創建了一個進程隔離空間。注意,每一個容器只能夠有一個進程隔離空間。
看到這個命令,讀者通常會有一個疑問:docker start 和 docker run命令有什麼區別。
從圖片可以看出,docker run 命令先是利用鏡像創建了一個容器,然後運行這個容器。這個命令非常的方便,並且隱藏了兩個命令的細節,但從另一方面來看,這容易讓用戶產生誤解。
題外話:繼續我們之前有關於Git的話題,我認為docker run命令類似於git pull命令。git pull命令就是git fetch 和 git merge兩個命令的組合,同樣的,docker run就是docker create和docker start兩個命令的組合。
docker ps 命令會列出所有運行中的容器。這隱藏了非運行態容器的存在,如果想要找出這些容器,我們需要使用下面這個命令。
docker ps –a命令會列出所有的容器,不管是運行的,還是停止的。
docker images命令會列出了所有頂層(top-level)鏡像。實際上,在這里我們沒有辦法區分一個鏡像和一個只讀層,所以我們提出了top-level 鏡像。只有創建容器時使用的鏡像或者是直接pull下來的鏡像能被稱為頂層(top-level)鏡像,並且每一個頂層鏡像下面都隱藏了多個鏡像層。
docker images –a命令列出了所有的鏡像,也可以說是列出了所有的可讀層。如果你想要查看某一個image-id下的所有層,可以使用docker history來查看。
docker stop命令會向運行中的容器發送一個SIGTERM的信號,然後停止所有的進程。
docker kill 命令向所有運行在容器中的進程發送了一個不友好的SIGKILL信號。
docker stop和docker kill命令會發送UNIX的信號給運行中的進程,docker pause命令則不一樣,它利用了cgroups的特性將運行中的進程空間暫停。具體的內部原理你可以在這里找到: https://www.kernel.org/doc/Doc ... m.txt ,但是這種方式的不足之處在於發送一個SIGTSTP信號對於進程來說不夠簡單易懂,以至於不能夠讓所有進程暫停。
docker rm命令會移除構成容器的可讀寫層。注意,這個命令只能對非運行態容器執行。
docker rmi 命令會移除構成鏡像的一個只讀層。你只能夠使用docker rmi來移除最頂層(top level layer)(也可以說是鏡像),你也可以使用-f參數來強制刪除中間的只讀層。
docker commit命令將容器的可讀寫層轉換為一個只讀層,這樣就把一個容器轉換成了不可變的鏡像。
docker build命令非常有趣,它會反復的執行多個命令。
我們從上圖可以看到,build命令根據Dockerfile文件中的FROM指令獲取到鏡像,然後重復地1)run(create和start)、2)修改、3)commit。在循環中的每一步都會生成一個新的層,因此許多新的層會被創建。
docker exec 命令會在運行中的容器執行一個新進程。
docker inspect命令會提取出容器或者鏡像最頂層的元數據。
docker save命令會創建一個鏡像的壓縮文件,這個文件能夠在另外一個主機的Docker上使用。和export命令不同,這個命令為每一個層都保存了它們的元數據。這個命令只能對鏡像生效。
docker export命令創建一個tar文件,並且移除了元數據和不必要的層,將多個層整合成了一個層,只保存了當前統一視角看到的內容(譯者註:expoxt後 的容器再import到Docker中,通過docker images –tree命令只能看到一個鏡像;而save後的鏡像則不同,它能夠看到這個鏡像的歷史鏡像)。
docker history命令遞歸地輸出指定鏡像的歷史鏡像。
❺ Docker(5)——數據管理
docker 容器的文件系統在宿主機上存在的方式很復雜,這會帶來下面幾個問題:
為了能夠 保存(持久化) 數據以及 共享 容器間的數據,docker 引入了數據卷(volume) 機制。數據卷是存在於一個或多個容器中的特定文件或文件夾,它可以繞過默認的聯合文件系統,以正常的文件或者目錄的形式存在於宿主機上。
其生存周期獨立於容器的生存周期。
容器中主要有 兩種 管理數據方式: 數據卷(Data Volumes) , 數據卷容器(Data Volume Containers) 。
數據卷是一個可供容器使用的特殊目錄,它繞過文件系統,可以提供很多有用的特性:
數據卷的使用類似 linux 下對目錄或文件進行 mount 操作,目前Docker提供了 三種 不同的方式將數據從宿主機掛載到容器中,分別是
其中 volume 、 bind mount 比較常用, tmpfs mount 基本不會用.
volumes作為Docker管理宿主機文件系統的一部分 ,默認位於 /var/lib/docker/volumes 目錄中,不是宿主機已有數據,而是新建的。
docker 專門提供了 volume 子命令來操作數據卷:
先創建一個名稱為 hello 的數據卷並通過 ls 命令進行查看:
然後可以使用 inspect 命令看看數據卷hello的詳細信息
該數據卷使用的 Driver 為默認的 local ,表示數據卷使用宿主機的本地存儲;
Mountpoint 是 volumes 的掛載點,默認是本機 /var/lib/docker/volumes 下的一個目錄。
所有Container的數據都保存在了這個目錄下邊,由於沒有在創建時指定卷,所以Docker幫我們默認創建許多匿名卷。
使用 -v 選項也可以指定掛載一個本地的已有目錄到容器中去作為數據卷:
docker run -it –-name robot1 -v /var/data:/opt/mydata ros/kinetic /bin/bash
上面的命令掛載主機的 /var/data 目錄到容器的 /opt/mydata 目錄。
這個功能在測試的時候特別方便,比如用戶可以放置一些程序或數據到本地目錄中,然後在容器中使用。另外,本地目錄的路徑必須是絕對路徑,如果目錄不存在,Docker 會自動創建。
Docker 掛載數據卷的默認許可權是可讀寫 rw ,用戶也可以通過 ro 標記指定為只讀:
docker run -it –-name robot1 -v /var/data:/opt/mydata:ro ros/kinetic /bin/bash
加了 :ro 之後,容器內掛載的數據卷內的數據就變成只讀的了。
除了把數據卷中的數據存儲在宿主機,docker 還允許我們通過指定 volume driver 的方式把數據卷中的數據存儲在其它的地方,比如 Azrue Storge 或 AWS 。
通過 vieux/sshfs 驅動把數據卷的存儲在雲主機上,docker 默認是不安裝 vieux/sshfs 插件的,我們可以通過下面的命令進行安裝:
docker plugin install --grant-all-permissions vieux/sshfs
然後通過 vieux/sshfs 驅動創建數據卷,並指定遠程主機的登錄用戶名、密碼和數據存放目錄:
注意:確保指定的遠程主機上的掛載點 /home/nick/sshvolume 目錄是存在的,否則在啟動容器時會報錯。
最後在啟動容器時指定掛載這個數據卷:
在容器中 /world 目錄下操作的文件都存儲在遠程主機的 /home/nick/sshvolume 目錄中。進入容器 testcon 然後在 /world 目錄中創建一個文件,然後打開遠程主機的 /home/nick/sshvolume 目錄進行查看,新建的文件會出現在那裡。
當使用 bind mounts 將主機上的目錄掛載到容器中時,目錄由其在主機上的完整或相對路徑引用。
bind mount 和 volume 其實都是利用宿主機的文件系統,不同之處在於volume是docker自身管理的目錄中的子目錄,所以不存在許可權引發的掛載的問題,並且目錄路徑是docker自身管理的,所以也不需要在不同的伺服器上指定不同的路徑,不需要關心路徑
bind mounts 可以掛載在宿主機系統的任意位置 ,但 bind mount 在不同的宿主機系統是不可移植的,比如Windows和Linux的目錄結構是不一樣的, bind mount 所指向的 host 目錄也不能一樣。這也是為什麼 bind mount 不能出現在Dockerfile中的原因,因為這樣Dockerfile就不可移植了。
如果使用 Bind mounts 掛載 宿主機目錄 到 容器內非空目錄 ,那麼 此容器中的非空目錄中的文件會被隱藏 ,容器訪問這個目錄時能夠訪問到的文件均來自於宿主機目錄。這也是Bind mounts模式和Volumes模式最大的行為上的不同。
掛載存儲在宿主機系統的內存中,而不會寫入宿主機的文件系統;
這張圖說明 bind mount 和 volume 其實都是利用宿主機的文件系統, Bind mounts 模式是將宿主機上的任意文件或文件夾掛載到容器,而 Volumes 本質上是將Docker服務管理的一塊區域(默認是 /var/lib/docker/volumes 下的文件夾)掛載到容器。所以 volume 不存在許可權引發的掛載的問題,並且目錄路徑是docker自身管理的,所以也不需要在不同的伺服器上指定不同的路徑,不需要關心路徑。
相對於 bind mount , volume 是Docker Engine在自己的「地盤」分配了一個路徑作為掛載點,自己地盤的許可權肯定是安排的明明白白。所以,以上掛載宿主機路徑的問題都解決了。
在使用 volume 作為數據卷掛載到容器時,直接用 volume 名稱代替宿主機路徑名就行:
docker run -d -v test_vol:/var/data some_image
這樣就將數據卷 test_vol 掛載到了容器內的 /var/data 目錄下。
命名的容器掛載數據卷,其它容器通過掛載這個(父容器)實現數據共享,掛載數據卷的容器,稱之為數據卷容器
可以利用數據卷容器對其中的數據卷進行備份、恢復,以實現數據的遷移。
備份
使用下面的命令來備份 mydata 數據卷容器內的數據卷:
sudo docker run --volumes-from mydata -v $(pwd):/backup –-name worker ubuntu tar cvf /backup/backup.tar /data
這個命令首先利用 Ubuntu 鏡像創建了一個容器 worker。又使用 --volumes-from mydata 參數來讓 worker 容器掛載 mydata 容器的數據卷。接下來使用 -v $(pwd):/backup 參數來掛載本地的當前目錄到 worker 容器的 /backup 目錄。
在 worker 容器啟動後,使用了 tar cvf /backup/backup.tar /data 命令來將 /data 下內容備份為容器內的 /backup/backup.tar,即宿主主機的當前目錄下的backup.tar。
恢復
如果要恢復數據到一個容器,可以按照下面的操作。首先創建一個帶有數據卷的容器 mydata2:
sudo docker run -v /data –-name mydata2 ubuntu /bin/bash
然後創建另一個新的容器,掛載 mydata2 的數據卷,並使用 tar 解壓縮備份文件到所掛載的容器卷中:
sudo docker run --volumes-from mydata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar
如果用戶需要在容器之間共享一些持續更新的數據,最簡單的方式是使用數據卷容器。數據卷容器其實就是一個普通的容器,專門用它提供數據卷供其他容器掛載。下面簡單介紹其使用方法。
首先要創建一個數據卷容器 mydata,並在其中創建一個數據卷掛載到 /data 目錄。
sudo docker run -it -v /data –-name mydata ubuntu
然後在其他容器中使用 --volumes-from 來掛載 mydata 容器中的數據卷。例如創建兩個容器 mycon1 和 mycon2,並從 mydata 容器掛載數據卷:
sudo docker run -it --volumes-from mydata –-name mycon1 ubuntu
sudo docker run -it --volumes-from mydata –-name mycon2 ubuntu
(注意,命令中沒有指定數據卷的信息,也就是說新容器中掛載數據卷的目錄和源容器中是一樣的。)
此時容器 mycon1 和 mycon2 都掛載同一個數據卷到相同的目錄 /data。三個容器任何一個在該目錄下寫入數據其他容器都能看到。
可以多次使用 --volumes-from 參數來從多個容器掛載多個數據卷。還可以從其他已經掛載了容器的容器來掛載數據卷。並且使用 --volumes-from 參數所掛載數據卷的容器自身並不需要保持在運行狀態。
但刪除掛載了數據卷的容器時,數據卷並不會被自動刪除。如果要刪除一個數據卷,必須在刪除最後一個還掛載著它的容器時顯式的使用 docker rm -v 命令來指定同時刪除關聯的容器。
如何對已經運行的容器掛載目錄?
https://blog.51cto.com/hjun169/2440799
手動修改mount掛載的json文件,狂神的那個視頻裡面也有寫。
深刻理解Docker鏡像大小
https://blog.csdn.net/shlazww/article/details/47375009
理解docker的分層鏡像實現 base 鏡像共享(DockerFile)
https://blog.csdn.net/lu_1110/article/details/106533490
❻ Docker存儲驅動之--overlay2
docker支持多種graphDriver,包括vfs、devicemapper、overlay、overlay2、aufs等等,其中最常用的就是aufs了,但隨著linux內核3.18把overlay納入其中,overlay的地位變得更重,最近也在自己的虛擬機上用overlay2作為docker存儲驅動實驗了一番,下面來做一個簡單的筆記和總結。
docker默認的存儲目錄是 /var/lib/docker ,下面我們簡單列印一下這個目錄:
在這里,我們只關心 image 和 overlay2 就足夠了。
做這個實驗之前,我們應該先啟動一個容器,在這里使用nginx作為實驗:
可以看到新啟動的nginx容器的id是 86b5733e54c7 ,我們繼續往下看。
上面說了,我們只需要關心 /var/lib/docker/image 和 /var/lib/docker/overlay2 ,可以先到 /var/lib/docker/image 列印一下:
我們只能看到overlay2這個目錄,想必聰明的你也猜到了,docker會在 /var/lib/docker/image 目錄下按每個存儲驅動的名字創建一個目錄,如這里的 overlay2 。
接下來,使用 tree 命令瀏覽一下這個目錄:
這里的關鍵地方是 imagedb 和 layerdb 目錄,看這個目錄名字,很明顯就是專門用來存儲元數據的地方,那為什麼區分image和layer呢?因為在docker中,image是由多個layer組合而成的,換句話就是layer是一個共享的層,可能有多個image會指向某個layer。
那如何才能確認image包含了哪些layer呢?答案就在 imagedb 這個目錄中去找。比如上面啟動的nginx容器,我們可以先找到這個容器對應的鏡像:
可以看到,imageID是 2bcb04bdb83f ,再次記住這個id,我們列印 /var/lib/docker/image/overlay2/imagedb/content/sha256 這個目錄:
第一行的 正是記錄我們 nginx 鏡像元數據的文件,接下來cat一下這個文件,得到一個長長的json:
由於篇幅原因,我只展示最關鍵的一部分,也就是 rootfs 。可以看到rootfs的diff_ids是一個包含了3個元素的數組,其實這3個元素正是組成 nginx 鏡像的3個layerID,從上往下看,就是底層到頂層,也就是說 5dacd731af1b0386ead06c8b... 是image的最底層。既然得到了組成這個image的所有layerID,那麼我們就可以帶著這些layerID去尋找對應的layer了。
接下來,我們返回到上一層的 layerdb 中,先列印一下這個目錄:
在這里我們只管 mounts 和 sha256 兩個目錄,再列印一下 sha256 目錄:
在這里,我們僅僅發現 5dacd731af1b038.. 這個最底層的layer,那麼剩餘兩個layer為什麼會沒有呢?那是因為docker使用了chainID的方式去保存這些layer,簡單來說就是chainID=sha256sum(H(chainID) diffid),也就是 5dacd731af1b038.. 的上一層的sha256 id是:
這個時候,你能看到 166d13b... 這個layer層的目錄了吧?依次類推,我們就能找出所有的layerID的組合。
但是上面我們也說了, /var/lib/docker/image/overlay2/layerdb 存的只是元數據,那麼真實的rootfs到底存在哪裡呢?其中 cache-id 就是我們關鍵所在了。我們列印一下 /var/lib/docker/image/overlay2/layerdb/sha256//cache-id :
沒錯,這個id就是對應 /var/lib/docker/overlay2/ 。因此,以此類推,更高一層的layer對應的cache-id也能找到對應的rootfs,當這些rootfs的diff目錄通過聯合掛載的方式掛載到某個目錄,就能完整整個容器需要的rootfs了。
❼ 誰可以簡單介紹一下docker到底是干什麼用的
參考sf上好雨科技的回答:
docker主要有2大核心貢獻和對於軟體交付的影響:
2大貢獻:
1、封裝,將運行環境與代碼封裝到一個盒子中
2、鏡像倉庫,將鏡像以類似代碼倉庫的方式分發
軟體交付的影響:作為一個IT界「集裝箱」 它把整個軟體交付的流程和方式都改變了,就相當於 集裝箱 一樣改變了整個航運、空運、陸運的方式,讓生產者產出的產品到最終用戶完全一致,無論中途經過多少過程。有了這個核心的「集裝箱」 整個生態都圍著它打轉。
❽ 如何使用Docker開源倉庫建立代理緩存倉庫
開源Docker倉庫v2 的其中一個最新特性:能夠被用作代理緩存倉庫,以緩存Docker Hub上的鏡像。運行一個緩存倉庫允許你在本地儲存鏡像,減少過多的通過互聯網從Docker Hub拉取鏡像,這個特性對於一些在他們環境中擁有數量龐大的Docker引擎的用戶來說很有用。跟著本篇教程,你可以讓Docker引擎從本地代理緩存倉庫拉取鏡像,而不是讓每個引擎總是從Docker Hub拉取,從而節省時間和帶寬。
你可以這樣開始:
####要求:
- Docker引擎1.8.3
- Docker倉庫v2
- 足夠儲存Docker鏡像的磁碟空間
- TLS證書和密鑰
持久化數據
在這個例子中,我們會假設你會儲存所有持久化數據在本地文件系統的<code>/data</code>路徑下,這個路徑下包含TLS證書和密鑰文件,配置文件和鏡像緩存文件。我們之後會用卷掛載這個目錄進運行倉庫的容器。
保護你的代理緩存倉庫安全
代理緩存倉庫需要一個TLS證書來保證Docker引擎和緩存倉庫之間的連接安全,在這個例子中,我們會放置我們證書文件(<code>domain.crt</code>)和密鑰文件(<code>domain.key</code>)在主機的<code>/data</code>目錄。
更多關於使用TLS加強倉庫安全的信息,請參照 Docker倉庫2.0文檔 。
創建代理緩存倉庫配置文件
下一步你需要創建一個配置文件,來把這個倉庫用作代理緩存。你可以用cat命令把<code>registry:2</code>鏡像中的預設配置文件重定向輸出到一個文件中:
sh
$ docker run -it --rm --entrypoint cat registry:2 \
/etc/docker/registry/config.yml > /data/config.yml
<code>我強烈建議從Docker鏡像中獲得這個默認配置,而不是使用例子中的配置,因為將來這個默認配置可能會有更新。</code>
默認的config.yml例子:
yaml
version: 0.1
log:
fields
service: registry
storage:
cache:
layerinfo: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
修改'http'這節配置上TLS:
yaml
http:
addr: :5000
tls:
certificate: /var/lib/registry/domain.crt
key: /var/lib/registry/domain.key
在配置文件中新加一節'proxy'來開啟緩存:
點擊打開文檔( https://github.com/docker/dist ... or.md )
yaml
proxy:
remoteurl: https://registry-1.docker.io
username: [username]
password: [password]
'username'和'password'這兩個選項是可選的,這是Docker Hub賬號的用戶名和密碼,設置上這兩個選項,會使代理緩存倉庫獲取到這個賬號的同等許可權,也就是說,這個用戶有許可權獲取的鏡像,這個緩存倉庫同樣有許可權獲取。
<code>請確保完全理解設置這個Docker Hub賬號背後意味著什麼,並且確保你鏡像的安全還有嚴格的訪問許可權!如果你不確定,請不要再配置包含用戶名和密碼,那麼你的代理緩存倉庫就只會緩存公共鏡像。</code>
啟動代理緩存倉庫的容器:
sh
$ docker run -d --restart=always -p 5000:5000 --name v2-mirror \
-v /data:/var/lib/registry registry:2 /var/lib/registry/config.yml
以上命令使用一個卷把宿主機上的/data掛載進了容器中,使容器能使用持久儲存鏡像緩存,TLS證書和密鑰,還有自定義的倉庫配置文件。
驗證你的代理緩存倉庫已經啟動並正常運行:
sh
$ curl -I https://mycache.example.com:5000/v2/
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: application/json; charset=utf-8
Docker-Distribution-Api-Version: registry/2.0
Date: Thu, 17 Sep 2015 21:42:02 GMT
配置你的Docker引擎使用代理緩存倉庫
修改Docker守護進程的啟動參數,加上<code>--registry-mirror</code>選項:
sh
--registry-mirror=https://<my-docker-mirror-host>:<port-number>
例如,如果你的緩存倉庫的主機名為mycache.example.com並且倉庫服務埠為5000,你需要加上以下選項到守護進程的參數:
sh
--registry-mirror=https://mycache.example.com:5000
參考 在各種的Linux分發版中配置運行Docker 了解更多信息關於如何添加Docker守護進程參數。
測試你的代理緩存倉庫
從Docker Hub上拉取一個你本地沒有的鏡像。例如,busybox:latest鏡像:
sh
$ docker pull busybox:latest
檢查緩存倉庫中的目錄,驗證busybox鏡像是否被緩存:
sh
$ curl https://mycache.example.com:5000/v2/_catalog
{"repositories":["library/busybox"]}
你也可以驗證latest標簽是否被緩存:
sh
$ curl https://mycache.example.com:5000/v2/library/busybox/tags/list
{"name":"library/busybox","tags":["latest"]}
現在開始當你拉取鏡像時,鏡像將被緩存到你的代理緩存倉庫,之後拉取相同的鏡像時會更快,並且這些鏡像緩存會維護自身,當他們不再被使用時將會自動清除。