Kubernetes現已成爲在私有云,公共雲以及混合雲環境中大規模部署容器化應用程序的事實標準。業內最大的幾家公有云平臺AWS,Google Cloud,Azure,IBM Cloud以及Oracle Cloud現已提供Kubernetes託管服務。node
Kubernetes 這個單詞來自於希臘語,含義是 舵手 或 領航員 。其詞根是 governor 和 cybernetic。 K8s 是它的縮寫,用 8 字替代了「ubernete」。mysql
Kubernetes的主要功能(如容器分組,覆蓋網絡,4層路由,secret管理等)整合到容器平臺DC/OS中。DC/OS還將Kubernetes做爲一個和Marathon類似的容器調度器集成進來linux
在Kubernetes中,service是核心,提供service的容器,爲了保證service的高可用,提供service的容器不能只有一個,須要一組,這一組容器咱們叫作pod,一個service裏有多個pod,pod就是linux機器上(node上)的docker容器,一個service裏面的多個pod能夠跨node, 一個pod 裏面的容器除了那個pause以外,其餘的都是一個鏡像啓動的容器,做用是同樣的。爲了service實現service和pod之間的關聯,又有了標籤的概念,功能相同的pod設定爲同一個標籤,好比,把全部提供MySQL服務的pod貼上標籤name=MySQL ,這樣MySQL service要做用於全部包含name=mysql標籤的pod上。nginx
pod運行在Node上,Node能夠是一臺物理機,也能夠是虛擬機,一般一個Node上會運行幾百個pod,每一個pod裏運行着一個特殊的容器,叫作Pause,其餘容器叫作業務容器,業務容器共享Pause容器的網絡和Volume掛載,所以同一個pod內的業務容器之間的通訊和數據交換更爲高效。web
Kubernetes總體上的架構,其應用程序的部署模型,服務發現和負載平衡,內部/外部路由分離,持久卷的使用,在節點上部署守護進程,部署有狀態的分佈式系統,運行後臺做業,部署數據庫,配置管理,憑據管理,滾動更新,自動伸縮以及包管理。 算法
在集羣管理方面,Kubernetes將集羣中的機器劃分爲一個master節點和一羣工做節點Node,其中master上運行着kube-apiserver、kube-controller-manager、kube-scheduler ,它們實現了資源管理、pod調度、彈性伸縮、安全控制、系統控制、糾錯等功能 ,Node是工做節點 ,運行應用程序 ,提供服務 , Node上的最小單元是pod ,Node上運行着Kubernetes ,Node上Kubernetes的kube-proxy服務進程、它們負責pod的建立、啓動、監控、重啓、銷燬、以及實現負載均衡。sql
擴容和升級須要一個關鍵的東西,Replication controller( RC ) ,RC須要包含3個關鍵信息 :docker
1)目標pod的定義數據庫
2)目標pod須要運行的副本數量(replicas)json
3)要監控 的目標pod標籤(Label)
工做過程 :RC裏定義好3個指標 ,Kubernetes會根據RC定義的Label篩選出對應的pod,並實時監控其狀態和數量,當實例數量少於定義的副本數 (replicas), 則會根據RC定義的pod 模板來建立新的pod,而後將此pod調度到合適的Node上啓動運行,該過程徹底自動化,無需人工干涉。
Kubernetes架構
這個接近完美的集羣管理器採起的基本設計決策之一是可以部署在虛擬機上運行的現有應用程序,而無需對應用程序代碼作出任何修改。從總體上來講,任何在虛擬機上運行的應用程序均可以經過簡單地容器化其組件的方式部署在Kubernetes上。這是經過其核心功能實現的:容器分組,容器編排,覆蓋網絡,基於4層虛擬IP路由系統的容器間路由,服務發現,對運行守護程序的支持,部署有狀態的應用程序組件,並且最重要的是,它還可以經過擴展容器編排器以支持複雜的編排要求。
宏觀的層面來說,Kubernetes提供了一組動態可擴展的主機,用於承載運行工做負載的容器,並使用一組稱爲master的管理主機,提供用於管理整個容器基礎設施的API。工做負載可能包括長期運行的服務,批處理做業和運行在特定容器主機上的守護程序。全部容器主機使用覆蓋網絡鏈接在一塊兒以提供容器間的路由。部署在Kubernetes上的應用程序在集羣網絡是能夠動態發現的,並且能夠經過傳統的負載均衡器暴露到外部網絡。集羣管理器的狀態存放在高度分佈式組織的k/v存儲裏,該存儲運行在master實例上。
快速精準地部署應用程序
即時伸縮你的應用程序
無縫展示新特徵
限制硬件用量僅爲所需資源
可移動: 公有云、私有云、混合雲、多態雲
可擴展: 模塊化、插件化、可掛載、可組合
自修復: 自動部署、自動重啓、自動複製、自動伸縮
Kubernetes 能在實體機或虛擬機集羣上調度和運行程序容器。並且,Kubernetes 也能讓開發者斬斷聯繫着實體機或虛擬機的「鎖鏈」,從以主機爲中心的架構躍至以容器爲中心的架構。該架構最終提供給開發者諸多內在的優點和便利。Kubernetes 提供給基礎架構以真正的以容器爲中心的開發環境。
諸如:
協調輔助進程,協助應用程序整合,維護一對一「程序 – 鏡像」模型。
掛載存儲系統
分佈式機密信息
檢查程序狀態
複製應用實例
使用橫向莢式自動縮放
命名與發現
負載均衡
滾動更新
資源監控
訪問並讀取日誌
程序調試
提供驗證與受權
以上兼具平臺即服務(PaaS)的簡化和基礎架構即服務(IaaS)的靈活,並促進了在平臺服務提供商之間的遷移。
Kubernetes 不是傳統的、全包容的平臺即服務(Paas)系統。它尊重用戶的選擇,這很重要。
並不限制支持的程序類型。它並不檢測程序的框架 (例如,Wildfly),也不限制運行時支持的語言集合 (好比, Java、Python、Ruby),也不只僅迎合 12 因子應用程序,也不區分 應用 與 服務 。Kubernetes 旨在支持儘量多種類的工做負載,包括無狀態的、有狀態的和處理數據的工做負載。若是某程序在容器內運行良好,它在 Kubernetes 上只可能運行地更好。
不提供中間件(例如消息總線)、數據處理框架(例如 Spark)、數據庫(例如 mysql),也不把集羣存儲系統(例如 Ceph)做爲內置服務。可是以上程序均可以在 Kubernetes 上運行。
沒有「點擊即部署」這類的服務市場存在。
不部署源代碼,也不編譯程序。持續集成 (CI) 工做流程是不一樣的用戶和項目擁有其各自不一樣的需求和表現的地方。因此,Kubernetes 支持分層 CI 工做流程,卻並不監聽每層的工做狀態。
容許用戶自行選擇日誌、監控、預警系統。( Kubernetes 提供一些集成工具以保證這一律念獲得執行)
不提供也無論理一套完整的應用程序配置語言/系統(例如 jsonnet)。
不提供也不配合任何完整的機器配置、維護、管理、自我修復系統。
另外一方面,大量的 PaaS 系統運行在 Kubernetes 上,諸如 Openshift、Deis,以及 Eldarion。你也能夠開發你的自定義PaaS,整合上你自選的CI系統,或者只在 Kubernetes 上部署容器鏡像。
由於 Kubernetes 運營在應用程序層面而不是在硬件層面,它提供了一些 PaaS 所一般提供的常見的適用功能,好比部署、伸縮、負載平衡、日誌和監控。然而,Kubernetes 並不是鐵板一塊,這些默認的解決方案是可供選擇,可自行增長或刪除的。
Kubernetes scheduler將始終確保每一個應用程序組件都是通過健康檢查的,提供高可用的服務,當副本數量設置爲多個時,每一個實例在多個主機上進行調度,而且若是其中一個主機變爲不可用時,全部在該臺主機上運行的容器都會被調度到剩餘的其餘主機。Kubernetes提供的一項迷人功能是兩級自動擴縮。首先,經過使用一個叫作Horizontal Pod Autoscaler(Pod自動水平擴縮器)的資源,它能夠爲用戶提供容器的自動擴縮功能,該資源將會監視資源的消耗並相應地擴展所需的容器數量。其次,它能夠根據資源需求添加或刪除主機來擴展容器集羣自己。 此外,隨着集羣聯盟(cluster federation)功能的引入,它甚至能夠使用單個API端點管理一組Kubernetes集羣,這些集羣甚至可能跨越多個數據中心。
應用部署模型
上圖展現的是Kubernetes宏觀層面的應用程序部署模型。它使用一個叫作ReplicaSet的資源來編排容器。將ReplicaSet視爲基於YAML或基於JSON的一個元數據文件,該文件裏定義了容器鏡像,端口,副本數,激活運行情況檢查(health check),存活情況檢查(liveness health check),環境變量,掛載卷以及建立和管理容器所需的一些安全規則。在Kubernetes上,容器老是以所謂Pods的形式成組建立,它是Kubernetes的一個元數據定義或者說是一個資源。每一個pod容許在使用了Linux namespace,cgroup和其餘內核功能的容器之間共享文件系統,網絡接口和操做系統用戶。ReplicaSet能夠由另一個名爲Deployments的高級資源管理,它被設計用於提供滾動更新和處理回滾等功能。
經過執行下面這樣一條簡單的CLI命令,用戶即可以經過定義一個deployment,在Kubernetes上部署一個容器化的應用程序:
kubectl run <application-name> --image=<container-image> --port=<port-no>
執行上述CLI命令後,它將使用給定的容器鏡像建立一個Deployment聲明,一個副本集以及一個採用了指定容器鏡像的Pod;添加一個應用名的選擇器標籤。根據當前的設計,由此建立的每一個pod將會擁有兩個容器,一個用於指定的應用程序組件,另一個即所謂的pause容器,用於鏈接網絡接口。
服務發現 & 負載均衡
Kubernetes的一個關鍵特性即是由SkyDNS和4層虛擬IP路由系統提供的服務發現和內部路由模型。這些功能能夠爲面向service的應用請求提供內部路由。能夠使用集羣網絡裏的service對經過副本集建立的一組pod提供負載平衡。service使用選擇器標籤鏈接到pod。每一個service都將會分配一個惟一的IP地址,一個由它的名稱派生出的主機名,而且將會以round robin的方式路由pods的請求。這些service甚至能夠爲可能須要支持會話親和性的應用程序提供基於IP哈希的路由機制。service能夠指定一組端口,而針對指定的service定義的一系列屬性將會以相同的形式一樣應用到全部端口上。所以,若是隻是某個給定端口須要支持會話親和性,而全部的其餘端口只須要round robin的方式路由的狀況下,可能須要用到多個service。
service內部工做原理
Kubernetes service背後是經過一個叫作kube-proxy的組件實現。kube-proxy實例運行在每一個節點上,並提供了三種代理模式:userspace,iptables和IPVS。目前的默認值是iptables。
在第一種代理模式下,userspace,kube-proxy自己將充當代理服務器的角色,而且將被一條iptable規則接受的請求代理到後端pod。在這種模式下,kube-proxy將在用戶空間中運行,而且將會在消息流上額外增長一跳(hop)。在iptables中,kube-proxy將建立一個iptable規則集合,用於未來自客戶端的入口請求在網絡層面直接轉發到後端pod的端口上,而不會在中間額外增長一跳。這種代理模式比第一種模式快得多,由於它是在內核空間中操做而不是在中間增長一臺額外的代理服務器。
第三種代理模式是在Kubernetes v1.8中添加的,它和第二種代理模式很是類似,該模式使用的是基於IPVS的虛擬服務器來路由請求,而無需用到iptable規則。IPVS是一個傳輸層的負載均衡功能,能夠在基於Netfilter的Linux內核中使用,而且提供了一組負載均衡算法。在iptables上使用IPVS的主要緣由是在使用iptables時同步代理規則帶來的性能開銷。當建立數千個service時,更新iptable規則須要至關長的時間,相比之下,使用IPVS只需幾毫秒。此外,IPVS使用哈希表線性的掃描iptables來查找代理規則。有關介紹IPVS代理模式的更多信息,請參閱華爲在KubeCon 2017上的「Scaling Kubernetes to Support 50,000 Services」[1]演示文稿。
內外路由分離
Kubernetes service可以經過兩種主流方式暴露給外部網絡。第一種是使用節點端口(node port),經過暴露節點上的動態端口,這些端口會將流量轉發到service端口。第二種是使用一個ingress controller配置的負載均衡器,它將會鏈接到同一個覆蓋網絡並將請求代理給service。ingress controller是一個後臺進程,它能夠跑在容器裏,該容器將會監聽Kubernetes API,根據指定的一組ingress動態配置並從新加載給定的負載均衡器。一個ingress定義了一組基於service的主機名及上下文路徑的路由規則。
一旦經過執行kubectl run命令,在Kubernetes上跑起來應用後,咱們能夠經過一個負載均衡器將其暴露給外部網絡,以下所示:
kubectl expose deployment <application-name> --type=LoadBalancer --name=<service-name>
上述命令將建立一個負載均衡器類型的service,而且使用在POD建立之初創建的選擇器標籤映射到相同的一組pod。這樣一來,根據Kubernetes集羣不一樣的配置方式,一個負載均衡器類型的service將會在底層基礎設施上建立出來,經過service或直接路由的形式將請求轉發到指定的一組pod。
持久卷用法
須要在文件系統上持久化存儲數據的應用程序能夠使用卷將存儲設備掛載到臨時容器,這和虛擬機的使用方式相似。Kubernetes巧妙地從新設計了這一律念,經過引入一個所謂的持久卷聲明(PVC)的中間資源,在物理存儲設備和容器之間作了解耦。一個PVC定義了磁盤大小,磁盤類型(ReadWriteOnce,ReadOnlyMany,ReadWriteMany),並將存儲設備動態連接到在pod中定義了的卷。該綁定過程能夠使用PV這樣一個靜態的形式,也能夠使用一個持久化存儲的provider動態實現。在這兩種方法裏,卷將一對一地連接到PV,而且取決於其配置,給定的數據即使pod被終止也將會被保留。根據使用的磁盤類型,多個pod將可以鏈接到同一磁盤並進行讀/寫。
支持ReadWriteOnce的磁盤將只能鏈接到單個pod,而且沒法同時在多個pod之間共享。可是,支持ReadOnlyMany的磁盤將可以在只讀模式下同時在多個pod之間共享。相反地,顧名思義,支持ReadWriteMany的磁盤能夠鏈接到多個pod,以便在讀寫模式下共享數據。 Kubernetes提供了一系列的卷插件,用於支持公有云平臺上可用的存儲服務,例如AWS EBS,GCE Peristent Disk,Azure File,Azure Disk和許多其餘衆所周知的存儲系統,如NFS,Glusterfs,Cinder等。
在節點上部署守護程序
Kubernetes提供了一個名爲DaemonSets的資源,用於在每一個Kubernetes節點上以守護進程的形式運行pod的副本。DaemonSets的一些用例以下:
須要部署到每一個節點上提供持久化存儲的集羣存儲守護程序(如glusterd,ceph)。
須要在每一個節點上運行的監控容器宿主機的節點監控守護程序,例如Prometheus Node Exporter。
須要在每一個節點上運行的,用做採集容器及Kubernetes組件日誌的日誌採集守護程序,例如fluentd或是logstash。
須要在一組節點上運行的提供外部路由的ingress controll pod。
部署有狀態的分佈式系統
容器化應用程序最艱鉅的任務之一莫過於設計有狀態分佈式組件部署架構的流程。無狀態組件能夠很容易地進行容器化,由於它們可能沒有預約的啓動順序,集羣要求,點對點的TCP鏈接,惟一的網絡標識符,優雅的啓動和終止需求等。像數據庫,大數據分析系統,分佈式k/v存儲以及消息代理這樣的系統,可能擁有須要支持上述這些功能的複雜分佈式架構。Kubernetes引入了StatefulSets資源來解決這些複雜的需求。
從總體上來講,StatefulSets相似於ReplicaSet,除了提供處理pod的啓動順序的能力,惟一地標識每一個pod以保留其狀態以外,它還同時提供如下特性:
穩定,惟一的網絡標識符。
穩定,持久化的存儲。
有序,優雅的部署和擴容。
有序,優雅的刪除和終止。
有序,自動地滾動更新。
這裏面的「穩定」指的是在跨pod從新調度時它將會保留網絡標識符和持久化存儲。如上圖所示,惟一的網絡標識符能夠經過使用headless service提供。Kubernetes提供了一些StatefulSets的示例,包括以分佈式的形式部署Cassandra以及Zookeeper。
執行後臺任務
除了ReplicaSet和StatefulSets以外,Kubernetes還提供了兩個額外的控制器,用於在後臺運行稱爲Jobs和CronJobs的工做負載。Jobs和CronJobs之間的區別在於Jobs執行一次即終止,而CronJobs會按照與標準Linux cron做業相似的給定時間間隔按期地執行。
部署數據庫
因爲存在對集羣,點對點鏈接,複製同步,灰度,備份管理等需求,在容器平臺上部署數據庫用於生產環境將會比部署應用程序稍微困難一些。正如以前所提到的那樣,Statefulsets專門爲解決此類複雜需求而設計,而現在已經有一些在Kubernetes上運行PostgreSQL和MongoDB集羣的方案。YouTube的數據庫集羣系統Vitess現現在已是一個CNCF項目,對於在Kubernetes上大規模灰度運行MySQL是一個很好的選擇。說是如此,咱們最好注意一下,這些方案目前仍然處於很是早期的開發階段,而若是現有的生產級別數據庫系統,仍然可用於給定的基礎架構,例如AWS上的RDS,GCP上的Cloud SQL,或是內部按需部署的數據庫集羣,考慮到安裝的複雜性以及維護成本,選擇這其中的一種方案可能更合適些。
配置管理
容器一般使用環境變量來參數化它們的運行時配置。可是,常見的企業應用程序每每使用大量的配置文件爲一個指定的部署提供所需的靜態配置。Kubernetes則提供了一個絕妙的辦法,使用名爲ConfigMaps的一種簡單資源來管理此類配置文件,而無需將它們打包到容器鏡像裏。能夠經過執行如下CLI命令,使用目錄,文件或文本值建立ConfigMaps:
kubectl create configmap <map-name> <data-source> # map-name: name of the config map # data-source: directory, file or literal value
建立ConfigMap後,能夠經過卷的形式將其掛載到pod。經過這種鬆耦合的架構,一個已經在運行的系統的配置能夠經過更新相關的ConfigMaps的方式無縫更新,而其滾動更新的執行流程這塊我將在下一節中詳細說明。值得一提的是,ConfigMaps如今不支持嵌套的目錄結構;所以,若是應用程序的嵌套目錄結構中存放有配置文件的話,則須要爲每個目錄層級建立一個ConfigMap。
憑證管理
與ConfigMaps相似,Kubernetes提供了另外一種名爲Secrets的寶貴資源,用於管理密碼,OAuth令牌和ssh密鑰等敏感信息。不然,在已運行的系統上更新該信息可能須要重建容器鏡像。
能夠使用如下方式建立用於管理基自己份驗證憑據的密鑰:
# write credentials to two files $ echo -n 'admin' > ./username.txt $ echo -n '1f2d1e2e67df' > ./password.txt # create a secret $ kubectl create secret generic app-credentials --from-file=./username.txt --from-file=./password.txt
建立secret後,pod能夠使用環境變量或掛載卷的方式來讀取它。相似地,能夠使用相同的方法將任何其餘類型的敏感信息注入到pod中。
滾動更新
上面這個動畫描述瞭如何使用藍/綠部署的方法爲已經運行的應用程序發佈應用程序更新,而無需任何宕機成本。這是Kubernetes提供的另外一個重磅功能,它容許應用程序不費吹灰之力便可無縫地發佈安全更新和向後兼容的變動。若是變動不向後兼容,則可能須要使用單獨的部署定義手動執行藍/綠部署。
這一方案容許經過一條簡單的CLI命令,發起一個部署以更新容器鏡像:
$ kubectl set image deployment/<application-name> <container-name>=<container-image-name>:<new-version>
一旦發起部署,能夠經過以下方式檢查部署進度的狀態:
$ kubectl rollout status deployment/<application-name>Waiting for rollout to finish: 2 out of 3 new replicas have been updated...deployment "<application-name>" successfully rolled out
使用相同的CLI命令kubectl set image deployment,可讓部署更新回滾到以前的狀態。
自動擴縮
Kubernetes容許使用ReplicaSet或Deployments手動調整pod數量。這能夠經過執行以下CLI命令來實現:
kubectl scale --replicas=<desired-instance-count> deployment/<application-name>
如上圖所示,能夠經過向Deployment添加另外一個名爲Horizontal Pod Autoscaler(HPA)的資源來擴展此功能,以便根據實際資源使用狀況動態擴縮容器。HPA將經過資源指標的API監視每一個pod的資源使用狀況,並通知Deployment相應地更改ReplicaSet的副本數。Kubernetes使用高檔延遲(upscale delay)和縮減延遲(downscale delay)來避免某些狀況下頻繁的資源使用波動而可能致使的顛簸。目前,HPA僅支持基於CPU的使用狀況進行擴展。若是有必要的話,還能夠經過Custom Metrics API加入自定義指標,這具體視應用程序的天然屬性而定。
包管理
Kubernetes社區發起了一個單獨的項目,爲Kubernetes實現了一個稱爲Helm的包管理器。它容許用戶使用一個名爲Chart的資源模板化並打包Kubernetes資源,好比Deployment,Service,ConfigMap,Ingress等,並容許在安裝時使用輸入參數配置它們。更重要的是,它容許在使用依賴項實現包的安裝時複用現有圖表。Helm存儲庫能夠託管在公有云或私有云環境中,用於管理應用程序的Chart。Helm提供了一個CLI,用於從給定的Helm存儲庫裏安裝應用程序到指定的Kubernetes環境中。
一些衆所周知的軟件應用程序的各類穩定Helm圖表能夠在它的Github存儲庫[2]中找到,也能夠在中心化的Helm服務器中找到:Kubeapps Hub[3]。
下圖爲開源自動化運維體系
鏈:
一、cobbler實現自動裝機
二、saltstack實現工程自動化配置
三、kubernetes實現容器自動化編排
四、zabbix實現自動化監控
五、elastic實現應用日誌自動化收集
六、jenkins實現開發持續化交付
1、Kubernetes
2014年出現的kubernetes(又叫k8s)更加煊赫一時。爲啥kubernetes又叫作k8s?聽說是由於kubernetes這個單詞太長,很差記,而首字母和尾字母中間有8個字母,因此就簡寫成了k8s。Kubernetes翻譯成中文,意思是「舵手」。
K8s簡單說,是用來實現容器集羣管理的系統,用於自動部署、擴展和管理容器。它是由Google公司開發,其原型爲Google內部容器管理系統Borg。
Borg通過十多年地優化、改進,其功能和效率不言而喻,k8s項目的目的就是把Borg最精華的部分提取出來,使如今的開發者可以更簡單、直接地應用。K8s自誕生之日起就註定了它的不平凡之路。
幾個概念:
node 一個物理機器,或一個虛擬機(KVM類型,而不是容器類型);將虛擬機做爲node通常是歷史緣由,或是爲了完全隔絕杜絕安全問題。
cluster 一組node須要被集中管理,統一叫一個cluster。一個cluster有一個master和多個node。每一個node裏面有一個kubelet用來服從master調度並管理node自己。
app containers 一個node裏面能夠有一個或多個容器化的應用程序,即app container。能夠簡單認爲就是docker容器。
pod 多個app containers之間可能須要共享硬盤,或共享同一個ip,這樣一組app containers合起來叫一個pod。典型應用如:一個容器不停產生日誌到本地硬盤,另外一個容器不停讀本地硬盤並上傳日誌到日誌服務器。
service 當一個node掛了時,上面的pod及pod裏面的container也天然都掛了。爲了死不掉,須要有個pod上層的抽象,pod掛了,service還在。service經過以下幾種方式暴露出來。
ClusterIP (default),cluser的內網ip,只能此cluster內可見
NodePort,端口NAT到cluster外面
LoadBalancer,在cluster外面搞個LB並分配個外面可見的固定IP給LB
ExternalName,相似CNAME方式
deployment 配置yaml格式,存在master上,當機器故障或須要橫向scale時或須要更新binary時,master根據配置搞定一切。
2、安裝
entos 7.2版本,咱們將master和node安裝在一臺機器上測試,如不一樣機器上,注意IP配置便可。
ip:192.168.1.101
master安裝:
yum -y install kubernetes-master etcd
node安裝:
yum -y install kubernetes-node etcd flannel docker
配置:
more /etc/etcd/etcd.conf
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.1.101:2380"
ETCD_LISTEN_CLIENT_URLS="http://192.168.1.101:2379,http://127.0.0.1:2379"
ETCD_MAX_SNAPSHOTS="5"
ETCD_NAME="etcd1"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.1.101:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.101:2379"
ETCD_INITIAL_CLUSTER="etcd1=http://192.168.1.101:2380"
more /etc/kubernetes/apiserver
KUBE_API_ADDRESS="--address=0.0.0.0"
KUBE_API_PORT="--port=8080"
KUBELET_PORT="--kubelet-port=10250"
KUBE_ETCD_SERVERS="--etcd-servers=http://127.0.0.1:2379"
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=192.168.0.0/16"
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,ResourceQuota"
KUBE_API_ARGS=""
more /etc/kubernetes/config
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow-privileged=false"
KUBE_MASTER="--master=http://192.168.1.101:8080"
3、啓動
master服務啓動:
systemctl start etcd.service
systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler
systemctl enable etcd.service
systemctl enable kube-apiserver
systemctl enable kube-controller-manager
systemctl enable kube-scheduler
node服務啓動:
systemctl start kubelet
systemctl start kube-proxy
systemctl enable kubelet
systemctl enable kube-proxy
4、建立服務測試
咱們使用yaml配置一個nginx的服務進行測試。
docker 下載兩個鏡像
docker pull registry.access.redhat.com/rhel7/pod-infrastructure:latest
docker pull nginx
more nginx.yaml
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
sessionAffinity: ClientIP
selector:
app: nginx
ports:
- port: 80
nodePort: 30080
建立一個服務
kubectl create -f nginx.yaml
打開web輸入http://192.168.1.101:30080
Kubernetes 從建立之初的核心模塊之一就是資源調度。想要在生產環境使用好 Kubernetes,必須對它的資源模型以及資源管理很是瞭解。
在 Kubernetes 中,有兩個基礎可是很是重要的概念:Node 和 Pod。Node 翻譯成節點,是對集羣資源的抽象;Pod 是對容器的封裝,是應用運行的實體。Node 提供資源,而 Pod 使用資源,這裏的資源分爲計算(CPU、Memory、GPU)、存儲(Disk、SSD)、網絡(Network Bandwidth、IP、Ports)。這些資源提供了應用運行的基礎,正確理解這些資源以及集羣調度如何使用這些資源,對於大規模的 Kubernetes 集羣來講相當重要,不只能保證應用的穩定性,也能夠提升資源的利用率。
CPU 可分配的是使用時間,也就是操做系統管理的時間片,每一個進程在必定的時間片裏運行本身的任務(另一種方式是綁核,也就是把 CPU 徹底分配給某個 Pod 使用,但這種方式不夠靈活會形成嚴重的資源浪費,Kubernetes 中並無提供);而對於內存,系統提供的是內存大小。
CPU 的使用時間是可壓縮的,換句話說它自己無狀態,申請資源很快,也能快速正常回收;而內存大小是不可壓縮的,由於它是有狀態的(內存裏面保存的數據),申請資源很慢(須要計算和分配內存塊的空間),而且回收可能失敗(被佔用的內存通常不可回收)。
把資源分紅可壓縮和不可壓縮,是由於在資源不足的時候,它們的表現很不同。對於不可壓縮資源,若是資源不足,也就沒法繼續申請資源(內存用完就是用完了),而且會致使 Pod 的運行產生沒法預測的錯誤(應用申請內存失敗會致使一系列問題);而對於可壓縮資源,好比 CPU 時間片,即便 Pod 使用的 CPU 資源不少,CPU 使用也能夠按照權重分配給全部 Pod 使用,雖然每一個人使用的時間片減小,但不會影響程序的邏輯。
在 Kubernetes 集羣管理中,有一個很是核心的功能:就是爲 Pod 選擇一個主機運行。調度必須知足必定的條件,其中最基本的是主機上要有足夠的資源給 Pod 使用。
用戶在 Pod 中能夠配置要使用的資源總量,Kubernetes 根據配置的資源數進行調度和運行。目前主要能夠配置的資源是 CPU 和 Memory,對應的配置字段是 spec.containers[].resource.limits/request.cpu/memory
。
須要注意的是,用戶是對每一個容器配置 Request 值,全部容器的資源請求之和就是 Pod 的資源請求總量,而咱們通常會說 Pod 的資源請求和 Limits。
Limits
和 Requests
的區別咱們下面會提到,這裏先說說比較容易理解的 CPU 和 Memory。
CPU
通常用核數來標識,一核 CPU 相對於物理服務器的一個超線程核,也就是操做系統 /proc/cpuinfo
中列出來的核數。由於對資源進行了池化和虛擬化,所以 Kubernetes 容許配置非整數個的核數,好比 0.5
是合法的,它標識應用能夠使用半個 CPU 核的計算量。CPU 的請求有兩種方式,一種是剛提到的 0.5
,1
這種直接用數字標識 CPU 核心數;另一種表示是 500m
,它等價於 0.5
,也就是說 1 Core = 1000m
。
內存比較容易理解,是經過字節大小指定的。若是直接一個數字,後面沒有任何單位,表示這麼多字節的內存;數字後面還能夠跟着單位, 支持的單位有 E
、P
、T
、G
、M
、K
,前者分別是後者的 1000
倍大小的關係,此外還支持 Ei
、Pi
、Ti
、Gi
、Mi
、Ki
,其對應的倍數關係是 2^10 = 1024
。好比要使用 100M 內存的話,直接寫成 100Mi
便可。
理想狀況下,咱們但願節點上全部的資源均可以分配給 Pod 使用,但實際上節點上除了運行 Pods 以外,還會運行其餘的不少進程:系統相關的進程(好比 SSHD、Udev等),以及 Kubernetes 集羣的組件(Kubelet、Docker等)。咱們在分配資源的時候,須要給這些進程預留一些資源,剩下的才能給 Pod 使用。預留的資源能夠經過下面的參數控制:
--kube-reserved=[cpu=100m][,][memory=100Mi][,][ephemeral-storage=1Gi]
:控制預留給 Kubernetes 集羣組件的 CPU、Memory 和存儲資源。
--system-reserved=[cpu=100mi][,][memory=100Mi][,][ephemeral-storage=1Gi]
:預留給系統的 CPU、Memory 和存儲資源。
這兩塊預留以後的資源纔是 Pod 真正能使用的,不過考慮到 Eviction 機制(下面的章節會提到),Kubelet 會保證節點上的資源使用率不會真正到 100%,所以 Pod 的實際可以使用資源會稍微再少一點。主機上的資源邏輯分配圖以下所示:
NOTE:須要注意的是,Allocatable 不是指當前機器上能夠分配的資源,而是指能分配給 Pod 使用的資源總量,一旦 Kubelet 啓動這個值是不會變化的。
Allocatable 的值能夠在 Node 對象的 status
字段中讀取,好比下面這樣:
status: allocatable: cpu: "2" ephemeral-storage: "35730597829" hugepages-2Mi: "0" memory: 3779348Ki Pods: "110" capacity: cpu: "2" ephemeral-storage: 38770180Ki hugepages-2Mi: "0" memory: 3881748Ki Pods: "110"
介紹 Kubernetes 中提供的讓咱們管理 Pod 資源的原生對象。
前面說過用戶在建立 Pod 的時候,能夠指定每一個容器的 Requests 和 Limits 兩個字段,下面是一個實例:
resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m"
Requests
是容器請求要使用的資源,Kubernetes 會保證 Pod 能使用到這麼多的資源。請求的資源是調度的依據,只有當節點上的可用資源大於 Pod 請求的各類資源時,調度器纔會把 Pod 調度到該節點上(若是 CPU 資源足夠,內存資源不足,調度器也不會選擇該節點)。
注意,調度器只關心節點上可分配的資源,以及節點上全部 Pods 請求的資源,而不關心節點資源的實際使用狀況,換句話說,若是節點上的 Pods 申請的資源已經把節點上的資源用滿,即便它們的使用率很是低,好比說 CPU 和內存使用率都低於 10%,調度器也不會繼續調度 Pod 上去。
Limits
是 Pod 能使用的資源上限,是實際配置到內核 cgroups 裏面的配置數據。對於內存來講,會直接轉換成 docker run
命令行的 --memory
大小,最終會配置到 cgroups 對應任務的 /sys/fs/cgroup/memory/……/memory.limit_in_bytes
文件中。
NOTE:若是 Limit 沒有配置,則代表沒有資源的上限,只要節點上有對應的資源,Pod 就能夠使用。
使用 Requests 和 Limits 概念,咱們能分配更多的 Pod,提高總體的資源使用率。可是這個體系有個很是重要的問題須要考慮,那就是怎麼去準確地評估 Pod 的資源 Requests?若是評估地太低,會致使應用不穩定;若是太高,則會致使使用率下降。這個問題須要開發者和系統管理員共同討論和定義。
爲每一個 Pod 都手動配置這些參數是挺麻煩的事情,Kubernetes 提供了 LimitRange
資源,可讓咱們配置某個 Namespace 默認的 Request 和 Limit 值,好比下面的實例:
apiVersion: "v1" kind: "LimitRange" metadata: name: you-shall-have-limits spec: limits: - type: "Container" max: cpu: "2" memory: "1Gi" min: cpu: "100m" memory: "4Mi" default: cpu: "500m" memory: "200Mi" defaultRequest: cpu: "200m" memory: "100Mi"
若是對應 Namespace 建立的 Pod 沒有寫資源的 Requests 和 Limits 字段,那麼它會自動擁有下面的配置信息:
內存請求是 100Mi,上限是 200Mi
CPU 請求是 200m,上限是 500m
固然,若是 Pod 本身配置了對應的參數,Kubernetes 會使用 Pod 中的配置。使用 LimitRange 可以讓 Namespace 中的 Pod 資源規範化,便於統一的資源管理。
資源管理和調度能夠認爲 Kubernetes 把這個集羣的資源整合起來,組成一個資源池,每一個應用(Pod)會自動從整個池中分配資源來使用。默認狀況下只要集羣還有可用的資源,應用就能使用,並無限制。Kubernetes 自己考慮到了多用戶和多租戶的場景,提出了 Namespace 的概念來對集羣作一個簡單的隔離。
基於 Namespace,Kubernetes 還可以對資源進行隔離和限制,這就是 Resource Quota 的概念,翻譯成資源配額,它限制了某個 Namespace 能夠使用的資源總額度。這裏的資源包括 CPU、Memory 的總量,也包括 Kubernetes 自身對象(好比 Pod、Services 等)的數量。經過 Resource Quota,Kubernetes 能夠防止某個 Namespace 下的用戶不加限制地使用超過時望的資源,好比說不對資源進行評估就大量申請 16核 CPU 32G 內存的 Pod。
下面是一個資源配額的實例,它限制了 Namespace 只能使用 20 核 CPU 和 1G 內存,而且能建立 10 個 Pod、20 個 RC、5 個 Service,可能適用於某個測試場景。
apiVersion: v1 kind: ResourceQuota metadata: name: quota spec: hard: cpu: "20" memory: 1Gi Pods: "10" replicationcontrollers: "20" resourcequotas: "1" services: "5"
Resource Quota 可以配置的選項還不少,好比 GPU、存儲、Configmaps、PersistentVolumeClaims 等等,更多信息能夠參考官方文檔。
Resource Quota 要解決的問題和使用都相對獨立和簡單,可是它也有一個限制:那就是它不能根據集羣資源動態伸縮。一旦配置以後,Resource Quota 就不會改變,即便集羣增長了節點,總體資源增多也沒有用。Kubernetes 如今沒有解決這個問題,可是用戶能夠經過編寫一個 Controller 的方式來本身實現。
Requests 和 Limits 的配置除了代表資源狀況和限制資源使用以外,還有一個隱藏的做用:它決定了 Pod 的 QoS 等級。
上一節咱們提到了一個細節:若是 Pod 沒有配置 Limits ,那麼它能夠使用節點上任意多的可用資源。這類 Pod 能靈活使用資源,但這也致使它不穩定且危險,對於這類 Pod 咱們必定要在它佔用過多資源致使節點資源緊張時處理掉。優先處理這類 Pod,而不是處理資源使用處於本身請求範圍內的 Pod 是很是合理的想法,而這就是 Pod QoS 的含義:根據 Pod 的資源請求把 Pod 分紅不一樣的重要性等級。
Kubernetes 把 Pod 分紅了三個 QoS 等級:
Guaranteed:優先級最高,能夠考慮數據庫應用或者一些重要的業務應用。除非 Pods 使用超過了它們的 Limits,或者節點的內存壓力很大並且沒有 QoS 更低的 Pod,不然不會被殺死。
Burstable:這種類型的 Pod 能夠多於本身請求的資源(上限由 Limit 指定,若是 Limit 沒有配置,則能夠使用主機的任意可用資源),可是重要性認爲比較低,能夠是通常性的應用或者批處理任務。
Best Effort:優先級最低,集羣不知道 Pod 的資源請求狀況,調度不考慮資源,能夠運行到任意節點上(從資源角度來講),能夠是一些臨時性的不重要應用。Pod 能夠使用節點上任何可用資源,但在資源不足時也會被優先殺死。
Pod 的 Requests 和 Limits 是如何對應到這三個 QoS 等級上的,用下面一張表格歸納:
問題:若是不配置 Requests 和 Limits,Pod 的 QoS 居然是最低的。沒錯,因此推薦你們理解 QoS 的概念,而且按照需求必定要給 Pod 配置 Requests 和 Limits 參數,不只可讓調度更準確,也能讓系統更加穩定。
NOTE:按照如今的方法根據 Pod 請求的資源進行配置不夠靈活和直觀,更理想的狀況是用戶能夠直接配置 Pod 的 QoS,而不用關心具體的資源申請和上限值。但 Kubernetes 目前尚未這方面的打算。
Pod 的 QoS 還決定了容器的 OOM(Out-Of-Memory)值,它們對應的關係以下:
QoS 越高的 Pod OOM 值越低,也就越不容易被系統殺死。對於 Bustable Pod,它的值是根據 Request 和節點內存總量共同決定的:
oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity
其中 memoryRequest
是 Pod 申請的資源,memoryCapacity
是節點的內存總量。能夠看到,申請的內存越多,OOM 值越低,也就越不容易被殺死。
QoS 的做用會在後面介紹 Eviction 的時候詳細講解。
除了 QoS,Kubernetes 還容許咱們自定義 Pod 的優先級,好比:
apiVersion: scheduling.k8s.io/v1alpha1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "This priority class should be used for XYZ service Pods only."
優先級的使用也比較簡單,只須要在 Pod.spec.PriorityClassName
指定要使用的優先級名字,便可以設置當前 Pod 的優先級爲對應的值。
Pod 的優先級在調度的時候會使用到。首先,待調度的 Pod 都在同一個隊列中,啓用了 Pod priority 以後,調度器會根據優先級的大小,把優先級高的 Pod 放在前面,提早調度。
若是在調度的時候,發現某個 Pod 由於資源不足沒法找到合適的節點,調度器會嘗試 Preempt 的邏輯。簡單來講,調度器會試圖找到這樣一個節點:找到它上面優先級低於當前要調度 Pod 的全部 Pod,若是殺死它們,能騰足夠的資源,調度器會執行刪除操做,把 Pod 調度到節點上。更多內容能夠參考:Pod Priority and Preemption - Kubernetes。
講述的都是理想狀況下 Kubernetes 的工做情況,咱們假設資源徹底夠用,並且應用也都是在使用規定範圍內的資源。
在管理集羣的時候咱們經常會遇到資源不足的狀況,在這種狀況下咱們要保證整個集羣可用,而且儘量減小應用的損失。保證集羣可用比較容易理解,首先要保證系統層面的核心進程正常,其次要保證 Kubernetes 自己組件進程不出問題;可是如何量化應用的損失呢?首先能想到的是若是要殺死 Pod,要儘可能減小總數。另一個就和 Pod 的優先級相關了,那就是儘可能殺死不那麼重要的應用,讓重要的應用不受影響。
Pod 的驅逐是在 Kubelet 中實現的,由於 Kubelet 能動態地感知到節點上資源使用率實時的變化狀況。其核心的邏輯是:Kubelet 實時監控節點上各類資源的使用狀況,一旦發現某個不可壓縮資源出現要耗盡的狀況,就會主動終止節點上的 Pod,讓節點可以正常運行。被終止的 Pod 全部容器會中止,狀態會被設置爲 Failed。
目前主要有三種狀況:實際內存不足、節點文件系統的可用空間(文件系統剩餘大小和 Inode 數量)不足、以及鏡像文件系統的可用空間(包括文件系統剩餘大小和 Inode 數量)不足。
下面這圖是具體的觸發條件:
有了數據的來源,另一個問題是觸發的時機,也就是到什麼程度須要觸發驅逐程序?Kubernetes 運行用戶本身配置,而且支持兩種模式:按照百分比和按照絕對數量。好比對於一個 32G 內存的節點當可用內存少於 10% 時啓動驅逐程序,能夠配置 memory.available<10%
或者 memory.available<3.2Gi
。
NOTE:默認狀況下,Kubelet 的驅逐規則是
memory.available<100Mi
,對於生產環境這個配置是不可接受的,因此必定要根據實際狀況進行修改。
由於驅逐 Pod 是具備毀壞性的行爲,所以必需要謹慎。有時候內存使用率增高只是暫時性的,有可能 20s 內就能恢復,這時候啓動驅逐程序意義不大,並且可能會致使應用的不穩定,咱們要考慮到這種狀況應該如何處理;另外須要注意的是,若是內存使用率太高,好比高於 95%(或者 90%,取決於主機內存大小和應用對穩定性的要求),那麼咱們不該該再多作評估和考慮,而是趕忙啓動驅逐程序,由於這種狀況再花費時間去判斷可能會致使內存繼續增加,系統徹底崩潰。
爲了解決這個問題,Kubernetes 引入了 Soft Eviction 和 Hard Eviction 的概念。
軟驅逐能夠在資源緊缺狀況並沒有哪些嚴重的時候觸發,好比內存使用率爲 85%,軟驅逐還須要配置一個時間指定軟驅逐條件持續多久才觸發,也就是說 Kubelet 在發現資源使用率達到設定的閾值以後,並不會當即觸發驅逐程序,而是繼續觀察一段時間,若是資源使用率高於閾值的狀況持續必定時間,纔開始驅逐。而且驅逐 Pod 的時候,會遵循 Grace Period ,等待 Pod 處理完清理邏輯。和軟驅逐相關的啓動參數是:
--eviction-soft
:軟驅逐觸發條件,好比 memory.available<1Gi
。
--eviction-sfot-grace-period
:觸發條件持續多久纔開始驅逐,好比 memory.available=2m30s
。
--eviction-max-Pod-grace-period
:Kill Pod 時等待 Grace Period 的時間讓 Pod 作一些清理工做,若是到時間尚未結束就作 Kill。
前面兩個參數必須同時配置,軟驅逐才能正常工做;後一個參數會和 Pod 自己配置的 Grace Period 比較,選擇較小的一個生效。
硬驅逐更加直接乾脆,Kubelet 發現節點達到配置的硬驅逐閾值後,當即開始驅逐程序,而且不會遵循 Grace Period,也就是說當即強制殺死 Pod。對應的配置參數只有一個 --evictio-hard
,能夠選擇上面表格中的任意條件搭配。
設置這兩種驅逐程序是爲了平衡節點穩定性和對 Pod 的影響,軟驅逐照顧到了 Pod 的優雅退出,減小驅逐對 Pod 的影響;而硬驅逐則照顧到節點的穩定性,防止資源的快速消耗致使節點不可用。
軟驅逐和硬驅逐能夠單獨配置,不過仍是推薦二者都進行配置,一塊兒使用。
上面已經總體介紹了 Kubelet 驅逐 Pod 的邏輯和過程。牽涉到一個具體的問題:要驅逐哪些 Pod?驅逐的重要原則是儘可能減小對應用程序的影響。
若是是存儲資源不足,Kubelet 會根據狀況清理狀態爲 Dead 的 Pod 和它的全部容器,以及清理全部沒有使用的鏡像。若是上述清理並無讓節點回歸正常,Kubelet 就開始清理 Pod。
一個節點上會運行多個 Pod,驅逐全部的 Pods 顯然是沒必要要的,所以要作出一個抉擇:在節點上運行的全部 Pod 中選擇一部分來驅逐。雖然這些 Pod 乍看起來沒有區別,可是它們的地位是不同的,
系統組件的 Pod 要比普通的 Pod 更重要,另外運行數據庫的 Pod 天然要比運行一個無狀態應用的 Pod 更重要。Kubernetes 又是怎麼決定 Pod 的優先級的呢?這個問題的答案就藏在咱們以前已經介紹過的內容裏:Pod Requests 和 Limits、優先級(Priority),以及 Pod 實際的資源使用。
簡單來講,Kubelet 會根據如下內容對 Pod 進行排序:Pod 是否使用了超過請求的緊張資源、Pod 的優先級、而後是使用的緊缺資源和請求的緊張資源之間的比例。具體來講,Kubelet 會按照以下的順序驅逐 Pod:
使用的緊張資源超過請求數量的 BestEffort
和 Burstable
Pod,這些 Pod 內部又會按照優先級和使用比例進行排序。
緊張資源使用量低於 Requests 的 Burstable
和 Guaranteed
的 Pod 後面纔會驅逐,只有當系統組件(Kubelet、Docker、Journald 等)內存不夠,而且沒有上面 QoS 比較低的 Pod 時纔會作。執行的時候還會根據 Priority 排序,優先選擇優先級低的 Pod。
波動有兩種狀況,第一種。驅逐條件出發後,若是 Kubelet 驅逐一部分 Pod,讓資源使用率低於閾值就中止,那麼極可能過一段時間資源使用率又會達到閾值,從而再次出發驅逐,如此循環往復……爲了處理這種問題,咱們能夠使用 --eviction-minimum-reclaim
解決,這個參數配置每次驅逐至少清理出來多少資源纔會中止。
另一個波動狀況是這樣的:Pod 被驅逐以後並不會今後消失不見,常見的狀況是 Kubernetes 會自動生成一個新的 Pod 來取代,並通過調度選擇一個節點繼續運行。若是不作額外處理,有理由相信 Pod 選擇原來節點的可能性比較大(由於調度邏輯沒變,而它上次調度選擇的就是該節點),之因此說可能而不是絕對會再次選擇該節點,是由於集羣 Pod 的運行和分佈和上次調度時極有可能發生了變化。
不管如何,若是被驅逐的 Pod 再次調度到原來的節點,極可能會再次觸發驅逐程序,而後 Pod 再次被調度到當前節點,循環往復…… 這種事情固然是咱們不肯意看到的,雖然看似複雜,但這個問題解決起來很是簡單:驅逐發生後,Kubelet 更新節點狀態,調度器感知到這一狀況,暫時不往該節點調度 Pod 便可。--eviction-pressure-transition-period
參數能夠指定 Kubelet 多久才上報節點的狀態,由於默認的上報狀態週期比較短,頻繁更改節點狀態會致使驅逐波動。
使用了上面多種參數的驅逐配置實例:
–eviction-soft=memory.available<80%,nodefs.available<2Gi \ –eviction-soft-grace-period=memory.available=1m30s,nodefs.available=1m30s \ –eviction-max-Pod-grace-period=120 \ –eviction-hard=memory.available<500Mi,nodefs.available<1Gi \ –eviction-pressure-transition-period=30s \ --eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"
Kubernetes 的調度器在爲 Pod 選擇運行節點的時候,只會考慮到調度那個時間點集羣的狀態,通過一系列的算法選擇一個當時最合適的節點。可是集羣的狀態是不斷變化的,用戶建立的 Pod 也是動態的,隨着時間變化,原來調度到某個節點上的 Pod 如今看來可能有更好的節點能夠選擇。好比考慮到下面這些狀況:
調度 Pod 的條件已經再也不知足,好比節點的 Taints 和 Labels 發生了變化。
新節點加入了集羣。若是默認配置了把 Pod 打散,那麼應該有一些 Pod 最好運行在新節點上。
節點的使用率不均勻。調度後,有些節點的分配率和使用率比較高,另一些比較低。
節點上有資源碎片。有些節點調度以後還剩餘部分資源,可是又低於任何 Pod 的請求資源;或者 Memory 資源已經用完,可是 CPU 還有挺多沒有使用。
想要解決上述的這些問題,都須要把 Pod 從新進行調度(把 Pod 從當前節點移動到另一個節點)。可是默認狀況下,一旦 Pod 被調度到節點上,除非給殺死不然不會移動到另一個節點的。
Kubernetes 社區孵化了一個稱爲 Descheduler
的項目,專門用來作重調度。重調度的邏輯很簡單:找到上面幾種狀況中已經不是最優的 Pod,把它們驅逐掉(Eviction)。
Descheduler 不會決定驅逐的 Pod 應該調度到哪臺機器,而是假定默認的調度器會作出正確的調度抉擇。也就是說,之因此 Pod 目前不合適,不是由於調度器的算法有問題,而是由於集羣的狀況發生了變化。若是讓調度器從新選擇,調度器如今會把 Pod 放到合適的節點上。這種作法讓 Descheduler 邏輯比較簡單,並且避免了調度邏輯出如今兩個組件中。
Descheduler 執行的邏輯是能夠配置的,目前有幾種場景:
RemoveDuplicates
:RS、Deployment 中的 Pod 不能同時出如今一臺機器上。
LowNodeUtilization
:找到資源使用率比較低的 Node,而後驅逐其餘資源使用率比較高節點上的 Pod,指望調度器可以從新調度讓資源更均衡。
RemovePodsViolatingInterPodAntiAffinity
:找到已經違反 Pod Anti Affinity 規則的 Pods 進行驅逐,多是由於反親和是後面加上去的。
RemovePodsViolatingNodeAffinity
:找到違反 Node Affinity 規則的 Pods 進行驅逐,多是由於 Node 後面修改了 Label。
固然,爲了保證應用的穩定性,Descheduler 並不會隨意地驅逐 Pod,仍是會尊重 Pod 運行的規則,包括 Pod 的優先級(不會驅逐 Critical Pod,而且按照優先級順序進行驅逐)和 PDB(若是違反了 PDB,則不會進行驅逐),而且不會驅逐沒有 Deployment、RS、Jobs 的 Pod 不會驅逐,Daemonset Pod 不會驅逐,有 Local storage 的 Pod 也不會驅逐。
Descheduler 不是一個常駐的任務,每次執行完以後會退出,所以推薦使用 CronJob 來運行。
總的來講,Descheduler 是對原生調度器的補充,用來解決原生調度器的調度決策隨着時間會變得失效,或者不夠優化的缺陷。
動態調整的思路:應用的實際流量會不斷變化,所以使用率也是不斷變化的,爲了應對應用流量的變化,咱們應用可以自動調整應用的資源。好比在線商品應用在促銷的時候訪問量會增長,咱們應該自動增長 Pod 運算能力來應對;當促銷結束後,有須要自動下降 Pod 的運算能力防止浪費。
運算能力的增減有兩種方式:改變單個 Pod 的資源,以及增減 Pod 的數量。這兩種方式對應了 Kubernetes 的 HPA 和 VPA。
橫向 Pod 自動擴展的思路是這樣的:Kubernetes 會運行一個 Controller,週期性地監聽 Pod 的資源使用狀況,當高於設定的閾值時,會自動增長 Pod 的數量;當低於某個閾值時,會自動減小 Pod 的數量。天然,這裏的閾值以及 Pod 的上限和下限的數量都是須要用戶配置的。
一個重要的信息:HPA 只能和 RC、Deployment、RS 這些能夠動態修改 Replicas 的對象一塊兒使用,而沒法用於單個 Pod、Daemonset(由於它控制的 Pod 數量不能隨便修改)等對象。
目前官方的監控數據來源是 Metrics Server 項目,能夠配置的資源只有 CPU,可是用戶能夠使用自定義的監控數據(好比:Prometheus)。其餘資源(好比:Memory)的 HPA 支持也已經在路上了。
和 HPA 的思路類似,只不過 VPA 調整的是單個 Pod 的 Request 值(包括 CPU 和 Memory)。VPA 包括三個組件:
Recommander:消費 Metrics Server 或者其餘監控組件的數據,而後計算 Pod 的資源推薦值。
Updater:找到被 VPA 接管的 Pod 中和計算出來的推薦值差距過大的,對其作 Update 操做(目前是 Evict,新建的 Pod 在下面 Admission Controller 中會使用推薦的資源值做爲 Request)。
Admission Controller:新建的 Pod 會通過該 Admission Controller,若是 Pod 是被 VPA 接管的,會使用 Recommander 計算出來的推薦值。
能夠看到,這三個組件的功能是互相補充的,共同實現了動態修改 Pod 請求資源的功能。相對於 HPA,目前 VPA 還處於 Alpha,而且尚未合併到官方的 Kubernetes Release 中,後續的接口和功能極可能會發生變化。
隨着業務的發展,應用會逐漸增多,每一個應用使用的資源也會增長,總會出現集羣資源不足的狀況。爲了動態地應對這一情況,咱們還須要 CLuster Auto Scaler,可以根據整個集羣的資源使用狀況來增減節點。
對於公有云來講,Cluster Auto Scaler 就是監控這個集羣由於資源不足而 Pending 的 Pod,根據用戶配置的閾值調用公有云的接口來申請建立機器或者銷燬機器。對於私有云,則須要對接內部的管理平臺。
目前 HPA 和 VPA 不兼容,只能選擇一個使用,不然二者會相互干擾。並且 VPA 的調整須要重啓 Pod,這是由於 Pod 資源的修改是比較大的變化,須要從新走一下 Apiserver、調度的流程,保證整個系統沒有問題。目前社區也有計劃在作原地升級,也就是說不經過殺死 Pod 再調度新 Pod 的方式,而是直接修改原有 Pod 來更新。
理論上 HPA 和 VPA 是能夠共同工做的,HPA 負責瓶頸資源,VPA 負責其餘資源。好比對於 CPU 密集型的應用,使用 HPA 監聽 CPU 使用率來調整 Pods 個數,而後用 VPA 監聽其餘資源(Memory、IO)來動態擴展這些資源的 Request 大小便可。固然這只是理想狀況,
集羣的資源使用並非靜態的,而是隨着時間不斷變化的,目前 Kubernetes 的調度決策都是基於調度時集羣的一個靜態資源切片進行的,動態地資源調整是經過 Kubelet 的驅逐程序進行的,HPA 和 VPA 等方案也不斷提出,相信後面會不斷完善這方面的功能,讓 Kubernetes 更加智能。
資源管理和調度、應用優先級、監控、鏡像中心等不少東西相關,是個很是複雜的領域。在具體的實施和操做的過程當中,經常要考慮到企業內部的具體狀況和需求,作出針對性的調整,而且須要開發者、系統管理員、SRE、監控團隊等不一樣小組一塊兒合做。可是這種付出從總體來看是值得的,提高資源的利用率能有效地節約企業的成本,也能讓應用更好地發揮出做用。
參考連接:
https://mp.weixin.qq.com/s/fzvL02oVd4VFgDty1UlCLg
https://mp.weixin.qq.com/s/Whpt8BqN3iBMKnuMkWpSxA