總結Docker的存儲和網絡相關

一、Docker概述

LXC所實現的隔離性主要是來自kernel的namespace, 其中pid, net, ipc, mnt, uts 等namespace將container的進程, 網絡, 消息, 文件系統和hostname 隔離開。

cgroups 實現了對資源的配額和度量。 cgroups 的使用非常簡單,提供類似文件的接口,在 /cgroup目錄下新建一個文件夾即可新建一個group,在此文件夾中新建task文件,並將pid寫入該文件,即可實現對該進程的資源控制。

二、網絡

(1)網絡模型

  • bridge:網橋網絡

    當Docker進程啓動時,會在主機上創建一個名爲docker0的虛擬網橋,此主機上啓動的Docker容器會連接到這個虛擬網橋上。虛擬網橋的工作方式和物理交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網絡中。

  • host:主機網絡

    如果啓動容器的時候使用host模式,那麼這個容器將不會獲得一個獨立的Network Namespace,而是和宿主機共用一個Network Namespace。容器將不會虛擬出自己的網卡,配置自己的IP等,而是使用宿主機的IP和端口。但是,容器的其他方面,如文件系統、進程列表等還是和宿主機隔離的。

  • none:禁用容器網絡

  • container:容器網絡

    這個模式指定新創建的容器和已經存在的一個容器共享一個 Network Namespace,而不是和宿主機共享。新創建的容器不會創建自己的網卡,配置自己的 IP,而是和一個指定的容器共享 IP、端口範圍等。同樣,兩個容器除了網絡方面,其他的如文件系統、進程列表等還是隔離的。兩個容器的進程可以通過 lo 網卡設備通信。

(2)網絡架構

Docker在1.9版本中(現在都1.17了)引入了一整套docker network子命令和跨主機網絡支持。這允許用戶可以根據他們應用的拓撲結構創建虛擬網絡並將容器接入其所對應的網絡。

其實,早在Docker1.7版本中,網絡部分代碼就已經被抽離並單獨成爲了Docker的網絡庫,即libnetwork。在此之後,容器的網絡模式也被抽像變成了統一接口的驅動。

爲了標準化網絡的驅動開發步驟和支持多種網絡驅動,Docker公司在libnetwork中使用了CNM(Container Network Model)。CNM定義了構建容器虛擬化網絡的模型。同時還提供了可以用於開發多種網絡驅動的標準化接口和組件。

libnetwork和Docker daemon及各個網絡驅動的關係可以通過下面的圖進行形象的表示。

總結Docker的存儲和網絡相關

如上圖所示,Docker daemon通過調用libnetwork對外提供的API完成網絡的創建和管理等功能。libnetwrok中則使用了CNM來完成網絡功能的提供。而CNM中主要有沙盒(sandbox)、端點(endpoint)、網絡(network)這3種組件。

libnetwork中內置的5種驅動則爲libnetwork提供了不同類型的網絡服務。接下來分別對CNM中的3個核心組件和libnetwork5種內置驅動進行介紹。

(3)CNM核心組件

1、沙盒(sandbox)
 一個沙盒也包含了一個容器網絡棧的信息。沙盒可以對容器的接口、路由和DNS設置等進行管理。沙盒的實現可以是Linux netwrok namespace、FreeBSD jail或者類似的機制。一個沙盒可以有多個端點和多個網絡。

2、端點(endpoint)
 一個端點可以加入一個沙盒和一個網絡。端點的實現可以是veth pair、Open vSwitch內部端口或者相似的設備。一個端點只可以屬於一個網絡並且只屬於一個沙盒。

3、網絡(network)
 一個網絡是一組可以直接互相聯通的端點。網絡的實現可以是Linux bridge、VLAN等。一個網絡可以包含多個端點

(4)libnetwork內置驅動

1、bridge驅動
此驅動爲Docker的默認設置驅動,使用這個驅動的時候,libnetwork將創建出來的Docker容器連接到Docker網橋上。作爲最常規的模式,bridge模式已經可以滿足Docker容器最基本的使用需求了。然而其與外界通信使用NAT,增加了通信的複雜性,在複雜場景下使用會有諸多限制。

2、host驅動
使用這種驅動的時候,libnetwork將不爲Docker容器創建網絡協議棧,即不會創建獨立的network namespace。Docker容器中的進程處於宿主機的網絡環境中,相當於Docker容器和宿主機共同用一個network namespace,使用宿主機的網卡、IP和端口等信息。

