Docker容器與容器雲

1.從容器到容器雲

Docker(初識Docker)是以Docker容器爲資源分割和調度的基本單位,封裝整個軟件運行時環境,爲開發者和系統管理員設計的,用於構建、發佈和運行分佈式應用的平臺。它是一個跨平臺、可移植並且簡單易用的容器解決方案。

容器技術帶來的主要好處:

  • 持續部署與測試,消除線上線下環境差異,保證應用生命週期的環境一致性和標準化,大大簡化持續集成、測試和發佈的過程;
  • 跨雲平臺支持,容器帶來的最大好處是其適配性,無需擔心雲平臺的捆綁;
  • 環境標準化和版本控制,一致性與標準化,對整個應用運行環境實現版本控制,快速回滾;
  • 高資源利用率與隔離,沒有管理程序的額外開銷,與底層共享操作系統,性能更加優良,精確對應用分配CPU、內存等資源;
  • 容器跨平臺性與鏡像,"構建一次,到處運行"
  • 易於理解且易用,docker原意是處理集裝箱的碼頭工人,集裝箱就是容器
  • 應用鏡像倉庫,類似GitHub,非常有用的應用商店,可以自由地下載微服務組件

什麼是容器雲?以容器爲資源分割和調度的基本單位,封裝整個軟件時運行環境,爲開發者和系統管理員提供用於構建、發佈和運行分佈式應用的平臺,容器雲不僅限於Docker。

2.Docker基礎

3.Docker核心原理解讀

Docker容器本質是宿主機(容器所在的運行環境,既可以是硬件服務器,也可以是虛擬機)上的進程。Docker通過namespace實現了資源隔離,通過cgroups實現了資源限制,通過寫時複製機制(copy-on-write)實現了高效的文件操作

3.1 Docker背後的內核知識

3.1.1 namespace資源隔離

文件系統隔離;分佈式環境下進行通信和定位,容器必然要有獨立的IP、端口、路由等,即網絡的隔離;同時,容器還需要一個獨立的主機名以便在網絡中標誌自己;有了網絡,自然離不開通信,也就想到了進程間通信需要隔離;還有權限的問題,對用戶和用戶組的隔離就實現了用戶權限的隔離;最後,運行在容器中的應用需要有進程號(PID),自然也需要與宿主機的PID進行隔離。

Linux內核實現namespace的一個主要目的就是實現輕量級虛擬化容器服務,在用一個namespace下的進程可以感知彼此的變化,而對外界的進程一無所知,這樣就可以讓容器中的進程產生錯覺,彷彿自己置身於一個獨立的系統環境中,以達到獨立和隔離的目的。

UTS(UNIX Time-sharing System) namespace提供了主機名和域名的隔離,擁有獨立的主機名和域名,在網絡上可以被視爲一個獨立的節點,而非宿主機上的一個進程,Docker中,每個鏡像基本都以自身所提供的服務名稱來命名鏡像的hostname,且不會對宿主機產生任何的影響;

IPC(Inter-Process Communication)涉及的IPC資源包括常見的信號量、消息隊列和共享內存;

PID namespace對進程PID重新標號,即兩個不同的namespace下的進程可以有相同的PID,具有樹狀層次體系,父節點可以看到子節點中的進程,並可以通過信號等方式對子節點中國的進程產生影響,反過來不成立;

mount namspace通過隔離文件系統掛載點對隔離文件系統提供支持,掛載傳播定義了掛載對象之間的關係,利用這些關係決定任何掛載對象中的掛載事件如何傳播到其它掛載對象。

network namespace主要提供關於網絡自願的隔離,包括網絡設備、IPv4和IPv6協議棧、IP路由表、防火牆、套接字等,未必是真正的網絡隔離,而是把網絡獨立出來,給外部用戶一種透明的感覺,彷彿在與一個獨立網絡實體進行通信。

user namespace主要隔離了安全相關的標識符和屬性,包括用戶ID、用戶組ID、root目錄、祕鑰以及特殊權限。

3.1.2 cgroups資源限制

上節,Docker通過使用資源隔離技術namespace,通過系統調用構建一個相對隔離的shell環境,也可以稱之爲簡單的容器。

cgroups是Linux內核提供的一種機制,這種機制可以根據需要把一系列系統任務及其子任務整合或分隔到按資源劃分等級的不同組內,從而爲系統資源管理提供一個統一的框架。通俗的講,cgroups可以限制、記錄任務組所使用的物理資源(包括CPU、Memory、IO等),爲容器虛實現虛擬化提供了基本保證,是Docker等一系列虛擬化管理工具的基石。本質來說,cgroups是內核附加在程序上的一系列鉤子(hook),當任務運行的過程中涉及某種資源時,就會觸發鉤子上所附帶的子系統進行檢測,根據資源類別的不同,使用對應的技術進行資源限制和優先級分配,比如對於memory子系統,當進程需要申請更多內存時,就會觸發cgroup用量檢測,用量超過cgroup規定的限額,則拒絕用戶的內存申請。

從單個任務的資源控制到操作系統層面的虛擬化,cgroups提供了以下四大功能:

  • 資源限制:cgroups可以對任務使用的資源總額進行限制,如設定運行時使用內存的上限,一旦超過這個配額髮出OOM提示;
  • 優先級分配:通過分配的CPU時間片數量及磁盤IO帶寬大小,實際上就相當於控制了任務運行的優先級;
  • 資源統計:可以統計系統的資源使用量,比如CPU使用時長、內存用量等,這個功能非常適用於計費;
  • 任務控制:可以對任務執行掛起、恢復等操作。

3.2 Docker架構概覽

圖-Docker架構圖

Images(Docker鏡像):只讀模板,包含創建Docker容器的說明,類似系統安裝光盤——使用系統安裝光盤可以安裝系統,同理使用Docker鏡像可以運行Docker鏡像中的程序。

Docker使用傳統的client-server架構模式,用戶通過client與Docker daemon建立通信,並將請求發送給後者,而Docker的後端是松耦合結構。

圖-Docker架構總覽

Docker daemon:最核心的後臺進程,負責響應來自Docker client的請求,然後將這些請求翻譯成系統調用完成容器管理操作。該進程會在後臺啓動一個API Server,負責接收由Docker client發送的請求;接收到的請求將通過Docker daemon分發調度,再由具體的函數執行請求。

Docker client:泛稱,用於向daemon發起請求,既可以是命令行工具docker,也可以是遵循了Docker API的客戶端,目前社區維護着的client非常豐富,涵蓋Java、Go、Ruby等。

image management:通過distribution、registry、layer、image、reference等模塊實現了Docker的鏡像管理。

execdriver、volumedriver、graphdriver:容器執行驅動、volume存儲驅動、鏡像存儲驅動。

network:libnetwork抽象出一個容器網絡模型,並給調用者提供了統一抽象接口,對真實的容器網絡抽象出沙盒sandbox、斷點endpoint、網絡network這三種對象,由具體網絡驅動操作對象,並通過網絡控制器提供一個統一接口供調用者管理網絡。

3.3 client和daemon

3.4 libcontainer

3.5 Docker鏡像管理

Docker鏡像是一個只讀的Docker容器模板,含有啓動Docker容器所需的文件系統結構及其內容,因此是啓動一個Docker容器的基礎。Docker鏡像的文件內容以及一些運行Docker容器的配置文件組成了Docker容器的靜態文件系統運行環境——rootfs。可以這麼理解,Docker鏡像是Docker容器的靜態視角,Docker容器是Docker鏡像的運行狀態。

Docker鏡像的主要特點

  • 分層

鏡像採用分層的方式構建,每個鏡像都由一系列的鏡像層組成,分層結構是Docker鏡像如此輕量的重要原因。

  • 寫時複製

多個容器之間共享鏡像,每個容器在啓動的時候並不需要單獨複製一份鏡像文件,而是將所有鏡像層以只讀的方式掛載到一個掛載點,再在上面覆蓋一個可讀寫的容器層。寫時複製配合分層機制減少了鏡像對磁盤空間的佔用和容器啓動的時間。

  • 內容尋址

根據文件內容來索引鏡像和鏡像層。使用內容hash值代替之前的UUID作爲鏡像層的唯一標誌,提高鏡像安全性,可以在pull、push、load和save操作後檢測數據的完整性,同時減少ID衝突增強了鏡像層的共享,對於來自不同構建的鏡像層,只要擁有相同的內容哈希,也能被不同的鏡像共享。

  • 聯合掛載

