歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
Docker原理分析
Docker架構
映象原理
映象是一個只讀的容器模板,含有啟動docker容器所需的檔案系統結構及內容
Docker以映象和在映象基礎上構建的容器為基礎,以容器開發、測試、釋出的單元將應用相關的所有元件和環境進行封裝,避免了應用在不同平臺間遷移所帶來的依賴問題,確保了應用在生產環境的各階段達到高度一致的實際效果。
主要特點
分層
映象採用分層構建,每個映象由一系列的映象層組成, 當需要修改容器內的某個檔案時,只對處於最上方的讀寫層進行變動,不覆蓋下面已有檔案系統的內容。 當提交這個修改過的容器檔案系統為一個新的映象時,儲存的內容僅為最上層讀寫檔案系統中被更新過的檔案。
+ bootfs 主要包含bootloader和kernel, bootloader主要是引導載入kernel, 當容器啟動成功後,kernel被載入到記憶體中後而引導檔案系統則會被解除安裝unmount+ rootfs 是容器在啟動時內部程式可見的檔案系統,通常包含一個作業系統執行所需的檔案系統
+ 傳統linux在核心啟動時首先會掛載一個只讀的rootfs,檢測器完整性之後再切換為讀寫模式
+ docker在掛載rootfs時也將其設為只讀模式,掛載完畢後利用聯合掛載技術在已有的只讀rootfs上再掛載一個讀寫層。
+ 只有執行中檔案系統發生變化,才會把變化的內容寫到讀寫層,並隱藏只讀層中的老版本檔案
+ rootfs包含的就是典型Linux系統中的 /dev,/proc,/bin, /etc等標準目錄和檔案。複製程式碼
寫時複製
可以在多個容器之間共享映象,每個容器啟動時不需要單獨複製一份映象檔案
將所有映象層以只讀方式掛載到一個掛載點,在上面覆蓋一個可讀寫的容器層。
寫時複製配合分層機制減少了映象對磁碟空間的佔用和容器啟動時間
內容定址
根據內容來索引映象和映象層
是對映象層的內容計算檢驗和,生成一個內容雜湊值作為映象層的唯一標識
對於來自不同構建的映象層,只要擁有相同的內容雜湊,也能被不同映象共享
聯合掛載
可以在一個掛載點掛載多個檔案系統,將掛載點的原目錄與被掛在內容進行整合,最終可見的檔案系統將包含整合後各層的檔案和目錄讀寫層處於容器檔案系統的最頂層,其下可能聯合掛載多個只讀層。
儲存管理
為了適應不同平臺不同場景的儲存需求,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 vethgateway
就是一個網路連線到另一個網路的“關口”,與本地網路連線的機器會把向外的流量傳遞到此地址中從而使那個地址成為本地子網以外的IP地址的"閘道器".iptables
linux核心的包過濾系統。在3、4層提供規則鏈對包進行標記、偽裝、轉發等
兩個例子
通過虛擬網路卡實現2個namespace通訊
1、建立虛擬網路環境
ip netns add net0
ip netns add net12、在虛擬的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 localhost3、建立一對虛擬網路卡
ip link add type veth4、把veth0移動到net0環境裡面,把veth1移動到net1環境裡面
ip link set veth0 netns net0
ip link set veth1 netns net14、配置虛擬網路卡
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 veth15、測試
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通訊及外網訪問原理
1、建立虛擬網路環境並連線網線
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 bridge2、在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 show3、配置虛擬環境網路卡
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-bridge4、測試
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.25、需要給當前網路環境配置一個網路卡ip不然沒有網路路由網路不可達
ip netns exec bridge ip address add 10.0.1.3/24 dev br6、想要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 up6、給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 A7、設定bridge預設閘道器
ip netns exec bridge route add default gw 172.22.0.10ip netns exec bridge ping 192.168.99.1008、設定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 不通9、地址偽裝
ip netns exec bridge iptables -t nat -A POSTROUTING -o B -j MASQUERADE
ip netns exec net0 ping 192.168.99.10010、讓虛擬網路卡訪問外網
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 MASQUERADE11、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 全景專案開發總結
【推薦】 移動端爬蟲工具與方法介紹