但是,容器其他方面,如文件系統、進程列表等還是和宿主機隔離的。host模式很好地解決了容器與外界通信的地址轉換問題,可以直接使用宿主機的IP進行通信,不存在虛擬化網絡帶來的額外性能負擔。但是host驅動也降低了容器與容器之間、容器與宿主機之間網絡層面的隔離性,引起網絡資源的競爭與衝突。
因此可以認爲host驅動適用於對於容器集羣規模不大的場景。

3、overlay驅動
此驅動採用IETE標準的VXLAN方式,並且是VXLAN中被普遍認爲最適合大規模的雲計算虛擬化環境的SDN controller模式。在使用過程中,需要一個額外的配置存儲服務,例如Consul、etcd和zookeeper。還需要在啓動Docker daemon的時候額外添加參數來指定所使用的配置存儲服務地址。

4、remote驅動
這個驅動實際上並未做真正的網絡服務實現,而是調用了用戶自行實現的網絡驅動插件,使libnetwork實現了驅動的可插件化,更好地滿足了用戶的多種需求。用戶只需要根據libnetwork提供的協議標準,實現其所要求的各個接口並向Docker daemon進行註冊。

5、null驅動
使用這種驅動的時候,Docker容器擁有自己的network namespace,但是並不爲Docker容器進行任何網絡配置。也就是說,這個Docker容器除了network namespace自帶的loopback網卡名,沒有其他任何網卡、IP、路由等信息,需要用戶爲Docker容器添加網卡、配置IP等。
這種模式如果不進行特定的配置是無法正常使用的,但是優點也非常明顯,它給了用戶最大的自由度來自定義容器的網絡環境。

三、存儲

Docker最開始採用AUFS作爲文件系統,也得益於AUFS分層的概念,實現了多個Container可以共享同一個image。但由於AUFS未併入Linux內核,且只支持Ubuntu,考慮到兼容性問題,在Docker 0.7版本中引入了存儲驅動, 目前,Docker支持AUFS、Btrfs、Device mapper、OverlayFS、ZFS五種存儲驅動。就如Docker官網上說的,沒有單一的驅動適合所有的應用場景,要根據不同的場景選擇合適的存儲驅動,纔能有效的提高Docker的性能。

(1)寫時複製(CoW)

所有驅動都用到的技術——寫時複製(CoW)。CoW就是copy-on-write,表示只在需要寫時纔去複製,這個是針對已有文件的修改場景。比如基於一個image啓動多個Container,如果爲每個Container都去分配一個image一樣的文件系統,那麼將會佔用大量的磁盤空間。而CoW技術可以讓所有的容器共享image的文件系統,所有數據都從image中讀取,只有當要對文件進行寫操作時,才從image裏把要寫的文件複製到自己的文件系統進行修改。所以無論有多少個容器共享同一個image,所做的寫操作都是對從image中複製到自己的文件系統中的複本上進行,並不會修改image的源文件,且多個容器操作同一個文件,會在每個容器的文件系統裏生成一個複本,每個容器修改的都是自己的複本,相互隔離,相互不影響。使用CoW可以有效的提高磁盤的利用率。

(2)用時分配(allocate-on-demand)

而寫時分配是用在原本沒有這個文件的場景,只有在要新寫入一個文件時才分配空間,這樣可以提高存儲資源的利用率。比如啓動一個容器,並不會爲這個容器預分配一些磁盤空間,而是當有新文件寫入時,才按需分配新空間。

(3)AUFS

AUFS(AnotherUnionFS)是一種Union FS,是文件級的存儲驅動。AUFS能透明覆蓋一或多個現有文件系統的層狀文件系統,把多層合併成文件系統的單層表示。簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下的文件系統。這種文件系統可以一層一層地疊加修改文件。無論底下有多少層都是隻讀的,只有最上層的文件系統是可寫的。當需要修改一個文件時,AUFS創建該文件的一個副本,使用CoW將文件從只讀層複製到可寫層進行修改,結果也保存在可寫層。在Docker中,底下的只讀層就是image,可寫層就是Container。

(4)Overlay

Overlay是Linux內核3.18後支持的,也是一種Union FS,和AUFS的多層不同的是Overlay只有兩層:一個upper文件系統和一個lower文件系統,分別代表Docker的鏡像層和容器層。當需要修改一個文件時,使用CoW將文件從只讀的lower複製到可寫的upper進行修改,結果也保存在upper層。在Docker中,底下的只讀層就是image,可寫層就是Container。

(5)Device mapper

