文章目錄
- 前言
- 一、namespace
- 1、認識namespace
- 2、基于 Linux Namespace 的隔離機制相比于虛擬化技術的不足之處
- 二、cgroups
- 1、為什么要限制容器
- 2、cgroups與容器最親密的限制能力
- 3、cgroups不足
- 三、rootfs
前言
容器本質:
namespace 空間隔離
cgroup 資源限制
rootfs 檔案系統
一、namespace
1、認識namespace
??namespace是Linux 容器中用來實作"隔離"的技術手段,namespace 技術實際上修改了應用行程看待整個計算機"視圖",即它的"視線"被作業系統做了限制,只能"看到"某些指定的內容,但對于宿主機來說,這些被"隔離"了的行程跟其他行程并沒有太大區別,
?? docker run -it busybox —name busybox /bin/sh 這條指令翻譯成人類的語言就是:請幫我啟動一個容器,在容器里執行 /bin/sh,并且給我分配一個命令列終端跟這個容器互動,docker exec -it busybox ps -ef在容器里執行 ps 指令會發現/bin/sh行程,就是這個容器內部的第 1 號行程(PID=1),而這個容器里一共只有兩個行程在運行,這就意味著,前面執行的 /bin/sh,以及我們剛剛執行的 ps,已經被 Docker 隔離在了一個跟宿主機完全不同的世界當中,
PID USER TIME COMMAND
1 root 0:00 /bin/sh
8 root 0:00 ps -ef
??在 Linux 系統中創建執行緒的系統呼叫是 clone(),比如:int pid = clone(main_function, stack_size, SIGCHLD, NULL); 這個系統呼叫就會為我們創建一個新的行程,并且回傳它的行程號 pid, 而當用 clone() 系統呼叫創建一個新行程時,就可以在引數中指定 CLONE_NEWPID 引數,比如:int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL); 這時,新創建的這個行程將會"看到"一個全新的行程空間,在這空間里,它的 PID 是 1,之所以說"看到",是因為這只是一個"障眼法",在宿主機真實的行程空間里,這個行程的 PID 還是真實的數值, 當然可以多次執行上面的 clone() 呼叫,這樣就會創建多個 PID Namespace,而每個 Namespace 里的應用行程,都會認為自己是當前容器里的第 1 號行程,它們既看不到宿主機里真正的行程空間,也看不到其他 PID Namespace 里的具體情況,
??除了剛用到的 PID Namespace,Linux 作業系統還提供了 Network 、IPC、Mount、UTS和 User 這些 Namespace,用來對各種不同的行程背景關系進行"障眼法"操作,
-
pid 命名空間
不同用戶的行程就是通過 pid 名字空間隔離開的,且不同名字空間中可以有相同 pid,所有的 LXC 行程在 Docker中的父行程為Docker行程,每個 LXC 行程具有不同的名字空間,同時由于允許嵌套,因此可以很方便的實作嵌套的Docker 容器, LXC:Linux Container容器是一種內核虛擬化技術,可以提供輕量級的虛擬化,以便隔離行程和資源,
-
net 命名空間
有了pid名字空間, 每個名字空間中的 pid 能夠相互隔離,但是網路埠還是共享 host 的埠,網路隔離是通過 net 名字空間實作的,每個 net 名字空間有獨立的 網路設備, IP 地址, 路由表, /proc/net 目錄,這樣每個容器的網路就能隔離開來,Docker 默認采用 veth 的方式,將容器中的虛擬網卡同 host 上的一 個Docker 網橋 docker0 連接在一起,
-
ipc 命名空間
容器中行程互動還是采用了 Linux 常見的行程間互動方法(interprocess communication - IPC), 包括信號量、訊息佇列和共享記憶體、socket、管道等,然而同 VM 不同的是,容器的行程間互動實際上還是 host 上具有相同 pid 名字空間中的行程間互動,因此需要在 IPC 資源申請時加入名字空間資訊,每個 IPC 資源有一個唯一的 32 位 id,
-
mnt命名空間
類似 chroot,將一個行程放到一個特定的目錄執行,mnt 名字空間允許不同名字空間的行程看到的檔案結構不同,這樣每個名字空間中的行程所看到的檔案目錄就被隔離開了,同 chroot 不同,每個名字空間中的容器在 /proc/mounts 的資訊只包含所在名字空間的 mount point,
-
uts 命名空間
UTS(“UNIX Time-sharing System”) 名字空間允許每個容器擁有獨立的 hostname 和 domain name, 使其在網路上可以被視作一個獨立的節點而非主機上的一個行程,
-
user 命名空間
每個容器可以有不同的用戶和組 id, 也就是說可以在容器內用容器內部的用戶執行程式而非主機上的用戶,
??所以,Docker 容器這個聽起來玄而又玄的概念,實際上是在創建容器行程時,指定了這個行程所需要啟用的一組 Namespace 引數,這樣,容器就只能"看"到當前 Namespace 所限定的資源、檔案、設備、狀態,或者配置,而對于宿主機以及其他不相關的程式,它就完全看不到了,所以說,容器,其實是一種特殊的行程而已,所以說人們常把docker與虛擬機相比,其實不恰當,到此下面這張docker與虛擬機的對比圖,可以很容易就看懂了,
??容器與虛擬機對比圖:

