您好,登錄后才能下訂單哦!
一、Docker(linux container)所依賴的底層技術(隔離技術)
1 Namespace
用來做容器的隔離,有了namespace,在docker container里頭看來,就是一個完整的linux的世界。在host看來,container里的進程,就是一個普通的host進程,namespace提供這種pid的映射和隔離效果,host承載著container,就好比一個世外桃源。
namespace包括:pid namespace、net namespace、ipc namespace、mnt namespace、uts namespace、user namespace
例如我們運行一個容器
查看容器的進程號
可以看到該容器的pid是3894,在宿主的/proc目錄下存在3894進程的目錄
通過kill可以結束該容器
查看/proc/[pid]/ns文件
從3.8版本的內核開始,用戶就可以在/proc/[pid]/ns文件下看到指向不同namespace號的文件,效果如下所示,形如[4026531839]者即為namespace號。
我們運行一個容器并獲取容器的pid
獲取容器的pid
#ls -l /proc/pid/ns <<pid表示應用容器的PID
如果兩個進程指向的namespace編號相同,就說明他們在同一個namespace下,否則則在不同namespace里面。
例如我們再創建一個容器,網絡模式為container (使用 --net=container:NAMEorID 指定)
從上面可以看出兩個容器的net namespace編號相同,說明他們在同一個net namespace下,共用一個網絡。
Docker使用了pid、network、mnt、ipc、uts等命名空間來隔離進程、網絡、文件系統等資源。注意,由于Linux并不是namespace了所有東西(如cgroups、/sys、SELinux、/dev/sd*、內核模塊等),僅靠這幾個namespace是無法實現像KVM那樣的完全資源隔離的。
pid namespace:PID namespace隔離非常實用,它對進程PID重新標號,即兩個不同namespace下的進程可以有同一個PID,實現進程隔離,容器只能看到自己的進程,并且每個容器都有一個pid為1的父進程,kill掉該進程容器內的所有進程都會停止;
net namespace:實現網絡隔離,每個容器都可以設置自己的interface、routers、iptables等;docker默認采用veth的方式將container中的虛擬網卡同host上的一個docker bridge: docker0連接在一起;
ipc namespace:container中進程交互還是采用linux常見的進程間交互方法(interprocess communication - IPC),容器中進程間通信采用的方法包括常見的信號量、消息隊列和共享內存。然而與虛擬機不同的是,容器內部進程間通信對宿主機來說,實際上是具有相同PID namespace中的進程間通信,在同一個IPC namespace下的進程彼此可見,而與其他的IPC namespace下的進程則互相不可見。
mnt namespace:通過隔離文件系統掛載點對隔離文件系統提供支持,不同mnt namespace中的文件結構發生變化也互不影響。你可以通過/proc/[pid]/mounts查看到所有掛載在當前namespace中的文件系統,還可以通過/proc/[pid]/mountstats看到mount namespace中文件設備的統計信息,包括掛載文件的名字、文件系統類型、掛載位置等等
注:43234是容器的進程號
uts namspace:UTS namespace提供了主機名和域名的隔離,這樣每個容器就可以擁有了獨立的主機名和域名,在網絡上可以被視作一個獨立的節點而非宿主機上的一個進程。
user namespace:每個container可以有不同的 user 和 group id, 也就是說可以在container內部用container內部的用戶執行程序而非Host上的用戶。
對于容器所依賴的內核文件系統(這些都是non-namespaced),為了保證安全性,docker將其限制為只讀的,例如進入一個容器執行mount命令:
#mount
2 Cgroups
在前面了解了Docker背后使用的資源隔離技術namespace,通過系統調用構建一個相對隔離的shell環境,也可以稱之為一個簡單的“容器”。下面我們則要開始講解另一個強大的內核工具——cgroups。他不僅可以限制被namespace隔離起來的資源,還可以為資源設置權重、計算使用量、操控進程啟停等等。所以cgroups(Control groups)實現了對資源的配額和度量。
cgroups是什么?
cgroups(Control Groups)最初叫Process Container,由Google工程師(Paul Menage和Rohit Seth)于2006年提出,后來因為Container有多重含義容易引起誤解,就在2007年更名為Control Groups,并被整合進Linux內核。顧名思義就是把進程放到一個組里面統一加以控制
groups的作用
通俗的來說,cgroups可以限制、記錄、隔離進程組所使用的物理資源(包括:CPU、memory、IO等),為容器實現虛擬化提供了基本保證,是構建Docker等一系列虛擬化管理工具的基石。Cgroups提供了以下四大功能。
1)資源限制(Resource Limitation):cgroups可以對進程組使用的資源總額進行限制。如設定應用運行時使用內存的上限,一旦超過這個配額就發出OOM(Out of Memory)。
2)優先級分配(Prioritization):通過分配的CPU時間片數量及硬盤IO帶寬大小,實際上就相當于控制了進程運行的優先級。
3)資源統計(Accounting): cgroups可以統計系統的資源使用量,如CPU使用時長、內存用量等等,這個功能非常適用于計費。
4)進程控制(Control):cgroups可以對進程組執行掛起、恢復等操作。
下面就介紹cgroup如何做到內存,cpu和io速率的隔離
本文用腳本運行示例進程,來驗證Cgroups關于cpu、內存、io這三部分的隔離效果。
測試機器環境
執行mount命令查看cgroup的掛載點
從上圖可以看到cgroup掛載在/sys/fs/cgroup目錄
groups可以限制blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns等系統的資源,以下是主要子系統的說明:
blkio 這個子系統設置限制每個塊設備的輸入輸出控制。例如:磁盤,光盤以及usb等等。
cpu 這個子系統使用調度程序為cgroup任務提供cpu的訪問。
cpuacct 產生cgroup任務的cpu資源報告。
cpuset 如果是多核心的cpu,這個子系統會為cgroup任務分配單獨的cpu和內存。
devices 允許或拒絕cgroup任務對設備的訪問。
freezer 暫停和恢復cgroup任務。
memory 設置每個cgroup的內存限制以及產生內存資源報告。
net_cls 標記每個網絡包以供cgroup方便使用,它通過使用等級識別符(classid)標記網絡數據包,從而允許 Linux 流量控制程序(TC:Traffic Controller)識別從具體cgroup中生成的數據包。
ns:命名空間子系統
cgroups管理進程cpu資源
我們先看一個限制cpu資源的例子:
跑一個耗cpu的腳本
運行一個容器,在容器內創建腳本并運行腳本,腳本內容:
將容器切換到后臺運行
在宿主機上top可以看到這個腳本基本占了90%多的cpu資源
下面用cgroups控制這個進程的cpu資源
對于centos7來說,通過systemd-cgls來查看系統cgroups tree:
#systemd-cgls
注:4813就是我們所運行的容器pid
將cpu.cfs_quota_us設為50000,相對于cpu.cfs_period_us的100000是50%
進入容器,再次執行腳本,打開宿主機的另一個終端執行top命令
然后top的實時統計數據如下,cpu占用率將近50%,看來cgroups關于cpu的控制起了效果
CPU資源控制
CPU資源的控制也有兩種策略,一種是完全公平調度(CFS:Completely Fair Scheduler)策略,提供了限額和按比例分配兩種方式進行資源控制;另一種是實時調度(Real-Time Scheduler)策略,針對實時進程按周期分配固定的運行時間。配置時間都以微秒(μs)為單位,文件名中用us表示。
CFS調度策略下的配置
按權重比例設定CPU的分配
docker提供了–cpu-shares參數,在創建容器時指定容器所使用的CPU份額值。例如:
使用命令docker run -tid –-cpu-shares 100 鏡像,創建容器,則最終生成的cgroup的cpu份額配置可以下面的文件中找到:
# cat /sys/fs/cgroup/cpu/system.slice/docker-<容器的完整長ID>/cpu.shares
cpu-shares的值不能保證可以獲得1個vcpu或者多少GHz的CPU資源,僅僅只是一個加權值。
該加權值是一個整數(必須大于等于2)表示相對權重,最后除以權重總和算出相對比例,按比例分配CPU時間。
默認情況下,每個docker容器的cpu份額都是1024。單獨一個容器的份額是沒有意義的,只有在同時運行多個容器時,容器的cpu加權的效果才能體現出來。例如,兩個容器A、B的cpu份額分別為1000和500,在cpu進行時間片分配的時候,容器A比容器B多一倍的機會獲得CPU的時間片。如果容器A的進程一直是空閑的,那么容器B是可以獲取比容器A更多的CPU時間片的。極端情況下,比如說主機上只運行了一個容器,即使它的cpu份額只有50,它也可以獨占整個主機的cpu資源。
cgroups只在容器分配的資源緊缺時,也就是說在需要對容器使用的資源進行限制時,才會生效。因此,無法單純根據某個容器的cpu份額來確定有多少cpu資源分配給它,資源分配結果取決于同時運行的其他容器的cpu分配和容器中進程運行情況。
cpu-shares演示案例:
先刪除docker主機上運行的容器
Docker通過--cpu-shares 指定CPU份額
運行一個容器指定cpu份額為1024
注:
--cpu-shares 指定CPU份額,默認就是1024
--cpuset-cpus可以綁定CPU。例如,指定容器在--cpuset-cpus 0,1 或--cpuset-cpus 0-3
--cpu是stress命令的選項表示產生n個進程每個進程都反復不停的計算隨機數的平方根
stress命令是linux下的一個壓力測試工具。
在docker宿主機上打開一個terminal執行top
然后再啟動一個容器,--cpu-shares為512。
查看top的現實結果
可以看到container1的CPU占比為1024/(1024+512)=2/3,container2的CPU占比為512/(1024+512)=1/3
將container1的cpu.shares改為512,
#echo “512” >/sys/fs/cgroup/cpu/system.slice/docker-<容器的完整長ID>/cpu.shares
可以看到兩個容器的CPU占比趨于平均
設定CPU使用周期使用時間上限
cgroups 里,可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 來限制該組中的所有進程在單位時間里可以使用的 cpu 時間。cpu.cfs_period_us 就是時間周期,默認為 100000,即百毫秒。cpu.cfs_quota_us 就是在這期間內可使用的 cpu 時間,默認 -1,即無限制。
cpu.cfs_period_us:設定時間周期(單位為微秒(μs)),必須與cfs_quota_us配合使用。
cpu.cfs_quota_us :設定周期內最多可使用的時間(單位為微秒(μs))。這里的配置指task對單個cpu的使用上限。
舉個例子,如果容器進程需要每1秒使用單個CPU的0.2秒時間,可以將cpu-period設置為1000000(即1秒),cpu-quota設置為200000(0.2秒)。
當然,在多核情況下,若cfs_quota_us是cfs_period_us的兩倍,就表示在兩個核上完全使用CPU,例如如果允許容器進程需要完全占用兩個CPU,則可以將cpu-period設置為100000(即0.1秒),cpu-quota設置為200000(0.2秒)。
使用示例:
使用命令docker run創建容器
在宿主機上執行top
從上圖可以看到基本占了100%的cpu資源
則最終生成的cgroup的cpu周期配置可以下面的目錄中找到:
/sys/fs/cgroup/cpu/system.slice/docker-<容器的完整長ID>/
修改容器的cpu.cfs_period_us 和 cpu.cfs_quota_us值
執行top查看cpu資源
從上圖可以看到基本占了50%的cpu資源
RT調度策略下的配置 實時調度策略與公平調度策略中的按周期分配時間的方法類似,也是在周期內分配一個固定的運行時間。
cpu.rt_period_us :設定周期時間。
cpu.rt_runtime_us:設定周期中的運行時間。
cpuset - CPU綁定
對多核CPU的服務器,docker還可以控制容器運行限定使用哪些cpu內核和內存節點,即使用–cpuset-cpus和–cpuset-mems參數。對具有NUMA拓撲(具有多CPU、多內存節點)的服務器尤其有用,可以對需要高性能計算的容器進行性能最優的配置。如果服務器只有一個內存節點,則–cpuset-mems的配置基本上不會有明顯效果
注:
現在的機器上都是有多個CPU和多個內存塊的。以前我們都是將內存塊看成是一大塊內存,所有CPU到這個共享內存的訪問消息是一樣的。但是隨著處理器的增加,共享內存可能會導致內存訪問沖突越來越厲害,且如果內存訪問達到瓶頸的時候,性能就不能隨之增加。NUMA(Non-Uniform Memory Access)就是這樣的環境下引入的一個模型。比如一臺機器是有2個處理器,有4個內存塊。我們將1個處理器和兩個內存塊合起來,稱為一個NUMA node,這樣這個機器就會有兩個NUMA node。在物理分布上,NUMA node的處理器和內存塊的物理距離更小,因此訪問也更快。比如這臺機器會分左右兩個處理器(cpu1, cpu2),在每個處理器兩邊放兩個內存塊(memory1.1, memory1.2, memory2.1,memory2.2),這樣NUMA node1的cpu1訪問memory1.1和memory1.2就比訪問memory2.1和memory2.2更快。所以使用NUMA的模式如果能盡量保證本node內的CPU只訪問本node內的內存塊,那這樣的效率就是最高的。
使用示例:
表示創建的容器只能用0、1、2這三個內核。最終生成的cgroup的cpu內核配置如下:
cpuset.cpus:在這個文件中填寫cgroup可使用的CPU編號,如0-2,16代表 0、1、2和16這4個CPU。
cpuset.mems:與CPU類似,表示cgroup可使用的memory node,格式同上
通過docker exec <容器ID> taskset -c -p 1(容器內部第一個進程編號一般為1),可以看到容器中進程與CPU內核的綁定關系,可以認為達到了綁定CPU內核的目的。
總結:
CPU配額控制參數的混合使用
當上面這些參數中時,cpu-shares控制只發生在容器競爭同一個內核的時間片時,如果通過cpuset-cpus指定容器A使用內核0,容器B只是用內核1,在主機上只有這兩個容器使用對應內核的情況,它們各自占用全部的內核資源,cpu-shares沒有明顯效果。
cpu-period、cpu-quota這兩個參數一般聯合使用,在單核情況或者通過cpuset-cpus強制容器使用一個cpu內核的情況下,即使cpu-quota超過cpu-period,也不會使容器使用更多的CPU資源。
cpuset-cpus、cpuset-mems只在多核、多內存節點上的服務器上有效,并且必須與實際的物理配置匹配,否則也無法達到資源控制的目的。
在系統具有多個CPU內核的情況下,需要通過cpuset-cpus為容器CPU內核才能比較方便地進行測試。
內存配額控制
和CPU控制一樣,docker也提供了若干參數來控制容器的內存使用配額,可以控制容器的swap大小、可用內存大小等各種內存方面的控制。主要有以下參數:
Docker提供參數-m, --memory=""限制容器的內存使用量,如果不設置-m,則默認容器內存是不設限的,容器可以使用主機上的所有空閑內存
內存配額控制使用示例
設置容器的內存上限,參考命令如下所示
#docker run -dit --memory128m 鏡像
默認情況下,除了–memory指定的內存大小以外,docker還為容器分配了同樣大小的swap分區,也就是說,上面的命令創建出的容器實際上最多可以使用256MB內存,而不是128MB內存。如果需要自定義swap分區大小,則可以通過聯合使用–memory–swap參數來實現控制。
可以發現,使用256MB進行壓力測試時,由于超過了內存上限(128MB內存+128MB swap),進程被OOM(out of memory)殺死。
使用250MB進行壓力測試時,進程可以正常運行。
通過docker stats可以查看到容器的內存已經滿負載了。
#docker stats test2
對上面的命令創建的容器,可以查看到在cgroups的配置文件中,查看到容器的內存大小為128MB (128×1024×1024=134217728B),內存和swap加起來大小為256MB (256×1024×1024=268435456B)。
#cat /sys/fs/cgroup/memory/system.slice/docker-<容器的完整ID>/memory.limit_in_bytes
134217728
#cat /sys/fs/cgroup/memory/system.slice/docker-<容器的完整ID>/memory.memsw.limit_in_bytes
268435456
磁盤IO配額控制
主要包括以下參數:
--device-read-bps:限制此設備上的讀速度(bytes per second),單位可以是kb、mb或者gb。--device-read-iops:通過每秒讀IO次數來限制指定設備的讀速度。
--device-write-bps :限制此設備上的寫速度(bytes per second),單位可以是kb、mb或者gb。
--device-write-iops:通過每秒寫IO次數來限制指定設備的寫速度。
--blkio-weight:容器默認磁盤IO的加權值,有效值范圍為10-1000。
--blkio-weight-device:針對特定設備的IO加權控制。其格式為DEVICE_NAME:WEIGHT
磁盤IO配額控制示例
blkio-weight
使用下面的命令創建兩個–blkio-weight值不同的容器:
在容器中同時執行下面的dd命令,進行測試
注:oflag=direct規避掉文件系統的cache,把寫請求直接封裝成io指令發到硬盤
3 Chroot
如何在container里頭,看到的文件系統,就是一個完整的linux系統,有/etc、/lib 等,通過chroot實現
4 Veth
container里,執行ifconfig可以看到eth0的網卡,如何通信呢?其實是在host上虛擬了一張網卡出來(veth73f7),跟container里的網卡做了橋接,所有從container出來的流量都要過host的虛擬網卡,進container的流量也是如此。
5 Union FS
對于這種疊加的文件系統,有一個很好的實現是AUFS,這個可以做到以文件為粒度的copy-on-write,為海量的container的瞬間啟動。
6 Iptables, netfilter
主要用來做ip數據包的過濾,比如可以做container之間無法通信,container可以無法訪問host的網絡,但是可以通過host的網卡訪問外網等這樣的網絡策略
二、學習Docker也有一段時間了,了解了Docker的基本實現原理,也知道了Docker的使用方法,這里對Docker的一些典型應用場景做一個總結
1、配置簡化
這是Docker的主要使用場景。將應用的所有配置工作寫入Dockerfile中,創建好鏡像,以后就可以無限次使用這個鏡像進行應用部署了。這大大簡化了應用的部署,不需要為每次部署都進行繁瑣的配置工作,實現了一次打包,多次部署。這大大加快了應用的開發效率,使得程序員可以快速搭建起開發測試環境,不用關注繁瑣的配置工作,而是將所有精力都盡可能用到開發工作中去。
2、代碼流水線管理
代碼從開發環境到測試環境再到生產環境,需要經過很多次中間環節,Docker給應用提供了一個從開發到上線均一致的環境,開發測試人員均只需關注應用的代碼,使得代碼的流水線變得非常簡單,這樣應用才能持續集成和發布。
3、快速部署
在虛擬機之前,引入新的硬件資源需要消耗幾天的時間。Docker的虛擬化技術將這個時間降到了幾分鐘,Docker只是創建一個容器進程而無需啟動操作系統,這個過程只需要秒級的時間。
4、應用隔離
資源隔離對于提供共享hosting服務的公司是個強需求。如果使用VM,雖然隔離性非常徹底,但部署密度相對較低,會造成成本增加。
Docker容器充分利用linux內核的namespace提供資源隔離功能。結合cgroups,可以方便的設置每個容器的資源配額。既能滿足資源隔離的需求,又能方便的為不同級別的用戶設置不同級別的配額限制。
5、服務器資源整合
正如通過VM來整合多個應用,Docker隔離應用的能力使得Docker同樣可以整合服務器資源。由于沒有額外的操作系統的內存占用,以及能在多個實例之間共享沒有使用的內存,Docker可以比VM提供更好的服務器整合解決方案。
通常數據中心的資源利用率只有30%,通過使用Docker并進行有效的資源分配可以提高資源的利用率。
6、多版本混合部署
隨著產品的不斷更新換代,一臺服務器上部署多個應用或者同一個應用的多個版本在企業內部非常常見。但一臺服務器上部署同一個軟件的多個版本,文件路徑、端口等資源往往會發生沖突,造成多個版本無法共存的問題。
如果用docker,這個問題將非常簡單。由于每個容器都有自己獨立的文件系統,所以根本不存在文件路徑沖突的問題; 對于端口沖突問題,只需要在啟動容器時指定不同的端口映射即可解決問題。
7、版本升級回滾
一次升級,往往不僅僅是應用軟件本身的升級,通過還會包含依賴項的升級。但新舊軟件的依賴項很可能是不同的,甚至是有沖突的,所以在傳統的環境下做回滾一般比較困難。
如果使用docker,我們只需要每次應用軟件升級時制作一個新的docker鏡像,升級時先停掉舊的容器,然后把新的容器啟動。需要回滾時,把新的容器停掉,舊的啟動即可完成回滾,整個過程各在秒級完成,非常方便。
8、內部開發環境
在容器技術出現之前,公司往往是通過為每個開發人員提供一臺或者多臺虛擬機來充當開發測試環境。開發測試環境一般負載較低,大量的系統資源都被浪費在虛擬機本身的進程上了。
Docker容器沒有任何CPU和內存上的額外開銷,很適合用來提供公司內部的開發測試環境。而且由于Docker鏡像可以很方便的在公司內部共享,這對開發環境的規范性也有極大的幫助。
9、PaaS
使用Docker搭建大規模集群,提供PaaS。這一應用是最有前景的一個了,目前已有很多創業公司在使用Docker做PaaS了,例如云雀云平臺。用戶只需提交代碼,所有運維工作均由服務公司來做。而且對用戶來說,整個應用部署上線是一鍵式的,非常方便。
10、云桌面
在每一個容器內部運行一個圖形化桌面,用戶通過RDP或者VNC協議連接到容器。該方案所提供的虛擬桌面相比于傳統的基于硬件虛擬化的桌面方案更輕量級,運行速率大大提升。不過該方案仍處于實驗階段,不知是否可行。可以參考一下Docker-desktop方案。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。