聯合掛載技術可以在一個掛載點同時掛載多個文件系統,將掛載點的原目錄與被掛載內容進行整合,最終可見的文件系統將會包含整合之後的各層的文件和目錄。

docker鏡像關鍵概念

  • registry

用於保存Docker鏡像,包括鏡像層次結構和關於鏡像的元數據,可以將registry簡單地想象成類似Git倉庫之類的實體。

  • repository​​​​​​

鏡像集合,其中包含不同版本的鏡像,使用標籤進行版本區分,registry是repository的集合。

  • manifest

描述文件主要存在registry中作爲鏡像的元數據文件,在pull、push、save和load中作爲鏡像結構和基礎信息的描述文件。

  • image和layer

image用來存儲鏡像相關的元數據信息,主要包括鏡像的架構、默認配置信息、構建鏡像的容器配置信息等

layer鏡像層,是一個Docker用來管理鏡像層的中間概念。鏡像是由鏡像層組成的,而單個鏡像層可能被多個鏡像共享

  • Dockerfile

使用基本DSL語法來定義Docker鏡像,每一條指令描述了構建鏡像的步驟。

Docker鏡像構建操作

docker build,在一個鏡像的基礎上構建鏡像,使用Dockerfile或者Maven插件

docker commit,提交容器鏡像發生變更了的部分,即修改後容器鏡像與當前倉庫中對應鏡像之間的差異部分。

Docker鏡像的分發方法

如何實現在某臺機器上導出一個Docker容器並且在另外一臺機器上導入這一操作?

docker push/pull,通過Docker Hub的方式遷移

docker save通過線下包分發的方式遷移

docker export持久化容器,docker push和save用於持久化鏡像,將容器導出後再導入後容器會丟失所有的歷史,而保存在加載鏡像則沒有丟失歷史和層,可以通過docker tag命令實現歷史層回滾,但是持久化容器則不行。

3.6 Docker存儲管理

將元數據與鏡像文件的存儲完全隔離開,元數據管理包括repository元數據、image元數據、layer元數據。

3.7 Docker數據卷

數據卷volume機制,volume是存在於一個或多個容器中的特定文件或文件夾,這個目錄以獨立於聯合文件系統的形式在宿主機中存在,併爲數據的共享和持久化提供以下便利:volume在容器創建時就會初始化,在容器運行時就可以使用其中的文件;volume能在不同的容器之間共享和重用;對volume中數據的操作會馬上生效;對volume中數據的操作不會影響都鏡像本身;volume的生存週期獨立於容器的生存週期,即使容器刪除,volume仍然會存在,沒有任何容器使用的volume也不會被Docker刪除。當前官方默認使用宿主機的文件系統爲Docker容器提供volume。

3.8 Docker網絡管理

Docker daemon通過調用libnetwork對外提供的API完成網絡的創建和管理等功能,libnetwork中則使用了CNM來完成網絡功能的提供,CNM中主要有沙河sandbox、端點endpoint、網絡network這3種組件,內置的5種驅動則爲libnetwork提供了不同類型的網絡服務。

圖-Docker網絡虛擬化架構

3.9 Docker與容器安全

Docker的安全機制:

Docker daemon在以TCP形式提供服務的同時使用傳輸層安全協議TLS;
在構建和使用鏡像時會驗證鏡像的簽名證書;
通過cgroups和namespaces來對容器進行資源限制和隔離;
容器之間的網絡安全通過--icc標誌,可以禁止容器與容器之間通信,主要通過設定iptables規則實現;
提供自定義容器能力,在更細的粒度上限制容器進程所能使用的系統調用,分解超級用戶所擁有的權限,對於普通用戶,有時需要使用超級用戶權限的部分能力,但是爲了安全不便把普通用戶提升爲超級用戶,此時可以考慮爲該用戶增加一些能力,但不需要賦予其所有超級用戶權限;
通過自定義seccomp profile限制容器內進程系統調用的範圍等。

Docker安全問題:

磁盤資源限制問題,耗盡宿主機磁盤容量;
容器逃逸問題;
容器Dos攻擊與流量限制問題,如果在同一宿主機中的某一個容器搶佔了大部分帶寬,將會影響其它容器的使用;
超級權限問題,--privilege;

Docker安全的解決方案:

SELinux,內核實現的MAC(Mantory Access Control,強制訪問控制);
磁盤限額,--storage-opt=[]來進行磁盤限制,使用基於用戶和文件系統的quota技術;
宿主機內容器流量限制,Docker已經爲容器的資源限制做了許多工作,但是在網絡帶寬方面沒有進行限制,存在一些安全隱患,無限制的大流量訪問會破壞容器的實時交互能力,所以需要對容器流量進行限制,可以使用Linux流量控制模塊,對容器網卡流量進行限制,在一定程度上可以減輕Dos攻擊危害;
GRSecurity內核安全增強工具;
fork炸彈,就是即極快的速度創建大量進程,並以此消耗系統分配給進程的可用空間使進程表飽和,從而使系統無法運行新程序,佔用CPU和內存,從而導致系統與現有進程運行速度放緩,響應時間也會隨之大幅增加。到目前爲止,現有的方案都不算完美解決:通過ulimit限制最大進程數目,限制內存內核使用,cgroup pid子系統會在一定條件下拒絕fork調用從而完美解決fork炸彈的問題。

4.Docker高級實踐技巧

4.1 容器化思維

容器本質上是一個進程以及運行該進程所需要的各種依賴。

容器技術輕量級的特性和"構建一次,到處運行"的特性降低了微服務型應用的額外開銷,提升了微服務的應用開發流程效率,使得微服務的開發模式成爲可能。

4.2 Docker高級網絡實踐

4.3 Dockerfile最佳實踐

實踐心得:使用標籤,易讀,見名知意;
謹慎選擇基礎鏡像,儘量選擇當前官方鏡像庫中的鏡像;
充分利用緩存,保證指令的連續性,儘量將所有Dockerfile文件中的相同部分放在前面,而將不同的部分放在後面;
正確使用ADD與COPY指令,ADD指令獲取遠程URL中的壓縮包不推薦,應該使用RUN wget或RUN curl,這樣可以刪除解壓後不再需要的文件,並且不需要在鏡像中再添加一層,另外儘量使用docker volume共享文件,而不是ADD或COPY指令添加文件到鏡像中;
RUN指令,使用反斜槓分割,不要將所有的命令寫在一個RUN指令中。指令分層符合Docker的核心概念,即Docker提交鏡像是廉價的,鏡像之間有層次關係;
不要在Dockerfile中做端口映射,破壞可重複性和可移植性,端口映射應在docker run命令中-p參數指定,而不是EXPOSE指令;
使用Dockerfile共享Docker鏡像,若要共享鏡像,只需共享Dockerfile文件即可,Dockerfile可以加入版本控制,可以清楚鏡像構建過程,且具有確定性。

4.4 Docker容器的監控手段

監控維度包括:主機維度、鏡像維度、容器維度。

監控命令:docker ps查看正在運行容器信息、docker images查看主機上的鏡像信息、docker stats容器狀態信息的統計、docker inspect 查看鏡像或容器的底層詳細信息、docker top查看進程運行情況、docker port查看容器與主機之間的端口映射關係信息。

常用的容器監控工具:Google cAdvisor、Datadog

4.5 容器化應用構建的基礎:高可用配置中心

5.構建自己的容器雲

圖-一個基於容器的雲平臺

8.一切皆容器:Kubernetes

8.1 K8s是個什麼樣的項目

K8s是一個管理跨主機容器化應用的系統,實現了包括應用部署、高可用管理和彈性伸縮在內的一系列基礎功能並封裝成一套完整、簡單易用的Restful api對外提供服務。設計哲學之一就是維護應用容器集羣一直處於用戶期望的狀態,建立一套健壯的集羣自恢復機制,包括容器的自動重啓、自動重調度以及自動備份等。K8s引入了專門對容器進行分組管理的pod,從而建立一套將非容器化應用平滑遷移到容器雲上的機制,可以說整個K8s的設計理念就是在圍繞pod這個可以視作單個容器的容器組展開的。

8.2 K8s的設計解讀

