Docker NS詳解

      網友投稿 1014 2025-04-02

      docker底層原理

      一 背景

      1.1 容器與虛擬化

      Container(容器)是一種輕量級的虛擬化技術,它不需要模擬硬件創建虛擬機。在Linux系統里面,使用到Linux kernel的cgroups,namespace(ipc,network, user,pid,mount),capability等用于隔離運行環境和資源限制的技術,我們稱之為容器。

      容器技術早就出現。例如Solaris Zones 和 BSD jails 就是非 Linux 操作系統上的容器,而用于 Linux 的容器技術也有很多如 Linux-Vserver、OpenVZ 和 FreeVPS。雖然這些技術都已經成熟,但是這些解決方案還沒有將它們的容器支持集成到主流 Linux 內核。總的來說,容器不等同于docker,容器更不是虛擬機。

      LXC項目由一個 Linux 內核補丁和一些 userspace 工具組成,它提供一套簡化的工具來維護容器,用于虛擬環境的環境隔離、資源限制以及權限控制。LXC有點類似chroot,但是它比chroot提供了更多的隔離性。

      Docker最初目標是做一個特殊的LXC的開源系統,最后慢慢演變為它自己的一套容器運行時環境。Docker基于Linux kernel的CGroups,Namespace,UnionFileSystem等技術封裝成一種自定義的容器格式,用于提供一整套虛擬運行環境。毫無疑問,近些年來Docker已經成為了容器技術的代名詞,如其官網介紹的Docker is world’s leading software containerization platform。本文會先簡單介紹Docker基礎概念,然后會分析下Docker背后用到的技術。

      ![image-20210604223838338](/Users/xuel/Library/Application Support/typora-user-images/image-20210604223838338.png)

      二 namespace

      namespace隔離:

      IPC:主機名及域名,CLONE_NEWIPC

      MNT:掛載點,文件系統

      NET:網絡設備,網絡協議棧,端口等

      PID:進程編號

      USER:用戶和組

      UTS:主機名及域名,CLONE_NEWUTS

      2.1 PID Namespace

      在容器中,有自己的Pid namespace,因此我們看到的只有PID為1的初始進程以及它的子進程,而宿主機的其他進程容器內是看不到的。通常來說,Linux啟動后它會先啟動一個PID為1的進程,這是系統進程樹的根進程,根進程會接著創建子進程來初始化系統服務。PID namespace允許在新的namespace創建一棵新的進程樹,它可以有自己的PID為1的進程。在PID namespace的隔離下,子進程名字空間無法知道父進程名字空間的進程,如在Docker容器中無法看到宿主機的進程,而父進程名字空間可以看到子進程名字空間的所有進程。如圖所示:

      2.2 NS Namespace

      NS Namespace用于隔離掛載點,不同NS Namespace的掛載點互不影響。創建一個新的Mount Namespace效果有點類似chroot,不過它隔離的比chroot更加完全。這是歷史上的第一個Linux Namespace,由此得到了 NS 這個名字而不是用的 Mount。

      在最初的NS Namespace版本中,掛載點是完全隔離的。初始狀態下,子進程看到的掛載點與父進程是一樣的。在新的Namespace中,子進程可以隨意mount/umount任何目錄,而不會影響到父Namespace。使用NS Namespace完全隔離掛載點初衷很好,但是也帶來了某些情況下不方便,比如我們新加了一塊磁盤,如果完全隔離則需要在所有的Namespace中都掛載一遍。為此,Linux在2.6.15版本中加入了一個shared subtree特性,通過指定Propagation來確定掛載事件如何傳播。比如通過指定MS_SHARED來允許在一個peer group(子namespace 和父namespace就屬于同一個組)共享掛載點,mount/umount事件會傳播到peer group成員中。使用MS_PRIVATE不共享掛載點和傳播掛載事件。其他還有MS_SLAVE和NS_UNBINDABLE等選項。可以通過查看cat /proc/self/mountinfo來看掛載點信息,若沒有傳播參數則為MS_PRIVATE的選項。

      2.3 NET Namespace

      Docker NS詳解

      Docker容器中另一個重要特性是網絡獨立(之所以不用隔離一詞是因為容器的網絡還是要借助宿主機的網絡來通信的),使用到Linux 的 NET Namespace以及vet。veth主要的目的是為了跨NET namespace之間提供一種類似于Linux進程間通信的技術,所以veth總是成對出現,如下面的veth0和veth1。它們位于不同的NET namespace中,在veth設備任意一端接收到的數據,都會從另一端發送出去。veth實現了不同namespace的網絡數據傳輸。

      在Docker中,宿主機的veth端會橋接到網橋中,接收到容器中的veth端發過來的數據后會經由網橋docker0再轉發到宿主機網卡eth0,最終通過eth0發送數據。當然在發送數據前,需要經過iptables MASQUERADE規則將源地址改成宿主機ip,這樣才能接收到響應數據包。而宿主機網卡接收到的數據會通過iptables DNAT根據端口號修改目的地址和端口為容器的ip和端口,然后根據路由規則發送到網橋docker0中,并最終由網橋docker0發送到對應的容器中。

      Docker里面網絡模式分為bridge,host,overlay等幾種模式,默認是采用bridge模式網絡如圖所示。如果使用host模式,則不隔離直接使用宿主機網絡。overlay網絡則是更加高級的模式,可以實現跨主機的容器通信,后面會單獨總結下Docker網絡這個專題。

      2.4 USER Namespace

      user namespace用于隔離用戶和組信息,在不同的namespace中用戶可以有相同的 UID 和 GID,它們之間互相不影響。父子namespace之間可以進行用戶映射,如父namespace(宿主機)的普通用戶映射到子namespace(容器)的root用戶,以減少子namespace的root用戶操作父namespace的風險。user namespace功能雖然在很早就出現了,但是直到Linux kernel 3.8之后這個功能才趨于完善。

      創建新的user namespace之后第一步就是設置好user和group的映射關系。這個映射通過設置/proc/PID/uid_map(gid_map)實現,格式如下,ID-inside-ns是容器內的uid/gid,而ID-outside-ns則是容器外映射的真實uid/gid。比如0 1000 1表示將真實的uid=1000映射為容器內的uid=0,length為映射的范圍。

      ID-inside-ns ID-outside-ns length

      不是所有的進程都能隨便修改映射文件的,必須同時具備如下條件:

      修改映射文件的進程必須有PID進程所在user namespace的CAP_SETUID/CAP_SETGID權限。

      修改映射文件的進程必須是跟PID在同一個user namespace或者PID的父namespace。

      映射文件uid_map和gid_map只能寫入一次,再次寫入會報錯。

      docker1.10之后的版本可以通過在docker daemon啟動時加上–userns-remap=[USERNAME]來實現USER Namespace的隔離。我們指定了username=ssj啟動dockerd,查看subuid文件可以發現ssj映射的uid范圍是165536到165536+65536= 231072,而且在docker目錄下面對應ssj有一個獨立的目錄165536.165536存在。

      2.5 其他Namespace

      UTS namespace用于隔離主機名等。可以看到在新的uts namespace修改主機名并不影響原namespace的主機名。

      IPC Namespace用于隔離IPC消息隊列等。可以看到,新老ipc namespace的消息隊列互不影響。

      CGROUP Namespace是Linux4.6以后才支持的新namespace。容器技術使用namespace和cgroup實現環境隔離和資源限制,但是對于cgroup本身并沒有隔離。沒有cgroup namespace前,容器中一旦掛載cgroup文件系統,便可以修改整全局的cgroup配置。有了cgroup namespace后,每個namespace中的進程都有自己的cgroup文件系統視圖,增強了安全性,同時也讓容器遷移更加方便。在我測試的Docker18.03.1-ce版本中容器暫時沒有用到cgroup namespace,這里就不再展開。

      三 CGroups

      Linux CGroups用于資源限制,包括限制CPU、內存、blkio以及網絡等。通過工具cgroup-bin (sudo apt-get install cgroup-bin)可以創建CGroup并進入該CGroup執行命令。

      cpu.cfs_period_us 和 cpu.cfs_quota_us,它們分別用來限制該組中的所有進程在單位時間里可以使用的 cpu 時間,這里的 cfs(Completely Fair Scheduler) 是完全公平調度器的意思。cpu.cfs_period_us是時間周期,默認為100000,即100毫秒。而cpu.cfs_quota_us是在時間周期內可以使用的時間,默認為-1即無限制。cpu.shares用于限制cpu使用的,它用于控制各個組之間的配額。比如組cg1的cpu.shares為1024,組cg2的cpu.shares也是1024,如果都有進程在運行則它們都可以使用最多50%的限額。如果cg2組內進程比較空閑,那么cg1組可以將使用幾乎整個cpu,tasks存儲的是該組里面的進程ID。(注:debian8默認沒有cfs和memory cgroup支持,需要重新編譯內核及修改啟動參數,debian9默認已經支持)

      四 Capabilities

      我們在啟動容器時會時常看到這樣的參數–cap-add=NET_ADMIN,這是用到了Linux的capability特性。capability是為了實現更精細化的權限控制而加入的。我們以前熟知通過設置文件的SUID位,這樣非root用戶的可執行文件運行后的euid會成為文件的擁有者ID,比如passwd命令運行起來后有root權限,有SUID權限的可執行文件如果存在漏洞會有安全風險。(查看文件的capability的命令為 filecap -a,而查看進程capability的命令為 pscap -a,pscap和filecap工具需要安裝 libcap-ng-utils這個包)。

      五 Union File System

      UnionFS(聯合文件系統)簡單來說就是支持將不同的目錄掛載到同一個目錄中的技術。Docker支持的UnionFS包括OverlayFS,AUFS,devicemapper,vfs以及btrfs等,查看UnionFS版本可以用docker info查看對應輸出中的Storage項即可,早期的Docker版本用AUFS和devicemapper居多,新版本Docker在Linux3.18之后版本基本默認用OverlayFS,這里以OverlayFS來分析。

      OverlayFS與早期用過的AUFS類似,不過它比AUFS更簡單,讀寫性能更好,在docker-ce18.03版本中默認用的存儲驅動是overlay2,老版本overlay官方已經不推薦使用。它將兩個目錄upperdir和lowdir聯合掛載到一個merged目錄,提供統一視圖。其中upperdir是可讀寫層,對容器修改寫入在該目錄中,它也會隱藏lowerdir中相同的文件。而lowdir是只讀層,Docker鏡像在這層。

      讀取文件時,如果upperdir不存在該文件,則會從lowerdir直接讀取。

      修改文件時并不影響lowerdir中的文件,因為它是只讀的。

      如果修改的文件在upperdir不存在,則會從lowerdir拷貝到upperdir,然后在upperdir里面修改該文件,并不影響lowerdir目錄的文件。

      刪除文件則是將upperdir中將對應文件設置成了c類型,即字符設備類型來隱藏已經刪除的文件(與AUFS創建一個whiteout文件略有不同)。

      回到Docker里面,我們拉取一個nginx鏡像,有三層鏡像,可以看到在overlay2對應每一層都有個目錄(注意,這個目錄名跟鏡像層名從docker1.10版本后名字已經不對應了),另外的l目錄是指向鏡像層的軟鏈接。最底層存儲的是基礎鏡像debian/alpine,上一層是安裝了nginx增加的可執行文件和配置文件,而最上層是鏈接/dev/stdout到nginx日志文件。而每個子目錄下面的diff目錄用于存儲鏡像內容,work目錄是OverlayFS內部使用的,而link文件存儲的是該鏡像層對應的短名稱,lower文件存儲的是下一層的短名稱。

      此時我們啟動一個nginx容器,可以看到overlay2目錄多了兩個目錄,多出來的就是容器層的目錄和只讀的容器init層。容器目錄下面的merged就是我們前面提到的聯合掛載目錄了,而lowdir則是它下層目錄。而容器init層用來存儲與這個容器內環境相關的內容,如 /etc/hosts和/etc/resolv.conf文件,它居于其他鏡像層之上,容器層之下。

      如果我們在容器中修改文件,則會反映到容器層的merged目錄相關文件,容器層的diff目錄相當于upperdir,其他層是lowerdir。如果之前容器層diff目錄不存在該文件,則會拷貝該文件到diff目錄并修改。讀取文件時,如果upperdir目錄找不到,則會直接從下層的鏡像層中讀取。

      Docker

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:在線文檔怎么創建騰訊(騰訊文檔怎么做在線文檔)
      下一篇:怎樣取消wps保護表格模式(wps表格保護怎么消除)
      相關文章
      亚洲av午夜精品一区二区三区| 亚洲a∨无码男人的天堂| 亚洲色图综合网站| 亚洲色图在线播放| 亚洲AV人人澡人人爽人人夜夜| 亚洲伊人久久大香线焦| 亚洲综合色一区二区三区小说| 亚洲欧洲国产精品你懂的| 亚洲国产精品无码久久久不卡| 亚洲精品夜夜夜妓女网| 怡红院亚洲怡红院首页| 国产成人高清亚洲| 国产亚洲日韩一区二区三区| 色久悠悠婷婷综合在线亚洲| 国产成人精品日本亚洲专区 | 亚洲综合久久1区2区3区| 中文字幕亚洲精品资源网| 亚洲AV无码乱码精品国产| 午夜亚洲国产精品福利| jjzz亚洲亚洲女人| 狠狠色婷婷狠狠狠亚洲综合| 亚洲日韩中文字幕一区| 亚洲sm另类一区二区三区| 国产亚洲精品欧洲在线观看| 中文字幕乱码亚洲精品一区| 亚洲日韩乱码中文字幕| 朝桐光亚洲专区在线中文字幕| 亚洲成av人片在线看片| 亚洲久悠悠色悠在线播放| 亚洲精品自偷自拍无码| www亚洲精品少妇裸乳一区二区| 水蜜桃亚洲一二三四在线| 亚洲一本综合久久| 精品亚洲国产成人| 精品无码专区亚洲| 国产亚洲精品高清在线| 亚洲视频在线观看| 亚洲国产av美女网站| 亚洲AV无码一区二区三区系列| 亚洲网站视频在线观看| 国产l精品国产亚洲区在线观看|