近幾年 Docker 風靡技術圈,很多從業人員都或多或少使用過,也瞭解如何經過 Dockerfile 構建鏡像,從遠程鏡像倉庫拉取本身所需鏡像,推送構建好的鏡像至遠程倉庫,根據鏡像運行容器等。這個過程十分簡單,只需執行 docker build、docker pull、docker push、docker run 等操做便可。但你們是否想過鏡像在本地究竟是如何存儲的?容器又是如何根據鏡像啓動的?推送鏡像至遠程鏡像倉庫時,服務器又是如何存儲的呢?下面咱們就來簡單聊一聊。node
Docker 鏡像不是一個單一的文件,而是有多層構成。咱們可經過 Docker images 獲取本地的鏡像列表及對應的元信息, 接着可經過docker history <imageId> 查看某個鏡像各層內容及對應大小,每層對應着 Dockerfile 中的一條指令。Docker 鏡像默認存儲在 /var/lib/docker/<storage-driver>中,可經過 DOCKER_OPTS 或者 docker daemon 運行時指定 --graph= 或 -g 指定。git
Docker 使用存儲驅動來管理鏡像每層內容及可讀寫的容器層,存儲驅動有 DeviceMapper、AUFS、Overlay、Overlay二、btrfs、ZFS 等,不一樣的存儲驅動實現方式有差別,鏡像組織形式可能也稍有不一樣,但都採用棧式存儲,並採用 Copy-on-Write(CoW) 策略。且存儲驅動採用熱插拔架構,可動態調整。那麼,存儲驅動那麼多,該如何選擇合適的呢?大體可從如下幾方面考慮:github
Docker 容器實際上是在鏡像的最上層加了一層讀寫層,一般也稱爲容器層。在運行中的容器裏作的全部改動,如寫新文件、修改已有文件、刪除文件等操做其實都寫到了容器層。容器層刪除了,最上層的讀寫層跟着也刪除了,改動天然也丟失了。若要持久化這些改動,須經過 docker commit <containerId> [repository[:tag]] 將當前容器保存成爲一個新鏡像。若想將數據持久化,或是多個容器間共享數據,需將數據存儲在 Docker Volume 中,並將 Volume 掛載到相應容器中。算法
存儲驅動決定了鏡像及容器在文件系統中的存儲方式及組織形式,下面分別對常見的 AUFS、Overlay 做一簡單介紹。docker
AUFS 是 Debian (Stretch 以前的版本,Stretch默認採用 overlay2) 或 Ubuntu 系統上 Docker 的默認存儲驅動,也是 Docker 全部存儲驅動中最爲成熟的。具備啓動快,內存、存儲使用高效等特色。若是使用的 Linux 內核版本爲 4.0 或更高,且使用的是 Docker CE,可考慮使用overlay2 (比 AUFS 性能更佳)。後端
採用 AUFS 存儲驅動時,有關鏡像和容器的全部層信息都存儲在 /var/lib/docker/aufs/目錄下,下面有三個子目錄:api
讀文件服務器
容器進行讀文件操做有如下三種場景:網絡
修改文件或目錄架構
容器中進行文件的修改一樣存在三種場景:
OverlayFS 是一種相似 AUFS 的現代聯合文件系統,但實現更簡單,性能更優。OverlayFS 嚴格說來是 Linux 內核的一種文件系統,對應的 Docker 存儲驅動爲 overlay 或者 overlay2,overlay2 需 Linux 內核 4.0 及以上,overlay 需內核 3.18 及以上。且目前僅 Docker 社區版支持。條件許可的話,儘可能使用 overlay2,與 overlay 相比,它的 inode 利用率更高。
讀文件
讀文件存在如下三種場景:
修改文件或目錄
寫文件存在如下三種場景:
很多人可能常用 docker,那麼有沒有思考過鏡像推送至遠程鏡像倉庫,是如何保存的呢?Docker 客戶端是如何與遠程鏡像倉庫交互的呢?
咱們平時本地安裝的 docker 其實包含兩部分:docker client 與 docker engine,docker client 與 docker engine 間經過 API 進行通訊。Docker engine 提供的 API 大體有認證、容器、鏡像、網絡、卷、swarm 等,具體調用形式請參考:Docker Engine API。
Docker engine 與 registry (即:遠程鏡像倉庫)的通訊也有一套完整的 API,大體包含 pull、push 鏡像所涉及的認證、受權、鏡像存儲等相關流程,具體請參考:Registry API。目前經常使用 registry 版本爲 v2,registry v2 擁有斷點續傳、併發拉取鏡像多層等特色。能併發拉取多層是由於鏡像的元信息與鏡像層數據分開存儲,當 pull 一個鏡像時,先進行認證獲取到 token 並受權經過,而後獲取鏡像的 manifest 文件,進行 signature 校驗。校驗完成後,依據 manifest 裏的層信息併發拉取各層。其中 manifest 包含的信息有:倉庫名稱、tag、鏡像層 digest 等, 更多,請參考:manifest 格式文檔。
各層拉下來後,也會先在本地進行校驗,校驗算法採用 sha256。Push 過程則先將鏡像各層併發推至 Registry,推送完成後,再將鏡像的 manifest 推至 Registry。Registry 其實並不負責具體的存儲工做,具體存儲介質根據使用方來定,Registry 只是提供一套標準的存儲驅動接口,具體存儲驅動實現由使用方實現。
目前官方 Registry 默認提供的存儲驅動包括:微軟 Azure、Google GCS、Amazon S三、Openstack Swift、本地存儲等。若須要使用本身的對象存儲服務,則須要自行實現 Registry 存儲驅動。網易雲目前將鏡像存儲在本身的對象存儲服務 NOS 上,故專門針對 NOS 實現了一套存儲驅動,另外認證服務也對接了網易雲認證服務,並結合自身業務實現了一套認證、受權邏輯,並有效地限制了倉庫配額。
Registry 乾的事情其實很簡單,大體可分爲:1.讀配置;2.註冊handler;3.監聽。本質上 Registry 是個 HTTP 服務,啓動後,監聽在配置文件設定的某端口上。當 http 請求過來後,便會觸發以前註冊過的 handler。Handler 包含 manifest、tag、blob、blob-upload、blob-upload-chunk、catalog 等六類,具體請可參考 registry 源碼:/registry/handlers/app.go:92。配置文件包含監聽端口、auth 地址、存儲驅動信息、回調通知等。