用戶創建容器時,會給每個容器指定一個用來分組的標籤label,K8s根據標籤進行一些決策,比如從高可用角度用戶可以指定name=frontend標籤的容器不會被調度在同一個node上。

這些容器可以直接使用IP:port的方式進行通信,重新調度或重啓後容器ip會發生變化,所以K8s內置servive代理組件,當容器分配一個serveice代理後,這些容器就可以統一使用一個固定ip被訪問到。

replication controller、label和service,真正操作的對象都是一個統稱pod的邏輯對象而非容器,這是K8s和其它編排調度平臺最大的區別。pod可以想象成一個籃子,而容器則是籃子裏的雞蛋,當K8s需要調度容器時,直接把一個籃子連同裏面的雞蛋從一個宿主機調度到另一個宿主機,而不是一個一個地搬運裏面的雞蛋。具體關係體現如下:一個pod裏的容器能有多少資源取決於籃子的大小;label是貼在籃子上的;IP分配給籃子而不是容器,籃子裏面的所有容器共享這個IP;哪怕只有一個雞蛋(容器),K8s仍然會給它分配一個籃子。

pod設計解讀

K8s中,能夠被創建、調度和管理的最小單元是pod而非容器,一個pod是若干個Docker容器構成的容器組,pod裏的容器共享network namespace,並通過volume機制共享一部分存儲。

replication controller設計解讀

是K8s爲解決構造完全同質的pod副本問題而引入的資源對象,決定了一個pod有多少同時運行的副本,並保證這些副本的期望狀態與當前狀態一致。典型的應用場景包括:重調度,不管使用者想運行1個還是100個pod副本,replication controller都能保證指定數目的pod正常運行;彈性伸縮,無論是通過手動還是自動彈性伸縮控制代理來修改副本數replicas字段,都能輕鬆實現pod數量的彈性伸縮;滾動更新(灰度發佈),使用逐個替換pod的方式進行副本增刪操作,這使得容器的滾動更新會非常簡單;應用多版本release追蹤。

service設計解讀

由於重新調度等原因,pod在K8s中的IP地址不是固定的,因此需要一個代理來確保使用pod的應用不需要知曉pod的真實IP地址,另一個原因是當使用replication controller創建多個pod的副本時,需要一個代理來爲這些pod做負載均衡。

新一代副本控制器 replica set

升級版replication controller

Deployment

多用於爲pod和replica set提供更新,並且可以方便地追蹤觀察所屬的replica set或者pod數量以及狀態的變化。換言之,Deployment是爲了應用的更新而設計的。

DaemonSet

每當一個新的工作節點加入到集羣中,系統就會按照DaemonSet的配置在節點上運行相應的pod,負責這部分工作的是DaemonSet controller。

ConfigMap

包含了一系列的鍵值對,用於存儲被pod或者系統組件訪問的信息(不包含敏感信息)。

Job

專門支持batch job的資源——job,可以將其理解爲run to completion的任務。

Horizontal Pod Autoscaler

根據負載的變化對計算資源的分配進行自動的擴增或者收縮,最大程度減少費用或者其它代價。自動擴展主要分爲兩種,水平擴展即針對實例數目的增減,垂直擴展即單個實例可以使用的資源的增減,HPA屬於前者。HPA通過檢測CPU實際使用量求出當前的CPU使用率,對比該CPU使用率和預期值,通過調整副本數量使得CPU使用率儘量向期望值靠近。

8.3 K8s核心組件解讀

圖-K8s整體架構圖

由兩種節點組成:master節點和工作節點,前者是管理節點,後者是容器運行的節點。

master節點主要由3個重要的組件,分別是API server、scheduler和controller manager。API server組件負責響應用戶的管理請求、進行指揮協調等工作;scheduler的作用是將待調度的pod綁定到合適的工作節點;controller manager是一組控制器的合集,負責控制管理對應的資源,如副本replication個工作節點node等。

工作節點運行了兩個重要組件,分別爲kubelet和kube-proxy,前者可以看做一個管理維護pod運行的agent,後者負責將service的流量轉發到對應的endpoint。

