Docker容器的原理與實踐 (下)

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。html


Docker原理分析

Docker架構



鏡像原理

鏡像是一個只讀的容器模板,含有啓動docker容器所需的文件系統結構及內容
Docker以鏡像和在鏡像基礎上構建的容器爲基礎,以容器開發、測試、發佈的單元將應用相關的全部組件和環境進行封裝,避免了應用在不一樣平臺間遷移所帶來的依賴問題,確保了應用在生產環境的各階段達到高度一致的實際效果。linux

主要特色

  • 分層
    鏡像採用分層構建,每一個鏡像由一系列的鏡像層組成, 當須要修改容器內的某個文件時,只對處於最上方的讀寫層進行變更,不覆蓋下面已有文件系統的內容。 當提交這個修改過的容器文件系統爲一個新的鏡像時,保存的內容僅爲最上層讀寫文件系統中被更新過的文件。 docker


+ bootfs  主要包含bootloader和kernel, bootloader主要是引導加載kernel, 當容器啓動成功後,kernel被加載到內存中後而引導文件系統則會被卸載unmount+ rootfs  是容器在啓動時內部進程可見的文件系統,一般包含一個操做系統運行所需的文件系統
    + 傳統linux在內核啓動時首先會掛載一個只讀的rootfs,檢測器完整性以後再切換爲讀寫模式
    + docker在掛載rootfs時也將其設爲只讀模式,掛載完畢後利用聯合掛載技術在已有的只讀rootfs上再掛載一個讀寫層。
    + 只有運行中文件系統發生變化,纔會把變化的內容寫到讀寫層,並隱藏只讀層中的老版本文件
    + rootfs包含的就是典型Linux系統中的 /dev,/proc,/bin, /etc等標準目錄和文件。複製代碼
  • 寫時複製json

    • 能夠在多個容器之間共享鏡像,每一個容器啓動時不須要單獨複製一份鏡像文件api

    • 將全部鏡像層以只讀方式掛載到一個掛載點,在上面覆蓋一個可讀寫的容器層。安全

    • 寫時複製配合分層機制減小了鏡像對磁盤空間的佔用和容器啓動時間bash

  • 內容尋址網絡

    • 根據內容來索引鏡像和鏡像層架構

    • 是對鏡像層的內容計算檢驗和,生成一個內容哈希值做爲鏡像層的惟一標識app

    • 對於來自不一樣構建的鏡像層,只要擁有相同的內容哈希,也能被不一樣鏡像共享

  • 聯合掛載
    能夠在一個掛載點掛載多個文件系統,將掛載點的原目錄與被掛在內容進行整合,最終可見的文件系統將包含整合後各層的文件和目錄

    • 讀寫層處於容器文件系統的最頂層,其下可能聯合掛載多個只讀層。


存儲管理

爲了適應不一樣平臺不一樣場景的存儲需求,Docker提供了各類基於不一樣文件系統實現的存儲驅動來管理實際的鏡像文件


元數據管理

鏡像在設計上將元數據和文件存儲徹底隔離。Docker管理元數據採用的也正是從上至下repository、image、layer是3個層次。 因此repository與image兩個元數據並沒有物理上的鏡像文件與之對應,layer則存在物理上的鏡像文件與之對應。


  • 倉庫元數據
    文件中存儲了全部版本鏡像的名字和tag以及對應的鏡像ID(image/aufs)

  • 鏡像元數據
    文件中存儲了鏡像架構、操做系統、默認配置、該鏡像的容器ID和配置,構建鏡像的歷史信息以及rootfs組成(image/aufs/imagedb/content/sha256)

  • 分層元數據

    • 鏡像層
      描述不可改變的鏡像層(image/aufs/layerdb/sha256)

    • 容器層
      描述可讀寫的容器層(image/aufs/layerdb/mounts/),讀寫層的ID也對應容器的ID


存儲驅動

