<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC=3] ## 1.前言(什么是Docker Container) 如今,Docker技術大行其道,大家在嘗試以及玩轉Docker的同時,肯定離不開一個概念,那就是“容器”或者“Docker Container”。那么我們首先從實現的角度來看看“容器”或者“Docker Container”到底為何物。 逐漸熟悉Docker之后,大家肯定會深深得感受到:應用程序在Docker Container內部的部署與運行非常便捷,只要有Dockerfile,應用一鍵式的部署運行絕對不是天方夜譚; Docker Container內運行的應用程序可以受到資源的控制與隔離,大大滿足云計算時代應用的要求。毋庸置疑,Docker的這些特性,傳統模式下應用是完全不具備的。然而,這些令人眼前一亮的特性背后,到底是誰在“作祟”,到底是誰可以支撐Docker的這些特性?不知道這個時候,大家是否會聯想到強大的Linux內核。 其實,這很大一部分功能都需要歸功于Linux內核。那我們就從Linux內核的角度來看看Docker到底為何物,先從Docker Container入手。關于Docker Container,體驗過的開發者第一感覺肯定有兩點:內部可以跑應用(進程),以及提供隔離的環境。當然,后者肯定也是工業界稱之為“容器”的原因之一。 既然Docker Container內部可以運行進程,那么我們先來看Docker Container與進程的關系,或者容器與進程的關系。首先,我提出這樣一個問題供大家思考“容器是否可以脫離進程而存在”。換句話說,能否創建一個容器,而這個容器內部沒有任何進程。 可以說答案是否定的。既然答案是否定的,那說明不可能先有容器,然后再有進程,那么問題又來了,“容器和進程是一起誕生,還是先有進程再有容器呢?”可以說答案是后者。以下將慢慢闡述其中的原因。 闡述問題“容器是否可以脫離進程而存在”的原因前,相信大家對于以下的一段話不會持有異議:通過Docker創建出的一個Docker Container是一個容器,而這個容器提供了進程組隔離的運行環境。那么問題在于,容器到底是通過何種途徑來實現進程組運行環境的“隔離”。這時,就輪到Linux內核技術隆重登場了。 說到運行環境的“隔離”,相信大家肯定對Linux的內核特性namespace和cgroup不會陌生。namespace主要負責命名空間的隔離,而cgroup主要負責資源使用的限制。其實,正是這兩個神奇的內核特性聯合使用,才保證了Docker Container的“隔離”。那么,namespace和cgroup又和進程有什么關系呢?問題的答案可以用以下的次序來說明: (1) 父進程通過fork創建子進程時,使用namespace技術,實現子進程與其他進程(包含父進程)的命名空間隔離; (2) 子進程創建完畢之后,使用cgroup技術來處理子進程,實現進程的資源使用限制; (3) 系統在子進程所處namespace內部,創建需要的隔離環境,如隔離的網絡棧等; (4) namespace和cgroup兩種技術都用上之后,進程所處的“隔離”環境才真正建立,這時“容器”才真正誕生! 從Linux內核的角度分析容器的誕生,精簡的流程即如以上4步,而這4個步驟也恰好巧妙的闡述了namespace和cgroup這兩種技術和進程的關系,以及進程與容器的關系。進程與容器的關系,自然是:容器不能脫離進程而存在,先有進程,后有容器。然而,大家往往會說到“使用Docker創建Docker Container(容器),然后在容器內部運行進程”。對此,從通俗易懂的角度來講,這完全可以理解,因為“容器”一詞的存在,本身就較為抽象。如果需要更為準確的表述,那么可以是:“使用Docker創建一個進程,為這個進程創建隔離的環境,這樣的環境可以稱為Docker Container(容器),然后再在容器內部運行用戶應用進程。”當然,筆者的本意不是想否定很多人對于Docker Container或者容器的認識,而是希望和讀者一起探討Docker Container底層技術實現的原理。 對于Docker Container或者容器有了更加具體的認識之后,相信大家的眼球肯定會很快定位到namespace和cgroup這兩種技術。Linux內核的這兩種技術,竟然能起到如此重大的作用,不禁為之贊嘆。那么下面我們就從Docker Container實現流程的角度簡要介紹這兩者。 首先講述一下namespace在容器創建時的用法,首先從用戶創建并啟動容器開始。當用戶創建并啟動容器時,Docker Daemon 會fork出容器中的第一個進程A(暫且稱為進程A,也就是Docker Daemon的子進程)。Docker Daemon執行fork時,在clone系統調用階段會傳入5個參數標志CLONE_NEWNS、CLONE_NEWUTS、CLONE_NEWIPC、CLONE_NEWPID和CLONE_NEWNET(目前Docker 1.2.0還沒有完全支持user namespace)。Clone系統調用一旦傳入了這些參數標志,子進程將不再與父進程共享相同的命名空間(namespace),而是由Linux為其創建新的命名空間(namespace),從而保證子進程與父進程使用隔離的環境。另外,如果子進程A再次fork出子進程B和C,而fork時沒有傳入相應的namespace參數標志,那么此時子進程B和C將會與A共享同一個命令空間(namespace)。如果Docker Daemon再次創建一個Docker Container,容器內第一個進程為D,而D又fork出子進程E和F,那么這三個進程也會處于另外一個新的namespace。兩個容器的namespace均與Docker Daemon所在的namespace不同。Docker關于namespace的簡易示意圖如下: ![](https://box.kancloud.cn/2015-11-12_56443b0a0db51.jpg) 圖1.1 Docker中namespace示意圖 再說起cgroup,大家都知道可以使用cgroup為進程組做資源的控制。與namespace不同的是,cgroup的使用并不是在創建容器內進程時完成的,而是在創建容器內進程之后再使用cgroup,使得容器進程處于資源控制的狀態。換言之,cgroup的運用必須要等到容器內第一個進程被真正創建出來之后才能實現。當容器內進程被創建完畢,Docker Daemon可以獲知容器內進程的PID信息,隨后將該PID放置在cgroup文件系統的指定位置,做相應的資源限制。 可以說Linux內核的namespace和cgroup技術,實現了資源的隔離與限制。那么對于這種隔離與受限的環境,是否還需要配置其他必需的資源呢。這回答案是肯定的,網絡棧資源就是在此時為容器添加。當為容器進程創建完隔離的運行環境時,發現容器雖然已經處于一個隔離的網絡環境(即新的network namespace),但是進程并沒有獨立的網絡棧可以使用,如獨立的網絡接口設備等。此時,Docker Daemon會將Docker Container所需要的資源一一為其配備齊全。網絡方面,則需要按照用戶指定的網絡模式,配置Docker Container相應的網絡資源。 ## 2.Docker Container網絡分析內容安排 Docker Container網絡篇將從源碼的角度,分析Docker Container從無到有的過程中,Docker Container網絡創建的來龍去脈。Docker Container網絡創建流程可以簡化如下圖: ![](https://box.kancloud.cn/2015-11-12_56443b0a3e595.jpg) 圖2.1 Docker Container網絡創建流程圖 Docker Container網絡篇分析的主要內容有以下5部分: (1) Docker Container的網絡模式; (2) Docker Client配置容器網絡; (3) Docker Daemon創建容器網絡流程; (4) execdriver網絡執行流程; (5) libcontainer實現內核態網絡配置。 Docker Container網絡創建過程中,networkdriver模塊使用并非是重點,故分析內容中不涉及networkdriver。這里不少讀者肯定會有疑惑。需要強調的是,networkdriver在Docker中的作用:第一,為Docker Daemon創建網絡環境的時候,初始化Docker Daemon的網絡環境(詳情可以查看《Docker源碼分析》系列第六篇),比如創建docker0網橋等;第二,為Docker Container分配IP地址,為Docker Container做端口映射等。而與Docker Container網絡創建有關的內容極少,只有在橋接模式下,為Docker Container的網絡接口設備分配一個可用IP地址。 本文為[《Docker源碼分析》系列](http://www.infoq.com/cn/author/%E5%AD%99%E5%AE%8F%E4%BA%AE)第七篇——Docker Container網絡(上)。 ## 3.Docker Container網絡模式 正如在上文提到的,Docker可以為Docker Container創建隔離的網絡環境,在隔離的網絡環境下,Docker Container獨立使用私有網絡。相信很多的Docker開發者也是體驗過Docker這方面的網絡特性。 其實,Docker除了可以為Docker Container創建隔離的網絡環境之外,同樣有能力為Docker Container創建共享的網絡環境。換言之,當開發者需要Docker Container與宿主機或者其他容器網絡隔離時,Docker可以滿足這樣的需求;而當開發者需要Docker Container與宿主機或者其他容器共享網絡時,Docker同樣可以滿足這樣的需求。另外,Docker還可以不為Docker Container創建網絡環境。 總結Docker Container的網絡,可以得出4種不同的模式:bridge橋接模式、host模式、other container模式和none模式。以下初步介紹4中不同的網絡模式。 ### 3.1 bridge橋接模式 Docker Container的bridge橋接模式可以說是目前Docker開發者最常使用的網絡模式。Brdige橋接模式為Docker Container創建獨立的網絡棧,保證容器內的進程組使用獨立的網絡環境,實現容器間、容器與宿主機之間的網絡棧隔離。另外,Docker通過宿主機上的網橋(docker0)來連通容器內部的網絡棧與宿主機的網絡棧,實現容器與宿主機乃至外界的網絡通信。 Docker Container的bridge橋接模式可以參考下圖: ![](https://box.kancloud.cn/2015-11-12_56443b0a5334c.jpg) 圖3.1 Docker Container Bridge橋接模式示意圖 Bridge橋接模式的實現步驟主要如下: (1) Docker Daemon利用veth pair技術,在宿主機上創建兩個虛擬網絡接口設備,假設為veth0和veth1。而veth pair技術的特性可以保證無論哪一個veth接收到網絡報文,都會將報文傳輸給另一方。 (2) Docker Daemon將veth0附加到Docker Daemon創建的docker0網橋上。保證宿主機的網絡報文可以發往veth0; (3) Docker Daemon將veth1添加到Docker Container所屬的namespace下,并被改名為eth0。如此一來,保證宿主機的網絡報文若發往veth0,則立即會被eth0接收,實現宿主機到Docker Container網絡的聯通性;同時,也保證Docker Container單獨使用eth0,實現容器網絡環境的隔離性。 Bridge橋接模式,從原理上實現了Docker Container到宿主機乃至其他機器的網絡連通性。然而,由于宿主機的IP地址與veth pair的 IP地址均不在同一個網段,故僅僅依靠veth pair和namespace的技術,還不足以是宿主機以外的網絡主動發現Docker Container的存在。為了使得Docker Container可以讓宿主機以外的世界感知到容器內部暴露的服務,Docker采用NAT(Network Address Translation,網絡地址轉換)的方式,讓宿主機以外的世界可以主動將網絡報文發送至容器內部。 具體來講,當Docker Container需要暴露服務時,內部服務必須監聽容器IP和端口號port_0,以便外界主動發起訪問請求。由于宿主機以外的世界,只知道宿主機eth0的網絡地址,而并不知道Docker Container的IP地址,哪怕就算知道Docker Container的IP地址,從二層網絡的角度來講,外界也無法直接通過Docker Container的IP地址訪問容器內部應用。因此,Docker使用NAT方法,將容器內部的服務監聽的端口與宿主機的某一個端口port_1進行“綁定”。 如此一來,外界訪問Docker Container內部服務的流程為: (1) 外界訪問宿主機的IP以及宿主機的端口port_1; (2) 當宿主機接收到這樣的請求之后,由于DNAT規則的存在,會將該請求的目的IP(宿主機eth0的IP)和目的端口port_1進行轉換,轉換為容器IP和容器的端口port_0; (3) 由于宿主機認識容器IP,故可以將請求發送給veth pair; (4) veth pair的veth0將請求發送至容器內部的eth0,最終交給內部服務進行處理。 使用DNAT方法,可以使得Docker宿主機以外的世界主動訪問Docker Container內部服務。那么Docker Container如何訪問宿主機以外的世界呢。以下簡要分析Docker Container訪問宿主機以外世界的流程: (1) Docker Container內部進程獲悉宿主機以外服務的IP地址和端口port_2,于是Docker Container發起請求。容器的獨立網絡環境保證了請求中報文的源IP地址為容器IP(即容器內部eth0),另外Linux內核會自動為進程分配一個可用源端口(假設為port_3); (2) 請求通過容器內部eth0發送至veth pair的另一端,到達veth0,也就是到達了網橋(docker0)處; (3) docker0網橋開啟了數據報轉發功能(/proc/sys/net/ipv4/ip_forward),故將請求發送至宿主機的eth0處; (4) 宿主機處理請求時,使用SNAT對請求進行源地址IP轉換,即將請求中源地址IP(容器IP地址)轉換為宿主機eth0的IP地址; (5) 宿主機將經過SNAT轉換后的報文通過請求的目的IP地址(宿主機以外世界的IP地址)發送至外界。 在這里,很多人肯定會問:對于Docker Container內部主動發起對外的網絡請求,當請求到達宿主機進行SNAT處理后發給外界,當外界響應請求時,響應報文中的目的IP地址肯定是Docker宿主機的IP地址,那響應報文回到宿主機的時候,宿主機又是如何轉給Docker Container的呢?關于這樣的響應,由于port_3端口并沒有在宿主機上做相應的DNAT轉換,原則上不會被發送至容器內部。為什么說對于這樣的響應,不會做DNAT轉換呢。原因很簡單,DNAT轉換是針對容器內部服務監聽的特定端口做的,該端口是供服務監聽使用,而容器內部發起的請求報文中,源端口號肯定不會占用服務監聽的端口,故容器內部發起請求的響應不會在宿主機上經過DNAT處理。 其實,這一環節的內容是由iptables規則來完成,具體的iptables規則如下: ~~~ iptables -I FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT ~~~ 這條規則的意思是,在宿主機上發往docker0網橋的網絡數據報文,如果是該數據報文所處的連接已經建立的話,則無條件接受,并由Linux內核將其發送到原來的連接上,即回到Docker Container內部。 以上便是Docker Container中bridge橋接模式的簡要介紹。可以說,bridger橋接模式從功能的角度實現了兩個方面:第一,讓容器擁有獨立、隔離的網絡棧;第二,讓容器和宿主機以外的世界通過NAT建立通信。 然而,bridge橋接模式下的Docker Container在使用時,并非為開發者包辦了一切。最明顯的是,該模式下Docker Container不具有一個公有IP,即和宿主機的eth0不處于同一個網段。導致的結果是宿主機以外的世界不能直接和容器進行通信。雖然NAT模式經過中間處理實現了這一點,但是NAT模式仍然存在問題與不便,如:容器均需要在宿主機上競爭端口,容器內部服務的訪問者需要使用服務發現獲知服務的外部端口等。另外NAT模式由于是在三層網絡上的實現手段,故肯定會影響網絡的傳輸效率。 ### 3.2 host模式 Docker Container中的host模式與bridge橋接模式有很大的不同。最大的區別當屬,host模式并沒有為容器創建一個隔離的網絡環境。而之所以稱之為host模式,是因為該模式下的Docker Container會和host宿主機共享同一個網絡namespace,故Docker Container可以和宿主機一樣,使用宿主機的eth0,實現和外界的通信。換言之,Docker Container的IP地址即為宿主機eth0的IP地址。 Docker Container的host網絡模式可以參考下圖: ![](https://box.kancloud.cn/2015-11-12_56443b0a68562.jpg) 圖3.2 Docker Container host網絡模式示意圖 上圖最左側的Docker Container,即采用了host網絡模式,而其他兩個Docker Container依然沿用brdige橋接模式,兩種模式同時存在于宿主機上并不矛盾。 Docker Container的host網絡模式在實現過程中,由于不需要額外的網橋以及虛擬網卡,故不會涉及docker0以及veth pair。上文namespace的介紹中曾經提到,父進程在創建子進程時,如果不使用CLONE_NEWNET這個參數標志,那么創建出的子進程會與父進程共享同一個網絡namespace。Docker就是采用了這個簡單的原理,在創建進程啟動容器的過程中,沒有傳入CLONE_NEWNET參數標志,實現Docker Container與宿主機共享同一個網絡環境,即實現host網絡模式。 可以說,Docker Container的網絡模式中,host模式是bridge橋接模式很好的補充。采用host模式的Docker Container,可以直接使用宿主機的IP地址與外界進行通信,若宿主機的eth0是一個公有IP,那么容器也擁有這個公有IP。同時容器內服務的端口也可以使用宿主機的端口,無需額外進行NAT轉換。當然,有這樣的方便,肯定會損失部分其他的特性,最明顯的是Docker Container網絡環境隔離性的弱化,即容器不再擁有隔離、獨立的網絡棧。另外,使用host模式的Docker Container雖然可以讓容器內部的服務和傳統情況無差別、無改造的使用,但是由于網絡隔離性的弱化,該容器會與宿主機共享競爭網絡棧的使用;另外,容器內部將不再擁有所有的端口資源,原因是部分端口資源已經被宿主機本身的服務占用,還有部分端口已經用以bridge網絡模式容器的端口映射。 ### 3.3 other container模式 Docker Container的other container網絡模式是Docker中一種較為特別的網絡的模式。之所以稱為“other container模式”,是因為這個模式下的Docker Container,會使用其他容器的網絡環境。之所以稱為“特別”,是因為這個模式下容器的網絡隔離性會處于bridge橋接模式與host模式之間。Docker Container共享其他容器的網絡環境,則至少這兩個容器之間不存在網絡隔離,而這兩個容器又與宿主機以及除此之外其他的容器存在網絡隔離。 Docker Container的other container網絡模式可以參考下圖: ![](https://box.kancloud.cn/2015-11-12_56443b0a807d1.jpg) 圖3.3 Docker Container other container網絡模式示意圖 上圖右側的Docker Container即采用了other container網絡模式,它能使用的網絡環境即為左側Docker Container brdige橋接模式下的網絡。 Docker Container的other container網絡模式在實現過程中,不涉及網橋,同樣也不需要創建虛擬網卡veth pair。完成other container網絡模式的創建只需要兩個步驟: (1) 查找other container(即需要被共享網絡環境的容器)的網絡namespace; (2) 將新創建的Docker Container(也是需要共享其他網絡的容器)的namespace,使用other container的namespace。 Docker Container的other container網絡模式,可以用來更好的服務于容器間的通信。 在這種模式下的Docker Container可以通過localhost來訪問namespace下的其他容器,傳輸效率較高。雖然多個容器共享網絡環境,但是多個容器形成的整體依然與宿主機以及其他容器形成網絡隔離。另外,這種模式還節約了一定數量的網絡資源。但是需要注意的是,它并沒有改善容器與宿主機以外世界通信的情況。 ### 3.4 none模式 Docker Container的第四種網絡模式是none模式。顧名思義,網絡環境為none,即不為Docker Container任何的網絡環境。一旦Docker Container采用了none網絡模式,那么容器內部就只能使用loopback網絡設備,不會再有其他的網絡資源。 可以說none模式為Docker Container做了極少的網絡設定,但是俗話說得好“少即是多”,在沒有網絡配置的情況下,作為Docker開發者,才能在這基礎做其他無限多可能的網絡定制開發。這也恰巧體現了Docker設計理念的開放。 ## 4.作者介紹 孫宏亮,[DaoCloud](http://www.daocloud.io/)初創團隊成員,軟件工程師,浙江大學VLIS實驗室應屆研究生。讀研期間活躍在PaaS和Docker開源社區,對Cloud Foundry有深入研究和豐富實踐,擅長底層平臺代碼分析,對分布式平臺的架構有一定經驗,撰寫了大量有深度的技術博客。2014年末以合伙人身份加入DaoCloud團隊,致力于傳播以Docker為主的容器的技術,推動互聯網應用的容器化步伐。郵箱:[allen.sun@daocloud.io](mailto:allen.sun@daocloud.io) ## 5.下期預告 下期內容為:Docker源碼分析(八):Docker Container網絡(下)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看