日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Docker源码分析(六):Docker Daemon网络

發(fā)布時間:2025/4/5 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Docker源码分析(六):Docker Daemon网络 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

http://www.infoq.com/cn/articles/docker-source-code-analysis-part6

1. 前言

Docker作為一個開源的輕量級虛擬化容器引擎技術(shù),已然給云計算領(lǐng)域帶來了新的發(fā)展模式。Docker借助容器技術(shù)徹底釋放了輕量級虛擬化技術(shù)的威力,讓容器的伸縮、應(yīng)用的運(yùn)行都變得前所未有的方便與高效。同時,Docker借助強(qiáng)大的鏡像技術(shù),讓應(yīng)用的分發(fā)、部署與管理變得史無前例的便捷。然而,Docker畢竟是一項較為新穎的技術(shù),在Docker的世界中,用戶并非一勞永逸,其中最為典型的便是Docker的網(wǎng)絡(luò)問題。

毋庸置疑,對于Docker管理者和開發(fā)者而言,如何有效、高效的管理Docker容器之間的交互以及Docker容器的網(wǎng)絡(luò)一直是一個巨大的挑戰(zhàn)。目前,云計算領(lǐng)域中,絕大多數(shù)系統(tǒng)都采取分布式技術(shù)來設(shè)計并實現(xiàn)。然而,在原生態(tài)的Docker世界中,Docker的網(wǎng)絡(luò)卻是不具備跨宿主機(jī)能力的,這也或多或少滯后了Docker在云計算領(lǐng)域的高速發(fā)展。

工業(yè)界中,Docker的網(wǎng)絡(luò)問題的解決勢在必行,在此環(huán)境下,很多IT企業(yè)都開發(fā)了各自的新產(chǎn)品來幫助完善Docker的網(wǎng)絡(luò)。這些企業(yè)中不乏像Google一樣的互聯(lián)網(wǎng)翹楚企業(yè),同時也有不少初創(chuàng)企業(yè)率先出擊,在最前沿不懈探索。這些新產(chǎn)品中有,Google推出的容器管理和編排開源項目Kubernetes,Zett.io公司開發(fā)的通過虛擬網(wǎng)絡(luò)連接跨宿主機(jī)容器的工具Weave,CoreOS團(tuán)隊針對Kubernetes設(shè)計的網(wǎng)絡(luò)覆蓋工具Flannel,Docker官方的工程師Jér?me Petazzoni自己設(shè)計的SDN網(wǎng)絡(luò)解決方案Pipework,以及SocketPlane項目等。

對于Docker管理者與開發(fā)者而言,Docker的跨宿主機(jī)通信能力固然重要,但Docker自身的網(wǎng)絡(luò)架構(gòu)也同樣重要。只有深入了解Docker自身的網(wǎng)絡(luò)設(shè)計與實現(xiàn),才能在這基礎(chǔ)上擴(kuò)展Docker的跨宿主機(jī)能力。

Docker自身的網(wǎng)絡(luò)主要包含兩部分:Docker Daemon的網(wǎng)絡(luò)配置,Docker Container的網(wǎng)絡(luò)配置。本文主要分析Docker Daemon的網(wǎng)絡(luò)。

2. Docker Daemon網(wǎng)絡(luò)分析內(nèi)容安排

本文從源碼的角度,分析Docker Daemon在啟動過程中,為Docker配置的網(wǎng)絡(luò)環(huán)境,章節(jié)安排如下:

(1) Docker Daemon網(wǎng)絡(luò)配置;

(2) 運(yùn)行Docker Daemon網(wǎng)絡(luò)初始化任務(wù);

(3) 創(chuàng)建Docker網(wǎng)橋。

本文為《Docker源碼分析》系列第六篇——Docker Daemon網(wǎng)絡(luò)篇,第七篇將安排Docker Container網(wǎng)絡(luò)篇。

3. Docker Daemon網(wǎng)絡(luò)配置

Docker環(huán)境中,Docker管理員完全有權(quán)限配置Docker Daemon運(yùn)行過程中的網(wǎng)絡(luò)模式。 關(guān)于Docker的網(wǎng)絡(luò)模式,大家最熟知的應(yīng)該就是“橋接”的模式。下圖為橋接模式下,Docker的網(wǎng)絡(luò)環(huán)境拓?fù)鋱D(包括Docker Daemon網(wǎng)絡(luò)環(huán)境和Docker Container網(wǎng)絡(luò)環(huán)境):

