docker 核心

1. 基本架構

    Docker 包括客戶端,服務端兩大組件

  1.1 服務端

    Docker 服務端一般在宿主主機後臺運行,dockerd 作爲服務端接受來自客戶的請求,並 通過 containerd 具體處理與容器相關的請求,包括創建,運行,刪除容器等 服務端主要包 括四個組件:

dockerd: 客戶端提REST API , 響應來自客戶端的請求,採用模塊化的架構,通過專門的Engine模塊來分發 理各個來自客戶端的任務 可以單獨升級;

docker-proxy:是dockerd 的子進程,當需要進行容器端口映射時,docker-proxy 完成網絡映射配置

containerd:是dockerd 的子進程,gRPC 接口響應來自 dockerd 的請求,對下管理 runC 鏡像和容器環境可以單獨升級;

containerd-shim:是 containerd 的子進程,爲runC 容器提供支持,同時作爲容器內進程的根進程

    只允許本地的root用戶或docker用戶組成員訪問。可以通過-H選項來修改監聽方式

dockerd -H 127.0.0.1:1234

    Docker還支持通過TLS認證方式來驗證

    docker-proxy只有當啓動容器並且使用端口映射時候纔會執行,

docker run -itd -p 80:80 ubuntu:latest /bin/bash

1.2 客戶端

    用戶使用的 Docker 可執行命令即爲客戶端程序。與Docker 服務端保持運行方式不同, 客戶端發送命令後,等待服務端返回;一旦收到返回後,客戶端立刻執行結束並退出。用戶 執行新的命令,需要再次調用客戶端命令

docker -H tcp://127.0.0.1:1234 info