Device mapper是Linux內核2.6.9後支持的,提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶可以很方便的根據自己的需要制定實現存儲資源的管理策略。前面講的AUFS和OverlayFS都是文件級存儲,而Device mapper是塊級存儲,所有的操作都是直接對塊進行操作,而不是文件。Device mapper驅動會先在塊設備上創建一個資源池,然後在資源池上創建一個帶有文件系統的基本設備,所有鏡像都是這個基本設備的快照,而容器則是鏡像的快照。所以在容器裏看到文件系統是資源池上基本設備的文件系統的快照,並不有爲容器分配空間。當要寫入一個新文件時,在容器的鏡像內爲其分配新的塊並寫入數據,這個叫用時分配。當要修改已有文件時,再使用CoW爲容器快照分配塊空間,將要修改的數據複製到在容器快照中新的塊裏再進行修改。Device mapper 驅動默認會創建一個100G的文件包含鏡像和容器。每一個容器被限制在10G大小的卷內,可以自己配置調整。

(6)Btrfs

Btrfs被稱爲下一代寫時複製文件系統,併入Linux內核,也是文件級級存儲,但可以像Device mapper一直接操作底層設備。Btrfs把文件系統的一部分配置爲一個完整的子文件系統,稱之爲subvolume 。那麼採用 subvolume,一個大的文件系統可以被劃分爲多個子文件系統,這些子文件系統共享底層的設備空間,在需要磁盤空間時便從底層設備中分配,類似應用程序調用 malloc()分配內存一樣。爲了靈活利用設備空間,Btrfs 將磁盤空間劃分爲多個chunk 。每個chunk可以使用不同的磁盤空間分配策略。比如某些chunk只存放metadata,某些chunk只存放數據。這種模型有很多優點,比如Btrfs支持動態添加設備。用戶在系統中增加新的磁盤之後,可以使用Btrfs的命令將該設備添加到文件系統中。Btrfs把一個大的文件系統當成一個資源池,配置成多個完整的子文件系統,還可以往資源池裏加新的子文件系統,而基礎鏡像則是子文件系統的快照,每個子鏡像和容器都有自己的快照,這些快照則都是subvolume的快照。

當寫入一個新文件時,爲在容器的快照裏爲其分配一個新的數據塊,文件寫在這個空間裏,這個叫用時分配。而當要修改已有文件時,使用CoW複製分配一個新的原始數據和快照,在這個新分配的空間變更數據,變結束再更新相關的數據結構指向新子文件系統和快照,原來的原始數據和快照沒有指針指向,被覆蓋。

(7)ZFS

ZFS 文件系統是一個革命性的全新的文件系統,它從根本上改變了文件系統的管理方式,ZFS 完全拋棄了“卷管理”,不再創建虛擬的卷,而是把所有設備集中到一個存儲池中來進行管理,用“存儲池”的概念來管理物理存儲空間。過去,文件系統都是構建在物理設備之上的。爲了管理這些物理設備,併爲數據提供冗餘,“卷管理”的概念提供了一個單設備的映像。而ZFS創建在虛擬的,被稱爲“zpools”的存儲池之上。每個存儲池由若干虛擬設備(virtual devices,vdevs)組成。這些虛擬設備可以是原始磁盤,也可能是一個RAID1鏡像設備,或是非標準RAID等級的多磁盤組。於是zpool上的文件系統可以使用這些虛擬設備的總存儲容量。

在Docker裏ZFS的使用。首先從zpool裏分配一個ZFS文件系統給鏡像的基礎層,而其他鏡像層則是這個ZFS文件系統快照的克隆,快照是隻讀的,而克隆是可寫的,當容器啓動時則在鏡像的最頂層生成一個可寫層。

當要寫一個新文件時,使用按需分配,一個新的數據快從zpool裏生成,新的數據寫入這個塊,而這個新空間存於容器(ZFS的克隆)裏。
當要修改一個已存在的文件時,使用寫時複製,分配一個新空間並把原始數據複製到新空間完成修改。

(8)性能對比

AUFS和Overlay都是聯合文件系統,但AUFS有多層,而Overlay只有兩層,所以在做寫時複製操作時,如果文件比較大且存在比較低的層,則AUSF可能會慢一些。而且Overlay併入了linux kernel mainline,AUFS沒有,所以可能會比AUFS快。但Overlay還太年輕,要謹慎在生產使用。而AUFS做爲docker的第一個存儲驅動,已經有很長的歷史,比較的穩定,且在大量的生產中實踐過,有較強的社區支持。目前開源的DC/OS指定使用Overlay。

Overlay是文件級存儲,Device mapper是塊級存儲,當文件特別大而修改的內容很小,Overlay不管修改的內容大小都會複製整個文件,對大文件進行修改顯示要比小文件要消耗更多的時間,而塊級無論是大文件還是小文件都只複製需要修改的塊,並不是整個文件,在這種場景下,顯然device mapper要快一些。因爲塊級的是直接訪問邏輯盤,適合IO密集的場景。而對於程序內部複雜,大併發但少IO的場景,Overlay的性能相對要強一些。