圖3.1 Docker網(wǎng)絡(luò)橋接示意圖

然而,“橋接”是Docker網(wǎng)絡(luò)模式中最為常用的模式。除此之外,Docker還為用戶提供了更多的可選項,下文將對此一一說來。

3.1 Docker Daemon網(wǎng)絡(luò)配置接口

Docker Daemon每次啟動的過程中,都會初始化自身的網(wǎng)絡(luò)環(huán)境,這樣的網(wǎng)絡(luò)環(huán)境最終為Docker Container提供網(wǎng)絡(luò)通信服務(wù)。

Docker管理員配置Docker的網(wǎng)絡(luò)環(huán)境,可以在Docker Daemon啟動時,通過Docker提供的接口來完成。換言之,可以使用docker二進(jìn)制可執(zhí)行文件,運(yùn)行docker -d并添加相應(yīng)的flag參數(shù)來完成。

其中涉及的flag參數(shù)有EnableIptables、EnableIpForward、BridgeIface、BridgeIP以及InterContainerCommunication。該五個參數(shù)的定義位于./docker/daemon/config.go,具體代碼如下:

flag.BoolVar(&config.EnableIptables, []string{"#iptables", "-iptables"}, true, "Enable Docker's addition of iptables rules") flag.BoolVar(&config.EnableIpForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b") flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking") flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")

以下介紹這5個flag的作用:

  • EnableIptables:確保Docker對于宿主機(jī)上的iptables規(guī)則擁有添加權(quán)限;
  • EnableIpForward:確保net.ipv4.ip_forward可以使用,使得多網(wǎng)絡(luò)接口設(shè)備模式下,數(shù)據(jù)報可以在網(wǎng)絡(luò)設(shè)備之間轉(zhuǎn)發(fā);
  • BridgeIP:在Docker Daemon啟動過程中,為網(wǎng)絡(luò)環(huán)境中的網(wǎng)橋配置CIDR網(wǎng)絡(luò)地址;
  • BridgeIface:為Docker網(wǎng)絡(luò)環(huán)境指定具體的通信網(wǎng)橋,若BridgeIface的值為”none”,則說明不需要為Docker Container創(chuàng)建網(wǎng)橋服務(wù),關(guān)閉Docker Container的網(wǎng)絡(luò)能力;
  • InterContainerCommunication:確保Docker容器之間可以完成通信。

除了Docker會使用到的5個flag參數(shù)之外,Docker在創(chuàng)建網(wǎng)絡(luò)環(huán)境時,還使用一個DefaultIP變量,如下:

