本文大部分是原理,後期打算開個專欄,咱也玩玩知識付費~
一、發展史
在雲計算領域有幾個很常見的詞彙:IaaS、PaaS、SaaS。IaaS就是基礎平臺即服務,國內有阿里雲等;PaaS是平臺即服務,在早些時候新浪雲SAE較爲有名;SaaS就是軟件即服務,最大的Office廠商MS的Office365就是一個很好的代表。在最開始的時候PaaS基本就是人肉運維,慢慢的又出現了一系列的自動化工具,再後來專門做PaaS的一家公司創造了Docker。Docker變成了PaaS的一個標準,但是隨着容器化的發展也出現了一系列的問題。容器化後容器的映射關係變得異常艱難,而且這僅僅是容器化發展的一個小小的問題。那麼隨着容器化的步伐,衍生出了一些列的資源管理器,最開始是Apache Mesos,Mesos由加州的伯克利大學研發出來,隨後被推特選中,大規模的在推特盛行。在2019年5月,特推在舊金山開展了技術發佈會,在該會上產品負責人宣佈推特以後全部使用Kubernetes。第二款資源管理軟件是Docker自家推出的Docker Swarm平臺。Docker Swarm是一個非常輕量的資源管理平臺。但是Swarm功能較爲簡單,而且國內雲廠商阿里雲在2019年7月宣佈在選擇資源管理框架的時候不支持Swarm,默認Kubernetes。Google其實在Kubernetes誕生10年前就使用了容器化基礎架構Borg。Google爲了在資源管理器上佔有優勢,就使用Golang開發了Kubernetes。
類似於Borg系統,Kubernetes的架構如下:
- etcd是一個可信賴的分佈式鍵值存儲平臺,在v2版本中支持內存存儲,在v3中新增了本地存儲,因此當etcd關機後並不會丟失數據。在Kubernetes1.11之前還不支持v3版本的etcd。存儲Kubernetes集羣的所有重要的信息。
- APIServer是所有服務訪問的統一入口。
- Scheduler負責接收任務,選擇合適的節點進行任務的分配。
- Replication Controller負責維持期望的副本數目。
- Kubelet負責與Docker的容器引擎交互,實現容器的生命週期管理。
- Kube Proxy負責寫入規則到iptables或者ipvs實現服務映射訪問。
- CoreDNS實現爲集羣中的SVC創建A記錄,實現域名訪問。
- Dashborad實現集羣的B/S訪問體系。
- Ingress Controller實現七層代理,Kubernetes官方只能實現四層代理。
- Fedetation提供一個可以跨集羣中心的多Kubernetes統一管理功能。
- Prometheus和ELK提供Kubernetes集羣監控和日誌統一分析接入平臺。
二、POD
Pod分爲自主式Pod和控制器管理的Pod,自主式Pod一旦死亡就會無法拉起。在Pod中,有一個容器叫做pause,只有存在Pod,該容器就會啓動。在該Pod中的其他容器公用pause的網絡棧和存儲卷,因此在同一個Pod裏容器的端口不能衝突。
-
Replication Controller用來確保容器應用的副本數量始終保持在用戶定義的副本數量上,如果容器異常退出,那麼將會創建新的Pod來替代,如果創建過多,過多的Pod將會被回收。在新版本的Kubernetes中,建議使用ReplicaSet取代Replication Controller,原因是RS支持標籤操作。RS支持集合式的Selector。
-
雖然ReplicaSet可以獨立使用,但是還是建議使用Deployment來自動管理RS,這樣無需擔心跟其他機制的不兼容問題。
Deployment繼承了上面描述的Replication Controller全部功能。還支持事件和狀態查看,回滾,版本記錄,暫停和啓動。
-
HPA(Horizontal Pod Autoscaling)僅適用於Deployment和ReplicaSet,在v1版本中僅支持根據Pod的CPU利用率擴容。
HPA(Horizontal Pod Autoscaler)是kubernetes(以下簡稱k8s)的一種資源對象,能夠根據某些指標對在statefulSet、replicaController、replicaSet等集合中的pod數量進行動態伸縮,使運行在上面的服務對指標的變化有一定的自適應能力。
HPA目前支持四種類型的指標,分別是Resource、Object、External、Pods。其中在穩定版本autoscaling/v1中只支持對CPU指標的動態伸縮,在測試版本autoscaling/v2beta2中支持memory和自定義指標的動態伸縮,並以annotation的方式工作在autoscaling/v1版本中。
HPA在k8s中的結構:
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: php-apache namespace: default spec: # HPA的伸縮對象描述,HPA會動態修改該對象的pod數量 scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache # HPA的最小pod數量和最大pod數量 minReplicas: 1 maxReplicas: 10 # 監控的指標數組,支持多種類型的指標共存 metrics: # Object類型的指標 - type: Object object: metric: # 指標名稱 name: requests-per-second # 監控指標的對象描述,指標數據來源於該對象 describedObject: apiVersion: networking.k8s.io/v1beta1 kind: Ingress name: main-route # Value類型的目標值,Object類型的指標只支持Value和AverageValue類型的目標值 target: type: Value value: 10k # Resource類型的指標 - type: Resource resource: name: cpu # Utilization類型的目標值,Resource類型的指標只支持Utilization和AverageValue類型的目標值 target: type: Utilization averageUtilization: 50 # Pods類型的指標 - type: Pods pods: metric: name: packets-per-second # AverageValue類型的目標值,Pods指標類型下只支持AverageValue類型的目標值 target: type: AverageValue averageValue: 1k # External類型的指標 - type: External external: metric: name: queue_messages_ready # 該字段與第三方的指標標籤相關聯,(此處官方文檔有問題,正確的寫法如下) selector: matchLabels: env: "stage" app: "myapp" # External指標類型下只支持Value和AverageValue類型的目標值 target: type: AverageValue averageValue: 30
HPA的特性結合第三方的監控應用,使得部署在HPA伸縮對象(statefulSet、replicaController、replicaSet)上的服務有了非常靈活的自適應能力,能夠在一定限度內複製多個副本來應對某個指標的急劇飆升,也可以在某個指標較小的情況下刪除副本來讓出計算資源給其他更需要資源的應用使用,維持整個系統的穩定。非常適合於一些流量波動大,機器資源吃緊,服務數量多的業務場景,如:電商服務、搶票服務、金融服務等。
許多監控系統通過adapter實現了接口,給HPA提供指標數據。在這裏我們具體介紹一下prometheus監控系統的adapter。
prometheus是一個知名開源監控系統,具有數據維度多,存儲高效,使用便捷等特點。用戶可以通過豐富的表達式和內置函數,定製自己所需要的監控數據。
prometheus-adapter在prometheus和api-server中起到了適配者的作用。prometheus-adapter接受從HPA中發來,通過apiserver aggregator中轉的指標查詢請求,然後根據內容發送相應的請求給prometheus拿到指標數據,經過處理後返回給HPA使用。prometheus可以同時實現metrics.k8s.io
、custom.metrics.k8s.io
、external.metrics.k8s.io
三種api接口,代替k8s自己的matrics-server,提供指標數據服務。prometheus-adapter部署能否成功的關鍵在於配置文件是否正確。# 指標規則,可以多個規則共存,上一個規則的結果會傳給下一個規則 rules: # 計算指標數據的表達式 - metricsQuery: sum(rate(<<.Series>>{<<.LabelMatchers>>}[5m])) by (<<.GroupBy>>) # 指標重命名,支持正則表達式。這裏表示刪除指標名字中的"_seconds_total" name: as: "" matches: (.*)_seconds_total$ # 指標與k8s資源通過標籤關聯,這裏將指標通過標籤與k8s的namspace和pod相互關聯 resources: overrides: namespace: resource: namespace pod: resource: pod # 過濾指標條件 seriesFilters: [] # 指標查詢表達式,可以根據標籤等條件,篩選特定的指標 seriesQuery: '{namespace!="",pod!=""}'
v1的模板可能是大家平時見到最多的也是最簡單的,v1版本的HPA只支持一種指標 —— CPU。傳統意義上,彈性伸縮最少也會支持CPU與Memory兩種指標,爲什麼在Kubernetes中只放開了CPU呢?其實最早的HPA是計劃同時支持這兩種指標的,但是實際的開發測試中發現,內存不是一個非常好的彈性伸縮判斷條件。因爲和CPU不同,很多內存型的應用,並不會因爲HPA彈出新的容器而帶來內存的快速回收,因爲很多應用的內存都要交給語言層面的VM進行管理,也就是內存的回收是由VM的GC來決定的。這就有可能因爲GC時間的差異導致HPA在不恰當的時間點震盪,因此在v1的版本中,HPA就只支持了CPU一種指標。
-
StatefulSet是爲了解決有狀態服務的問題,應用場景包含:穩定的持久化存儲(Pod重新調度後還可以訪問到相同的持久化數據),穩定的網絡標誌(Pod重新部署後其Hostname和Podname不會發生變化),有序的刪除擴展收縮(Pod是有順序的,在容器部署或者擴展的時候要依據定義的順序依次進行,基於init containtor實現)。
-
DaemonSet確保全部或者一些Node上運行一個Pod副本。當有Node加入集羣的時候,會爲他們新增一個Pod。當有Node從集羣中移除的時候,這些Pod也會被回收。刪除DaemonSet將會刪除他們創建的所有Pod。典型的應用場景是:每個Node上運行的日誌收集、監控、存儲客戶端等。
- Job負責批處理的任務,它保證批處理任務的一個或者多個Pod成功結束。Cron Job管理基於時間的Job。
Pod內多個容器的網絡通信使用lo,各個Pod之間使用Overlay Network,Pod與Service之間使用Iptables規則,在新版本中Pod與Service使用LVS的轉發機制。
Flannel是CoreOS團隊針對Kubernetes設計的一個網絡規劃服務,簡單地說,它的功能是讓集羣中不同節點的主機創建Docker容器都具有全集羣唯一的虛擬IP地址。而且Flannel還可以在這些IP地址之間建立一個覆蓋網絡,通過這個覆蓋網絡將數據包原封不動的傳遞到目標容器內。
HPA與滾動更新的區別:
目前在kubernetes中,可以通過直接管理複製控制器來執行滾動更新,也可以使用deployment對象來管理底層副本集。HPA只支持後一種方法:HPA綁定到部署對象,設置部署對象的大小,部署負責設置底層副本集的大小。
HPA不能使用複製控制器的直接操作進行滾動更新,即不能將HPA綁定到複製控制器並進行滾動更新(例如,使用Kubectl滾動更新)。這不起作用的原因是,當滾動更新創建新的複製控制器時,HPA將不會綁定到新的複製控制器。
Flannel的跨主機通信機制:
在上圖中有兩個真實的主機,在同一個主機內的Docker容器互相通信通過Docker0網橋即可。但是跨主機通信通過Docker0網橋就無能爲力了。當Docker容器啓動的時候,Docker0網橋會爲其分配一個IP地址,當在宿主機上安裝Flanneld的時候會創建Flannel0網橋,Flannel0會監聽Docker0的全部數據。當宿主機中的一個Docker向另一個宿主機的Docker容器發起網絡請求的時候,數據包會被Flanneld截取,Flanneld將數據包進行封裝,發送至另一臺主機,另一臺主機的Flanneld程序解封裝數據包,通過Flannel0網橋和Docker0網橋分發數據到指定的容器。Flanneld封裝的數據包如下:
三、資源清單相關
在Kubernetes中的資源實例化後叫做對象。在Kubernetes中的名稱空間級別的資源有以下分類:
- 工作負載型資源:Pod、RS、Deployment、StatefulSet、DaemonSet、Job、CronJob
- 服務發現和負載均衡型資源:Service、Ingress
- 配置與存儲型資源:Volume、CSI(容器存儲接口)
- 特殊類型的資源:ConfigMap、Secret、DownwardAPI
集羣級別的資源有:Namespace、Node、Role、ClusterRole、RoleBinding、ClusterRoleBinding。
元數據型資源:HPA、PodTemplate、LimitRange。
四、POD生命週期
如下圖所示,一個Pod的創建,首先會進行容器環境的初始化,然後創建pause容器,用來共享網絡棧和存儲卷。然後執行多個initc,初始化容器initc的步驟是串行的。當所有的initc執行完成以後的start部分指的是在容器運行之前的操作,stop部分指的是容器運行結束的操作。在容器整個運行過程中liveness部分用來生存檢測。但是在容器的必要條件全局具備的時候還要進行readiness的就緒檢測的應用場景就是當一個Pod運行起來的時候,有可能Pod內部的服務還有有全部加載完成,因此需要進行就緒檢測。當就緒檢測通過的時候再將Pod的運行狀態改成RUNNING狀態。在Pod的運行過程中,一個極端的情況就是容器內應用剩下的全部都是殭屍進程了,但是表現的來的容器狀態還是運行中,因此需要在整個服務的運行週期內都需要進行服務的生存檢測。
Init容器與普通的容器相似,但是Init容器總是運行到成功完成爲止。每個Init容器都必須在下一個容器啓動完成以後運行。如果Pod的容器失敗,Kubernetes將會不斷的重啓該Pod,直到所有的Init容器全部成功運行爲止。只有當Pod對應的RestartPoliy爲Never的時候纔不會重新啓動。
Init容器與應用容器分離,因此Init容器可以包含一系列的應用容器運行過程中不需要的實用工具。還可以進行定製化安裝,由於Init容器的存在,可以大大降低應用容器的大小。Init容器使用Linux Namespace,所以相對應用程序來說具有不同的文件系統視圖,因此它們具備訪問Secret的權限,而應用程序的容器則不能。
在所有的Init容器沒有成功之前,Pod將不會變成Ready狀態,Init容器的端口將不會在Service中聚集。正在初始化中的Pod處於Pending狀態,但是會將Initializing狀態設置爲true。如果Pod重啓,所有的Init容器必須重新執行。
Init容器具有應用容器的全部字段,除去就緒檢測字段readinessProbe。在Pod中每個app和int的名稱必須是唯一的。
雖然initc可以進行就緒探測,但是在initc中進行就緒探測是不太友好的,因爲存在initc探測成功但是指定的容器連接目標容器失敗的情況,所以最好還是在主容器中進行探測。
五、探針技術
探針技術是kubelet對容器執行的定期診斷。kubelet調用容器實現的處理器有三種類型:ExecAction、TCPSocketAction、HTTPGetAction。
- ExecAction:在容器內執行指定的命令,如果命令退出的時候返回碼爲0,就會認爲是成功的。
- TCPSocketAction:對指定的端口上的容器的IP地址進行TCP檢查。如果端口打開,則會認爲是成功的。
- HTTPGetAction:對指定的端口和路徑上的容器的IP地址執行GET請求,如果響應碼大於等於200並且小於400,則會認爲是成功的。
對於每次探測,都會出現三種結果:成功、失敗和未知。
探測方案存在兩種形式:
- LivenessProbe:指示容器是否在運行。如果探測失敗,kubelet將會殺死容器。如果容器不提供探針,默認狀態是Success。
- ReadinessProbe:指示容器是否準備好服務請求。如果探測失敗,端點控制器將會從與Pod匹配的所有Service的端點中刪除該Pod的IP地址。初始延遲之前的就緒狀態默認爲Failure。如果容器不提供探針,默認狀態爲Success。
Pod的狀態:
- Pending:Pod已經被Kubernetes接受,但是有一個或者多個容器尚未創建。等待時間包括調度Pod的時間和通過網絡下載鏡像的時間。
- Running:該Pod已經被綁定在一個節點上,Pod中的所有容器都被創建。至少有一個容器正在運行或者處於啓動或重啓狀態。
- Succeeded:Pod中的所有容器都被成功終止,並且不會再重新啓動。
- Faild:Pod中的所有容器都已經終止了,並且至少有一個容器是因爲失敗而終止的,也就是說容器非0狀態或者被系統終止。
- Unknown:因爲某些原因無法取得Pod的狀態,通常是因爲與Pod所在的主機通信失敗。
六、資源控制器
Kubernetes中內建了很多類型的控制器,這些相當於一些狀態機,用來控制Pod的具體行爲。控制器有以下幾種類型:
- ReplicationController和ReplicaSet :用來確保容器應用的副本數目維持在用戶定義的副本數上。RS與RC的最大不同是支持label選擇。ReplicaSet的YAML應該在spec中指定replicas的數量和selector.matchLabels標籤選擇器來選擇通過Key-Value實現選擇POD,然後通過指定template.metadata和template.spec.containters
- Deployment:爲Pod的RS提供了一個聲明式定義的方法,用來替代以前的ReplicationController來方便管理應用。一般使用Deployment定義無狀態應用。
- 定義Deployment來創建Pod和RS
- 滾動升級和回滾應用
- 擴容和縮容
- 暫停和繼續Deployment
- DaemonSet:確保全部或者一些Node運行在一個Pod副本上。當有Node加入集羣的時候,也會爲它們新增加一個Pod。當有Node在集羣移除的時候,這些Pod也會被回收。刪除DaemonSet將會刪除它創建的所有Pod。
- StatefulSet:用於對待有狀態服務,通常需要持久化存儲,並且每個節點都需要特殊對待的服務。
- Job/CronJob
- Horizontal Pod Autoscling(HPA)
七、服務
Service也叫做SVC,用來進行服務發現。SVC中只有一種輪詢算法RR,並且只提供四層的負載均衡,無法實現七層負載均衡。
在Kubernetes中SVC有一下四種類型:
-
ClusterIP:默認類型,自動分配一個僅Cluster內部可以訪問的虛擬IP。在同一個Node上的Pod和Node本身可以訪問nginx-svc。
在YAML配置文件中指定spec.ports.port和spec.ports.targetPort
-
NodePort:在ClusterIP基礎上爲Service在每臺機器上綁定端口,這樣就可以通過NodeIP:NodePort來訪問該服務。
在YAML配置文件中指定spec.ports.port和spec.ports.targetPort以及spec.ports.nodePort,NodePort在30000往上分配。在集羣外使用NodePort訪問。
- LoadBalancer:在NodePort的基礎上,藉助Cloud Provider創建一個外部的(調用IaaS提供商的API)負載均衡器,並將請求轉發到NodeIP:NodePort上。
- ExternalName:把集羣外部的服務引入到集羣內部來,在集羣內部直接使用。沒有任何代理被創建,只有v1.7或者更高版本的kube-dns來支持。通常是CNAME記錄,指向FQDN。
在Kubernetes集羣中,每個Node運行一個kube-proxy進程。kube-proxy負責爲service實現了一種VIP的形式,而不是ExternalName。在Kubernetes v1.0版本中,代理完全在userspace中。在Kubernetes v1.1版本中,新增了iptables代理,但是並不是默認的運行模式。從Kubernetes v1.2開始,默認使用ipatables代理。在Kubernetes v1.8.0-beta0中,添加了ipvs代理。在Kubernetes v1.14版本中,默認使用ipvs代理。在Kubernetes v1.1版本中,新增加了IngressAPI,用來提供七層服務。Kubernetes在歷次版本迭代中不使用DNS作爲負載均衡的最主要原因是DNS負載均衡方式會在客戶端緩存。
八、代理
- userspace模式:在UserSpace模式下,Client想要訪問Pod必須通過iptables找到kube-proxy在到想要訪問的Pod,即使是同一個主機內的Pod。而且api server也需要對kube-proxy進行監控,因此kube-proxy壓力較大。
-
IPVS代理模式:這種模式下,kube-proxy會監視Kubernetes的Service對象和Endpoint對象,調用netlink接口來相應的創建ipvs規則並定期與Kubernetes的Service對象和Endpoint對象同步ipvs規則,來確保ipvs狀態與期望的一致。訪問服務的時候,流量將被重定向到其中一個後端的Pod。與iptables類似,ipvs類似於netfilter的hook功能,但是使用Hash表作爲底層數據結構並在內核空間中工作。這意味着ipvs可以更快的進行流量重定向,並且在同步代理規則時具備更好的性能。此外,ipvs爲負載均衡算法提供了更多的選項:RR(輪詢調度)、LC(最小連接數)、DH(目標Hash)、SH(源Hash)、SED(最短期望延遲)、NQ(不排隊調度)。IPVS模式假定在kube-proxy之前的節點上都已經安裝了IPVS模塊,當kube-proxy以IPVS代理模式啓動的時候,kube-proxy將驗證節點上是否安裝了IPVS模塊。如果未安裝,則kube-proxy將回退iptables代理模式。
爲了實現上面的各種代理模式,api server用戶通過kubectl命令向api server發送創建service命令,api server接收到請求將數據存儲到etcd中。kube-proxy的每個節點中都有一個叫做kube-proxy的進程,這個進程負責感知service和pod的變化,並將變化的信息寫入本地的iptables或者ipvs。使用iptables的NAT等技術或者ipvs將Virtual IP的流量轉至Endpoint中。
爲了v1.11使用ipvs,需要手動注入兩個內核模塊變量,否則自動降級到iptables:
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables echo 1 > /proc/sys/net/ipv4/ip_forward
並且:在配置文件/etc/sysconfig/kubelet中添加:
KUBE_PROXY_MODE=ipvs
並且使用modprobe在系統啓動時開啓如下模塊:ip_vs,ip_vs_rr,ip_vs_wrr,ip_vs_sh,nf_conntrack_ipv4
- Ingress:API Server通過建立連接監聽事件變化,變化的事件寫入UpdateChannel中,Nginx的主進程定期拉取UpdateChannel中的數據。將拉取來的數據寫到syncQueue中,SyncController定期獲取syncQueue中的數據,如果特別緊急的數據可以直接通過Store協程與SyncController協程進行交互,而不需中間的流程。如果Nginx的配置文件需要重寫,那麼重寫完成後需要reload。
Ingress是一個特殊的調度器,主要是進行SSL會話卸載,支持七層均衡,Service只能發生四層均衡,因此可以在後端的POD上只運行HTTP,外部客戶端與Ingress保持HTTPS會話。
九、存儲
Kubernetes的存儲主要有:ConfigMap存儲、Secret存儲、Volume卷存儲。
ConfigMap功能在Kubernetes v1.2中版本中引入,許多應用程序會從配置文件、命令行參數和環境變量中讀取配置信息。ConfigMap API給我們提供了容器中的注入配置信息的機制,ConfigMap可以被用來保存單個屬性,也可以用來保存整個配置文件或者JSON二進制大對象。
- 使用目錄創建
- 使用文件創建
- 使用字面值創建
Secret用來存儲密碼、Token、祕鑰等敏感數據的配置問題,Secret可以以Volume或者環境變量的方式使用。Secret有3種類型:
- Service Account:用來訪問Kubernetes API,有Kubernetes自動創建,並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中。
- Opaque:Base64編碼格式的Secret,用來存儲密碼和祕鑰等。
- kubernetes.io/dockerconfiguration:用來存儲私有的docker registry的認證信息。
Kubernetes中的卷的生命與它封裝的Pod相同。但是卷的生命要比所有容器都長,當容器重新啓動的時候,存儲在卷中的數據不會丟失。Kubernetes支持多種類型的卷,Pod可以使用任意數量的卷。Kubernetes支持以下類型的卷:
- awsElasticBlockStore、azureDisk、azureFile、cephfs、csi、downwardAPI、emptyDir
- fc、docker、gcePersistentDisk、gitRepo、glusterfs、hostPath、iscsi、local、nfs
- persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret
- storageos、vsphereVolume
常用的卷存儲類型:
- emptyDir:當Pod分配給節點時,首先創建emprtyDir卷,並且只要該Pod在該節點運行,該卷就會存在。該捲起初是空的,Pod容器可以讀取和寫入emptyDir卷中相同的文件,儘管該卷可以掛載到每個容器中相同或者不同路徑上。當出於任何原因從節點刪除該Pod的時候,emptyDir將會永久刪除。emptyDir一般用於暫存空間,長時間計算崩潰恢復時的檢查點和Web服務器提供的數據。
- hostPath:將主機節點的文件系統中的文件或者目錄掛在到集羣中,一般用於運行時需要訪問Docker內部的容器(使用/var/lib/docker)或者在容器中運行cADriver(使用/dev/cgroups)。
十、PV和PVC
PV的出現主要爲明確運維工程師和開發工程師的主要任務,PV是Kubernetes提供的抽象存儲的概念,但是系統中可能成千上萬個PV,因此出現了PVC。PVC會自動匹配PV進行綁定。PV獨立於Pod之上。PV分爲靜態PV和動態PV。靜態PV是集羣管理員創建的一些PV。當管理員創建的靜態PV都不滿足用戶時,集羣可能會動態的嘗試爲PVC創建卷。PV一旦被綁定,就會具備排他性,PV和PVC一一對應。
PVC保護的目的是確保由Pod正在使用的PVC不會被系統移除。當啓用PVC保護的Alpha功能的時候,如果一個用戶刪除了正在使用的PVC,則這個PVC不會立即刪除,而是將會被延遲,直到這個PVC不再被任何Pod使用。
PV是以插件的形式實現的:
- GCEPersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC(Fibre Channel)
- FlexVolume、Flocker、NFS、iSCSI、RBD(Ceph Block Device)、CephFS
- Cinder(OpenStack Block Storage)、Glusterfs、VsphereVolume、QuobyteVolumes
- HostPath、VMware Photon、Portworx Volumes、ScaleIO Volumes、StorageOS
PV具備一定的訪問模式:
- ReadWriteOnce(RWO):該卷可以被單個節點以讀寫方式掛載
- ReadOnlyMany(ROX):該卷可以被多個節點以只讀方式掛載
- ReadWriteMany(RWX):該卷可以被多個節點以讀寫方式掛載
在Kubernetes中回收策略:
- Retain(保留):手動回收
- Recycle(回收):基本刪除,相當於rm -rf
- Delete(刪除):關聯的存儲資產將被刪除
目前只有NFS和HostPath支持回收策略,其他僅支持刪除策略。
狀態:
- Available(可用):一塊空閒資源你還沒有進行任何聲明綁定
- Bound(已綁定):卷已經被聲明綁定
- Released(已釋放):聲明被刪除,但是資源還未被集羣重新聲明
- Failed(失敗):該卷的自動回收
十一、StatefulSet
- 匹配Pod Name的模式爲StatefulSetName-序號
- StatefulSet爲每個Pod副本創建了一個DNS域名,這個域名的格式爲:PodName.HeadlessServerName,也就意味着服務間是通過Pod域名來通信的而不是通過PodIP。當Node發生故障的時候,Pod會被漂移到其他的Node上,PodIP會發生變化,但是Pod的域名不會發生變化。
- StatefulSet使用Headless服務來控制Pod的域名,格式爲:ServiceName.NameSpace.svc.ClusterDomainName
- 根據volumeClaimTemplate爲每個Pod創建一個PVC,PVC的命名匹配模式是:VolumeClaimTemplate.name-PodName。
- 刪除Pod不會刪除PVC,手動刪除PVC將會釋放PV。
十二、集羣調度
Scheduler 是Kubernetes 的調度器,主要的任務是把定義的 pod 分配到集羣的節點上。要考慮的問題:
- 公平:如何保證每個節點都能被分配資源
- 資源高效利用:集羣所有資源最大化被使用
- 效率:調度的性能要好,能夠儘快地對大批量的 pod 完成調度工作
- 靈活:允許用戶根據自己的需求控制調度的邏輯
Sheduler 是作爲單獨的程序運行的,啓動之後會一直監聽 API Server,獲取 PodSpec.NodeName 爲空的Pod, 對每個Pod 都會創建一個 binding,表明該 pod 應該放到哪個節點上。因爲不爲空的已經制定了Node節點。
調度分爲幾個部分:
-
首先是過濾掉不滿足條件的節點,這個過程稱爲 predicate。
- 然後對通過的節點按照優先級排序,這個是 priority ;最後從中選擇優先級最高的節點。如果中間任何一步驟有錯誤,就直接返回錯誤。
Predicate 有一系列的算法可以使用:
- PodFitsResources :節點上剩餘的資源是否大於 pod 請求的資源
- PodFitsHost :如果 pod 指定了 NodeName,檢查節點名稱是否和 NodeName 匹配
- PodFitsHostPorts :節點上已經使用的 port 是否和 pod 申請的 port 衝突
- PodSelectorMatches :過濾掉和 pod 指定的 label 不匹配的節點
- NoDiskConflict :已經 mount 的 volume 和 pod 指定的 volume 不衝突,除非它們都是隻讀
如果在 predicate 過程中沒有合適的節點,pod 會一直在 pending 狀態,不斷重試調度,直到有節點滿足條件。經過這個步驟,如果有多個節點滿足條件,就繼續 priorities 過程:按照優先級大小對節點排序。
優先級由一系列鍵值對組成,鍵是該優先級項的名稱,值是它的權重(該項的重要性)。這些優先級選項包括:
- LeastRequestedPriority :通過計算 CPU 和 Memory 的使用率來決定權重,使用率越低權重越高。換句話說,這個優先級指標傾向於資源使用比例更低的節點
- BalancedResourceAllocation :節點上 CPU 和 Memory 使用率越接近,權重越高。這個應該和上面的一起使用,不應該單獨使用
- ImageLocalityPriority :傾向於已經有要使用鏡像的節點,鏡像總大小值越大,權重越高
通過算法對所有的優先級項目和權重進行計算,得出最終的結果。
除了 kubernetes 自帶的調度器,你也可以編寫自己的調度器。通過 spec:schedulername 參數指定調度器的名字,可以爲 pod 選擇某個調度器進行調度。比如下面的 pod 選擇 my-scheduler 進行調度,而不是默認的 default-scheduler。
節點親和性配置:pod.spec.nodeAffinity
- preferredDuringSchedulingIgnoredDuringExecution:軟策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
POD親和性配置:pod.spec.affinity.podAffinity/podAntiAffinity
-
preferredDuringSchedulingIgnoredDuringExecution:軟策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
親和性/反親和性調度策略比較如下:
調度策略 | 匹配標籤 | 操作符 | 拓撲域支持 | 調度目標 |
---|---|---|---|---|
nodeAffinity | 主機 | In, NotIn, Exists, DoesNotExist, Gt, Lt | 否 | 指定主機 |
podAffinity | POD | In, NotIn, Exists, DoesNotExist | 是 | POD與指定POD的同一個拓撲域 |
podAnitAffinity | POD | In, NotIn, Exists, DoesNotExist | 是 | POD與指定POD不在同 一拓撲域 |
節點親和性,是 pod 的一種屬性(偏好或硬性要求),它使 pod 被吸引到一類特定的節點。Taint 則相反,它使節點能夠 一類特定的 pod。
Taint 和 toleration 相互配合,可以用來避免 pod 被分配到不合適的節點上。每個節點上都可以應用一個或多個 taint ,這表示對於那些不能容忍這些 taint 的 pod,是不會被該節點接受的。如果將 toleration 應用於 pod 上,則表示這些 pod 可以(但不要求)被調度到具有匹配 taint 的節點上。
污點 ( Taint ) 的組成:
使用 kubectl taint 命令可以給某個 Node 節點設置污點,Node 被設置上污點之後就和 Pod 之間存在了一種相 斥的關係,可以讓 Node 拒絕 Pod 的調度執行,甚至將 Node 已經存在的 Pod 驅逐出去。
每個污點的組成如下:
key=value:effect
每個污點有一個 key 和 value 作爲污點的標籤,其中 value 可以爲空,effect 描述污點的作用。當前 taint effect 支持如下三個選項:
- NoSchedule :表示 k8s 將不會將 Pod 調度到具有該污點的 Node 上
- PreferNoSchedule :表示 k8s 將盡量避免將 Pod 調度到具有該污點的 Node 上
- NoExecute :表示 k8s 將不會將 Pod 調度到具有該污點的 Node 上,同時會將 Node 上已經存在的 Pod 驅逐出去
污點的設置、查看和去除:
# 設置污點
kubectl taint nodes node1 key1=value1:NoSchedule
# 節點說明中,查找 Taints 字段 kubectl describe pod pod-name
# 去除污點
kubectl taint nodes node1 key1:NoSchedule-
容忍:
設置了污點的 Node 將根據 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之間產生 互斥的關係,Pod 將在一定程度上不會被調度到 Node 上。 但我們可以在 Pod 上設置容忍 ( Toleration ) ,意思 是設置了容忍的 Pod 將可以容忍污點的存在,可以被調度到存在污點的 Node 上。
具體的參數:pod.spec.tolerations
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
- 其中 key, vaule, effect 要與 Node 上設置的 taint 保持一致
- operator 的值爲 Exists 將會忽略 value 值
- tolerationSeconds 用於描述當 Pod 需要被驅逐時可以在 Pod 上繼續保留運行的時間
當不指定 key 值時,表示容忍所有的污點 key:
tolerations:
- operator: "Exists"
當不指定 effect 值時,表示容忍所有的污點作用:
tolerations:
- key: "key"
operator: "Exists"
有多個 Master 存在時,防止資源浪費,可以如下設置:
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
指定調度節點:
-
Pod.spec.nodeName:將 Pod 直接調度到指定的 Node 節點上,會跳過 Scheduler 的調度策略,該匹配規則是強制匹配。
- Pod.spec.nodeSelector.type:通過 kubernetes 的 label-selector 機制選擇節點,由調度器調度策略匹配 label, 而後調度 Pod 到目標節點,該匹配規則屬於強制約束
十三、集羣安全
Kubernetes 作爲一個分佈式集羣的管理工具,保證集羣的安全性是其一個重要的任務。API Server 是集羣內部 各個組件通信的中介,也是外部控制的入口。所以 Kubernetes 的安全機制基本就是圍繞保護 API Server 來設計 的。Kubernetes 使用了認證(Authentication)、鑑權(Authorization)、准入控制(Admission Control)三步來保證API Server的安全。
對於認證和鑑權,K8S提供了插件接口來提供認證鑑權的第三方實現,對於認證插件,主要一個插件通過認證和鑑權,則不會嘗試其他插件。
三種方式:
- HTTP Token 認證:通過一個 Token 來識別合法用戶
HTTP Token 的認證是用一個很長的特殊編碼方式的並且難以被模仿的字符串 - Token 來表達客戶的一 種方式。Token 是一個很長的很複雜的字符串,每一個 Token 對應一個用戶名存儲在 API Server 能訪 問的文件中。當客戶端發起 API 調用請求時,需要在 HTTP Header 裏放入 Token - HTTP Base 認證:通過 用戶名+密碼 的方式認證
用戶名+:+密碼 用 BASE64 算法進行編碼後的字符串放在 HTTP Request 中的 Heather
Authorization 域裏發送給服務端,服務端收到後進行編碼,獲取用戶名及密碼 - 最嚴格的 HTTPS 證書認證:基於 CA 根證書籤名的客戶端身份認證方式
兩種訪問類型:
Kubenetes 組件對 API Server 的訪問:kubectl、Controller Manager、Scheduler、kubelet、kube- proxy。Kubernetes 管理的 Pod 對容器的訪問:Pod(dashborad 也是以 Pod 形式運行)。
Controller Manager、Scheduler 與 API Server 在同一臺機器,所以直接使用 API Server 的非安全端口。訪問, --insecure-bind-address=127.0.0.1。kubectl、kubelet、kube-proxy 訪問 API Server 就都需要證書進行 HTTPS 雙向認證
證書頒發方式:
- 手動簽發:通過 k8s 集羣的跟 ca 進行簽發 HTTPS 證書
- 自動簽發:kubelet 首次訪問 API Server 時,使用 token 做認證,通過後,Controller Manager 會爲kubelet 生成一個證書,以後的訪問都是用證書做認證了
kubeconfig 文件包含集羣參數(CA證書、API Server地址),客戶端參數(上面生成的證書和私鑰),集羣 context 信息(集羣名稱、用戶名)。Kubenetes 組件通過啓動時指定不同的 kubeconfig 文件可以切換到不同的集羣。
Pod中的容器訪問API Server。因爲Pod的創建、銷燬是動態的,所以要爲它手動生成證書就不可行了。Kubenetes使用了Service Account解決Pod 訪問API Server的認證問題。
Secret 與 SA 的關係:Kubernetes 設計了一種資源對象叫做 Secret,分爲兩類,一種是用於 ServiceAccount 的 service-account- token, 另一種是用於保存用戶自定義保密信息的 Opaque。ServiceAccount 中用到包含三個部分:Token、 ca.crt、namespace。
默認將證書和TOKEN存儲到/run/secrets/kubernetes.io/serviceaccount
- token是使用 API Server 私鑰簽名的 JWT。用於訪問API Server時,Server端認證
- ca.crt,根證書。用於Client端驗證API Server發送的證書
- namespace, 標識這個service-account-token的作用域名空間
kubectl get secret --all-namespaces
kubectl describe secret default-token-5gm9r --namespace=kube-system
默認情況下,每個 namespace 都會有一個 ServiceAccount,如果 Pod 在創建時沒有指定 ServiceAccount, 就會使用 Pod 所屬的 namespace 的 ServiceAccount。
上面認證過程,只是確認通信的雙方都確認了對方是可信的,可以相互通信。而鑑權是確定請求方有哪些資源的權 限。API Server 目前支持以下幾種授權策略 (通過 API Server 的啓動參數 “--authorization-mode” 設置)。
- AlwaysDeny:表示拒絕所有的請求,一般用於測試
- AlwaysAllow:允許接收所有請求,如果集羣不需要授權流程,則可以採用該策略
- ABAC(Attribute-Based Access Control):基於屬性的訪問控制,表示使用用戶配置的授權規則對用戶 請求進行匹配和控制
- Webbook:通過調用外部 REST 服務對用戶進行授權
- RBAC(Role-Based Access Control):基於角色的訪問控制,現行默認規則
RBAC(Role-Based Access Control)基於角色的訪問控制,在 Kubernetes 1.5 中引入,現行版本成爲默認標準。相對其它訪問控制方式,擁有以下優勢:
- 對集羣中的資源和非資源均擁有完整的覆蓋
- 整個 RBAC 完全由幾個 API 對象完成,同其它 API 對象一樣,可以用 kubectl 或 API 進行操作
- 可以在運行時進行調整,無需重啓 API Server
RBAC:
K8S 1.6之後支持。RBAC 引入了 4 個新的頂級資源對象:Role、ClusterRole、RoleBinding、ClusterRoleBinding,4 種對象類型均可以通過 kubectl 與 API 操作。
需要注意的是 Kubenetes 並不會提供用戶管理,那麼 User、Group、ServiceAccount 指定的用戶又是從哪裏 來的呢? Kubenetes 組件(kubectl、kube-proxy)或是其他自定義的用戶在向 CA 申請證書時,需要提供一個 證書請求文件:
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [{
"C": "CN",
"ST": "HangZhou",
"L": "XS",
"O": "system:masters", "OU": "System"
}]
}
API Server會把客戶端證書的 CN 字段作爲User,把 names.O 字段作爲Group。kubelet 使用 TLS Bootstaping 認證時,API Server 可以使用 Bootstrap Tokens 或者 Token authentication file 驗證 =token,無論哪一種,Kubenetes 都會爲 token 綁定一個默認的 User 和 Group。
Pod使用 ServiceAccount 認證時,service-account-token 中的 JWT 會保存 User 信息。
有了用戶信息,再創建一對角色/角色綁定(集羣角色/集羣角色綁定)資源對象,就可以完成權限綁定了。
Role and ClusterRole:
在 RBAC API 中,Role 表示一組規則權限,權限只會增加(累加權限),不存在一個資源一開始就有很多權限而通過 RBAC 對其進行減少的操作。Role 可以定義在一個 namespace 中,如果想要跨 namespace 則可以創建 ClusterRole。
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
ClusterRole 具有與 Role 相同的權限角色控制能力,不同的是 ClusterRole 是集羣級別的,ClusterRole 可以用於:
- 集羣級別的資源控制( 例如 node 訪問權限 )
- 非資源型 endpoints( 例如 /healthz 訪問 )
- 所有命名空間資源控制(例如 pods )
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
RoleBinding and ClusterRoleBinding
RoloBinding 可以將角色中定義的權限授予用戶或用戶組,RoleBinding 包含一組權限列表(subjects),權限列 表中包含有不同形式的待授予權限資源類型(users, groups, or service accounts);RoloBinding 同樣包含對被 Bind 的 Role 引用;RoleBinding 適用於某個命名空間內授權,而 ClusterRoleBinding 適用於集羣範圍內的授權。
將 default 命名空間的 pod-reader Role 授予 jane 用戶,此後 jane 用戶在 default 命名空間中將具有 pod- reader 的權限。
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
RoleBinding 同樣可以引用 ClusterRole 來對當前 namespace 內用戶、用戶組或 ServiceAccount 進行授權, 這種操作允許集羣管理員在整個集羣內定義一些通用的 ClusterRole,然後在不同的 namespace 中使用 RoleBinding 來引用。
使用 ClusterRoleBinding 可以對整個集羣中的所有命名空間資源權限進行授權。
Kubernetes 集羣內一些資源一般以其名稱字符串來表示,這些字符串一般會在 API 的 URL 地址中出現;同時某些資源也會包含子資源。
RoleBinding 和 ClusterRoleBinding 可以將 Role 綁定到 Subjects;Subjects 可以是 groups、users 或者 service accounts。
Subjects 中 Users 使用字符串表示,它可以是一個普通的名字字符串。也可以是 email 格式的郵箱地址。甚至是一組字符串形式的數字 ID 。但是 Users 的前綴 system: 是系統 保留的,集羣管理員應該確保普通用戶不會使用這個前綴格式。Groups 書寫格式與 Users 相同,都爲一個字符串,並且沒有特定的格式要求;同樣 system: 前綴爲系統保留。
准入控制是API Server的插件集合,通過添加不同的插件,實現額外的准入控制規則。甚至於API Server的一些主要的功能都需要通過 Admission Controllers 實現,比如 ServiceAccount。
官方文檔上有一份針對不同版本的准入控制器推薦列表,其中最新的 1.14 的推薦列表是:
NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota。
列舉幾個插件的功能:
- NamespaceLifecycle: 防止在不存在的 namespace 上創建對象,防止刪除系統預置 namespace,刪除 namespace 時,連帶刪除它的所有資源對象。
- LimitRanger:確保請求的資源不會超過資源所在 Namespace 的 LimitRange 的限制。
- ServiceAccount: 實現了自動化添加ServiceAccount。
- ResourceQuota:確保請求的資源不會超過資源的 ResourceQuota 限制。
十四、YAML配置說明
一個YAML文件包括了apiVersion用於說明操作資源的API版本,可以通過kubectl api-versions
查看;kins用於指定內建的資源,包括以下類型:
- 工作負載:Pod,ReplicaSet,DaemonSet,StatefulSet,Deployment,Job,CrontabJob,
- 服務發現,負載均衡:Service,Ingress
- 配置,存儲:ConfigMap,Secret,DownwardAPI
- 集羣級別資源:Namespace,Node,Role,ClusterRole,RoleBinding,ClusterRoleBinding
- 元數據類型資源:HPA,PodTemplate,LimitRange
還有元數據metadata,主要包括以下節點:
- name,在同一個kind中,必須是唯一的
- namespace,集羣級別的概念
- labels,標籤,可以通過標籤選擇器來選擇對應的資源
- annontions,註解
最重要的是spec字段,定義了用戶的期望狀態;status字段定義了初始狀態,這個是隻讀的,用戶不能更改。
對於YAML配置文件的說明,可以通過kubectl explain 字段.字段..
查看幫助。
spec字段必須包含containers列表,每個容器的定義包含name和image字段。對於一些容器可能需要指令,那麼可以設置command列表。
K8S新一代資源指標架構:
- 核心指標流水線,由bukelet、metrics-server和aip server構成。
- 監控流水線,用於收集各類型信提供給終端用戶。
日誌收集:
- sidecar方式
- 在節點上部署統一收集
十五、Helm
helm 類似於Linux系統下的包管理器,如yum/apt等,可以方便快捷的將之前打包好的yaml文件快速部署進kubernetes內,方便管理維護。
- helm:一個命令行下客戶端工具,主要用於kubernetes應用chart的創建/打包/發佈已經創建和管理和遠程Chart倉庫。
- Tiller:helm的服務端,部署於kubernetes內,Tiller接受helm的請求,並根據chart生成kubernetes部署文件(helm稱爲release),然後提交給 Kubernetes 創建應用。Tiller 還提供了 Release 的升級、刪除、回滾等一系列功能。
- Chart: helm的軟件包,採用tar格式,其中包含運行一個應用所需的所有鏡像/依賴/資源定義等,還可能包含kubernetes集羣中服務定義
- Release:在kubernetes中集羣中運行的一個Chart實例,在同一個集羣上,一個Chart可以安裝多次,每次安裝均會生成一個新的release。
- Repository:用於發佈和存儲Chart的倉庫
Chart install工作原理:
-
helm從制定目錄或tar文件解析chart結構信息
-
helm將制定的chart結構和value信息通過gRPC協議傳遞給tiller
-
tiller根據chart和values生成一個release
- tiller通過json將release發送給kubernetes,生成release
Chart update工作原理:
- helm從制定的目錄或tar文件解析chart結構信息
- helm將制定的chart結構和value信息通過gRPC協議傳給tiller
- tiller生成release並更新制定名稱的release的history
- tiller將release信息發送給kubernetes用於更新release
Chart Rollback工作原理:
- helm將會滾的release名稱傳遞給tiller
- tiller根據release名稱查找history
- tiller從history中獲取到上一個release
- tiller將上一個release發送給kubernetes用於替換當前release
Chart處理依賴工作原理:
Tiller 在處理 Chart 時,直接將 Chart 以及其依賴的所有 Charts 合併爲一個 Release,同時傳遞給 Kubernetes。因此 Tiller 並不負責管理依賴之間的啓動順序。Chart 中的應用需要能夠自行處理依賴關係。
Chart文件組織:
myapp/ # Chart 目錄
├── charts # 這個 charts 依賴的其他 charts,始終被安裝
├── Chart.yaml # 描述這個 Chart 的相關信息、包括名字、描述信息、版本等
├── templates # 模板目錄
│ ├── deployment.yaml # deployment 控制器的 Go 模板文件
│ ├── _helpers.tpl # 以 _ 開頭的文件不會部署到 k8s 上,可用於定製通用信息
│ ├── ingress.yaml # ingress 的模板文件
│ ├── NOTES.txt # Chart 部署到集羣后的一些信息,例如:如何使用、列出缺省值
│ ├── service.yaml # service 的 Go 模板文件
│ └── tests
│ └── test-connection.yaml
└── values.yaml # 模板的值文件,這些值會在安裝時應用到 GO 模板生成部署文件
name: 由於 DNS 系統的限制,該字段限制爲 63 個字符。因此,release 名稱限制爲 53 個字符。Kubernetes 1.3 及更早版本僅限於 24 個字符(即 14 個字符名稱)。