FanXing Blog

FanXing Blog

一个热爱编程的高中生,正在努力成为一位优秀的后端工程师~

Day5. Go語言精進之路:Docker

復習一下 Docker 的一些基礎知識,還有對一些概念的理解 (


關於 Docker#

Docker 是一款開源的應用容器引擎,它利用容器化技術,極大的加速了應用的構建、測試和部署。其核心思想是將應用程序及其所有依賴都打包進一個稱為 "容器" 的標準化單位中,這個容器可以在任何支持 Docker 的系統上運行,實現了前所未有的可移植性和環境一致性。

和傳統的虛擬化技術(例如:KVM)相比,Docker 容器更加高效和輕量化,虛擬機需要在每個應用程序中運行一個完整的操作系統,而 Docker 則和宿主機共享系統內核,這顯著的減少了資源開銷和啟動速度。這種架構不僅是提高了資源利用率,同時也為應用提供了更強的隔離性,使得每個容器都獨立於宿主機和其他容器運行。

Docker 不僅僅是一個工具,它引領了一場 “雲原生” 時代的變革,深刻的改變了企業管理應用交付和部署周期的方式。

Docker Engine 核心架構#

Docker Engine 是 Docker 平台的核心,它是一個 客戶端 - 服務端 架構的應用程序,負責容器創建、運行和管理,這種架構使得 Docker 命令的執行和其產生的實際效果通過 API 解耦。

客戶端 - 服務端#

Docker Engine 本質上就是一個開源的容器化技術,用於構建和容器化應用程序。主要包含以下幾部分:

  1. 伺服器 (Server):一個長期運行的守護進程(daemon process),名為 dockerd,這個守護進程負責實際執行構建鏡像、運行容器、管理網絡和存儲等核心任務
  2. REST APIdockerd 通過一個 REST API 功能,暴露其功能接口,這個 API 接口可以用來與 dockerd 交互,API 可以通過 UNIX 套接字網絡接口 訪問
  3. 客戶端 (Client):命令行工具,名為 docker。用戶通過 docker 發出命令,這些命令轉換為對 dockerdREST API 請求,並發送給 dockerd 進行執行。

這種架構使得 Docker 非常靈活,例如:docker 可以在本地運行,但是 dockerd 可以在遠程伺服器上運行,使用網絡進行通訊。此外,由於所有操作都使用 API 進行,這也為自動化工具和腳本提供了便利,無需笨拙的包裝本地 CLI 命令。

主要組件#

Docker 守護進程 (dockerd)#

dockerd 是一個在宿主機上運行的後台服務。它監聽來自 Docker 客戶端(通過 Docker API)的請求,並管理 Docker 對象,如鏡像 (images)、容器 (containers)、網絡 (networks) 和數據卷 (volumes)。它是 Docker 架構的核心元素,負責構建、運行和分發 Docker 容器。dockerd 可以使用符合 OCI(Open Container Initiative)標準的運行時,如 ContainerDCRI-O 來運行容器。

OCI(Open Container Initiative)是一個在 Linux 基金會支持下的開放治理結構(項目),旨在圍繞容器格式和運行時創建開放的行業標準 。它於 2015 年 6 月由 Docker、CoreOS 及其他容器行業領導者共同發起成立 。

ContainerD 是一個業界標準的核心容器運行時,它管理其主機系統上完整的容器生命周期。這包括了鏡像的傳輸和存儲、容器的執行和監管、存儲以及網絡連接等任務。

CRI-O 是一個實現了 Kubernetes 容器運行時接口 (CRI) 的工具,它允許 Kubernetes 使用符合 OCI (Open Container Initiative) 標準的運行時來管理 pod 和容器。

守護進程的配置可以通過 daemon.json 文件進行。它可以通過 Unix 套接字(默認為 /var/run/docker.sock)、TCP 套接字或文件描述符(fd)來監聽 API 請求。

Docker Engine API(REST API)#

  • Docker Endgine API 是一個由 dockerd 提供的 Restful API。Docker 客戶端通過這個 API 與 dockerd 通信,該 API 規範了應用程序可以向守護進程發送指令的接口,從而管理容器的構建、運行與分發等任務。
  • 這個 API 基於 HTTP,支持通過 Unix 套接字TLS 加密的 TCP 連接 進行訪問。對於 HTTPS 加密的套接字,僅支持 TLS 1.0 及更高版本,出於安全原因不支持 SSLv3 及更低版本。

Docker 客戶端 (Docker Client - Docker Cli)#

Docker CLI 是與 Docker 交互的主要方式,通常是一個 CLI 工具,命令以 docker 開頭。用戶通過該 CLI 工具發送命令給 dockerd。一個 Docker CLI 可以和多個 dockerd 進行交互。

核心概念#

Container#

容器是一種標準化的軟件單元,它將應用程序代碼及其所有依賴項打包在一起,確保應用程序在從一個計算環境遷移到另一個計算環境時能夠快速、可靠地運行 。這種自包含的特性確保了在開發者筆記本電腦上正常工作的應用程序,在私有數據中心或公有雲中也能同樣正常工作 。

從本質上講,容器虛擬化了操作系統層面,允許應用程序在隔離的用戶空間中運行,同時共享主機操作系統的內核 。容器不僅僅是簡單的軟件包,它們是按照一種通用方式進行打包的,這種標準化是其廣泛採用的關鍵推動因素 。

容器技術的核心在於其 “操作系統虛擬化” 的理念 ,這與虛擬機 (VM) 所採用的硬件虛擬化形成了鮮明對比 。虛擬機通過 Hypervisor(虛擬機監控器)在物理硬件之上創建完整的虛擬硬件環境,每個虛擬機都擁有自己獨立的操作系統。而容器則直接在宿主機的操作系統內核之上創建隔離的運行環境。

容器化#

容器化是一種軟件部署過程,它將應用程序的代碼及其運行所需的所有文件、庫、配置和二進制文件打包成一個單一的可執行鏡像 。這個鏡像隨後作為容器運行。該過程隔離了應用程序,使其僅與主機共享操作系統內核,從而允許單個軟件包在多種設備或操作系統上運行 。

容器化的典型生命周期通常包括三個階段 :

  1. 開發 (Develop): 開發者在提交源代碼時,將應用程序的依賴項定義在一個容器鏡像文件中。該文件通常與源代碼一同存儲。

  2. 構建 (Build): 鏡像被發布到一個容器倉庫 (Container Repository),在那裡進行版本控制並標記為不可變。這個階段實質上創建了容器。

  3. 部署 (Deploy): 將容器化的應用程序在本地、持續集成 / 持續交付 (CI/CD) 管道中,或在生產環境中運行。

容器化技術有效解決了傳統軟件開發中因操作系統、庫版本或配置差異導致應用程序在不同環境中行為不一致的問題,即所謂的 “在我機器上能跑” 的難題 。

容器鏡像的 “不可變性” 是實現部署一致性和可靠性的基石。這意味著一旦鏡像構建完成,它就不會再被修改。所有從該鏡像衍生的容器實例都將是完全相同的 。這種特性促使運維實踐從修補實時系統轉向用新的、更新的鏡像替換舊系統 。這種不可變性確保了應用程序的每個版本實例都完全相同,消除了配置漂移,使得部署高度可預測和可重複。這反過來又簡化了回滾和調試過程。

容器技術的核心工作原理#

操作系統級虛擬化原理#

容器技術依賴於操作系統級虛擬化,這是一種輕量級的虛擬化形式,其核心思想是操作系統內核允許多個隔離的用戶空間實例存在 。這些隔離的實例即為容器。與虛擬機所採用的硬件虛擬化不同 —— 硬件虛擬化需要 Hypervisor 為每個客戶操作系統模擬硬件 —— 操作系統級虛擬化直接在宿主操作系統的內核中構建抽象層 。

所有容器共享同一個宿主操作系統的內核。這意味著在容器內部運行的操作系統用戶空間(例如,特定的 Linux 發行版)必須與宿主機的內核兼容(例如,Linux 容器運行在 Linux 宿主機上)。儘管如此,每個容器都擁有自己獨立的根文件系統,並且可以在用戶空間運行不同的發行版(例如,在同一個 Linux 內核上運行不同的 Linux 發行版)。由於容器內的程序通過正常的系統調用接口與內核通信,因此操作系統級虛擬化幾乎沒有性能開銷,這是其相較於虛擬機的一個顯著優勢 。

共享內核架構是一把雙刃劍:它既是容器實現高效率(低開銷、高密度)的主要原因 ,也是其安全方面的一個核心關切點(內核漏洞可能會影響所有容器)。

此外,對宿主機操作系統內核的兼容性要求(例如,Linux 容器在 Linux 上運行,Windows 容器在 Windows 上運行)意味著容器並非像虛擬機那樣,可以作為在任何宿主機上運行任何操作系統的通用解決方案 。

核心內核機制:命名空間 (Namespaces) 與控制組 (cgroups)#

  • 命名空間 (Namespaces):提供進程隔離。它們將內核資源進行分區,使得一組進程看到的是一套資源,而另一組進程看到的是另一套不同的資源 。這給予了進程仿佛運行在各自獨立系統中的錯覺。
    • Linux 內核實現了多種類型的命名空間:
      • PID (Process ID) 命名空間:隔離進程 ID。每個 PID 命名空間可以擁有自己的 PID 為 1 的進程。
      • **NET (Network) 命名空間:** 隔離網絡接口、IP 地址、路由表、端口號等網絡資源。
      • **MNT (Mount) 命名空間:** 隔離文件系統掛載點,允許不同命名空間中的進程擁有不同的文件系統層級視圖。
      • **UTS (UNIX Timesharing System) 命名空間:** 隔離主機名和域名
      • **IPC (Inter-Process Communication) 命名空間:** 隔離 System V IPC 對象(如消息隊列、信號量、共享內存)
      • **User 命名空間:** 隔離用戶和組 ID,允許一個進程在命名空間內有 root 權限,而在宿主機則沒有這些權限。
  • 控制組 (cgroups): 用於管理和限制一組進程的資源使用(如 CPU、內存、I/O、網絡帶寬)。進程可以被組織成層級樹狀結構。關鍵的 cgroup 控制器包括:CPU(調節 CPU 周期分配)、內存(限制內存使用量)、設備(控制設備訪問)、I/O(調節 I/O 速率)以及 Freezer(暫停和恢復進程組)。

容器平台利用這些特性來創建和管理容器。

命名空間的細粒度特性允許進行精確調整的隔離。這意味著並非所有容器都需要相同級別的隔離;某些容器可以為特定目的共享某些命名空間(例如網絡命名空間),這提供了靈活性,但如果管理不當,也會增加複雜性和潛在的安全風險 。

容器引擎與運行時#

容器引擎(或稱容器運行時)是負責從容器鏡像創建並運行容器的軟件 。它充當容器與操作系統之間的中介,提供並管理容器所需的資源 。常見的容器引擎包括 Docker Engine 、containerd 和 CRI-O 。

現代容器引擎通常將高級管理功能(如鏡像拉取、API 處理)與低級容器執行分離開來:

  • containerd: 這是一個行業標準的的核心容器運行時,負責管理其宿主系統的完整容器生命周期,從鏡像傳輸和存儲到容器執行和監管,再到低級存儲和網絡附件的管理 。Docker 已將 containerd 捐贈給雲原生計算基金會 (CNCF) 。
  • runc: 這是一個符合 OCI 規範的低級命令行工具,用於創建和運行容器。它被 containerd 等高級運行時所使用 。

容器運行時負責為容器設置命名空間和 cgroups,然後在其中運行應用程序進程 。

Image#

理解容器鏡像#

容器鏡像是一個輕量級、獨立、可執行的軟件包,包含了運行應用程序所需的一切:代碼、運行時、系統工具、系統庫和設置 。它代表了封裝應用程序及其所有軟件依賴項的二進制數據 。鏡像是只讀的模板 ;當容器運行時,它便是該鏡像的一個實例 。

基於不可變性原則,所有源自同一鏡像的容器都是相同的 。

鏡像通常由一個名為Dockerfile的文本文件創建,該文件包含組裝鏡像的指令 。鏡像名稱可以包含倉庫主機名、路徑、名稱以及標籤 (tag) 或摘要 (digest)(例如,fictional.registry.example/imagename:tag)。標籤用於表示不同版本(例如,v1.42.0),而摘要是鏡像內容的唯一哈希值,確保了內容的不可篡改性 。

使用基於內容的摘要來標識鏡像 為鏡像的完整性和精確版本控制提供了強有力的保證。這對於安全性(確保拉取的是未被篡改的鏡像)和可復現性(確保構建和部署的一致性)至關重要,尤其是在複雜的軟件供應鏈中。在 Kubernetes 的 CI/CD 實踐中,推薦使用不可變的鏡像標籤(通常解析為摘要)。

容器鏡像的分層架構#

容器鏡像由一系列層 (layers) 组成 。Dockerfile 中的每條指令(如RUNCOPYADD)通常會創建一個新的層 。這些層堆疊在一起,每一層都代表了相對於前一層的文件系統變更(添加、修改或刪除文件)。

層一旦創建就是不可變的 。當容器運行時,一個可寫層(“容器層”)會添加到不可變的鏡像層之上 。這種分層架構帶來了諸多好處,例如:

  • 效率: 層可以在鏡像之間共享。如果多個鏡像共享相同的基礎層(例如,操作系統層、運行時層),這些層在主機上只需存儲一次,下載一次即可 。這節省了磁碟空間和網絡帶寬。
  • 更快的構建: Docker 可以緩存層。如果 Dockerfile 中的某條指令及其上下文沒有改變,就可以重用緩存中已有的層,從而加快鏡像構建速度 。

分層文件系統是使容器變得實用的關鍵優化。如果沒有它,每個鏡像都將是整體式的,快速下載和高效存儲的優勢將不復存在,這將嚴重阻礙容器的採用。分層文件系統直接解決了這個問題,使得擁有許多共享共同基礎的不同鏡像成為可能。

結構不良的 Dockerfile 可能導致鏡像臃腫,中間層包含不必要的數據,或者緩存未命中從而減慢構建速度 。

Registry#

容器倉庫 (Container Repository) 用於存儲容器鏡像 。而 容器註冊中心 (Container Registry) 則是一個或多個倉庫的集合,通常還包括身份驗證、授權和存儲管理等功能 。倉庫可以是公開的(例如 Docker Hub),也可以是私有的(供內部團隊或受控共享使用)。

Volume#

Docker Volume 提供了一種獨立於容器生命周期的數據存儲和管理方案,解決了容器臨時性存儲帶來的數據丟失問題。

理解 Volume#

Docker Volume 是由 Docker 管理的、用於持久化容器數據的存儲機制 。它本質上是宿主機文件系統中的一個特定目錄,但其創建、生命周期和管理都由 Docker 引擎負責 。在 Linux 系統中,這些 Volume 通常存儲在 /var/lib/docker/volumes/ 目錄下 。一個關鍵的原則是,非 Docker 進程不應直接修改這些由 Docker 管理的文件和目錄,以避免潛在的數據損壞或管理衝突 。

Volume 可以被一個或多個容器掛載和使用 。當容器被刪除時,其關聯的 Volume 默認情況下會繼續存在,確保了數據的持久性 。Volume 可以是命名的(Named Volume),也可以是匿名的(Anonymous Volume)。命名卷具有用戶指定的明確名稱,便於管理和引用;而匿名卷在創建時會由 Docker 分配一個隨機的、唯一的名稱 。

Volume 的重要性和優勢#

Docker Volume 在容器化應用中扮演著至關重要的角色,其重要性和優勢體現在多個方面:

  • 數據持久化 (Data Persistence): 這是 Volume 最核心的功能。Docker 容器的存儲層默認是臨時的,當容器停止或被刪除時,容器內部所做的任何文件系統更改都會丟失 。Volume 的生命周期獨立於容器,即使容器被刪除,存儲在 Volume 中的數據默認也會保留下來,從而實現了關鍵數據的持久化存儲 。
  • 數據共享與重用 (Data Sharing and Reuse): Volume 可以在多個容器之間共享和重用 。這意味著不同的容器可以訪問和操作同一份數據,非常適用於微服務架構中需要協作處理數據的場景。
  • 性能提升 (Performance Improvement): 與直接在容器的可寫層中進行讀寫操作相比,使用 Volume 通常能提供更好的 I/O 性能 。這是因為 Volume 的讀寫操作通常直接作用於宿主機的文件系統,繞過了容器存儲驅動(如 OverlayFS、AUFS)所引入的聯合文件系統(Union File System)的額外抽象層和寫時複製(Copy-on-Write)機制的開銷 。
  • 易於備份與遷移 (Easier Backup and Migration): 由於 Volume 由 Docker 統一管理,並且其數據存儲在宿主機的特定位置,這使得對 Volume 中的數據進行備份、恢復和遷移變得相對簡單和直接 。
  • 獨立於容器鏡像 (Independent of Container Image): 對 Volume 中數據的修改不會影響到容器所使用的原始鏡像 。這符合 Docker 鏡像應保持無狀態和不可變性的最佳實踐,使得鏡像可以被更廣泛地復用。
  • 支持 Volume 驅動 (Support for Volume Drivers): Docker Volume 支持通過 Volume 驅動與各種外部存儲系統集成,如雲存儲服務(AWS S3, Azure Files)、網絡文件系統(NFS)等 。
  • 跨平台兼容性 (Cross-Platform Compatibility): Volume 可以在 Linux 和 Windows 容器上工作,提供了跨操作系統的持久化數據解決方案 。

這些優勢使得 Volume 成為 Docker 官方推薦的數據持久化方式 。它不僅解決了數據持久化的問題,還通過提供標準化的管理接口和對外部存儲的良好支持,增強了 Docker 應用在生產環境中的可靠性和可管理性。

工作機制#

Volume 如何實現數據持久化#

Docker Volume 實現數據持久化的核心機制在於將容器內指定的路徑(掛載點)與宿主機文件系統上由 Docker 管理的一個特定目錄進行映射 。當容器向其內部的掛載點寫入數據時,這些數據實際上被寫入了宿主機上的對應目錄,從而脫離了容器自身臨時的可寫層 。

具體來說,其工作機制可以概括為:

  1. 創建與映射: 當創建一個 Volume(無論是顯式使用 docker volume create 命令,還是在運行容器時隱式創建)時,Docker 會在宿主機的特定存儲位置(如 Linux 上的 /var/lib/docker/volumes/)為該 Volume 分配一個目錄 。
  2. 繞過聯合文件系統 (UFS): Volume 的讀寫操作繞過了容器存儲驅動所管理的聯合文件系統 (Union File System) 。容器的可寫層通常基於 UFS 實現寫時複製 (Copy-on-Write),這會帶來一定的性能開銷。而 Volume 直接將 I/O 操作導向宿主機的文件系統,減少了抽象層,從而提高了性能 。
  3. 獨立生命周期: Volume 的數據存儲在宿主機上,其生命周期獨立於任何使用它的容器 。這意味著即使所有使用該 Volume 的容器都被刪除,Volume 及其中的數據默認情況下仍然存在於宿主機上,直到被顯式刪除 。

這種機制類似於操作系統中的 “掛載” 概念 。

數據填充與覆蓋機制#

當將 Volume 掛載到容器內的一個目錄時,Docker 處理該目錄下已存在數據的方式取決於 Volume 的狀態(是否為空)以及掛載選項。

  • 掛載空 Volume 到已有數據的容器目錄:
    • 如果將一個的 Volume(無論是新創建的還是已存在但為空的 Volume)掛載到容器內一個已存在文件或目錄的路徑上,Docker 默認會將容器內該路徑下的現有文件和目錄複製到這個空的 Volume 中 。
    • 如果不想發生這種複製行為,可以使用 volume-nocopy 選項(在使用 --mount 語法時)或 -v 語法的相應變體來阻止 。
  • 掛載非空 Volume 到容器目錄 (無論容器目錄是否為空):
    • 如果將一個非空的 Volume(即 Volume 中已經包含數據)掛載到容器內的一個路徑上,那麼容器內該路徑原有的任何內容都將被這個 Volume 的內容所遮蔽 (obscured) 或覆蓋 。
    • 對於 Docker 容器,一旦 Volume 被掛載,沒有直接的 “卸載” 操作來恢復被遮蔽的文件;通常需要重新創建容器並且不掛載該 Volume 才能訪問原始文件 。
  • 啟動容器時指定不存在的 Volume:
    • 如果在啟動容器時指定了一個尚不存在的 Volume 名稱,Docker 會自動為你創建一個空的 Volume,然後應用上述 “掛載空 Volume 到已有數據的容器目錄” 的邏輯,即容器內掛載點的數據會被複製到新創建的 Volume 中 。

這些數據填充和覆蓋機制為初始化 Volume 數據提供了便利,同時也需要用戶注意其行為,以避免意外覆蓋或丟失數據。

Volume 的類型#

Docker Volume 主要分為命名卷和匿名卷,它們在創建和引用方式上有所不同。

  • 命名卷 (Named Volumes)

命名卷是指在創建時被賦予一個明確、易於識別的名稱的 Volume 。這是 Docker 官方推薦使用的 Volume 類型,尤其是在開發和生產環境中 。

命名卷的引入,使得 Volume 不再僅僅是容器的附屬品,而是可以獨立管理和維護的數據存儲單元。

  • 匿名卷 (Anonymous Volumes)

匿名卷是在創建時沒有被賦予明確名稱的 Volume 。當它們被初始化時,Docker 會為其分配一個隨機生成的、全局唯一的 ID(通常是一個長哈希字符串)作為其 “名稱” 。

匿名卷提供了數據持久化的能力,但由於其管理上的不便,Docker 官方和社區通常推薦在需要持久化數據的場景中使用命名卷 。

高級應用與特性#

數據共享機制#

  • 容器間共享 (Sharing Between Containers):

這是 Volume 的一個核心功能。多個容器可以同時掛載並訪問同一個命名卷 。當一個容器向該共享卷中寫入數據時,其他掛載了相同卷的容器可以立即看到這些更改。這種機制對於構建需要協作的微服務應用至關重要。

  • 容器與宿主機共享 (Sharing Between Container and Host):
    • 通過 Volume:Docker 管理的 Volume 存儲在宿主機文件系統的特定位置(通常是 /var/lib/docker/volumes/)。儘管 Docker 官方不推薦非 Docker 進程直接修改這些由 Docker 管理的文件 ,但宿主機上的進程(例如備份工具、監控代理)在技術上是可以訪問這些路徑的。然而,需要注意的是,直接從宿主機操作 Volume 內容可能會繞過 Docker 的管理機制,帶來潛在風險。Docker 文檔明確指出,如果需要從宿主機和容器雙向訪問文件,綁定掛載是更合適的選擇 。
    • 通過綁定掛載 (Bind Mounts): 這是實現容器與宿主機之間直接、雙向文件共享的更常用且推薦的方式 。用戶可以明確指定宿主機上的一個目錄或文件,將其映射到容器內部。這種方式透明度高,宿主機和容器對共享數據的修改會立即相互可見。

在微服務和分佈式應用架構中,Volume 的共享能力是關鍵的賦能因素。它允許狀態、配置或中間處理數據在不同的服務組件(容器)之間解耦地傳遞和共享。

Volume 驅動 (Volume Drivers)#

Volume 驅動是 Docker 存儲架構的核心組成部分,它賦予了 Docker Volume 極大的靈活性和可擴展性,使其能夠與各種不同的存儲後端集成 。

  • 工作原理: Volume 驅動充當 Docker 引擎與具體存儲系統之間的橋樑。當 Docker 需要對 Volume 執行操作(如創建、刪除、掛載、卸載、獲取路徑等)時,它會調用相應的 Volume 驅動插件來實現這些功能。
  • 默認驅動 (local): Docker 自帶一個名為 local 的默認 Volume 驅動 。這個驅動將 Volume 數據存儲在 Docker 宿主機的本地文件系統上。對於單機部署和本地開發環境,local 驅動通常已足夠使用。
  • 內置與第三方驅動: 除了 local 驅動,Docker 生態系統還支持許多其他的內置或第三方 Volume 驅動。這些驅動可以將 Volume 數據存儲在:
    • 網絡文件系統 (NFS): 允許多個 Docker 主機共享存儲在 NFS 伺服器上的 Volume。
    • 雲存儲服務: 例如 AWS Elastic Block Store (EBS), AWS S3 (通過特定驅動如 Cloudstor ), Azure Disk Storage, Azure Files, Google Persistent Disk 等。這使得容器化應用可以利用雲平台提供的持久化、高可用和可擴展的存儲服務。
    • 分佈式文件系統: 如 GlusterFS, Ceph 等。
    • 塊存儲設備: 通過 iSCSI, Fibre Channel 等協議連接的存儲區域網 (SAN) 設備。

Volume 驅動機制體現了 Docker 平台在存儲方面的開放性和可插拔架構。它使得 Docker 不僅僅局限於本地存儲,而是能夠融入企業複雜的數據中心環境或無縫對接雲原生存儲服務。這種能力對於在生產環境中大規模部署有狀態的 Docker 應用至關重要,因為它允許企業根據自身需求(性能、成本、可用性、合規性等)選擇最合適的存儲解決方案,並將其與 Docker 工作負載集成。

Networks#

理解 Docker Networks#

Docker 網絡是 Docker 平台的一個核心組成部分,專門設計用於滿足容器化應用的通信需求 。根本目標是提供一種機制,使得容器能夠相互通信,容器能夠與它們的宿主機通信,以及容器能夠與外部網絡(如互聯網或其他局域網內的服務)通信 。

簡而言之,Docker 網絡負責管理和編排容器的所有網絡交互。

Docker 網絡可以被視為一個由兩個或多個能夠通過物理或虛擬方式進行通信的設備(在這裡主要是容器和主機)組成的集群。Docker 包含一個專門的網絡單元,用於處理容器、Docker 主機以及外部用戶之間執行的所有通信任務。

網絡模型#

Docker 的網絡功能是基於一個名為容器網絡模型 (Container Network Model, CNM) 的規範來實現的。CNM 是一個開放的設計規範,它定義了 Docker 網絡的基礎構建塊和交互方式,為 Docker 提供了靈活且可插拔的網絡架構 。

CNM 的核心組件包括沙箱 (Sandbox)、端點 (Endpoint) 和網絡 (Network)。

  • 沙箱 (Sandbox): 沙箱代表了一個容器的網絡棧配置。它包含了容器的網絡接口(如 eth0)、路由表、DNS 設置等網絡相關的配置信息。每個容器都擁有一個獨立的網絡沙箱,這確保了容器之間在網絡層面上的隔離。沙箱的實現通常依賴於操作系統的網絡命名空間技術 。

網絡命名空間 (Network Namespace) 是一種 Linux 內核提供的強大功能,它允許創建多個隔離的網絡棧 (network stack) 。可以將其理解為在同一個操作系統內核下,創建出多個獨立的、虛擬的網絡環境。

Docker 利用了 Linux 網絡命名空間技術來實現容器之間以及容器與宿主機之間的網絡隔離 。

  • 端點 (Endpoint): 端點是一個虛擬的網絡接口,其主要作用是將沙箱(即容器的網絡棧)連接到一個或多個網絡上。一個端點只能屬於一個網絡,並且也只能屬於一個沙箱。
  • 網絡 (Network): 網絡是一組可以直接相互通信的端點的集合。從概念上講,網絡是 Docker 中實現容器間連接和隔離的基本單位。Docker 允許創建多種類型的網絡(例如橋接網絡、覆蓋網絡等),每種類型的網絡都由特定的網絡驅動程序實現,並為連接到該網絡的容器提供特定的通信能力 。

這三個組件協同工作,構成了 Docker 網絡的基礎。當一個容器需要連接到一個網絡時,Docker 引擎會為該容器創建一個沙箱(如果尚不存在),然後創建一個端點,並將這個端點的一端連接到容器的沙箱,另一端連接到指定的網絡。通過這種方式,容器便獲得了在該網絡中進行通信的能力。

CNM 的設計不僅僅是 Docker 內部實現的一個細節,它更提供了一個標準化的模型。這種模型化的方法使得 Docker 網絡本身具有高度的可插拔性和可擴展性。

網絡驅動#

Docker 網絡驅動是實現 CNM 規範的具體模塊,它們負責創建和管理網絡,並定義容器與主機系統之間、容器與容器之間的通信規則和行為 。

概述#

網絡驅動在 Docker 架構中扮演著連接層和實現層。它們是 Docker 引擎與底層網絡基礎設施(無論是物理網絡還是虛擬網絡)之間的橋樑。當用戶發出創建網絡或連接容器到網絡的指令時,Docker 引擎會調用相應的網絡驅動來執行這些操作。

每個網絡驅動都封裝了一套特定的網絡技術和邏輯。例如,bridge 驅動利用 Linux 內核的橋接功能和 iptables 規則,overlay 驅動則依賴於 VXLAN 隧道技術和鍵值存儲來實現跨主機通信。

Bridge 網絡驅動#

Bridge 網絡驅動是 Docker 中最常用也是默認的網絡模式 。當安裝 Docker 時,系統會自動創建一個名為 bridge(有時也被稱為 docker0)的默認虛擬網橋 。所有新啟動的容器,如果在啟動時沒有明確指定連接到其他網絡,都会默認連接到這個 bridge 網絡上。

  • 工作原理、默認網橋

Bridge 驅動的工作機制是在 Docker 主機上創建一個軟件實現的虛擬網橋。這個虛擬網橋的行為類似於一個物理交換機,所有連接到該網橋的容器都會獲得一個 IP 地址,並且可以在這個虛擬網絡內部相互通信 。容器通過一對虛擬以太網設備(veth pair)連接到這個網橋,一端在容器的網絡命名空間內(通常命名為 eth0),另一端連接到宿主機上的虛擬網橋。

Host 網絡驅動#

Host 網絡驅動提供了一種特殊的網絡模式,它完全移除了容器與 Docker 主機之間的網絡隔離層 。當容器使用 Host 網絡模式時,它不再擁有自己獨立的網絡命名空間,而是直接共享和使用宿主機的網絡命名空間 。

  • 機制、性能影響與隔離性降低

在這種模式下,容器不會被分配自己獨立的 IP 地址。相反,它直接使用宿主機的 IP 地址和端口空間 。這意味著,如果一個在 Host 模式下運行的容器內應用監聽了某個端口(例如 80 端口),那麼該應用將直接在宿主機的相應 IP 地址的 80 端口上提供服務,對外部網絡可見。

所有與端口映射相關的 Docker 命令參數(如 -p HOST_PORT:CONTAINER_PORT--publish-P--publish-all)在 Host 模式下都會被忽略

  • 性能優勢:Host 模式最顯著的優點是網絡性能。由於容器直接使用主機的網絡棧,數據包無需經過 Docker 引擎引入的網絡地址轉換 (NAT) 或用戶態代理(userland-proxy)的額外處理層次。這消除了這些中間層可能帶來的性能開銷和延遲,使得 Host 模式下的網絡通信速度幾乎等同於直接在主機上運行應用。

  • 隔離性降低與安全風險:由於容器共享主機的網絡,它不再受到 Docker 默認提供的網絡隔離保護。容器內的進程可以直接訪問主機的所有網絡接口,並且主機上的其他進程也可以直接訪問容器內監聽的端口。這種直接暴露帶來了顯著的安全風險,因為容器的漏洞可能會直接影響到主機。

用戶態代理 (userland proxy),通常指的是 Docker 引擎中的 docker-proxy 進程,它在特定的網絡場景下負責端口轉發 。

None 網絡驅動#

None 網絡驅動為 Docker 容器提供了最高級別的網絡隔離。當一個容器被配置為使用 none 網絡驅動時,Docker 不會為其配置任何外部網絡接口,除了一個必需的環回接口 (loopback interface, 通常是 lo) 。

這代表著,連接到 none 網絡的容器:

  • 沒有 eth0 或類似的外部網絡接口。
  • 沒有被分配 IP 地址(除了環回地址 127.0.0.1)。
  • 無法與 Docker 主機通信(除了通過環回接口與自身通信)。
  • 無法與其他容器通信。
  • 無法訪問外部網絡(如互聯網)。

Overlay 網絡驅動#

Overlay 網絡驅動是專為跨多個 Docker 主機環境設計的解決方案,其核心目標是使運行在不同物理或虛擬機上的容器能夠像在同一個局域網中一樣相互通信 。

這對於構建和管理分佈式應用,尤其是在 Docker Swarm 集群模式下部署微服務架構,是至關重要的。

  • 工作原理 (VXLAN):Overlay 網絡通過在現有的物理網絡(通常是 Layer 3 網絡)之上創建一個虛擬的 Layer 2 網絡來實現跨主機通信。它通常采用 VXLAN (Virtual Extensible LAN) 隧道技術 。VXLAN 將原始的以太網幀(容器發出的網絡包)封裝在一個 UDP 包中,然後通過底層的主機網絡進行傳輸。當這個 UDP 包到達目標主機時,目標主機上的 Docker 引擎會解封裝,提取出原始的以太網幀,並將其傳遞給目標容器。通過這種方式,即使容器分布在不同的物理網絡段,它們也感覺自己連接在同一個虛擬的二層網絡上,可以使用相同的子網 IP 地址進行通信。
  • Docker Swarm 集成: Overlay 網絡是 Docker Swarm 模式的核心網絡功能和默認選擇 。當初始化一個 Swarm 集群或將節點加入 Swarm 時,Docker 會自動創建一些預定義的 overlay 網絡(如 ingress 網絡,用於處理外部流量的路由網格)。用戶也可以輕鬆創建自定義的 overlay 網絡,並將 Swarm 服務部署到這些網絡上。Swarm 管理器會負責協調和管理 overlay 網絡的配置,確保跨集群中所有節點的容器路由正確無誤。
  • 服務發現: 在 Docker Swarm 的 overlay 網絡中,內置了強大的服務發現機制 。當一個服務(由一個或多個容器副本組成)部署到 overlay 網絡時,Swarm 會為該服務分配一個虛擬 IP (VIP),並且 Swarm 內嵌的 DNS 伺服器會將服務名解析到這個 VIP。這意味著,網絡內的其他容器或服務可以直接通過服務名來訪問目標服務,而無需關心其副本運行在哪個節點上,也無需知道具體的容器 IP 地址。
  • 負載均衡: Docker Swarm 利用 overlay 網絡和其 ingress 路由網格,為發布到集群外部的服務提供自動的負載均衡 。當外部請求到達集群中任何一個節點的已發布端口時,Swarm 會將流量分發到該服務的某個健康運行的容器副本上,無論該副本位於哪個節點。對於集群內部的服務間通信,通過服務名訪問時,Swarm 也會在服務的多個副本之間進行負載均衡。

Overlay 網絡驅動通過其強大的跨主機通信能力、與 Swarm 的深度集成以及內置的服務發現和負載均衡特性,為構建和運維複雜的分佈式容器化應用提供了堅實的基礎。

Macvlan 網絡驅動#

容器作為物理網絡設備

Macvlan 網絡驅動允許為 Docker 容器分配一個獨立的 MAC (Media Access Control) 地址,並將其虛擬網絡接口直接橋接到宿主機的物理網絡接口上 。這使得容器在物理網絡層面看起來就像一個獨立的物理設備,擁有自己的 MAC 地址和 IP 地址,可以直接參與到宿主機所在的局域網中,與其他物理設備或虛擬機進行通信,而無需經過 Docker 主機的 NAT 或端口映射。

  • 限制與注意事項
    • 主機與容器通信:默認情況下,使用 Macvlan 網絡的容器無法直接與它們的宿主機進行通信。這是因為流量從容器發出後,會直接通過父物理接口出去,宿主機的網絡棧無法截獲這些發往自身的流量。如果需要宿主機與 Macvlan 容器通信,通常需要額外的網絡配置。
    • 網絡設備支持: 宿主機的物理網絡接口(或其驅動程序)需要支持混雜模式 (promiscuous mode),以便能夠接收發往多個 MAC 地址的幀 。
    • IP 地址和 MAC 地址管理: 需要仔細規劃 IP 地址和 MAC 地址的分配,以避免與現有網絡中的其他設備衝突。過多的 MAC 地址也可能給網絡交換機帶來壓力(MAC 地址表耗尽)或觸發安全策略(如端口安全限制)。
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。