1、Docker事實
1)容器技術的興起源於Pass技術的普及
2)Docker公司發布的Docker項目具有里程碑式的意義
3)Docker項目通過容器鏡像解決了應用打包這個根本性難題
4)容器本身沒有價值,有價值的是容器編排
5)容器是一個單進程模型
2、容器的隔離
容器其實是一種沙盒技術,沙盒就是能夠像集裝箱一樣,把應用裝起來的技術,這樣應用與應用之間就因為有了邊界而不至於互相干擾,而被裝進集裝箱的應用也可以被方便地搬來搬去
那這個邊界是如何實現的呢?
程序運行起來后計算機執行環境的總和,就是進程。
容器技術的核心功能就是通過約束和修改進程的動態表現,從而為其創造出一個邊界,Cgroup技術是用來制造約束的主要手段,而namespace是用來修改進程視圖的主要方法
docker run -it busybox /bin/sh //-it 告訴Docker啟動容器后需要分配一個文本輸入/輸出環境 //啟動一個容器,在容器里執行/bin/sh,並且分配一個命令行終端跟這個容器交互
每當在宿主機上運行一個/bin/sh程序,操作系統都會給它分配一個進程編號,如PID=100,這個編號是進程的唯一標識。而現在通過Docker把/bin/sh程序運行在一個容器中,Docker會讓這些進程只能看到重新計算過的進程編號,比如PID=1,可實際上它們在宿主機的操作系統中,還是原來的第100號進程。這種技術就是Linux里面的Namespace機制
Namespace只是Linux創建新進程的一個可選參數,在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
這時新創建的進程就會看到一個全新的進程空間,在這個進程空間里,它的PID是1。之所以說看到,因為這其實只是一個障眼法,在宿主機真實的進程空間里,這個進程的PID還是真實的數值100
還可以多次執行上面的clone() 調用,這樣就會創建多個PID NameSpace,而每個Namespace里的應用進程,都會認為自己是當前容器里的第1號進程,它們既看不到宿主機里真正的進程空間,也看不到其他PID Namespace里面的具體情況
Docker實際上是在創建容器進程時,指定了這個進程所需要啟用的一組Namespace參數,這樣容器就只能看到當前Namespace所限定的資源、文件、設備、狀態。而對於宿主機以及其他不相干的程序,就完全看不見了
常見的Namespace及其作用見下表
namespace | 引入的相關內核版本 | 被隔離的全局系統資源 | 在容器語境下的隔離效果 |
---|---|---|---|
Mount namespaces | Linux 2.4.19 | 文件系統掛接點 | 每個容器能看到不同的文件系統層次結構 |
UTS namespaces | Linux 2.6.19 | nodename 和 domainname | 每個容器可以有自己的 hostname 和 domainame |
IPC namespaces | Linux 2.6.19 | 特定的進程間通信資源,包括System V IPC 和 POSIX message queues | 每個容器有其自己的 System V IPC 和 POSIX 消息隊列文件系統,因此,只有在同一個 IPC namespace 的進程之間才能互相通信 |
PID namespaces | Linux 2.6.24 | 進程 ID 數字空間 (process ID number space) | 每個 PID namespace 中的進程可以有其獨立的 PID; 每個容器可以有其 PID 為 1 的root 進程;也使得容器可以在不同的 host 之間遷移,因為 namespace 中的進程 ID 和 host 無關了。這也使得容器中的每個進程有兩個PID:容器中的 PID 和 host 上的 PID。 |
Network namespaces | 始於Linux 2.6.24 完成於 Linux 2.6.29 | 網絡相關的系統資源 | 每個容器用有其獨立的網絡設備,IP 地址,IP 路由表,/proc/net 目錄,端口號等等。這也使得一個 host 上多個容器內的同一個應用都綁定到各自容器的 80 端口上。 |
User namespaces | 始於 Linux 2.6.23 完成於 Linux 3.8) | 用戶和組 ID 空間 | 在 user namespace 中的進程的用戶和組 ID 可以和在 host 上不同; 每個 container 可以有不同的 user 和 group id;一個 host 上的非特權用戶可以成為 user namespace 中的特權用戶; |
3、使用Namespace進行容器的隔離有什么好處呢?
由上面的內容可以得到用戶運行在容器里的應用進程跟宿主機的其他進程一樣,都是由宿主機操作系統統一管理,只不過這些被隔離的進程擁有額外設置過的Namespace參數
虛擬機技術作為應用沙盒,必須要由Hypervisor來負責創建虛擬機,這個虛擬機里面必須運行一個完整的Guest OS才能執行用戶的應用進程,這就不可避免地帶來了額外的資源消耗和占用。此外用戶應用運行在虛擬機里面,它對宿主機操作系統的調用就不可避免地要經過虛擬化軟件的攔截和處理,對計算資源、網絡和I/O的損耗非常大
而容器化后的應用依然是宿主機上的一個普通進程,那虛擬化帶來的性能損耗是不存在的,並且使用Namespace作為隔離手段的容器並不需要單獨的Guest OS ,這使得容器額外的資源占用可忽略不計
4、使用Namespace進行容器的隔離有什么缺點呢?
最大的缺點就是隔離不徹底
1)容器知識運行在宿主機上的一種特殊的進程,那么多個容器之間使用的就還是同一個宿主機的操作系統內核
2)在Linux內核中,有很多資源和對象是不能被Namespace化的,最典型的例子是:時間
即如果某個容器修改了時間,那整個宿主機的時間都會隨之修改
3)容器給應用暴露出來的攻擊面比較大,在生產環境中,沒有人敢把運行在物理機上的Linux容器暴露在公網上
5、為什么需要對容器進行限制呢?
雖然容器內的第1號進行在障眼法的干擾下只能看到容器里的情況,但是宿主機上,它作為第100號進程與其他進程之間依然是平等競爭關系,即第100號進程表面上雖然被隔離氣力啊,但是它所能夠使用的資源卻是隨時可以被宿主機上的其他進程或容器占用的,這個進程也可能自己把所以的資源吃光
Linux Cgroups(Linux Control Group,限制一個進程組能夠使用的資源上限)是Linux內核中用來為進程設置資源限制的一個重要功能
root@ht-1:/proc# mount -t cgroup cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb)
在/sys/fs/cgroup下面有很多如cpu、memory這樣的子目錄,也叫子系統,這些都是這台機器當前可以被Cgroup進行限制的資源種類,而在子系統對應的資源種類下,你就可以看到該類資源具體可以被限制的方法
對CPU來說可以看到如下幾個配置文件
root@ht-1:/sys/fs/cgroup/cpu# ls cgroup.clone_children cgroup.sane_behavior cpuacct.usage cpu.cfs_period_us cpu.shares docker kubepods notify_on_release system.slice user.slice cgroup.procs cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.stat init.scope kube-proxy release_agent tasks
這樣的配置文件如何用呢?
//新建一個目錄,這個目錄稱為控制組 root@ht-1:/sys/fs/cgroup/cpu# mkdir container //自動生成該子系統對應的資源限制文件 root@ht-1:/sys/fs/cgroup/cpu# ls container/ cgroup.clone_children cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.stat tasks cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.shares notify_on_release
root@ht-1:/sys/fs/cgroup/cpu# cat cpu.cfs_quota_us -1 //-1表示沒有任何限制 root@ht-1:/sys/fs/cgroup/cpu# cat cpu.cfs_period_us 100000 //默認100ms(100000us)
echo 20000 > /cpu.cfs_quota_us //該控制組限制的進程只能使用20ms的CPU時間,即只能使用到20% 的CPU帶寬
Cgroups也有不完善的地方,最大的缺點就是/proc文件系統(存儲記錄當前內核運行狀態的一系列特殊文件)的問題,當在容器里執行top指令,顯示的信息是宿主機的信息
造成這個問題的原因是,/proc文件系統不了解Cgruops限制的存在
6、總結
一個容器其實就是一個啟用了多個Linux Namespace的應用進程,而這個進程能夠使用的資源量,受Cgroups配置的限制
容器的本質是一個進程,用戶的應用進程實際上就是容器里PID=1的進程,也是其他后續創建的所有進程的父進程,這意味着沒有辦法同時運行兩個不同的應用,除非你能事先找到一個公共的PID=1的程序來充當兩個不同應用的父進程