opts.IPVar(&config.DefaultIp, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")

該變量的作用是:當(dāng)綁定容器的端口時,將DefaultIp作為默認(rèn)使用的IP地址。

具備了以上Docker Daemon的網(wǎng)絡(luò)背景知識,以下著重舉例分析使用BridgeIP和BridgeIface,在啟動Docker Daemon時進(jìn)行網(wǎng)絡(luò)配置:

啟動Docker Daemon使用命令

用途注釋

docker -d

啟動Docker Daemon,使用默認(rèn)網(wǎng)橋docker0,不指定CIDR網(wǎng)絡(luò)地址

docker -d -b=”xxx”

啟動Docker Daemon,使用

網(wǎng)橋xxx,不指定CIDR網(wǎng)絡(luò)地址

docker -d --bip=”172.17.42.1”

啟動Docker Daemon,使用默認(rèn)網(wǎng)橋docker0,使用指定CIDR網(wǎng)絡(luò)地址”172.17.42.1”

docker -d --bridge=”xxx” --bip=”10.0.42.1”

報錯,出現(xiàn)兼容性問題,不能同時指定”BridgeIP”和”BridgeIface”

docker -d --bridge=”none”

啟動Docker Daemon,

不創(chuàng)建Docker網(wǎng)絡(luò)環(huán)境

深入理解BridgeIface與BridgeIP,并熟練使用相應(yīng)的flag參數(shù),即做到了如何配置Docker Daemon的網(wǎng)絡(luò)環(huán)境。需要特別注意的是,Docker Daemon的網(wǎng)絡(luò)與Docker Container的網(wǎng)絡(luò)存在很大的區(qū)別。Docker Daemon為Docker Container創(chuàng)建網(wǎng)絡(luò)的大環(huán)境,Docker Container的網(wǎng)絡(luò)需要Docker Daemon的網(wǎng)絡(luò)提供支持,但不唯一。舉一個形象的例子,Docker Daemon可以創(chuàng)建docker0網(wǎng)橋,為之后Docker Container的橋接模式提供支持,然而Docker Container仍然可以根據(jù)用戶需求創(chuàng)建自身網(wǎng)絡(luò),其中Docker Container的網(wǎng)絡(luò)可以是橋接模式的網(wǎng)絡(luò),同時也可以直接共享使用宿主機(jī)的網(wǎng)絡(luò)接口,另外還有其他模式,會在《Docker源碼分析》系列的第七篇——Docker Container網(wǎng)絡(luò)篇中詳細(xì)介紹。

3.2 Docker Daemon網(wǎng)絡(luò)初始化

正如上一節(jié)所言,Docker管理員可以通過與網(wǎng)絡(luò)相關(guān)的flag參數(shù)BridgeIface與BridgeIP,來為Docker Daemon創(chuàng)建網(wǎng)路環(huán)境。最簡單的,Docker管理員通過執(zhí)行”docker -d”就已經(jīng)完成了運(yùn)行Docker Daemon,而Docker Daemon在啟動的時候,根據(jù)以上兩個flag參數(shù)的值,創(chuàng)建相應(yīng)的網(wǎng)絡(luò)環(huán)境。

Docker Daemon網(wǎng)絡(luò)初始化流程圖如下:

圖 3.2 Docker Daemon網(wǎng)絡(luò)初始化流程圖

Docker Daemon網(wǎng)絡(luò)初始化的流程總體而言,主要是根據(jù)解析flag參數(shù)來決定到底建立哪種類型的網(wǎng)絡(luò)環(huán)境。從流程圖中可知,Docker Daemon創(chuàng)建網(wǎng)絡(luò)環(huán)境時有兩個分支,不難發(fā)現(xiàn)分支代表的分別是:為Docker創(chuàng)建一個網(wǎng)絡(luò)驅(qū)動、以及對Docker的網(wǎng)絡(luò)不做任何的操作。

以下參照Docker Daemon網(wǎng)絡(luò)初始化流程圖具體分析實現(xiàn)步驟。

3.2.1 啟動Docker Daemon傳遞flag參數(shù)

用戶啟動Docker Daemon,并在命令行中選擇性的傳入所需要的flag參數(shù)。

3.2.2 解析網(wǎng)絡(luò)flag參數(shù)

flag包對命令行中的flag參數(shù)進(jìn)行解析,其中和Docker Daemon網(wǎng)絡(luò)配置相關(guān)的flag參數(shù)有5個,分別是:EnableIptables、EnableIpForward、BridgeIP、BridgeIface以及InterContanierCommunication,各個flag參數(shù)的作用上文已有介紹。

3.2.3 預(yù)處理flag參數(shù)

預(yù)處理與網(wǎng)絡(luò)配置相關(guān)的flag參數(shù)信息,包括檢測配置信息的兼容性、以及判斷是否創(chuàng)建Docker網(wǎng)絡(luò)環(huán)境。

首先檢驗是否會出現(xiàn)彼此不兼容的配置信息,源碼位于./docker/daemon/daemon.go#L679-L685。

這部分的兼容信息有兩種。第一種是BridgeIP和BridgeIface配置信息的兼容性,具體表現(xiàn)為用戶啟動Docker Daemon時,若同時指定了BridgeIP和BridgIface的值,則出現(xiàn)兼容問題。原因為這兩者屬于互斥對,換言之,若用戶指定了新建網(wǎng)橋的設(shè)備名,那么該網(wǎng)橋已經(jīng)存在,無需指定網(wǎng)橋的IP地址BridgeIP;若用戶指定了新建網(wǎng)橋的網(wǎng)絡(luò)IP地址BridgeIP,那么該網(wǎng)橋肯定還沒有新建成功,則Docker Daemon在新建網(wǎng)橋時使用默認(rèn)網(wǎng)橋名“docker0”。具體如下:

// Check for mutually incompatible config options if config.BridgeIface != "" && config.BridgeIP != "" {return nil, fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one.") }

第二種是EnableIptables和InterContainerCommunication配置的兼容性,具體是指不能同時指定這兩個flag參數(shù)為false。原因很簡單,如果指定InterContainerCommunication為false,則說明Docker Daemon不允許創(chuàng)建的Docker容器之間互相進(jìn)行通信。但是為了達(dá)到以上目的,Docker正是使用iptables過濾規(guī)則。因此,再次設(shè)定EnableIptables為false,關(guān)閉iptables的使用,即出現(xiàn)了自相矛盾的結(jié)果。代碼如下:

if !config.EnableIptables && !config.InterContainerCommunication {return nil, fmt.Errorf("You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.")}

檢驗完系統(tǒng)配置信息的兼容性問題,Docker Daemon接著會判斷是否需要為Docker Daemon配置網(wǎng)絡(luò)環(huán)境。判斷的依據(jù)為BridgeIface的值是否與DisableNetworkBridge的值相等,DisableNetworkBridge在./docker/daemon/config.go#L13中被定義為const量,值為字符串”none”。因此,若BridgeIface為”none”,則DisableNetwork為true,最終Docker Daemon不會創(chuàng)建網(wǎng)絡(luò)環(huán)境;若BridgeIface不為”none”,則DisableNetwork為false,最終Docker Daemon需要創(chuàng)建網(wǎng)絡(luò)環(huán)境(橋接模式)。

3.2.4 確定Docker網(wǎng)絡(luò)模式

Docker網(wǎng)絡(luò)模式由配置信息DisableNetwork決定。由于在上一環(huán)節(jié)已經(jīng)得出DisableNetwork的值,故這一環(huán)節(jié)可以確定Docker網(wǎng)絡(luò)模式。該部分的源碼實現(xiàn)位于./docker/daemon/daemon.go#L792-L805,如下:

if !config.DisableNetwork {job := eng.Job("init_networkdriver")job.SetenvBool("EnableIptables", config.EnableIptables)job.SetenvBool("InterContainerCommunication", config.InterContainerCommunication)job.SetenvBool("EnableIpForward", config.EnableIpForward)job.Setenv("BridgeIface", config.BridgeIface)job.Setenv("BridgeIP", config.BridgeIP)job.Setenv("DefaultBindingIP", config.DefaultIp.String())if err := job.Run(); err != nil {return nil, err} }

若DisableNetwork為false,則說明需要創(chuàng)建網(wǎng)絡(luò)環(huán)境,具體的模式為創(chuàng)建Docker網(wǎng)橋模式。創(chuàng)建網(wǎng)絡(luò)環(huán)境的步驟為:

(1) 創(chuàng)建名為”init_networkdriver”的job;

(2) 為該job配置環(huán)境變量,設(shè)置的環(huán)境變量有EnableIptables、InterContainerCommunication、EnableIpForward、BridgeIface、BridgeIP以及DefaultBindingIP;

(3) 運(yùn)行job。

運(yùn)行”init_network”即為創(chuàng)建Docker網(wǎng)橋,這部分內(nèi)容將會在下一節(jié)詳細(xì)分析。

若DisableNetwork為true。則說明不需要創(chuàng)建網(wǎng)絡(luò)環(huán)境,網(wǎng)絡(luò)模式屬于none模式。

以上便是Docker Daemon網(wǎng)絡(luò)初始化的所有流程。

3.3 創(chuàng)建Docker網(wǎng)橋

Docker的網(wǎng)絡(luò)往往是Docker開發(fā)者最常提起的話題。而Docker網(wǎng)絡(luò)中最常使用的模式為bridge橋接模式。本小節(jié)將詳細(xì)分析創(chuàng)建Docker網(wǎng)橋的創(chuàng)建流程。

創(chuàng)建Docker網(wǎng)橋的實現(xiàn)通過”init_network”這個job的運(yùn)行來完成。”init_network”的實現(xiàn)為InitDriver函數(shù),位于./docker/daemon/networkdriver/bridge/driver.go#L79,運(yùn)行流程如下:

圖3.3 Docker Daemon創(chuàng)建網(wǎng)橋流程圖

3.3.1 提取環(huán)境變量

在InitDriver函數(shù)的實現(xiàn)過程中,Docker首先提取”init_networkdriver”這個job的環(huán)境變量。這樣的環(huán)境變量共有6個,各自的作用在上文已經(jīng)詳細(xì)說明。具體的實現(xiàn)代碼為:

var (network *net.IPNetenableIPTables = job.GetenvBool("EnableIptables")icc = job.GetenvBool("InterContainerCommunication")ipForward = job.GetenvBool("EnableIpForward")bridgeIP = job.Getenv("BridgeIP") )if defaultIP := job.Getenv("DefaultBindingIP"); defaultIP != "" {defaultBindingIP = net.ParseIP(defaultIP) }bridgeIface = job.Getenv("BridgeIface")

3.3.2 確定Docker網(wǎng)橋設(shè)備名

提取job的環(huán)境變量之后,Docker隨即確定最終使用網(wǎng)橋設(shè)備的名稱。為此,Docker首先創(chuàng)建了一個名為usingDefaultBridge的bool變量,含義為是否使用默認(rèn)的網(wǎng)橋設(shè)備,默認(rèn)值為false。接著,若環(huán)境變量中bridgeIface的值為空,則說明用戶啟動Docker時,沒有指定特定的網(wǎng)橋設(shè)備名,因此Docker首先將usingDefaultBridge置為true,然后使用默認(rèn)的網(wǎng)橋設(shè)備名DefaultNetworkBridge,即docker0;若bridgeIface的值不為空,則判斷條件不成立,繼續(xù)往下執(zhí)行。這部分的代碼實現(xiàn)為:

usingDefaultBridge := false if bridgeIface == "" {usingDefaultBridge = truebridgeIface = DefaultNetworkBridge }

3.3.3 查找bridgeIface網(wǎng)橋設(shè)備

確定Docker網(wǎng)橋設(shè)備名bridgeIface之后,Docker首先通過bridgeIface設(shè)備名在宿主機(jī)上查找該設(shè)備是否真實存在。若存在,則返回該網(wǎng)橋設(shè)備的IP地址,若不存在,則返回nil。實現(xiàn)代碼位于./docker/daemon/networkdriver/bridge/driver.go#L99,如下:

addr, err := networkdriver.GetIfaceAddr(bridgeIface)

GetIfaceAddr的實現(xiàn)位于./docker/daemon/networkdriver/utils.go,實現(xiàn)步驟為:首先通過Golang中net包的InterfaceByName方法獲取名為bridgeIface的網(wǎng)橋設(shè)備,會得出以下結(jié)果:

  • 若名為bridgeIface的網(wǎng)橋設(shè)備不存在,直接返回error;
  • 若名為bridgeIface的網(wǎng)橋設(shè)備存在,返回該網(wǎng)橋設(shè)備的IP地址。

需要強(qiáng)調(diào)的是:GetIfaceAddr函數(shù)返回error,說明當(dāng)前宿主機(jī)上不存在名為bridgeIface的網(wǎng)橋設(shè)備。而這樣的結(jié)果會有兩種不同的情況:第一,用戶指定了bridgeIface,那么usingDefaultBridge為false,而該bridgeIface網(wǎng)橋設(shè)備在宿主機(jī)上不存在;第二,用戶沒有指定bridgeIface,那么usingDefaultBridge為true,bridgeIface名為docker0,而docker0網(wǎng)橋在宿主機(jī)上也不存在。

當(dāng)然,若GetIfaceAddr函數(shù)返回的是一個IP地址,則說明當(dāng)前宿主機(jī)上存在名為bridgeIface的網(wǎng)橋設(shè)備。這樣的結(jié)果同樣會有兩種不同的情況:第一,用戶指定了bridgeIface,那么usingDefaultBridge為false,而該bridgeIface網(wǎng)橋設(shè)備在宿主機(jī)上已經(jīng)存在;第二,用戶沒有指定bridgeIface,那么usingDefaultBridge為true,bridgeIface名為docker0,而docker0網(wǎng)橋在宿主機(jī)上也已經(jīng)存在。第二種情況一般是:用戶在宿主機(jī)上第一次啟動Docker Daemon時,創(chuàng)建了默認(rèn)網(wǎng)橋設(shè)備docker0,而后docker0網(wǎng)橋設(shè)備一直存在于宿主機(jī)上,故之后在不指定網(wǎng)橋設(shè)備的情況下,重啟Docker Daemon,會出現(xiàn)docker0已經(jīng)存在的情況。

以下兩小節(jié)將分別從bridgeIface已創(chuàng)建與bridgeIface未創(chuàng)建兩種不同的情況分析。

3.3.4 bridgeIface已創(chuàng)建的情況

Docker Daemon所在宿主機(jī)上bridgeIface的網(wǎng)橋設(shè)備存在時,Docker Daemon仍然需要驗證用戶在配置信息中是否為網(wǎng)橋設(shè)備指定了IP地址。

用戶啟動Docker Daemon時,假如沒有指定bridgeIP參數(shù)信息,則Docker Daemon使用名為bridgeIface的原有的IP地址。

當(dāng)用戶指定了bridgeIP參數(shù)信息時,則需要驗證:指定的bridgeIP參數(shù)信息與bridgeIface網(wǎng)橋設(shè)備原有的IP地址信息是否匹配。若兩者匹配,則驗證通過,繼續(xù)往下執(zhí)行;若兩者不匹配,則驗證不通過,拋出錯誤,顯示“bridgeIP與已有網(wǎng)橋配置信息不匹配”。該部分內(nèi)容位于./docker/daemon/networkdriver/bridge/driver.go#L119-L129,代碼如下:

network = addr.(*net.IPNet) // validate that the bridge ip matches the ip specified by BridgeIP if bridgeIP != "" {bip, _, err := net.ParseCIDR(bridgeIP)if err != nil {return job.Error(err)}if !network.IP.Equal(bip) {return job.Errorf("bridge ip (%s) does not match existing bridge configuration %s", network.IP, bip)} }

3.3.5 bridgeIface未創(chuàng)建的情況

Docker Daemon所在宿主機(jī)上bridgeIface的網(wǎng)橋設(shè)備未創(chuàng)建時,上文已經(jīng)介紹將存在兩種情況:

l 用戶指定的bridgeIface未創(chuàng)建;

l 用戶未指定bridgeIface,而docker0暫未創(chuàng)建。

當(dāng)用戶指定的bridgeIface不存在于宿主機(jī)時,即沒有使用Docker的默認(rèn)網(wǎng)橋設(shè)備名docker0,Docker打印日志信息“指定網(wǎng)橋設(shè)備未找到”,并返回網(wǎng)橋未找到的錯誤信息。代碼實現(xiàn)如下:

if !usingDefaultBridge {job.Logf("bridge not found: %s", bridgeIface)return job.Error(err) }

當(dāng)使用的默認(rèn)網(wǎng)橋設(shè)備名,而docker0網(wǎng)橋設(shè)備還未創(chuàng)建時,Docker Daemon則立即實現(xiàn)創(chuàng)建網(wǎng)橋的操作,并返回該docker0網(wǎng)橋設(shè)備的IP地址。代碼如下:

// If the iface is not found, try to create it job.Logf("creating new bridge for %s", bridgeIface) if err := createBridge(bridgeIP); err != nil {return job.Error(err) }job.Logf("getting iface addr") addr, err = networkdriver.GetIfaceAddr(bridgeIface) if err != nil {return job.Error(err) } network = addr.(*net.IPNet)

創(chuàng)建Docker Daemon網(wǎng)橋設(shè)備docker0的實現(xiàn),全部由createBridge(bridgeIP)來實現(xiàn),createBridge的實現(xiàn)位于./docker/daemon/networkdriver/bridge/driver.go#L245。

createBridge函數(shù)實現(xiàn)過程的主要步驟為:

(1) 確定網(wǎng)橋設(shè)備docker0的IP地址;

(2) 通過createBridgeIface函數(shù)創(chuàng)建docker0網(wǎng)橋設(shè)備,并為網(wǎng)橋設(shè)備分配隨機(jī)的MAC地址;

(3) 將第一步中已經(jīng)確定的IP地址,添加給新創(chuàng)建的docker0網(wǎng)橋設(shè)備;

(4) 啟動docker0網(wǎng)橋設(shè)備。

以下詳細(xì)分析4個步驟的具體實現(xiàn)。

首先Docker Daemon確定docker0的IP地址,實現(xiàn)方式為判斷用戶是否指定bridgeIP。若用戶未指定bridgeIP,則從Docker預(yù)先準(zhǔn)備的IP網(wǎng)段列表addrs中查找合適的網(wǎng)段。具體的代碼實現(xiàn)位于./docker/daemon/networkdriver/bridge/driver.go#L257-L278,如下:

if len(bridgeIP) != 0 {_, _, err := net.ParseCIDR(bridgeIP)if err != nil {return err}ifaceAddr = bridgeIP } else {for _, addr := range addrs {_, dockerNetwork, err := net.ParseCIDR(addr)if err != nil {return err}if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil {if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil {ifaceAddr = addrbreak} else {log.Debugf("%s %s", addr, err)}}} }

其中為網(wǎng)橋設(shè)備準(zhǔn)備的候選網(wǎng)段地址addrs為:

addrs = []string{"172.17.42.1/16", // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23"10.0.42.1/16", // Don't even try using the entire /8, that's too intrusive"10.1.42.1/16","10.42.42.1/16","172.16.42.1/24","172.16.43.1/24","172.16.44.1/24","10.0.42.1/24","10.0.43.1/24","192.168.42.1/24","192.168.43.1/24","192.168.44.1/24", }

通過以上的流程的執(zhí)行,可以確定找到一個可用的IP網(wǎng)段地址,為ifaceAddr;若沒有找到,則返回錯誤日志,表明沒有合適的IP地址賦予docker0網(wǎng)橋設(shè)備。

第二個步驟通過createBridgeIface函數(shù)創(chuàng)建docker0網(wǎng)橋設(shè)備。createBridgeIface函數(shù)的實現(xiàn)如下:

func createBridgeIface(name string) error {kv, err := kernel.GetKernelVersion()// only set the bridge's mac address if the kernel version is > 3.3// before that it was not supportedsetBridgeMacAddr := err == nil && (kv.Kernel >= 3 && kv.Major >= 3)log.Debugf("setting bridge mac address = %v", setBridgeMacAddr)return netlink.CreateBridge(name, setBridgeMacAddr) }

以上代碼通過宿主機(jī)Linux內(nèi)核信息,確定是否支持設(shè)定網(wǎng)橋設(shè)備的MAC地址。若Linux內(nèi)核版本大于3.3,則支持配置MAC地址,否則則不支持。而Docker在不小于3.8的內(nèi)核版本上運(yùn)行才穩(wěn)定,故可以認(rèn)為內(nèi)核支持配置MAC地址。最后通過netlink的CreateBridge函數(shù)實現(xiàn)創(chuàng)建docker0網(wǎng)橋。

Netlink是Linux中一種較為特殊的socket通信方式,提供了用戶應(yīng)用間和內(nèi)核進(jìn)行雙向數(shù)據(jù)傳輸?shù)耐緩健T谶@種模式下,用戶態(tài)可以使用標(biāo)準(zhǔn)的socket API來使用netlink強(qiáng)大的功能,而內(nèi)核態(tài)需要使用專門的內(nèi)核API才能使用netlink。

Libcontainer的netlink包中CreateBridge實現(xiàn)了創(chuàng)建實際的網(wǎng)橋設(shè)備,具體使用系統(tǒng)調(diào)用的代碼如下:

syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), SIOC_BRADDBR, uintptr(unsafe.Pointer(nameBytePtr)))