爲支持寫時複製特性,根據不一樣操做系統底層的支持提供不一樣的存儲驅動

  • aufs(advanced multi layered unification file system)
    是一種支持聯合掛載的文件系統,支持不一樣目錄掛載到同一個目錄,掛載對用戶來講是透明的。

    • 從最頂層的讀寫層開始向下尋找,本層沒有則根據層與層之間的關係到下一層找

    • 若是文件不存在則在讀寫層新建一個,不然向上面同樣從頂層開始查找,找到後複製到讀寫層進行修改

    • 若是文件僅僅在讀寫層則直接刪除;不然須要刪除讀寫層的備份,再在讀寫層中建立whiteout文件來標誌這個文件不存在,而不會真正刪除底層文件(會有.wh.開頭的隱藏文件)

    • 若是這個文件在讀寫層存在對應的whiteout文件則先將whiteout文件刪除再新建

    • 文件操做

  • btrfs

  • zfs

  • devicemapper

  • overlay
    overlayFS是一種新型聯合文件系統,容許一個文件系統與另一個文件系統重疊,在上層文件系統中記錄更改,而下層的文件系統保持不變。

  • vfs

    存儲目錄

/var/lib/docker  
               /aufs                                aufs驅動工做的目錄
                    /diff                           mount-id容器層,文件系統全部層的存儲目錄(下載的鏡像內容就保存在這裏)和容器層文件的差別變化會在這裏出現。
                         /mount-id-init             最後一個只讀層,用於掛載並從新生成dev/etc所列文件,這些文件與容器內的環境息息相關,但不適合被打包做爲鏡像的文件內容,
                                                    又不該該直接修改在宿主機文件上,因此設計了mountID-init層單獨處理這些文件。這一層只在容器啓動時添加。
                    /layers                         mount-id存儲上述全部aufs層之間的關係等元數據,記錄該層所依賴的全部其它層
                    /mnt                            aufs文件系統的掛載點,graphdriver會將diff中屬於容器鏡像的全部層目錄以只讀方式掛到mnt 
               /container                           容器配置文件目錄
               /image 
                     /aufs                          存儲鏡像與鏡像層元數據信息,真正的鏡像層內容保存在aufs/diff
                          /imagedb/content          存儲全部鏡像的元數據
                          /layerdb                  存儲全部鏡像層和容器層的元數據
                                  /mounts           存儲容器層元數據
                                         /init-id   init層id,這個layer存放的位置
                                         /mount-id  mount層id,這個layer存放的位置
                                         /parent    父layer的chain-id
                                  /share256         存儲鏡像層元數據
                                         /cache-id  該層數據存放的位置,在對應驅動目錄下
                                         /diff      標識每個layer
                                         /parent    標識父layer的chain-id
                                         /size      存放layer的數據大小
                                         /tar-split.json.gz 存放layer層的json信息
                          /repositories.json        記錄鏡像倉庫中全部鏡像的repository和tag名
               /volumes                             volumes的工做目錄,存放全部volume數據和元數據

讀寫層、volumes、init-layer、只讀層這幾部分結構共同組成了一個容器所需的文件系統。
diff-id:經過docker pull下載鏡像時,鏡像的json文件中每個layer都有一個惟一的diff-idchain-id:chain-id是根據parent的chain-id和自身的diff-id生成的,假如沒有parent,則chain-id等於diff-id,假若有parent,則chain-id等於sha256sum( 「parent-chain-id diff-id」)
cache-id:隨機生成的64個16進制數。cache-id標識了這個layer的數據具體存放位置複製代碼

數據卷

volume是存在於一個或多個容器中的特定文件或文件夾,這個目錄以獨立聯合文件系統的形式存在於宿主機中


  • 特色

    • 容器建立時就會初始化,在容器運行時就可使用其中的文件

    • 能在不一樣的容器中共享和重用

    • 對volume中的數據操做不會影響到鏡像自己

    • 生命週期獨立,即便刪除容器volume依然存在

Namespace