2. 命名空間

    命名空間(namespace)是Linux內核的一個強大特性,每個容器都可以擁有自己單獨的命名空間,運行在其中的應用都像是在獨立的操作系統環境中一樣。

    在操作系統中,包括內核,文件系統、網絡、進程號( Process ID, PID )、用戶號( User ID, UID 進程間通信( InterProcess Communication, IPC )等資源,所有的資源都是應用進程直接共享的。

2.1 進程命名空間

    Linux 通過進程命名空間管理進程號,對於同一進程(同一個task struct ),在不同的命名空間中,看到的進程號不相同。 每個進程命名空間有一套自己的進程號管理方法 進程命 名空間是一個父子關係的結構,子空間中的進程對於父 間是可見的

root@9c991b075087:/# ps -ef |grep docker 
root        36    26  0 08:25 pts/1    00:00:00 grep --color=auto docker

    新建一個ubuntu容器,執行sleep名。此時,docker-containerd進程作爲父進程,會爲每個容器啓動一個docker-containerd-shim進程,作爲該容器內所有進程的根進程 

docker run --name test -d ubuntu sleep 9999

從宿主機上查看新建容器的進程的父進程

ps -ef|grep sleep

2.2 IPC命名空間

    容器中的進程交互採用Linux常見的進程交互方法(Interprocess Communication,IPC)包括信號量,信息隊列和共享內存等方式。PID命名空間和IPC命名空間可以組合起來一起使用,同個IPC命名空間內的進程可以彼此可見,允許交互;不同空間的進程則無法交互

2.3 網絡命名空間

    有了進程命名空間後,不同命名空間中的進程號可以相互隔離,但是網絡端口端口還是共享本地系統的端口。

    通過網絡命名空間,可以實現網絡隔離。一個網絡命名空間爲進程提供了一個完全獨立的網絡協議棧的視圖。包括網絡設備接口,IPV4和IPV6協議棧,IP路由表,防火牆規則,sockets等,這樣每個容器的網絡就能隔離開來。

    Docker 採用虛擬網絡社保(Virtual Network Device, VND)的方式,講不同命名空間的網絡設備連接到一起。默認情況下,Docker在宿主機上創建過個虛擬機網橋(默認網橋docker0)容器中的虛擬網卡通過網橋進行連接

     使用docker network ls命令可以查看當前系統網橋

docker network ls

    使用brctl工具(需安裝bridge-utils工具包),還可以看到連接到網橋上的虛擬網口的信息。每個容器默認分配一個網橋的虛擬網口,並將docker0的IP地址設置爲默認的網關,容器發起的網絡流量通過宿主機的iptables規則進行轉發

yum install bridge-utils
brctl show 

2.4 掛載命名空間

    類似於chroot,掛載(Mount,MNT)命名空間可以將一個進程的根文件系統限制到一個特定的目錄下。

    瓜子啊命名空間允許不同命名空間的進程看到的本地文件位於宿主機中不同路徑下,每個命名空間的進程所看到的文件目錄彼此是隔離的。例如,不同命名空間中的進程,都文衛自己獨佔了一個完成的跟文件系統(rootfs),但實際上,不同命名空間中的文件彼此隔離,不會造成相互影響 ,同時也無法影響宿主機文件系統中的 他路徑。

2.5 UTS命名空間

    UTS (UNIX Time-sharing System )命名空間允許每個容器擁有獨立的主機名和域名,從而可以虛擬出一個有獨立主機名和網絡空間的環境,就跟網絡上一臺獨立的主機一樣。

    如果沒有於動指定主機名稱,Docker容器的主機名就是返回的容器 ID 的前6字節前綴,否則爲指定的用戶名:     

[root@kubernetes ~]# docker run --name test1 -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1;done"
f3bd04e73b4c7cd49101a7f1cba2bd94ccf9996bab391cf9ce1e696ec339334f

[root@kubernetes ~]# docker  inspect -f {{".Config.Hostname"}} test1 
f3bd04e73b4c
[root@kubernetes ~]# docker run --hostname test2 --name test2 -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1;done"
a82b4b257e50b2423d2b4f46165ca435b508c9f4efeca3e68816a23d0b6da2b5

[root@kubernetes ~]# docker inspect -f {{".Config.Hostname"}} test2
test2

2.6 用戶命名空間

    每個容器可以有不同的用戶和組 id 也就是說,可以在容器內使用特定的內部用戶執行 程序,而非本地系統上存在的用戶

    每個容器內部都可以有最高權限的 root 帳號,但跟宿主主機不在一個命名空間。 通過使用隔離的用戶命名空間,可以提高安全 性,避 容器內的進程獲取 外的權限;同時通過使用不同用戶也可以進一步在容器內控制權限

    在容器內創建了 test 用戶,只有普通權限,無法訪問更高權限的資源:

[root@kubernetes ~]# docker run --rm -it ubuntu bash
root@2dc7d7a868d0:/# cat /proc/1/e
environ  exe      
root@2dc7d7a868d0:/# cat /proc/1/e
environ  exe      
root@2dc7d7a868d0:/# cat /proc/1/environ 
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=2dc7d7a868d0TERM=xtermHOME=/root
root@2dc7d7a868d0:/# useradd -ms /bin/bash test 
root@2dc7d7a868d0:/# su test 
test@2dc7d7a868d0:/$ cat /proc/1
1/  17/ 18/ 
test@2dc7d7a868d0:/$ cat /proc/1
1/  17/ 18/ 
test@2dc7d7a868d0:/$ cat /proc/1/e
environ  exe      
test@2dc7d7a868d0:/$ cat /proc/1/e
environ  exe      
test@2dc7d7a868d0:/$ cat /proc/1/environ 
cat: /proc/1/environ: Permission denied

3. 控制組

    控制組(CGroups)是 Linux 內核的一個特性,主要用來對共享資源進行隔離、限制、審 計等 只有將分配到容器的資源進行控制,才能避免多個容器同時運行時對宿主機系統的資 源競爭。每個控制組是一組對資源的限制,支持層級化結構

資源限制( resource limiting ):可將組設置一定的內存限制 比如:內存子系統可以爲進程組設定一個內存使用上限,一旦進程組使用的內存達到限額再申請內存,就會出發 Out of Memory 警告。

優先級(prioritization ):通過優先級讓一些組優先得到更多的 CPU 等資源

資源審計( accounting ):用來統計系統實際上把多少資源用到適合的目的上,可以使用cpuacct 子系統記錄某個進程組使用的 CPU 時間

隔離( isolation ):爲組隔離命名空間,這樣使得一個組不會看到另一個組的進程網絡連接和文件系統。

控制(control ):執行掛起 恢復和重啓動等操作

可在/sys/fs/cgroup/memory/docker/目錄下看到對Docker組應用的各種限制項,包括全侷限制和位於子目錄中對某個容器單獨限制。

ls /sys/fs/cgroup/memory/docker/

用戶可以通過修改這些穩健值來控制組,從而限制Docker應用資源。如:限制docker組中的所有進程使用的物理的物理內存總量不超過100MB

echo 104857600 > /sys/fs/cgroup/memory/docker/memory.limit_in_bytes

進容器文件夾,查看對應容器的限制和使用狀態

cd /sys/fs/cgroup/memory/docker/容器ID
cat memory.stat

4. 聯合文件系統

     聯合文件系統(UnionFS )是一種輕量級的高性能分層文件系統,它支持將文件系統中的 修改信息作爲一次提交,並層層疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下, 應用看到的是掛載的最終結果 聯合文件系統是實現 Docker 鏡像的技術基礎

     Docker 鏡像可以通過分層來進行繼承 例如,用戶基於基 鏡像(用來生成其他鏡像 礎,往往沒有父鏡像)來製作各種不同的應用鏡像 這些鏡像共享同一個基礎鏡像層,提 高了存儲效率 此外, 用戶改變了一 Docker 鏡像(比如升級程序到新的版本),則 建一個新的層( layer 因此,用戶不用替換整個原鏡像或者重新建立,只需要添加新層即 用戶分發鏡像的時候,也只需要分發被改動的新層內容(增量部分) 這讓 Docker 像管理變得十分輕量和快速

4.1 docker存儲原理

    Docker 目前通過插件化方式支持多種文件系統後端 Debian/Ubuntu 上成熟的 (Another Union File System ,或 v2 版本往後的 Advanced multi lay red Unification File System), 就是一種聯合文件系統實現 支持爲每一個成員目錄(類 Git 的分支)設定只讀 ( readonly 、讀寫( readwrite )或寫出( whiteout-able )權限,同時 裏有 似分 概念,對只讀權限的分支可以邏輯上進行增量地修改(不影響只讀部分的)

    Docker 鏡像自身就是由多個文件層組成,每一層有基於 容的唯一的編號(層 ID 以通過 docker history 查看一個鏡像由哪些層組成 

[root@kubernetes ~]# docker history ubuntu:latest
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
4e5021d210f6        9 days ago          /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
<missing>           9 days ago          /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B                  
<missing>           9 days ago          /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B                
<missing>           9 days ago          /bin/sh -c [ -z "$(apt-get indextargets)" ]     987kB               
<missing>           9 days ago          /bin/sh -c #(nop) ADD file:594fa35cf803361e6…   63.2MB  

    對於 Docker 鏡像來說,這些層的內容都是不可修改的 、只讀的。 而當 Docker 利用鏡像 啓動一個容器時,將在鏡像文件系統的最頂端再掛載一個新的可讀寫的層給容器 容器中的 內容更新將會發生在可讀寫層 當所操作對象位於較深的某層時,需要先複製到最上層的可 讀寫層 當數據對象較大時,往往意味着較差 IO 性能 因此,對於 IO 敏感型應用,一般 推薦將容器修改的數據通過 volume 方式掛載,而不是直接修改鏡像內數據

    對於頻繁啓停 Docker 容器的場景下,文件系統的 IO 性能也將十分關鍵

4.2 docker 存儲結構

    所有的鏡像和容器都存儲都在 Docker 指定的存儲目錄下,,默 認路徑是/ ar lib docker 在這個目錄下面,存儲由 Docker 鏡像和容器運行相關的文件和 目錄,可能包括 build er containerd containers image network ufs /ov lay2 plugins run times swarm tmp trust volumes 等。

    其中,如果使用 AUFS 存儲後端,則最關鍵的就是 aufs 目錄,保存 Docker 鏡像和容器 相關數據和信息 包括 layers,diff 和mnt 三個子目錄

    layers 子目錄包含層屬性文件,用來保存各個鏡像層的元數據:某鏡像的某層下面包括 哪些層

    mnt 子目錄下面的子目錄是各個容器最終的掛載點,所有相關的 AUFS 層在這裏掛載到 一起,形成最終效果 一個運行中容器的根文件系統就掛載在這下面的子目錄上

4.3 多種文件系統比較

    Docker 目前支持的聯合文件系統種類包括 AUFS 、怕也、 Device Mapp町、 overlay over lay2 vfs zfs 等,

AUFS :最早支持的文件系統,對 Debian/Ubuntu 支持好,雖然沒有合併到 Linux內核中,但成熟度很高;

btrfs 參考 zfs 等特性設計的文件系統,由 Linux 社區開發,試圖未來取代 DeviceMapper ,成熟度有待提高;

Device Mapper : RedHat 公司和 Docker 團隊一起開發用於支持陽fEL 的文件系統,內核支持,性能 咯慢,成熟度高;

overlay :類似於 AUFS 的層次化文件系統,性能更好,從 Linux 3.18 開始已經合併到內核,但成熟度有待提高;

overlay 2 : Docker 1.12 後推出,原生支持 128 層,效率比 OverlayPS 高,較新版本的Docker 支持,要求內核大於 4.0;

vfs 基於普通文件系統( ext nfs 等)的中間層抽象,性能差,比較佔用空間,成熟度也一般

zfs :最初設計爲 Solaris 10 上的寫時文件系統,擁有不少好的特性,但對 Linux 支持還不夠成熟

5. LIux網絡虛擬化

    Docker 的本地網絡實現其實就是利用了 Linux 上的網絡命名空間和虛擬網絡設備(特別是 veth pair)

    5.1 基本原理

    直觀上看,要實現網絡通信,機器需要至少一個網絡接口(物理接口或虛擬接口)與外 界相通,並可以收發數據包;此外,如果不同子網之間要進行通信,還需要額外的路由機制

    Docker 中的網絡接口默認都是虛擬接口 虛擬接口的最大優勢就是轉發效率極高 這是 因爲 Linux 通過在內核中進行數據複製來實現虛擬接口之間的數據轉發,即發送接口的發送 緩存中的數據包將被直接複製到接收接口的接收緩存中,而無須通過外部物理網絡設備進行 交換 對於本地系統和容器內系統來看,虛擬接口跟一個正常的以太網卡相比並無區別,只是它的速度要快得多

    Docker 容器網絡就很好地利用了 Linux 擬網絡技術,它在本地主機和容器內分別創建 一個虛擬接口 veth 並連通(這樣的一對虛擬 接口 叫做 veth pair )

    5.2 網絡創建過程

    一般情況下, Docker 建一個容器的時候, 會具體執行如下操作

    1 建一對虛擬接口,分別放到本地主 機和新容器的命名空間中;

    2. 本地主機一氣端 的虛擬接口連接到默認 dockerO 網橋或指定網橋上,並具有一個以 veth 開頭的唯一名字,如 veth1234;

    3 )器一端的虛擬接口將放到新創建的容器中,並修改名字作爲 ethO 這個接口只在容 器的命名空間可見;

   4 )從網橋可用地址段中獲取 個空閒地址分配給容器的 ethO (例如 172.17.0.2/16 ),並 配置默認路由網關爲 dockerO 網卡的內部接口 dockerO IP 地址(例如 172 17.42.1/16)

    在使用 docker [container] run 命令啓動容器的時候,可以通過一口et 參數來指 定容器的網絡配置,有5個可選值 bridge ,none,container host 和用戶定義的網絡

net=bridge: 默認值,在 Docker 網橋 docker0 上爲容器創建新的網絡棧;

net=none:讓 Docker 將新容器放到隔離的網絡棧中,但是不進行網絡配置。之後,用戶可以自行配置

net=container:NAME_or_ID :讓 Docker 將新建容器的進程放到一個已存在容器的網絡校中,新容器進程有自己的文件系統、進程列表和資源限制,但會和已存在的容器共享 地址和端口等網絡資源,兩者進程可以直接通過 lo 環回接口通信;

net=host :告訴 Docker 不要將容器網絡放到隔離的命名 間中,即不要容器化容器內的網絡 此時容器使用本地主機的網絡,它擁有完全的本地主機接口訪問權容器進程跟主機其他 root 進程一樣可以打開低範圍的端口,可以訪問本地網絡服務(比如 D-bus ),還可以讓容器做一些影響整個主機系統的事情, 比如重啓主機
因此使用這個選項的時候要非常小心 如果進一步使用一privil eged=true數,容器甚至會被允許直接配置主機的網絡技;

net=user_defined_network :用戶自行用 network 相關命令創建一個網絡,之後將容器連接到指定的己創建網絡上去

5.3 手動配置網絡

    使用--net=none 後, Docker 將不對容器網絡進行配置

docker run -i -t --rm --net=none ubuntu /bin/bash

在本地主機查找容器的進程 id,併爲它創建網絡命名空間:

[root@kubernetes /var/lib/docker]# docker inspect -f '{{.State.Pid}}' 01a20a2350fd
26513
[root@kubernetes /var/lib/docker]# pid=26513
[root@kubernetes /var/lib/docker]# mkdir -p /var/run/netns

[root@kubernetes /var/lib/docker]# ln -s /proc/$pid/ns/net /var/run/netns/$pid

檢查橋接網卡的IP和子網掩碼信息

[root@kubernetes /var/lib/docker]# ip addr show docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:26:29:1d:89 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

創建一對"veth pair" 接口A和B,綁定A接口到網橋docker0 並啓用它:

ip link addr A type veth peer name B

brctl addif docker0 A

ip link set A up

將B接口放到容器的網絡命名空間,命名爲eth0,啓動它並配置一個可用IP(橋接網段)和默認網關;

ip link set B netns $pid

ip netns exec $pid ip link set dev B name eth0

ip netns exec $pid ip link set eth0 up

ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0

ip netns exec $pid ip route add default via 172.17.42.1

    當容器終止後, Dock 會清空容器,容器內的網絡接口會隨網絡命名空間 起被清除, A接口也被自動從 dockerO 卸載並清除, 此外,在刪除 /var/run/netns 下的內容之前,用戶可 以使用 ip netns exec 命令在指定網絡命名空間中進行配置, 從而更新 器內的網絡配置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章