位於master節點的APIServer將負責與master節點、工作節點上各個組件之間的交互,以及集羣外用戶(例如用戶的kubectl命令)與集羣的交互,在集羣中處於消息收發的中心地位;其它各個組件各司其職,共同完成應用分發、部署與運行的工作。

8.4 K8s存儲核心原理

K8s擁有自研的volume,使用方式類似於虛擬機的磁盤,需要給pod(即一個邏輯上的虛擬機)掛一個磁盤,然後在該pod裏的進程(容器)才能通過volumeMounts的方式使用掛載磁盤,pod內的容器共享這個volume。對比Docker本身提供的volume機制,K8s volume本質上也是爲了進行數據的持久化,除此之外還有以下幾點區別:

K8s volume生命週期與pod相同,volume會隨着pod的銷燬而銷燬,但是不會因爲pod內某個容器的重啓而銷燬;同時根據volume的用途場景進行了細化,比如不隨pod銷燬而銷燬的persistent volume,支持敏感信息存儲的secret,用來在pod之間傳遞相應配置信息的configmap。

8.5 K8s網絡核心原理

單pod單IP模型,爲每個pod分配一個K8s集羣私有網絡地址段的IP地址,通過該IP地址,pod能夠跨網絡與其它物理機、虛擬機或容器進行通信,pod內的容器全部共享這pod的網絡配置,彼此之間使用localhost通信,彷彿他們運行在一個機器上一樣,這樣不需要顯式爲相互通信的pod內的容器創建Docker link。

8.6 K8s多租戶管理與資源控制

K8s提供類似namespace概念,實現了在一定程度上多租戶資源邏輯上的隔離;提供了多種類型的認證授權機制;還提供了可擴展的多維度資源管控機制admission controller。

namespace將一個K8s集羣對象進行細分的類似於DNS子域名的概念,能夠幫助不同的租戶共享一個K8s集羣;

K8s用戶認證機制包括5種,包括基於客戶端證書的認證機制,基於token的認證機制,基於OpenID Connect ID Token的認證機制,Basic認證機制,Keystone認證機制。

K8s用戶授權機制,認證和授權是分開的,授權發生在認證完成之後,認證過程是檢驗發起API請求的用戶是不是他所聲稱的那個人,而授權過程則判斷此用戶是否是否有執行該API請求的權限,因此授權是以認證的結果作爲基礎的。

K8s多維度資源管理機制admission control,APIServer會在用戶的請求通過認證授權之後調用admission control,對用戶請求進行進一步的審覈,所以即使用戶請求通過了認證授權,也有可能因爲申請的資源超過資源配額而被admission control駁回,作爲多維度可擴展的資源管理機制,每個維度通過一個插件實現,例如NamespaceLifecycle、LimitRanger、SecurityContextDeny插件等

8.7 K8s高級實踐

應用健康檢查,通過kublelet完成

進程級健康檢查,即檢驗容器進程是否存活,kubelet定期通過daemon獲取所有Docker進程的運行情況,如果發現未正常運行則重新啓動該容器進程。

業務級健康檢查,如業務死鎖容器無法正常響應用戶的業務請求,爲此K8s引入了一個在容器內執行的活性探針(liveness probe),以支持用戶自己實現應用業務級的健康檢查,具體健康與否需要用戶自定義,K8s支持的健康檢查動作包括:HTTP Get,調用Web應用的web hook通過HTTP狀態碼判定;Container Exec,在用戶容器內執行一次命令根據退出碼判定;TCP Socket,打開一個到用戶容器的socket連接,如果連接建立成功則代表容器運行正常。

高可用性

常見問題集,使用K8s多集羣。

日誌

K8s組件日誌和容器日誌

集成DNS

容器上下文環境

8.8 K8s未來動向

8.9 不要停止思考

K8s從一開始專注於實現“一切皆容器”的設計思路,即如何完成服務器集羣的統一抽象,並且嘗試使用容器及更高的抽象方式pod作爲載體將各種各樣的任務負載統一起來。

9.使用Docker Compose編排微服務

Compose用於定義和運行多個容器Docker應用程序的工具。具體demo可以參考周立的博客-Docker系列教程

參考資料

《Docker容器與容器雲 第2版》浙江大學SEL實驗室著

阮一峯-Docker入門教程

可能是把Docker的概念講的最清楚的一篇文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章