同一個namespace下的進程能夠感知彼此的變化,而對外界進程一無所知


  • UTS 隔離主機名與域名

  • IPC 隔離信號量、消息隊列和共享內存

  • PID 隔離進程編號

    • 不一樣的PID namespaces會造成一個層級體系

    • 每一個pid namespace的第一個進程 pid 1會像傳統linux的init進程號同樣擁有特權

  • Net 隔離網絡設備、網絡棧、端口

  • Mount 隔離掛載點(文件系統)

  • User 隔離用戶和用戶組


例子


mkdir newroot
cd newroot
cp -r /bin/ binchroot newrootexit複製代碼


Cgroup

根據需求把一系列的系統任務和子任務整合到按資源劃分等級的不一樣組內,從而爲系統資源管理提供一個統計的框架


主要做用

  • 資源限制

  • 優先級分配

  • 資源統計

  • 任務控制


主要特色

  • cgroup的api是以一個爲文件系統的方式實現,用戶態的程序能夠經過文件操做實現cgroup的組織管理

  • 組織管理操做單元能夠細粒度到線程級別,能夠建立和銷燬cgroup從而實現資源再分配和管理

  • 全部資源管理的功能都以子系統的方式實現,接口統一

  • 子任務建立之初與父任務處於同一個cgroups控制組


相關術語

task

表示系統的一個進程或線程


cgroup

按某種資源控制標準劃分而成的任務組,包含一個或多個子系統

  • 實現形式表現爲一個文件系統mount -t cgroup

  • docker實現

    • 會在單獨掛載了每個子系統的控制組目錄下建立一個名爲docker的控制組

    • 在docker控制組裏面再爲每一個容器建立一個容器id爲名稱的容器控制組

    • 容器裏的全部進程號都會寫到該控制組tasks中,並在控制組文件cpu.cfs_quota_us中寫入預設的限制參數值

  • 工做原理

    • 本質上來講,cgroups是內核附加在程序上的一系列鉤子,經過程序運行時對資源的調度觸發相應的鉤子以達到資源追蹤和限制的目的

    • 進程所需內存超過它所屬cgroup最大限制之後,若是設置了oom control,進程會收到oom信號並結束,不然進程會掛起,進入睡眠狀態,直到cgroup中的其餘進程釋放了足夠的內存資源爲止


subsystem

資源調度器,cpu子系統能夠控制cpu分配時間(/sys/fs/cgroup/cpu/docker/)


  • blkio 能夠爲塊設備設定輸入、輸出限制,好比物理驅動設備(磁盤、固態硬盤、USB)

  • cpu 使用調度程序控制任務對CPU的使用

  • cpuacct 自動生成cgroup中任務對cpu資源使用狀況的報告

  • cpuset 爲cgroup中的任務分配獨立的cpu和內存

  • devices 能夠開啓或關閉cgroup中任務對設備的訪問

  • freezer 能夠掛起或恢復cgroup中的任務

  • memory 能夠設定cgroup中任務對內存使用量的限定

  • perf_event 使用後使cgroup中的任務能夠進行統一的性能測試

  • net_cls 經過使用等級識別符標記網絡數據包,從而容許linux流量控制程序(traffic controller)識別從具體cgroup中生成的數據包


hierachy

層級是由一系列cgroup以一個樹狀結構排列而成,每一個層級經過綁定對應的子系統進行資源控制

依賴其餘的內核能力

  • seccomp(secure computing mode)
    安全計算模式,這個模式能夠設置容器在對系統進行調用時進行一些篩選,也就是所謂的白名單。 是一種簡潔的sandboxing機制。能使一個進程進入到一種「安全」運行模式,該模式下的進程只能調用4種系統調用(system calls),即read(), write(), exit()和sigreturn(),不然進程便會被終止。

  • SELinux
    安全加強式Linux(SELinux, Security-Enhanced Linux)是一種強制訪問控制(mandatory access control)的實現

  • Netlink
    用來讓不一樣的容器之間進行通訊,可用於進程間通訊,Linux內核與用戶空間的進程間、用戶進程間的通信

  • Netfilter
    Linux內核中的一個軟件框架,用於管理網絡數據包。
    不只具備網絡地址轉換(NAT)的功能,也具有數據包內容修改、以及數據包過濾等防火牆功能。

  • AppArmor
    相似於selinux,主要的做用是設置某個可執行程序的訪問控制權限,能夠限制程序 讀/寫某個目錄/文件,打開/讀/寫網絡端口等等

  • capability
    Linux把原來和超級用戶相關的高級權限劃分紅爲不一樣的單元,稱爲Capability,能夠單獨啓用或者關閉, 它打破了UNIX/LINUX操做系統中超級用戶/普通用戶的概念,由普通用戶也能夠作只有超級用戶能夠完成的工做