創(chuàng)建完網(wǎng)橋設(shè)備之后,為docker0網(wǎng)橋設(shè)備配置MAC地址,實現(xiàn)函數(shù)為setBridgeMacAddress。

第三個步驟是為創(chuàng)建docker0網(wǎng)橋設(shè)備綁定IP地址。上一步驟僅完成了創(chuàng)建名為docker0的網(wǎng)橋設(shè)備,之后仍需要為docker0網(wǎng)橋設(shè)備綁定IP地址。具體代碼實現(xiàn)為:

if netlink.NetworkLinkAddIp(iface, ipAddr, ipNet); err != nil {return fmt.Errorf("Unable to add private network: %s", err) }

NetworkLinkAddIP的實現(xiàn)同樣位于libcontainer中的netlink包,主要的功能為:通過netlink機(jī)制為一個網(wǎng)絡(luò)接口設(shè)備綁定一個IP地址。

第四個步驟是啟動docker0網(wǎng)橋設(shè)備。具體實現(xiàn)代碼為:

if err := netlink.NetworkLinkUp(iface); err != nil {return fmt.Errorf("Unable to start network bridge: %s", err)}

NetworkLinkUp的實現(xiàn)同樣位于libcontainer中的netlink包,功能為啟動docker網(wǎng)橋設(shè)備。