Device mapper和Btrfs都是直接對塊操作,都不支持共享存儲,表示當有多個容器讀同一個文件時,需要生活多個複本,所以這種存儲驅動不適合在高密度容器的PaaS平臺上使用。而且在很多容器啓停的情況下可能會導致磁盤溢出,造成主機不能工作。Device mapper不建議在生產使用。Btrfs在docker build可以很高效。
ZFS最初是爲擁有大量內存的Salaris服務器設計的,所在在使用時對內存會有影響,適合內存大的環境。ZFS的COW使碎片化問題更加嚴重,對於順序寫生成的大文件,如果以後隨機的對其中的一部分進行了更改,那麼這個文件在硬盤上的物理地址就變得不再連續,未來的順序讀會變得性能比較差。ZFS支持多個容器共享一個緩存塊,適合PaaS和高密度的用戶場景。

特點 優點 缺點 適用場景
AUFS 聯合文件系統,沒有加入內核開發主線,文件級別的存儲 作爲Docker的第一個存儲歷史悠久,社區支持好 如果存在多層,文件較大且存在的層較低,速度會慢 大併發但是IO少
overlayFS 聯合文件系統,已經併入開發主線,文件級別的存儲 只有兩層 不管如何修改文件,都會複製整個文件,對大文件的修改消耗更多的時間 大併發但是IO少
Device Mapper 已經併入內核開發主線,塊級別的存儲 對於文件的修改只會複製修改的塊 不支持共享存儲,多個容器需要同時讀取相同文件時會出現多個副本,在很多容器啓動停止的時候可能會導致磁盤溢出 適合IO密集的場景
Btrfs 已經併入內核開發主線,文件級別的存儲 可以像Device Mapper那樣操作底層設備,支持動態添加設備 不支持共享存儲,多個容器需要同時讀取相同文件時會出現多個副本 不適合在容器高度密集的PaaS上使用
ZFS 將所有設備集中到一個存儲池來管理 支持多個容器共享一個緩存塊,適合內存大的環境 COW使得碎片化問題更加嚴重,文件在物理磁盤上不再連續,順序讀寫性能變差 適合PaaS和高度容器密集的場景

四、面試題整理

(1)可以在一個容器中同時運行多個應用進程嗎?

一般不推薦在同一個容器內運行多個應用進程,如果有類似需求,可以通過額外的進程管理機制,比如supervisord來管理所運行的進程

(2)很多應用容器都是默認後臺運行的,怎麼查看它們的輸出和日誌信息?

使用docker logs,後面跟容器的名稱或者ID信息

(3)如何臨時退出一個正在交互的容器的終端,而不終止它?

按Ctrl+p,後按Ctrl+q,如果按Ctrl+c會使容器內的應用進程終止,進而會使容器終止。

(4)構建Docker鏡像應該遵循哪些原則?

整體遠側上,儘量保持鏡像功能的明確和內容的精簡,要點包括:

  • 儘量選取滿足需求但較小的基礎系統鏡像,建議選擇debian:wheezy鏡像,僅有86MB大小
  • 清理編譯生成文件、安裝包的緩存等臨時文件
  • 安裝各個軟件時候要指定準確的版本號,並避免引入不需要的依賴
  • 從安全的角度考慮,應用盡量使用系統的庫和依賴
  • 使用Dockerfile創建鏡像時候要添加.dockerignore文件或使用乾淨的工作目錄

(5)倉庫(Repository)、註冊服務器(Registry)、註冊索引(Index)有何關係?

首先,倉庫是存放一組關聯鏡像的集合,比如同一個應用的不同版本的鏡像,註冊服務器是存放實際的鏡像的地方,註冊索引則負責維護用戶的賬號,權限,搜索,標籤等管理。註冊服務器利用註冊索引來實現認證等管理。

(6)Docker的配置文件放在那裏。如何修改配置?

Ubuntu系統下Docker的配置文件是/etc/default/docker,CentOS系統配置文件存放在/etc/sysconfig/docker

(7)如何更改Docker的默認存儲設置?

Docker的默認存放位置是/var/lib/docker,如果希望將Docker的本地文件存儲到其他分區,可以使用Linux軟連接的方式來做。

(8)如何使用Docker構建與環境無關的系統?

  • Volumes
  • 環境變量注入
  • 只讀文件系統

