一、先談談程式
在正式介紹Namespace之前,先介紹下程式,因為容器本質上是程式,但是在介紹程式之前,先理清下“程式”和“程式”的關係,這是IT從業人員在日常工作中經常碰到的兩個詞彙,舉個通俗點的例子幫助大家理解,“程式”可以看成是一張機械圖,圖上的內容都是手工畫上去的,相當於是計算機的輸入,在機械圖未正式設計出產品的時候,它是靜態的,而當工程師按照機械圖正式設計各個零部件、組合、齧合到最後產品成型的整個動態過程,可看成是一個程式,因此程式可認為是程式執行起來後的計算機執行環境的總和,是靜態程式的具體實現。
二、容器的namespace
在上節中介紹了什麼是程式,可通過ps命名檢視當前宿主機正在執行的程式,如下圖所示
[root@k8s-master James]# ps PID TTY TIME CMD 101614 pts/1 00:00:00 su 101657 pts/1 00:00:00 bash 103878 pts/1 00:00:00 ps
容器本質上對於Linux作業系統來說,和上述程式一樣,但是會在程式上加入了很多namespace來實現程式、掛載點、網路、使用者資訊之間的隔離,這樣容器看上去就像一個沙箱,在沙箱內部只能看到和操作限定namespace下的系統資源,以PID namespace為例,我們先建立1個容器
[root@k8s-master James]# docker run -it -d busybox /bin/sh
01a0fd62d2110e54b0c3635b2897e7c18e6b78f026fa57b4214d7662dd3b38ba
[root@k8s-master James]# docker exec -it 01a0fd62d2110e54b0c3635b2897e7c18e6b78f026fa57b4214d7662dd3b38ba /bin/sh
/ # ps
PID USER TIME COMMAND
1 root 0:00 /bin/sh
6 root 0:00 /bin/sh
11 root 0:00 ps
這條命名的意思是我想啟動一個容器執行/bin/bash命令,並分配一個偽終端的互動視窗和容器進行互動,docker run是建立和啟動容器的意思,-it表示就是分配一個偽終端的輸入和輸出互動視窗,busybox是映象,/bin/bash是操作程式。當建立成功後,輸入ps可檢視容器中正在執行的程式,可以看到有2個程式,PID代表程式的唯一編號,可以看到1號PID的操作程式為/bin/bash,之前談到容器也是一種程式,回到宿主機檢視下此容器的程式
[root@k8s-master James]# docker container top 01a0fd62d2110e54b0c3635b2897e7c18e6b78f026fa57b4214d7662dd3b38ba UID PID PPID C STIME TTY TIME CMD root 24691 24668 0 18:34 pts/0 00:00:00 /bin/sh
其PID為24691了,此PID在容器中不存在,也就是說容器中只能看到自己程式執行後的程式,而看不到其他容器和宿主機上的程式,且容器裡面的程式編號PID也做了障眼法般的處理,與宿主機上的PID不一致,而實現這個技術的就是PID namespace,而容器類似的還有NET、IPC、MNT、UTS、USER的namespace,其對應隔離的內容如下表所示:
namespace | 隔離的內容 |
---|---|
PID | 程式 |
IPC | 訊號量、訊息佇列和共享內容 |
UTS | 主機名、域名 |
NET | 網路裝置、網路棧、埠 |
MNT | 檔案系統 |
User | 使用者和使用者組 |
網路相對比較複雜,我們再詳細深入看下,從上表可以看出不同的網路名稱空間可以隔離網路裝置、網路棧、埠等,為了達到這個目標,LINUX系統就需要支援虛擬化網路協議棧的多個例項,且這些獨立的協議棧可處於不同的網路名稱空間來進行隔離,達到彼此無法進行通訊隔離的效果,如果想要不同的網路名稱空間裝置進行通訊,應如何操作呢?答案是:veth裝置對,什麼是veth裝置對,你可以認為是一根管道,一端連線一個網路名稱空間的協議棧,另外一端連線另外一個網路名稱空間的協議棧來實現通訊,那如何建立veth裝置對呢?接下來一起操作下
在root使用者下,先建立一個網路名稱空間
[root@k8s-master zhanglei]# ip netns add net_test1 [root@k8s-master zhanglei]# ip netns list net_test1
再建立一個裝置對veth:
[root@k8s-master zhanglei]# ip link add veth0 type veth peer name veth1 [root@k8s-master zhanglei]# ip link show 60: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
可以看到veth0和veth1的裝置對已經生成,veth的裝置支援在不同的網路名稱空間進行轉移,將veth0裝置住轉移到net_test1名稱空間
[root@k8s-master zhanglei]# ip link set veth0 netns net_test1
此時在宿主機上ip link show,因不同的網路名稱空間裝置是隔離的,且veth0裝置已經被移動到net_test1網路名稱空間,因此將無法在宿主機網路名稱空間再次找到veth0
[root@k8s-master zhanglei]# ip netns exec net_test1 ip link show 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 60: veth0@if59: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 26:c0:29:4d:77:28 brd ff:ff:ff:ff:ff:ff link-netnsid 0
同時我們將另veth1設定到名稱空間net_test2裡面
[root@k8s-master zhanglei]# ip netns add net_test2 [root@k8s-master zhanglei]# ip netns show net_test2 net_test1 (id: 24)
[root@k8s-master zhanglei]# ip link set veth1 netns net_test2 [root@k8s-master zhanglei]# ip netns exec net_test2 ip link show 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 59: veth1@if60: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 7e:06:da:3a:09:44 brd ff:ff:ff:ff:ff:ff link-netns net_test1
到這裡,veth0和veth1這個裝置對已經配置在不同的網路名稱空間了,但是此時還不能通訊,就像電線已經搭好,但是還未通電,誰來扮演通電的角色呢?答案就是:IP
[root@k8s-master zhanglei]# ip netns exec net_test1 ip addr add 10.1.1.1/24 dev veth0 [root@k8s-master zhanglei]# ip netns exec net_test2 ip addr add 10.1.1.2/24 dev veth1 [root@k8s-master zhanglei]# ip netns exec net_test1 ip link set dev veth0 up [root@k8s-master zhanglei]# ip netns exec net_test2 ip link set dev veth1 up [root@k8s-master zhanglei]# ip netns exec net_test1 ping 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. 64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.097 ms 64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.061 ms 64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=0.032 ms 64 bytes from 10.1.1.2: icmp_seq=4 ttl=64 time=0.024 ms
[root@k8s-master zhanglei]# ip netns exec net_test2 ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.030 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.090 ms
64 bytes from 10.1.1.1: icmp_seq=3 ttl=64 time=0.032 ms
64 bytes from 10.1.1.1: icmp_seq=4 ttl=64 time=0.033 ms
[root@k8s-master zhanglei]# ip netns exec net_test2 ping 10.1.1.1 PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data. 64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.030 ms 64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.090 ms 64 bytes from 10.1.1.1: icmp_seq=3 ttl=64 time=0.032 ms 64 bytes from 10.1.1.1: icmp_seq=4 ttl=64 time=0.033 ms
如上,給每個裝置配備1個ip地址,配備成功後再啟動裝置,然後就可以進行相互的通訊了,前面提到每個容器擁有自己單獨的網路名稱空間,而網路名稱空間之間的通訊是通過裝置對,而上面演示的就是名稱空間通過裝置對進行通訊的全過程,
事實上容器之間、容器與宿主機之間都是通過裝置對的方式進行通訊的,當然實際上容器網路名稱空間的建立、裝置對的建立、IP的分配並不是手動進行的,都是容器在建立的時候會自動完成,對使用者來說是無感知的,這裡是方便展示內部原理,採用的手動的形式。
三、總結
本文主要重點介紹了容器PID和NET 名稱空間(namespace)的隔離原理,其他namespace的隔離原理類似,容器本質上一種特殊的程式,它雖然提供了隔離技術,但與虛擬機器的隔離技術要區別開來,虛擬機器是在宿主機上通過Hypervisor虛擬了一個獨立的GuestOS,它的隔離是徹底的,虛擬機器上的程式在宿主機上無法檢視;而容器本質上是宿主機上的程式,它的隔離機制是通過在程式上加入不同的namespace引數來實現資源、檔案、裝置、狀態,或者配置的隔離,兩者隔離的本質是有差異的,你或許有個疑問,既然容器是宿主機上的一個程式,而不同的程式可以相互共享宿主機的核心,一旦一個容器的應用逃逸影入侵宿主機,是不是會有可能會影響宿主機或者其他容器應用呢,事實上,docker容器的確存在這樣的安全隱患,由此看來,docker容器的隔離性並不像虛擬機器般隔離的徹底,細心的讀者,可能發現在這裡為什麼特意加入docker,這是因為有其他型別的容器既可以提供虛擬機器級般的隔離能力同時又擁有容器的高效能,這就是Kata容器,什麼是Kata容器?這裡先賣個關子,後續專文介紹下,感謝您的閱讀!
作者簡介:雲端計算容器\Docker\K8s\Serverless方向產品經理,學點技術,為更好地設計產品。