網絡原理

一些概念

  • net namespace
    隔離網絡棧,有本身的接口、路由、防火牆規則

  • bridge
    至關於交換機,爲鏈接在其上的設備轉發數據幀

  • veth
    至關於交換機上的端口,是一對虛擬網卡,用於不一樣網絡空間進行通訊的方式,從一張veth網卡發出的數據包能夠直接到達它的peer veth

  • gateway
    就是一個網絡鏈接到另外一個網絡的「關口」,與本地網絡鏈接的機器會把向外的流量傳遞到此地址中從而使那個地址成爲本地子網之外的IP地址的"網關".

  • iptables
    linux內核的包過濾系統。在三、4層提供規則鏈對包進行標記、假裝、轉發等


兩個例子

經過虛擬網卡實現2個namespace通訊

一、建立虛擬網絡環境
ip netns add net0
ip netns add net十二、在虛擬的net0環境中執行
ip netns exec net0 ifconfig -aip netns exec net0 ping localhost
ip netns exec net0 ip link set lo up
ip netns exec net0 ping localhost三、建立一對虛擬網卡
ip link add type veth四、把veth0移動到net0環境裏面,把veth1移動到net1環境裏面
ip link set veth0 netns net0
ip link set veth1 netns net1四、配置虛擬網卡
ip netns exec net0 ip link set veth0 up
ip netns exec net0 ip address add 10.0.1.1/24 dev veth0
ip netns exec net1 ip link set veth1 up
ip netns exec net1 ip address add 10.0.1.2/24 dev veth1五、測試
ip netns exec net0 ping -c 3 10.0.1.2ip netns exec net0 ping -c 3 10.0.1.1ip netns exec net1 ping -c 3 10.0.1.1複製代碼

經過網橋實現多個namespace通訊及外網訪問原理

一、建立虛擬網絡環境並鏈接網線
ip netns add net0
ip netns add net1
ip netns add bridge

ip link add type veth
ip link set dev veth0 name net0-bridge netns net0
ip link set dev veth1 name bridge-net0 netns bridge

ip link add type veth
ip link set dev veth0 name net1-bridge netns net1
ip link set dev veth1 name bridge-net1 netns bridge二、在bridge中建立虛擬網橋
ip netns exec bridge brctl addbr br
ip netns exec bridge ip link set dev br up
ip netns exec bridge ip link set dev bridge-net0 up
ip netns exec bridge ip link set dev bridge-net1 up
ip netns exec bridge brctl addif br bridge-net0
ip netns exec bridge brctl addif br bridge-net1
ip netns exec bridge brctl show三、配置虛擬環境網卡
ip netns exec net0 ip link set dev net0-bridge up
ip netns exec net0 ip address add 10.0.1.1/24 dev net0-bridge

ip netns exec net1 ip link set dev net1-bridge up
ip netns exec net1 ip address add 10.0.1.2/24 dev net1-bridge四、測試
ip netns exec net0 ping -c 3 10.0.1.2ip netns exec net1 ping -c 3 10.0.1.1ip netns exec bridge ping -c 3 10.0.1.2五、須要給當前網絡環境配置一個網卡ip否則沒有網絡路由網絡不可達
ip netns exec bridge ip address add 10.0.1.3/24 dev br六、想要ping宿主機網絡怎麼辦?
ip netns exec bridge ping 192.168.99.100ip link add A type veth peer name B
ip link set B netns bridge
ip netns exec bridge ip link set dev B up