(9)Docker容器有幾種狀態?

  • 運行
  • 已暫停
  • 重新啓動
  • 已退出

(10)什麼類型的應用程序 - 無狀態或有狀態更適合Docker容器?

最好爲Docker Container創建無狀態應用程序。我們可以從應用程序中創建一個容器,並從應用程序中取出可配置的狀態參數。現在我們可以在生產和具有不同參數的QA環境中運行相同的容器。這有助於在不同場景中重用相同的圖像。使用Docker Containers比使用有狀態應用程序更容易擴展無狀態應用程序。

(11)Dockerfile中的命令COPY和ADD命令有什麼區別?

一般而言,雖然ADD並且COPY在功能上類似,但是COPY是優選的。那是因爲它比ADD更透明。COPY僅支持將本地文件基本複製到容器中,而ADD具有一些功能(如僅限本地的tar提取和遠程URL支持),這些功能並不是很明顯。因此,ADD的最佳用途是將本地tar文件自動提取到鏡像中

(12)解釋一下dockerfile的ONBUILD指令?

當鏡像用作另一個鏡像構建的基礎時,ONBUILD指令向鏡像添加將在稍後執行的觸發指令。如果要構建將用作構建其他鏡像的基礎的鏡像(例如,可以使用特定於用戶的配置自定義的應用程序構建環境或守護程序),這將非常有用。

(13)Docker鏡像和層有什麼區別?

  • 鏡像:Docker鏡像是由一系列只讀層構建的
  • 層:每個層代表鏡像Dockerfile中的一條指令

(14)什麼是Docker Swarm

Docker Swarm是Docker的本機羣集。它將Docker主機池轉變爲單個虛擬Docker主機。Docker Swarm提供標準的Docker API,任何已經與Docker守護進程通信的工具都可以使用Swarm透明地擴展到多個主機。

(15)如何在生產中監控Docker?

Docker提供docker stats和docker事件等工具來監控生產中的Docker。我們可以使用這些命令獲取重要統計數據的報告。

  • Docker統計數據:當我們使用容器ID調用docker stats時,我們獲得容器的CPU,內存使用情況等。它類似於Linux中的top命令。
  • Docker事件:Docker事件是一個命令,用於查看Docker守護程序中正在進行的活動流。

一些常見的Docker事件是:attach,commit,die,detach,rename,destroy等。我們還可以使用各種選項來限制或過濾我們感興趣的事件。

(16)爲什麼Docker Compose不會等待容器準備就緒,然後繼續以依賴順序啓動下一個服務?

Docker Compose是 docker 提供的一個命令行工具,用來定義和運行由多個容器組成的應用。使用 compose,我們可以通過 YAML 文件聲明式的定義應用程序的各個服務,並由單個命令完成應用的創建和啓動。

Compose按照依賴順服啓動和停止容器,決定依賴關係語句有 depends_on, links, volumes_from, 和network_mode: "service:...".

但是,對於啓動,Compose不會等到容器“準備好它運行“。這裏有一個很好的理由:

  • 等待數據庫(例如)準備就緒的問題實際上只是分佈式系統更大問題的一個子集。在生產中,您的數據庫可能隨時變得不可用或移動主機。您的應用程序需要能夠適應這些類型的故障。
  • 要處理此問題,請將應用程序設計爲在發生故障後嘗試重新建立與數據庫的連接。如果應用程序重試連接,它最終可以連接到數據庫。
  • 最佳解決方案是在啓動時以及出於任何原因丟失連接時,在應用程序代碼中執行此檢查。

(17)容器與主機之間的數據拷貝命令

docker cp 命令用於容器與主機之間的數據拷貝。
主機到容器:
docker cp /www 96f7f14e99ab:/www/
容器到主機:
docker cp 96f7f14e99ab:/www /tmp/

(18)啓動nginx容器(隨機端口映射),並掛載本地文件目錄到容器html的命令

docker run -d -P --name nginx2 -v /home/nginx:/usr/share/nginx/html nginx

(19)Docker如何在非Linux系統中運行容器

通過添加到Linux內核版本2.6.24的名稱空間功能,可以實現容器的概念。容器將其ID添加到每個進程,並向每個系統調用添加新的訪問控制檢查。它由clone()系統調用訪問,該調用允許創建先前全局命名空間的單獨實例。

如果由於Linux內核中可用的功能而可以使用容器,那麼顯而易見的問題是非Linux系統如何運行容器。Docker for Mac和Windows都使用Linux VM來運行容器。Docker Toolbox用於在Virtual Box VM中運行容器。但是,最新的Docker在Windows中使用Hyper-V,在Mac中使用Hypervisor.framework。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章