至此,docker0網(wǎng)橋歷經(jīng)確定IP、創(chuàng)建、綁定IP、啟動四個環(huán)節(jié),createBridge關(guān)于docker0網(wǎng)橋設(shè)備的工作全部完成。

3.3.6 獲取網(wǎng)橋設(shè)備的網(wǎng)絡(luò)地址

創(chuàng)建完網(wǎng)橋設(shè)備之后,網(wǎng)橋設(shè)備必然會存在一個網(wǎng)絡(luò)地址。網(wǎng)橋網(wǎng)絡(luò)地址的作用為:Docker Daemon在創(chuàng)建Docker Container時,使用該網(wǎng)絡(luò)地址為Docker Container分配IP地址。

Docker使用代碼network = addr.(*net.IPNet)獲取網(wǎng)橋設(shè)備的網(wǎng)絡(luò)地址。

3.3.7 配置Docker Daemon的iptables

創(chuàng)建完網(wǎng)橋之后,Docker Daemon為容器以及宿主機(jī)配置iptables,包括為container之間所需要的link操作提供支持,為host主機(jī)上所有的對外對內(nèi)流量制定傳輸規(guī)則等。該部分詳情可以參看《Docker源碼分析(四):Docker Daemon之NewDaemon實現(xiàn)》。代碼位于./docker/daemon/networkdriver/bridge/driver/driver.go#L133,如下:

// Configure iptables for link support if enableIPTables {if err := setupIPTables(addr, icc); err != nil {return job.Error(err)} }// We can always try removing the iptables if err := iptables.RemoveExistingChain("DOCKER"); err != nil {return job.Error(err) }if enableIPTables {chain, err := iptables.NewChain("DOCKER", bridgeIface)if err != nil {return job.Error(err)}portmapper.SetIptablesChain(chain) }

3.3.8 配置網(wǎng)絡(luò)設(shè)備間數(shù)據(jù)報轉(zhuǎn)發(fā)功能

在Linux系統(tǒng)上,數(shù)據(jù)包轉(zhuǎn)發(fā)功能是被默認(rèn)禁止的。數(shù)據(jù)包轉(zhuǎn)發(fā),就是當(dāng)host主機(jī)存在多個網(wǎng)絡(luò)設(shè)備時,如果其中一個接收到數(shù)據(jù)包,并需要將其轉(zhuǎn)發(fā)給另外的網(wǎng)絡(luò)設(shè)備。通過修改/proc/sys/net/ipv4/ip_forward的值,將其置為1,則可以保證系統(tǒng)內(nèi)數(shù)據(jù)包可以實現(xiàn)轉(zhuǎn)發(fā)功能,代碼如下:

if ipForward {// Enable IPv4 forwardingif err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil {job.Logf("WARNING: unable to enable IPv4 forwarding: %s\n", err)} }

3.3.9 注冊網(wǎng)絡(luò)Handler

創(chuàng)建Docker Daemon網(wǎng)絡(luò)環(huán)境的最后一個步驟是:注冊4個與網(wǎng)絡(luò)相關(guān)的Handler。這4個Handler分別是allocate_interface、release_interface、allocate_port和link,作用分別是為Docker Container分配網(wǎng)絡(luò)設(shè)備,回收Docker Container網(wǎng)絡(luò)設(shè)備、為Docker Container分配端口資源、以及為Docker Container間執(zhí)行l(wèi)ink操作。

至此,Docker Daemon的網(wǎng)絡(luò)環(huán)境初始化工作全部完成。

4 總結(jié)

在工業(yè)界,Docker的網(wǎng)絡(luò)問題備受關(guān)注。Docker的網(wǎng)絡(luò)環(huán)境可以分為Docker Daemon網(wǎng)絡(luò)和Docker Container網(wǎng)絡(luò)。本文從Docker Daemon的網(wǎng)絡(luò)入手,分析了大家熟知的Docker 橋接模式。

Docker的容器技術(shù)以及鏡像技術(shù),已經(jīng)給Docker實踐者帶來了諸多效益。然而Docker網(wǎng)絡(luò)的發(fā)展依然具有很大的潛力。下一篇Docker Container網(wǎng)絡(luò)篇,將會帶來更為靈活的Docker網(wǎng)絡(luò)配置。

5 作者介紹

孫宏亮,DaoCloud初創(chuàng)團(tuán)隊成員,軟件工程師,浙江大學(xué)VLIS實驗室應(yīng)屆研究生。讀研期間活躍在PaaS和Docker開源社區(qū),對Cloud Foundry有深入研究和豐富實踐,擅長底層平臺代碼分析,對分布式平臺的架構(gòu)有一定經(jīng)驗,撰寫了大量有深度的技術(shù)博客。2014年末以合伙人身份加入DaoCloud團(tuán)隊,致力于傳播以Docker為主的容器的技術(shù),推動互聯(lián)網(wǎng)應(yīng)用的容器化步伐。郵箱:allen.sun@daocloud.io

6 參考文獻(xiàn)

http://www.cnblogs.com/iceocean/articles/1594195.html

http://docs.studygolang.com/pkg/net/

轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/9602909.html

總結(jié)

以上是生活随笔為你收集整理的Docker源码分析(六):Docker Daemon网络的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。