ip link set A up六、給AB網卡配置ip
ip netns exec bridge ip address add 172.22.0.11/24 dev B
ip address add 172.22.0.10/24 dev A七、設置bridge默認網關
ip netns exec bridge route add default gw 172.22.0.10ip netns exec bridge ping 192.168.99.100八、設置net0默認網關
ip netns exec net0 ping 192.168.99.100ip netns exec net0 route add default gw 10.0.1.3ip netns exec net0 ping 192.168.99.100 不通九、地址假裝
ip netns exec bridge iptables -t nat -A POSTROUTING -o B -j MASQUERADE
ip netns exec net0 ping 192.168.99.100十、讓虛擬網卡訪問外網
ip netns exec net0 ping 8.8.8.8iptables -t filter -I FORWARD -o A -j ACCEPT
iptables -t filter -I FORWARD -i A -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -s 172.22.0.0/24 -j MASQUERADE十一、vbox訪問mac地址
ip netns exec bridge ping 192.168.99.1iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
ip netns exec bridge ping 192.168.99.1複製代碼

影響網絡連通的幾個要素


  • 內核是否開啓IP轉發支持
    cat /proc/sys/net/ipv4/ip_forward
    sysctl -w net.ipv4.ip_forward=1

  • 防火牆轉發規則是否打開

  • 是否開啓IP地址假裝

  • 是否正確設置網關

CNM網絡模型

  • 沙盒,一個沙盒包含了一個容器網絡棧信息。沙盒能夠對容器的接口、路由和DNS設置等進行管理。一個沙盒能夠有多個端點和多個網絡。

  • 端點,一個端點能夠加入一個沙盒和一個網絡。端點的實現可使veth pair、open vSwitch內部端口。

  • 網絡,一個網絡是一組能夠互相聯通的端點。網絡的實現能夠是linux bridge、VLAN等。

內置驅動




  • bridge
    默認設置,libnetwork將建立出來的容器鏈接到docker網橋。其與外界使用NAT.

    操做例子

    docker network create br3
      docker run -itd --name busybox-bridge --net=br3 busybox
    
      docker run -itd --name busybox-bridge-none busybox
      docker network connect br3 busybox-bridge-none
      查看容器網絡和宿主機網橋及其上端口複製代碼
  • host
    libnetwork不爲docker容器建立網絡協議棧及獨立的network namespace。

    • 容器使用宿主機的網卡、IP、端口、路由、iptable規則

    • host驅動很好的解決了容器與外界通訊的地址轉換問題,但也下降了容器與容器建、容器與宿主機之間的網絡隔離性,引發網絡資源競爭的衝突。

      例子

      docker run -itd --name busybox-host --net=host busybox複製代碼
  • container

    • 指定新建立的容器和已存在的容器共享一個網絡空間

    • 兩個容器的進程能夠經過lo網卡設備通訊

      例子

      docker run -itd --name busybox busyboxdocker run -itd --name busybox-container --net=container:busybox-bridge busybox複製代碼
  • none
    容器擁有本身的network namespace,但不進行任何網絡配置。

    例子


docker run -itd --name busybox-none --net=none busybox複製代碼
  • overlay

    • 使用標準的VXLAN

    • 使用過程當中須要一個額外的配置存儲服務如consul、etcd、Zookeeper

    • 須要在daemon啓動的時候額外添加參數來指定配置存儲服務地址

  • remote
    實現插件化,調用用戶自行實現的網絡驅動插件

DNAT來實現外部訪問容器

docker run -itd -p 9901:991 --name busybox-dnat busybox複製代碼


容器雲平臺


雲平臺廠家


若是你從頭看到尾那真是太棒了,若是你也對docker技術感興趣歡迎popo(hzchenzhiliang@corp.netease.com)聯繫交流。

相關閱讀:Docker容器的原理與實踐(上)

本文來自網易實踐者社區,經做者陳志良受權發佈。


相關文章:
【推薦】 KOL運營之——如何與網文做者高效地約稿?
【推薦】 ThreeJs 3D 全景項目開發總結
【推薦】 移動端爬蟲工具與方法介紹

相關文章
相關標籤/搜索