1、Docker(linux container)所依賴的底層技術(隔離技術)php
1 Namespacenode
用來作容器的隔離,有了namespace,在docker container裏頭看來,就是一個完整的linux的世界。在host看來,container裏的進程,就是一個普通的host進程,namespace提供這種pid的映射和隔離效果,host承載着container,就比如一個世外桃源。linux
namespace包括:pid namespace、net namespace、ipc namespace、mnt namespace、uts namespace、user namespace程序員
例如咱們運行一個容器docker
查看容器的進程號shell
能夠看到該容器的pid是3894,在宿主的/proc目錄下存在3894進程的目錄centos
經過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調度策略下的配置
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的網卡訪問外網等這樣的網絡策略
2、學習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方案。