2、基于 Linux Namespace 的隔離機制相比于虛擬化技術的不足之處
1. 隔離得不徹底
??既然容器只是運行在宿主機上的一種特殊的行程,那么多個容器之間使用的就還是同一個宿主機的作業系統內核,
??盡管可以在容器里通過 Mount Namespace 單獨掛載其他不同版本的作業系統檔案,比如 CentOS 或者 Ubuntu,但這并不能改變共享宿主機內核的事實,如果你要在 Windows 宿主機上運行 Linux 容器,或者在低版本的 Linux 宿主機上運行高版本的 Linux 容器,都是行不通的,
??而相比之下,擁有硬體虛擬化技術和獨立 Guest OS 的虛擬機就要方便得多了,最極端的例子是,Microsoft 的云計算平臺 Azure,實際上就是運行在 Windows 服務器集群上的,但這并不妨礙你在它上面創建各種 Linux 虛擬機出來,
2. 在 Linux 內核中,有很多資源和物件是不能被 Namespace 化的,最典型的例子就是:時間
??如果你的容器中的程式使用 settimeofday(2) 系統呼叫修改了時間,整個宿主機的時間都會被隨之修改,這顯然不符合用戶的預期,相比于在虛擬機里面可以隨便折騰的自由度,在容器里部署應用的時候,“什么能做,什么不能做”,就是用戶必須考慮的一個問題,
3. 因為共享宿主機內核的事實,容器給應用暴露出來的攻擊面是相當大的
??盡管實踐中可以使用 Seccomp 等技術,對容器內部發起的所有系統呼叫進行過濾和甄別來進行安全加固,但這種方法因為多了一層對系統呼叫的過濾,一定會拖累容器的性能,何況,默認情況下,誰也不知道到底該開啟哪些系統呼叫,禁止哪些系統呼叫,
??所以,生產環境中,沒有人敢把運行在物理機上的 Linux 容器直接暴露到公網上,當然,基于虛擬化或者獨立內核技術的容器實作,則可以比較好地在隔離與性能之間做出平衡,
二、cgroups
??一個正在運行的 Docker 容器,其實就是一個啟用了多個 Linux Namespace 的應用行程,而這個行程能夠使用的資源量,則受 Cgroups 配置的限制,
??Linux Cgroups 的全稱是 Linux Control Group,它最主要的作用,是限制一個行程組能夠使用的資源上限,包括 CPU、記憶體、磁盤、網路帶寬等,Cgroups提供了對一組行程及將來的子行程的資源的限制,控制和統計的能力,這些資源包括CPU,記憶體,存盤,網路等,通過Cgroups,可以方便的限制某個行程的資源占用,并且可以實時的監控行程的監控和統計資訊,
??此外,Cgroups 還能夠對行程進行優先級設定、審計,以及將行程掛起和恢復等操作,
1、為什么要限制容器
?? 以 PID Namespace 為例,雖然容器內的 1 號行程在"障眼法"的干擾下只能看到容器里的情況,但是宿主機上,它作為第 100 號行程與其他所有行程之間依然是平等競爭關系,這就意味著,雖然第 100 號行程表面上被隔離了起來,但是它所能夠使用到的資源(比如 CPU、記憶體),卻是可以隨時被宿主機上的其他行程占用的,當然,這個行程自己也可能把所有資源吃光,這些情況,顯然都不是一個"沙盒"應該表現出來的合理行為, 而Linux Cgroups 就是 Linux 內核中用來為行程設定資源限制的一個重要功能,
2、cgroups與容器最親密的限制能力
?? Linux 中,Cgroups 給用戶暴露出來的操作介面是檔案系統,即它以檔案和目錄的方式組織在作業系統的 /sys/fs/cgroup 路徑下,
?? 用 mount 指令把它們展示出來:
mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
?? 輸出結果,是一系列檔案系統目錄,在 /sys/fs/cgroup 下面有很多諸如 cpuset、cpu、 memory 這樣的子目錄,也叫子系統,這些都是這臺機器當前可以被 Cgroups 進行限制的資源種類,而在子系統對應的資源種類下,你就可以看到該類資源具體可以被限制的方法,
?? 比如,對 CPU 子系統來說,可以看到如下幾個組態檔:
ls /sys/fs/cgroup/cpu
cgroup.clone_children cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release
cgroup.procs cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks
?? 比如:cfs_period 和 cfs_quota 這兩個引數需要組合使用,可以用來限制行程在長度為 cfs_period 的一段時間內,只能被分配到總量為 cfs_quota 的 CPU 時間,
?? Linux Cgroups 的設計,簡單粗暴地理解,就是一個子系統目錄加上一組資源限制檔案的組合,而對于 Docker 等 Linux 容器專案來說,它們只需要在每個子系統下面,為每個容器創建一個控制組(即創建一個新目錄),然后在啟動容器行程之后,把這個行程的 PID 填寫到對應控制組的 tasks 檔案中就可以了,
?? 至于在這些控制組下面的資源檔案里填上什么值,就靠用戶執行 docker run 時的引數指定了,比如這樣一條命令:
docker run -it --cpu-period=100000 --cpu-quota=20000 daocloud.io/centos /bin/bash
?? 在centos7里面是下面這個目錄:
cat /sys/fs/cgroup/cpu,cpuacct/system.slice/docker-92f76c52e9c0c34e0f8e5bac01f75919ce59838951a86501e7114d218f8aaf3e.scope/cpu.cfs_quota_us
20000
?? 這就意味著這個 Docker 容器,只能使用到 20% 的 CPU 帶寬,
?? 由于一個容器的本質就是一個行程,用戶的應用行程實際上就是容器里 PID=1 的行程,也是其他后續創建的所有行程的父行程,這就意味著,在一個容器中,你沒辦法同時運行兩個不同的應用,除非你能事先找到一個公共的 PID=1 的程式來充當兩個不同應用的父行程,這也是為什么很多人都會用 systemd 或者 supervisord 這樣的軟體來代替應用本身作為容器的啟動行程,
?? 這是因為容器本身的設計,就是希望容器和應用能夠同生命周期,這個概念對后續的容器編排非常重要,否則,一旦出現類似于"容器是正常運行的,但是里面的應用早已經掛了"的情況,編排系統處理起來就非常麻煩了,
3、cgroups不足
??Cgroups 對資源的限制能力也有很多不完善的地方,被提及最多的自然是 /proc 檔案系統的問題,
??眾所周知,Linux 下的 /proc 目錄存盤的是記錄當前內核運行狀態的一系列特殊檔案,用戶可以通過訪問這些檔案,查看系統以及當前正在運行的行程的資訊,比如 CPU 使用情況、記憶體占用率等,這些檔案也是 top 指令查看系統資訊的主要資料來源,
??但是,你如果在容器里執行 top 指令,就會發現,它顯示的資訊居然是宿主機的 CPU 和記憶體資料,而不是當前容器的資料,造成這個問題的原因就是,/proc 檔案系統并不知道用戶通過 Cgroups 給這個容器做了什么樣的資源限制,即:/proc 檔案系統不了解 Cgroups 限制的存在,
三、rootfs
??實際上,Mount Namespace 是基于對 chroot 的不斷改良才被發明出來的,它也是 Linux作業系統里的第一個 Namespace,當然,為了能夠讓容器的這個根目錄看起來更“真實”,我們一般會在這個容器的根目錄下掛載一個完整作業系統的檔案系統,而這個掛載在容器根目錄上、用來為容器行程提供隔離后執行環境的檔案系統,就是所謂的“容器鏡像”,它還有一個更為專業的名字,叫作:rootfs(根檔案系統),
??Docker 公司在實作 Docker 鏡像時并沒有沿用以前制作 rootfs 的標準流程,而是做了一個小小的創新,當然,這個想法不是憑空臆造出來的,而是用到了一種叫作聯合檔案系統(Union FileSystem)的能力,Union File System 也叫 UnionFS,最主要的功能是將多個不同位置的目錄聯合掛載(union mount)到同一個目錄下,現在高版本的docker都使用OverlayFS檔案系統,OverlayFS類似AUFS,
??Docker 在鏡像的設計中,引入了層(layer)的概念,也就是說,用戶制作鏡像的每一步操作,都會生成一個層,也就是一個增量 rootfs,它最關鍵的目錄結構在 /var/lib/docker 路徑下的 diff 目錄:
ls /var/lib/docker/overlay/diff/<layer_id>
#我們可以使用 docker image inspect 查詢Layers
docker image inspect eureka-server:t1
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:a2ae92ffcd29f7ededa0320f4a4fd709a723beae9a4e681696874932db7aee2c",
"sha256:0eb22bfb707db44a8e5ba46a21b2ac59c83dfa946228f04be511aba313bdc090",
"sha256:30339f20ced009fc394410ac3360f387351641ed40d6b2a44b0d39098e2e2c40",
"sha256:ce6c8756685b2bff514e0b28f78eedb671380084555af2b3833e54bb191b262a",
"sha256:a3483ce177ce1278dd26f992b7c0cfe8b8175dd45bc28fee2628ff2cf063604c",
"sha256:6ed1a81ba5b6811a62563b80ea12a405ed442a297574de7440beeafe8512a00a",
"sha256:c3fe59dd955634c3fa1808b8053353f03f4399d9d071be015fdfb98b3e105709",
"sha256:35c20f26d18852b74cc90afc4fb1995f1af45537a857eef042a227bd8d0822a3",
"sha256:70ecef0b4267941f5c4384f7938cbadafd36fdbfadf52c2be58ce2418236f180",
"sha256:984b7bf3b699879285c3d6dc3f6bba8bbf273ad0576f40c4b89ca055441e6644"
]
},
?&emsp可以看到,eureka-server這個共有10層layer,也就是10個rootfs,每一層都是基礎OS、java環境、eureka服務所用檔案系統的一部分,當運行景象時,這些檔案系統會掛載到一個統一的目錄—— /var/lib/docker/overlay2/,可以嘗試去查看每個目錄下檔案,
ll 32183dfcce4ea5f0cc2f5dca77937f14aeeb6527ef94014d583dfa7817d23b81/diff/ #讀寫層
total 32
drwxr-xr-x 10 root root 4096 Nov 14 2019 etc
-rw-r--r-- 1 root root 920 Nov 11 2019 ip.txt
dr-xr-x--- 2 root root 4096 Nov 14 2019 root
drwxr-xr-x 2 root root 4096 Nov 14 2019 run
drwxr-xr-x 2 root root 4096 Nov 14 2019 test
drwxrwxrwt 2 root root 4096 Nov 14 2019 tmp
drwxr-xr-x 8 root root 4096 Jun 1 2018 usr
drwxr-xr-x 7 root root 4096 Jun 1 2018 var
ll 32183dfcce4ea5f0cc2f5dca77937f14aeeb6527ef94014d583dfa7817d23b81-init/diff/
total 8
drwxr-xr-x 4 root root 4096 Nov 14 2019 dev
drwxr-xr-x 2 root root 4096 Nov 14 2019 etc
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/290770.html
標籤:其他
上一篇:計算機網路(三):應用層
