k8s技術分享
背景
容器:隔離不同服務,避免依賴衝突;爲服務提供一致性的環境;(docker, rkt......)
微服務化:大型單體應用被拆分成很多小的、可獨立部署的組件,以便於單組件快速迭代升級、降低單模塊的物理資源;
帶來的問題:部署、運維的人力成本大幅提升(組件間依賴關係、擴縮容、故障檢測和處理、負載均衡、自恢復);
解決方案:自動編排技術(Google k8s、Docker Swarm、Apache Mesos)
簡單來說,就是docker規模增長帶來了如何自動化編排的問題,k8s就是答案之一。
什麼是k8s
關鍵詞:2014 google Borg Omega 部署平臺 go
k8s有什麼優勢
屏蔽底層細節:通過基礎設施抽象,提供一個統一的部署平臺給開發者,開發者無需關心底層硬件、操作系統細節;
高利用率:自動調度,提高資源利用率;
豐富的運維功能:提供服務發現、擴縮容、負載均衡、故障檢測和處理、選主等功能;
市場份額:77%(2019)
google搜索熱度:
k8s概念和架構
資源概述
k8s中的操作對象都被看作是資源,如節點、pod、Deployment、LimitRequest,可以類比java中的類
資源名稱 |
概念 |
|
---|---|---|
node | 工作節點 | |
pod(po) |
pod是對容器的封裝,是k8s管理的最小單元,一個pod裏可以有多個應用容器,也可以有多個init containers;
|
|
service(svc) |
service是對pod 封裝,對pod的訪問可以通過service,實現負載均衡,避免pod擴縮容、重新部署帶來的ip、端口變更;
|
|
endpoint | service和pod之間不是直連,service通過selector選擇器,篩選pod構造出包含pod 的ip、端口的endpoint資源。可以創建一個沒有selector的service,手動構造endpoint。 | |
ReplicationCotroller(rc) | k8s用於管理集羣內pod的資源,通過在rc內指定selector來關聯pod。rc內需要指定pod 副本數量。如果pod修改了標籤,會成爲孤兒節點,脫離rc管控,rc也會立刻新建一個pod。 | |
ReplicaSet(rs) | rc的升級版,支持更高級語法的selector,例如多標籤選擇、In、NotIn等語法。 | |
Deployment |
封裝了rs,支持滾動升級
|
|
StatefulSet | 類比ReplicaSet,用於部署有狀態服務的pod | |
DaemonSet(ds) | ds能夠在每個節點上都部署pod,而不是指定數量,一般用於系統初始化等全局設置。ds也可以指定selector,這樣就在指定的節點上部署pod。 | |
Job和CronJob | 不同於pod的持續運行,Job/CronJob只運行一次/定時執行,也可以指定執行次數和併發度。 | |
PersistentVolume(pv)和PersistentVolumeClaim(pvc) |
用於持久化存儲 local模式:指定HostPath type的volume,然後通過volumeMount掛載到容器 非local模式:運維人員事先創建好一系列不同類型的pv(如gcePersistentDisk,awsElasticBlockStore,azureDisk,cinder,cephfs),在pvc中定義pv的大小和讀寫模式,然後pod中就可以直接引用,再通過volumeMount掛載到容器,好處是解耦了實際存儲方案。 |
|
Horizontal Pod Autoscaler(hpa) |
關聯到某個deployment,檢測pod的運行資源佔用情況(cpu、內存,也可以自定義),實現動態擴縮容,最終儘量接近pod的資源申請值。 | |
ConfigMap(cm) | 本質上是一種卷(volume),用於應用配置抽離,通過卷掛載或者環境變量的方式注入到容器中 | |
ServiceAccount |
k8s集羣內身份,每個命名空間如果沒有手動創建,則會默認創建一個名爲‘default’的sa。 授權基於RBAC,通過roleBinding綁定到role,或通過clusterRoleBinding綁定到clusterRole來賦予權限。role和clusterRole的區別是,role是某個命名空間下的資源,clusterRole是集羣維度的。 |
k8s集羣架構
控制平面組件
控制平面組件包括Kubernetes API server、etcd分佈式持久存儲、調度器、控制其管理器
組件 |
知識點 |
---|---|
Kubernetes API server |
|
etcd |
|
調度器 |
|
控制器管理器 |
|
工作節點組件
組件 |
知識點 |
---|---|
kubelet |
|
kube-proxy |
|
一次deploy的過程
一個pod 的部署流程
(1)客戶端發起請求創建Deployment(如kubectl),API server 創建一個Deployment資源
(2)監聽Deployment事件的Deployment Controller收到消息,請求API server創建ReplicaSet
(3)監聽ReplicaSet事件的ReplicaController收到消息,請求API server創建pod
(4)監聽pod創建事件的調度器收到消息,選擇出合適的node發給API server
(5)監聽node資源的kubelet收到消息,在本node上創建pod
k8s計算資源管理
問題一:pod如何描述自身所需的計算資源量?
在pod中可以指定requests和limits,
資源requests是指定pod申請的系統資源,如CPU/內存量,跟pod實際使用量無關,即pod實際使用量可能大於、等於或小於requests,是給調度器使用的。
資源limits是資源實際使用量,用於避免某個pod佔用過多系統資源。k8s調度時可能超售。當pod使用比limits更多的CPU時不會被kill,但使用比limits更多的內存時會被kill。
limits >= requests
|
問題二:系統資源不夠時,k8s按什麼機制kill pod?
QoS:按照requests和limits的關係,pod被分成了三檔,沒有指定requests和limits爲BestEffort,指定了requests和limits且相等爲Guaranteed,其他爲Burstable。
kill順序爲BestEffort -> Burstable -> Guaranteed
如果QoS level一樣,則根據使用率來kill,即實際使用量/requests量,較大的會先被kill(爲什麼是這樣的策略呢?我理解requests是調度時分配的額度,比例越高意味着pod可用資源越接近枯竭,就更應該儘早重新調度);
問題三:是否有集羣維度的統一限制?
有,通過創建LimitRange資源,可以做如下限制:
(1)pod可申請的最小資源
(2)pod可申請的最大資源
(3)container可申請的最大資源
(4)container未指定時分配的默認requests和limits
(5)request/limits比例的最大值
(6)container可申請的PVC最大值
|
問題四:是否可以在namespace維度限制quota?是否可以限制pod創建數量?
可以,通過創建ResourceQuota資源,可以限制命名空間所有pod可使用的cpu、內存最大值。還可以限制各種資源的創建量,如pod、ReplicationController等。
|
pod的安全機制
問題一:pod內運行的container是相互隔離的,那pod之間有絕對的網絡、權限安全性嗎?
並沒有,需要限制。
從幾個方面考慮這個問題,首先,pod可以直接使用宿主機的命名空間的(詳見問題二),意味着有能力在pod內部看到宿主機的進程、網絡流量,乃至直接與非本pod內的container進行進程間通信。其次,pod默認啓動用戶是root,結合上一條可知,如果不加限制,是有能力以root身份操作宿主機的。
問題二:pod如何使用宿主機的命名空間?
網絡命名空間:pod描述文件中指定hostNetwork:true
綁定宿主機端口:pod描述文件中指定hostPort(網絡namespace還是跟宿主機隔離的)
PID命名空間:pod描述文件中指定hostPID: true
IPC命名空間:pod描述文件中指定hostIPC: true
|
問題三:如何在pod描述中增加限制,避免鏡像被篡改時可能出現越權操作?
解決方案是securityContext資源:securityContext可以在pod級別對所有container生效,也可以在container級別指定。在securityContext裏可以做這些約束:
(1)指定容器運行進程的用戶ID:runAsUser(指定UID)
(2)阻止以root運行:runAsNonRoot(不care是哪個用戶運行)
(3)指定特權模式:privileged(kube-proxy就是一個需要使用privileged=true的例子,因爲要修改iptables等)
(4)單獨指定權限:capabilities裏指定add/drop內核功能(例如drop SYS_TIME禁止容器修改系統時間)
(5)阻止根文件系統寫入:readOnlyRootFileSystem(對具體要寫的卷再單獨指定readOnly=false)
|
問題四:上面的方案都是依賴pod自己遵守規定,有沒有集羣維度的統一管理?
有,通過創建PodSecurityPolicy資源,它可以指定securityContext裏能做到的一切。
首先創建PodSecurityPolicy(psp),然後創建role,關聯此psp,然後再創建roleBinding資源,將此policy賦權給具體的serviceAccount。
|
k8s與juju對比
對juju瞭解不多,僅限於使用,因此只在使用層面上進行對比。
|
k8s |
juju |
---|---|---|
|
k8s |
juju |
描述文件 | 一組yaml文件 | charm包 |
部署 |
kubectl create pod/replicaset/deployment -f xxx.yaml 無需關心底層節點,在描述文件裏指定tag和副本數量 |
juju deploy ${appDir} -n 3 --to 7,8,9 需要了解節點編號 |
升級 | deployment有變更時會自動執行,能夠記錄歷史變更記錄,kubectl rollout就可以快速回滾(還可以指定回滾目標版本) | 需要手動執行juju upgrade,無歷史記錄 |
擴容 | 修改deployment replica數量,就能夠自動擴容 | 手動執行juju add-unit docker-nginx -n 3 --to 7,8,9 |
探活 | 支持自定義livenessProbe(Exec / HTTP GET / TCP socket) | 沒有探活機制,只監測docker存活狀態。 |
組件間依賴 | 在initContainer裏檢測依賴服務是否正常,每次部署都會檢測; |
在metadata.yaml裏指定requires服務,只有首次部署會自動添加,後續修改依賴不會自動添加,需要手動執行juju add-relation/juju remove-relation |
hook |
支持Post-start hook(跟容器啓動程序是異步的)和pre-stop hook(在容器終止之前執行) | charm包裏也可以指定多種stage hook |
進入容器 | 底層抽象的更加徹底,直接在集羣內任意機器上執行kubectl exec -it ${podName} /bin/sh | 需要登陸對應宿主機,再docker exec -it ${dockerName} /bin/sh |
配置文件 | 通過init container掛載;通過configMap注入; | 卷掛載方式注入容器 |
相關資料
https://www.kubernetes.org.cn/k8s
https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands