主頁 >  其他 > Kubernetes 運維學習筆記

Kubernetes 運維學習筆記

2020-11-07 04:18:44 其他

一、Kubernetes 介紹

Kubernetes是一個全新的基于容器技術的分布式架構領先方案, 它是Google在2014年6月開源的一個容器集群管理系統,使用Go語言開發,Kubernetes也叫K8S,K8S是Google內部一個叫Borg的容器集群管理系統衍生出來的,Borg已經在Google大規模生產運行十年之久,K8S主要用于自動化部署、擴展和管理容器應用,提供了資源調度、部署管理、服務發現、擴容縮容、監控等一整套功能,2015年7月,Kubernetes v1.0正式發布,截止到2017年9月29日最新穩定版本是v1.8,Kubernetes目標是讓部署容器化應用簡單高效,

Kubernetes最初源于谷歌內部的Borg,提供了面向應用的容器集群部署和管理系統,Kubernetes 的目標旨在消除編排物理/虛擬計算,網路和存盤基礎設施的負擔,并使應用程式運營商和開發人員完全將重點放在以容器為中心的原語上進行自助運營,Kubernetes 也提供穩定、兼容的基礎(平臺),用于構建定制化的workflows 和更高級的自動化任務,

Kubernetes 具備完善的集群管理能力,包括多層次的安全防護和準入機制、多租戶應用支撐能力、透明的服務注冊和服務發現機制、內建負載均衡器、故障發現和自我修復能力、服務滾動升級和在線擴容、可擴展的資源自動調度機制、多粒度的資源配額管理能力,Kubernetes 還提供完善的管理工具,涵蓋開發、部署測驗、運維監控等各個環節,

二、Kubernetes主要功能

Kubernetes是docker容器用來編排和管理的工具,它是基于Docker構建一個容器的調度服務,提供資源調度、均衡容災、服務注冊、動態擴縮容等功能套件,Kubernetes提供應用部署、維護、 擴展機制等功能,利用Kubernetes能方便地管理跨機器運行容器化的應用,其主要功能如下:

資料卷: Pod中容器之間共享資料,可以使用資料卷,

應用程式健康檢查: 容器內服務可能行程堵塞無法處理請求,可以設定監控檢查策略保證應用健壯性,

復制應用程式實體: 控制器維護著Pod副本數量,保證一個Pod或一組同類的Pod數量始終可用,

彈性伸縮: 根據設定的指標(CPU利用率)自動縮放Pod副本數,

服務發現: 使用環境變數或DNS服務插件保證容器中程式發現Pod入口訪問地址,

負載均衡: 一組Pod副本分配一個私有的集群IP地址,負載均衡轉發請求到后端容器,在集群內部其他Pod可通過這個ClusterIP訪問應用,

滾動更新: 更新服務不中斷,一次更新一個Pod,而不是同時洗掉整個服務,

服務編排: 通過檔案描述部署服務,使得應用程式部署變得更高效,

資源監控: Node節點組件集成cAdvisor資源收集工具,可通過Heapster匯總整個集群節點資源資料,然后存盤到InfluxDB時序資料庫,再由Grafana展示,

提供認證和授權: 支持屬性訪問控制(ABAC)、角色訪問控制(RBAC)認證授權策略,

除此之外, Kubernetes主要功能還體現在:
-  使用Docker對應用程式包裝(package)、實體化(instantiate)、運行(run),
-  將多臺Docker主機抽象為一個資源,以集群的方式運行、管理跨機器的容器,包括任務調度、資源管理、彈性伸縮、滾動升級等功能,
-  使用編排系統(YAML File)快速構建容器集群,提供負載均衡,解決容器直接關聯及通信問題
-  解決Docker跨機器容器之間的通訊問題,
-  自動管理和修復容器,簡單說,比如創建一個集群,里面有十個容器,如果某個容器例外關閉,那么,會嘗試重啟或重新分配容器,始終保證會有十個容器在運行,反而殺死多余的,Kubernetes的自我修復機制使得容器集群總是運行在用戶期望的狀態. 當前Kubernetes支持GCE、vShpere、CoreOS、OpenShift,

kubernetes的集群至少有兩個主機組成:master + node ,即為master/node架構,master為集群的控制面板,master主機需要做冗余,一般建議為3臺;而node主機不需要做冗余,因為node的主要作用是運行pod,貢獻計算能力和存盤能力,而pod控制器會自動管控pod資源,如果資源少,pod控制器會自動創建pod,即pod控制器會嚴格按照用戶指定的副本來管理pod的數量,客戶端的請求下發給master,即把創建和啟動容器的請求發給master,master中的調度器分析各node現有的資源狀態,把請求呼叫到對應的node啟動容器,

可以理解為kubernetes把容器抽象為pod來管理1到多個彼此間有非常緊密聯系的容器,但是LAMP的容器主機A,M,P只是有關聯,不能說是非常緊密聯系,因此A,M,P都要運行在三個不同的pod上,在kubernetes中,要運行幾個pod,是需要定義一個組態檔,在這個組態檔里定義用哪個控制器啟動和控制幾個pod,在每個pod里要定義那幾臺容器,kubernetes通過這個組態檔,去創建一個控制器,由此控制器來管控這些pod,如果這些pod的某幾個down掉后,控制器會通過健康監控功能,隨時監控pod,發現pod例外后,根據定義的策略進行操作,即可以進行自愈,

kubernetes內部需要5套證書,手動創建或者自動生成,分別為:
1. etcd內部通信需要一套ca和對應證書,
2. etcd與外部通信也要有一套ca和對應證書,
3. APIserver間通信需要一套證書,
4. apiserver與node間通信需要一套證書,
5. node和pod間通信需要一套ca證書,

目前來說還不能實作把所有的業務都遷到kubernetes上,如存盤,因為這個是有狀態應用,出現錯誤排查很麻煩,所以目前kubernetes主要是運行無狀態應用,

所以一般而言,負載均衡器運行在kubernetes之外,nginx或者tomcat這種無狀態的應用運行于kubernetes集群內部,而資料庫如mysql,zabbix,zoopkeeper等有狀態的,一般運行于kubernetes外部,通過網路連接,實作kubernetes集群的pod呼叫這些外部的有狀態應用,

三、Kubernetes架構和組件

kubernetes主要由以下幾個核心組件組成:
etcd: 集群的主資料庫,保存了整個集群的狀態; etcd負責節點間的服務發現和配置共享,etcd分布式鍵值存盤系統, 用于保持集群狀態,比如Pod、Service等物件資訊,
kube-apiserver: 提供了資源操作的唯一入口,并提供認證、授權、訪問控制、API注冊和發現等機制;這是kubernetes API,作為集群的統一入口,各組件協調者,以HTTPAPI提供介面服務,所有物件資源的增刪改查和監聽操作都交給APIServer處理后再提交給Etcd存盤,
kube-controller-manager: 負責維護集群的狀態,比如故障檢測、自動擴展、滾動更新等;它用來執行整個系統中的后臺任務,包括節點狀態狀況、Pod個數、Pods和Service的關聯等, 一個資源對應一個控制器,而ControllerManager就是負責管理這些控制器的,
kube-scheduler: 資源調度,按照預定的調度策略將Pod調度到相應的機器上;它負責節點資源管理,接受來自kube-apiserver創建Pods任務,并分配到某個節點,它會根據調度演算法為新創建的Pod選擇一個Node節點,
kubectl: 客戶端命令列工具,將接受的命令格式化后發送給kube-apiserver,作為整個系統的操作入口,
kubelet: 負責維護容器的生命周期,負責管理pods和它們上面的容器,images鏡像、volumes、etc,同時也負責Volume(CVI)和網路(CNI)的管理;kubelet運行在每個計算節點上,作為agent,接受分配該節點的Pods任務及管理容器,周期性獲取容器狀態,反饋給kube-apiserver; kubelet是Master在Node節點上的Agent,管理本機運行容器的生命周期,比如創建容器、Pod掛載資料卷、下載secret、獲取容器和節點狀態等作業,kubelet將每個Pod轉換成一組容器,
container runtime: 負責鏡像管理以及Pod和容器的真正運行(CRI);
kube-proxy: 負責為Service提供cluster內部的服務發現和負載均衡;它運行在每個計算節點上,負責Pod網路代理,定時從etcd獲取到service資訊來做相應的策略,它在Node節點上實作Pod網路代理,維護網路規則和四層負載均衡作業,
docker或rocket(rkt): 運行容器,

除了上面的幾個核心組建, 還有一些常用插件(Add-ons):
kube-dns: 負責為整個集群提供DNS服務;
Ingress Controller: 為服務提供外網入口;
Heapster: 提供資源監控;
Dashboard: 提供GUI;
Federation: 提供跨可用區的集群;
Fluentd-elasticsearch: 提供集群日志采集、存盤與查詢;

其中:
master組件包括: kube-apiserver, kube-controller-manager, kube-scheduler;
Node組件包括: kubelet, kube-proxy, docker或rocket(rkt);
第三方服務:etcd

Kubernetes Master控制組件,調度管理整個系統(集群),包含如下組件:
Kubernetes API Server: 作為Kubernetes系統入口,其封裝了核心物件的增刪改查操作,以RESTful API介面方式提供給外部客戶和內部組件呼叫,維護的REST物件持久化到Etcd中存盤,
Kubernetes Scheduler: 為新建立的Pod進行節點(node)選擇(即分配機器),負責集群的資源調度,組件抽離,可以方便替換成其他調度器,
Kubernetes Controller: 負責執行各種控制器,目前已經提供了很多控制器來保證Kubernetes的正常運行,
Replication Controller: 管理維護Replication Controller,關聯Replication Controller和Pod,保證Replication Controller定義的副本數量與實際運行Pod數量一致,
Node Controller: 管理維護Node,定期檢查Node的健康狀態,標識出(失效|未失效)的Node節點,
Namespace Controller: 管理維護Namespace,定期清理無效的Namespace,包括Namesapce下的API物件,比如Pod、Service等,
Service Controller: 管理維護Service,提供負載以及服務代理,
EndPoints Controller: 管理維護Endpoints,關聯Service和Pod,創建Endpoints為Service的后端,當Pod發生變化時,實時更新Endpoints  (即Pod Ip + Container Port),
Service Account Controller: 管理維護Service Account,為每個Namespace創建默認的Service Account,同時為Service Account創建Service Account Secret,
Persistent Volume Controller: 管理維護Persistent Volume和Persistent Volume Claim,為新的Persistent Volume Claim分配Persistent Volume進行系結,為釋放的Persistent Volume執行清理回收,
Daemon Set Controller: 管理維護Daemon Set,負責創建Daemon Pod,保證指定的Node上正常的運行Daemon Pod,
Deployment Controller: 管理維護Deployment,關聯Deployment和Replication Controller,保證運行指定數量的Pod,當Deployment更新時,控制實作Replication Controller和 Pod的更新,
Job Controller: 管理維護Job,為Jod創建一次性任務Pod,保證完成Job指定完成的任務數目
Pod Autoscaler Controller: 實作Pod的自動伸縮,定時獲取監控資料,進行策略匹配,當滿足條件時執行Pod的伸縮動作,

Kubernetes Node運行節點,運行管理業務容器,包含如下組件:
Kubelet: 負責管控容器,Kubelet會從Kubernetes API Server接收Pod的創建請求,啟動和停止容器,監控容器運行狀態并匯報給Kubernetes API Server,
Kubernetes Proxy: 負責為Pod創建代理服務,Kubernetes Proxy會從Kubernetes API Server獲取所有的Service資訊,并根據Service的資訊創建代理服務,實作Service到Pod的請求路由和轉發,從而實作Kubernetes層級的虛擬轉發網路,
Docker:  Node上需要運行容器服務

                                                    Kubernetes的分層設計理念                                                      
Kubernetes設計理念和功能類似Linux的分層架構,如下圖:

核心層:Kubernetes最核心的功能,對外提供API構建高層的應用,對內提供插件式應用執行環境;
應用層:部署(無狀態應用、有狀態應用、批處理任務、集群應用等)和路由(服務發現、DNS決議等);
管理層:系統度量(如基礎設施、容器和網路的度量),自動化(如自動擴展、動態Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等);
介面層:kubectl命令列工具、客戶端SDK以及集群聯邦;
生態系統:在介面層之上的龐大容器集群管理調度的生態系統,可以劃分為兩個范疇:    
-  Kubernetes外部:日志、監控、配置管理、CI、CD、Workflow、FaaS、OTS應用、ChatOps等;    
-  Kubernetes內部:CRI、CNI、CVI、鏡像倉庫、Cloud Provider、集群自身的配置和管理等;

四、Kubernetes基本物件概念

Kubernetes中的大部分概念Node、Pod、Replication Controller、Service等都可以看作一種“資源物件”,幾乎所有的資源物件都可以通過kubectl工具(API呼叫)執行增、刪、改、查等操作并將其保存在etcd中持久化存盤,從這個角度來看,kubernetes其實是一個高度自動化的資源控制系統,通過跟蹤對比etcd庫里保存的“資源期望狀態”與當前環境中的“實際資源狀態”的差異來實作自動控制和自動糾錯的高級功能,

基本物件:
Pod: Pod是最小部署單元,一個Pod有一個或多個容器組成,Pod中容器共享存盤和網路,在同一臺Docker主機上運行; Pod 中的容器會作為一個整體被Master調度到一個Node上運行,pod 是一組container,pod里面的container是共享網路堆疊和存盤卷等資源,是一個整體. pod 可以認為是容器組的概念,里面有個infra container 負責pod內所有container 共享 namespace,docker的容器可以類比成OS中的行程,而K8S的pod則更像是OS中的“行程組”概念,
Service : Service一個應用服務抽象,定義了Pod邏輯集合和訪問這個Pod集合的策略,Service代理Pod集合對外表現是為一個訪問入口,分配一個集群IP地址,來自這個IP的請求將負載均衡轉發后端Pod中的容器,Service通過LableSelector選擇一組Pod提供服務,
Volume: 資料卷,共享Pod中容器使用的資料,
Namespace: 命名空間將物件邏輯上分配到不同Namespace,可以是不同的專案、用戶等區分管理,并設定控制策略,從而實作多租戶,命名空間也稱為虛擬集群,
Lable: 標簽用于區分物件(比如Pod、Service),鍵/值對存在;每個物件可以有多個標簽,通過標簽關聯物件,

基于基本物件更高層次抽象:  
ReplicaSet: 下一代ReplicationController,確保任何給定時間指定的Pod副本數量,并提供宣告式更新等功能,RC與RS唯一區別就是lableselector支持不同,RS支持新的基于集合的標簽,RC僅支持基于等式的標簽,
Deployment: Deployment是一個更高層次的API物件,它管理ReplicaSets和Pod,并提供宣告式更新等功能,官方建議使用Deployment管理ReplicaSets,而不是直接使用ReplicaSets,這就意味著可能永遠不需要直接操作ReplicaSet物件,負責無狀態應用pod控制,支持二級控制器(HPA,HorizontalPodAutoscaler水平pod自動控制器),
StatefulSet: StatefulSet適合持久性的應用程式,有唯一的網路識別符號(IP),持久存盤,有序的部署、擴展、洗掉和滾動更新,負責有狀態應用pod控制,
DaemonSet: DaemonSet確保所有(或一些)節點運行同一個Pod,當節點加入Kubernetes集群中,Pod會被調度到該節點上運行,當節點從集群中移除時,DaemonSet的Pod會被洗掉,洗掉DaemonSet會清理它所有創建的Pod,
Job: 一次性任務,運行完成后Pod銷毀,不再重新啟動新容器,還可以任務定時運行,Kubernetes中的Job 用于運行結束就洗掉的應用,

                                                                                                                                             

API物件是K8s集群中管理操作單元,K8s集群系每支持一項新功能,引入一項新技術,一定會新引入對應的API物件,支持對該功能的管理操作,例如副本集Replica Set對應的API物件是RS,Kubernetes中所有的配置都是通過API物件的spec去設定的,也就是用戶通過配置系統的理想狀態來改變系統,這是k8s重要設計理念之一,即所有的操作都是宣告式 (Declarative) 的而不是命令式(Imperative)的,宣告式操作在分布式系統中好處是穩定,不怕丟操作或運行多次,例如設定副本數為3的操作運行多次也還是一個結果, 而給副本數加1的操作就不是宣告式的, 運行多次結果就錯了,

Cluster
Cluster 是計算、存盤和網路資源的集合,Kubernetes 利用這些資源運行各種基于容器的應用

Master
kubernetes集群的管理節點,負責管理集群,提供集群的資源資料訪問入口,擁有Etcd存盤服務(可選),運行Api Server行程,Controller Manager服務行程及Scheduler服務行程,關聯作業節點Node,Kubernetes API server提供HTTP Rest介面的關鍵服務行程,是Kubernetes里所有資源的增、刪、改、查等操作的唯一入口,也是集群控制的入口行程;Kubernetes Controller Manager是Kubernetes所有資源物件的自動化控制中心;Kubernetes Schedule是負責資源調度(Pod調度)的行程.

Node
Node是Kubernetes集群架構中運行Pod的服務節點(亦叫agent或minion),Node是Kubernetes集群操作的單元,用來承載被分配Pod的運行,是Pod運行的宿主機,關聯Master管理節點,擁有名稱和IP、系統資源資訊,運行docker eninge服務,守護行程kunelet及負載均衡器kube-proxy. 每個Node節點都運行著以下一組關鍵行程: 
-  kubelet:負責對Pod對于的容器的創建、啟停等任務
-  kube-proxy:實作Kubernetes Service的通信與負載均衡機制的重要組件
-  Docker Engine(Docker):Docker引擎,負責本機容器的創建和管理作業

Node節點可以在運行期間動態增加到Kubernetes集群中,默認情況下,kubelet會想master注冊自己,這也是Kubernetes推薦的Node管理方式,kubelet行程會定時向Master匯報自身情報,如作業系統、Docker版本、CPU和記憶體,以及有哪些Pod在運行等等,這樣Master可以獲知每個Node節點的資源使用情況,冰實作高效均衡的資源調度策略,、

Pod
運行于Node節點上,若干相關容器的組合,Pod內包含的容器運行在同一宿主機上,使用相同的網路命名空間、IP地址和埠,能夠通過localhost進行通,Pod是Kurbernetes進行創建、調度和管理的最小單位,它提供了比容器更高層次的抽象,使得部署和管理更加靈活,一個Pod可以包含一個容器或者多個相關容器,

Pod其實有兩種型別:普通Pod和靜態Pod,后者比較特殊,它并不存在Kubernetes的etcd存盤中,而是存放在某個具體的Node上的一個具體檔案中,并且只在此Node上啟動,普通Pod一旦被創建,就會被放入etcd存盤中,隨后會被Kubernetes Master調度到摸個具體的Node上進行系結,隨后該Pod被對應的Node上的kubelet行程實體化成一組相關的Docker容器并啟動起來,在默認情況下,當Pod里的某個容器停止時,Kubernetes會自動檢測到這個問起并且重啟這個Pod(重啟Pod里的所有容器),如果Pod所在的Node宕機,則會將這個Node上的所有Pod重新調度到其他節點上,

Pod是在K8s集群中運行部署應用或服務的最小單元,它是可以支持多容器的,Pod的設計理念是支持多個容器在一個Pod中共享網路地址和檔案系統,可以通過行程間通信和檔案共享這種簡單高效的方式組合完成服務.比如你運行一個作業系統發行版的軟體倉庫,一個Nginx容器用來發布軟體,另一個容器專門用來從源倉庫做同步,這兩個容器的鏡像不太可能是一個團隊開發的,但是他們一塊兒作業才能提供一個微服務;這種情況下,不同的團隊各自開發構建自己的容器鏡像,在部署的時候組合成一個微服務對外提供服務,

kubernetes的最核心功能就是為了運行pod,其他組件是為了pod能夠正常運行而執行的,pod可以分為兩類:
1. 自主式pod
2. 控制器管理的pod

一個pod上有兩類元資料,label 和 annotation
label:標簽,對資料型別和程度要求嚴格,
annotation:注解,用于存盤自己定義的復雜元資料,用來描述pod的屬性

外部請求訪問內部的pod經過了三級轉發,第一級先到nodeip(宿主機ip)對應的埠,然后被轉為cluster ip的service 埠,然后轉換為PodIP的containerPort,

Kubernetes 引入 Pod 主要基于下面兩個目的:
- 可管理性
有些容器天生就是需要緊密聯系, 一起作業,Pod 提供了比容器更高層次的抽象,將它們封裝到一個部署單元中,Kubernetes 以 Pod 為最小單位進行調度、擴展、共享資源、管理生命周期,

- 通信和資源共享
Pod 中的所有容器使用同一個網路 namespace,即相同的 IP 地址和 Port 空間,它們可以直接用 localhost 通信,同樣的,這些容器可以共享存盤,當 Kubernetes 掛載 volume 到 Pod,本質上是將 volume 掛載到 Pod 中的每一個容器,

File Puller 會定期從外部的 Content Manager 中拉取最新的檔案,將其存放在共享的 volume 中,Web Server 從 volume 讀取檔案,回應 Consumer 的請求,這兩個容器是緊密協作的,它們一起為 Consumer 提供最新的資料;同時它們也通過 volume 共享資料,所以放到一個 Pod 是合適的,

Controller
Kubernetes 通常不會直接創建 Pod,而是通過 Controller 來管理 Pod 的,Controller 中定義了 Pod 的部署特性,比如有幾個副本,在什么樣的 Node 上運行等,為了滿足不同的業務場景, Kubernetes 提供了多種 Controller,包括 Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等. 

Replication Controller (副本集RC)
Replication Controller用來管理Pod的副本,保證集群中存在指定數量的Pod副本,集群中副本的數量大于指定數量,則會停止指定數量之外的多余容器數量,反之,則會啟動少于指定數量個數的容器,保證數量不變,Replication Controller是實作彈性伸縮、動態擴容和滾動升級的核心,

通過監控運行中的Pod來保證集群中運行指定數目的Pod副本,少于指定數目,RC就會啟動運行新的Pod副本;多于指定數目,RC就會殺死多余的Pod副本 (這是k8s早期技術概念)

Replica Set (副本集RS)
RS是新一代RC,提供同樣的高可用能力,區別主要在于RS后來居上,能支持更多種類的匹配模式,副本集物件一般不單獨使用,而是作為Deployment的理想狀態引數使用. Replica Set 實作了 Pod 的多副本管理,使用 Deployment 時會自動創建 ReplicaSet,也就是說 Deployment 是通過 ReplicaSet 來管理 Pod 的多個副本,我們通常不需要直接使用 ReplicaSet,

Deployment (部署)
Deployment 是最常用的 Controller,Deployment 可以管理 Pod 的多個副本,并確保 Pod 按照期望的狀態運行,Deployment是一個比RS應用模式更廣的API物件,支持動態擴展,可以創建一個新的服務,更新一個新的服務,也可以是滾動升級一個服務,滾動升級一個服務,實際是創建一個新的RS,然后逐漸將新RS中副本數增加到理想狀態,將舊RS中的副本數減小到0的復合操作 (逐步升級新得副本,剔除舊的副本). 
總結:RC、RS和Deployment只是保證了支撐服務的微服務Pod的數量.

DaemonSet
DaemonSet 用于每個 Node 最多只運行一個 Pod 副本的場景,正如其名稱所揭示的,DaemonSet 通常用于運行 daemon,

StatefuleSet
StatefuleSet 能夠保證 Pod 的每個副本在整個生命周期中名稱是不變的,而其他 Controller 不提供這個功能,當某個 Pod 發生故障需要洗掉并重新啟動時,Pod 的名稱會發生變化,同時 StatefuleSet 會保證副本按照固定的順序啟動、更新或者洗掉,

Service

Service定義了Pod邏輯集合和訪問該集合的策略,是真實服務的抽象,Service提供了統一的服務訪問入口以及服務代理和發現機制,關聯多個相同Label的Pod,用戶不需要了解后臺Pod是如何運行,
外部系統訪問Service的問題:
->  首先需要弄明白Kubernetes的三種IP這個問題
       Node IP:Node節點的IP地址
    -  Pod IP: Pod的IP地址
     Cluster IP:Service的IP地址
->   首先,Node IP是Kubernetes集群中節點的物理網卡IP地址,所有屬于這個網路的服務器之間都能通過這個網路直接通信,這也表明Kubernetes集群之外的節點訪問Kubernetes集群之內的某個節點或者TCP/IP服務的時候,必須通過Node IP進行通信
->  其次,Pod IP是每個Pod的IP地址,他是Docker Engine根據docker0網橋的IP地址段進行分配的,通常是一個虛擬的二層網路,

最后Cluster IP是一個虛擬的IP,但更像是一個偽造的IP網路,原因有以下幾點: 
->  Cluster IP僅僅作用于Kubernetes Service這個物件,并由Kubernetes管理和分配P地址
->  Cluster IP無法被ping,他沒有一個“物體網路物件”來回應
->  Cluster IP只能結合Service Port組成一個具體的通信埠,單獨的Cluster IP不具備通信的基礎,并且他們屬于Kubernetes集群這樣一個封閉的空間,
->  Kubernetes集群之內,Node IP網、Pod IP網于Cluster IP網之間的通信,采用的是Kubernetes自己設計的一種編程方式的特殊路由規則,

RC、RS和Deployment只是保證了支撐服務的微服務Pod的數量,但是沒有解決如何訪問這些服務的問題,一個Pod只是一個運行服務的實體,隨時可能在一個節點上停止,在另一個節點以一個新的IP啟動一個新的Pod,因此不能以確定的IP和埠號提供服務,要穩定地提供服務需要服務發現和負載均衡能力,服務發現完成的作業,是針對客戶端訪問的服務,找到對應的的后端服務實體,在K8s集群中,客戶端需要訪問的服務就是Service物件,每個Service會對應一個集群內部有效的虛擬IP,集群內部通過虛擬IP訪問一個服務,在K8s集群中微服務的負載均衡是由Kube-proxy實作的,Kube-proxy是K8s集群內部的負載均衡器,它是一個分布式代理服務器,在K8s的每個節點上都有一個;這一設計體現了它的伸縮性優勢,需要訪問服務的節點越多,提供負載均衡能力的Kube-proxy就越多,高可用節點也隨之增多,與之相比,我們平時在服務器端做個反向代理做負載均衡,還要進一步解決反向代理的負載均衡和高可用問題,

Kubernetes 運行容器(Pod)與訪問容器(Pod)這兩項任務分別由 Controller 和 Service 執行,

Namespace
名字空間為K8s集群提供虛擬的隔離作用,K8s集群初始有兩個名字空間,分別是默認名字空間default和系統名字空間kube-system,除此以外,管理員可以可以創建新的名字空間滿足需要,

Label
Kubernetes中任意API物件都是通過Label進行標識,Label的實質是一系列的Key/Value鍵值對,其中key于value由用戶自己指定,Label可以附加在各種資源物件上,如Node、Pod、Service、RC等,一個資源物件可以定義任意數量的Label,同一個Label也可以被添加到任意數量的資源物件上去,Label是Replication Controller和Service運行的基礎,二者通過Label來進行關聯Node上運行的Pod,

我們可以通過給指定的資源物件捆綁一個或者多個不同的Label來實作多維度的資源分組管理功能,以便于靈活、方便的進行資源分配、調度、配置等管理作業,
一些常用的Label如下:
版本標簽:"release":"stable","release":"canary"......
環境標簽:"environment":"dev","environment":"qa","environment":"production"
架構標簽:"tier":"frontend","tier":"backend","tier":"middleware"
磁區標簽:"partition":"customerA","partition":"customerB"
質量管控標簽:"track":"daily","track":"weekly"

Label相當于我們熟悉的標簽,給某個資源物件定義一個Label就相當于給它大了一個標簽,隨后可以通過Label Selector(標簽選擇器)查詢和篩選擁有某些Label的資源物件,Kubernetes通過這種方式實作了類似SQL的簡單又通用的物件查詢機制,

Label Selector在Kubernetes中重要使用場景如下:
-> kube-Controller行程通過資源物件RC上定義Label Selector來篩選要監控的Pod副本的數量,從而實作副本數量始終符合預期設定的全自動控制流程;
-> kube-proxy行程通過Service的Label Selector來選擇對應的Pod,自動建立起每個Service島對應Pod的請求轉發路由表,從而實作Service的智能負載均衡;
-> 通過對某些Node定義特定的Label,并且在Pod定義檔案中使用Nodeselector這種標簽調度策略,kuber-scheduler行程可以實作Pod”定向調度“的特性;

                                                                                                                                                                 
Master管理節點和Node作業節點的各組件關系:

Kuberneter作業流程:
1)通過kubectl向kubernetes Master發出指令, Master節點主要提供API Server、Scheduler、Controller組件,接收kubectl命令,從Node節點獲取Node資源資訊,并發出調度任務,
2)Node節點提供kubelet、kube-proxy,每個node節點都安裝docker,是實際的執行者,kubernetes不負責網路,所以一般是用flannel或者weave,
3)etcd是一個鍵值存盤倉庫,etcd負責服務發現和node資訊存盤,不過需要注意的是:由于etcd是負責存盤,所以不建議搭建單點集群,如zookeeper一樣,由于存在選舉策略,所以一般推薦奇數個集群,如3,5,7,只要集群半數以上的結點存活,那么集群就可以正常運行,否則集群可能無法正常使用,

Master:集群控制管理節點,所有的命令都經由master處理,

Node:是kubernetes集群的作業負載節點,Master為其分配作業,當某個Node宕機時,Master會將其作業負載自動轉移到其他節點,

Node節點可動態增加到kubernetes集群中,前提是這個節點已經正確安裝、配置和啟動了上述的關鍵行程,默認情況下,kubelet會向Master注冊自己,這也kubernetes推薦的Node管理方式,一旦Node被納入集群管理范圍,kubelet會定時向Master匯報自身的情況,以及之前有哪些Pod在運行等,這樣Master可以獲知每個Node的資源使用情況,并實作高效均衡的資源調度策略,如果Node沒有按時上報資訊,則會被Master判斷為失聯,Node狀態會被標記為Not Ready,隨后Master會觸發作業負載轉移流程,

Pod:是kubernetes最重要也是最基本的概念,每個Pod都會包含一個 “根容器”,還會包含一個或者多個緊密相連的業務容器,

Kubernetes為每個Pod都分配了唯一IP地址, 稱之為PodIP, 一個Pod里多個容器共享PodIP地址. 要求底層網路支持集群內任意兩個Pod之間的直接通信,通常采用虛擬二層網路技術來實作 (Flannel).

Label:是一個key=value的鍵值對,其中key與value由用戶指定, 可以附加到各種資源物件上, 一個資源物件可以定義任意數量的Label,可以通過LabelSelector(標簽選擇器)查詢和篩選資源物件,

RC:Replication Controller宣告某個Pod的副本數在任意時刻都符合某個預期值,定義包含如下:
-  Pod期待的副本數(replicas);
-  用于篩選目標Pod的Label Selector;
-  當Pod副本數小于期望時,用于新的創建Pod的模板template;

需要注意
-  通過改變RC里的Pod副本數量,可以實作Pod的擴容或縮容功能;
-  通過改變RC里Pod模板中的鏡像版本,可以實作Pod的滾動升級功能;

Service:“微服務”,kubernetes中的核心,通過分析、識別并建模系統中的所有服務為微服務,最終系統有多個提供不同業務能力而又彼此獨立的微服務單元所組成,服務之間通過TCP/IP進行通信,每個Pod都會被分配一個單獨的IP地址,而且每個Pod都提供了一個獨立的Endpoint以被客戶端訪問,

客戶端如何訪問?
部署負載均衡器,為Pod開啟對外服務埠,將Pod的Endpoint串列加入轉發串列中,客戶端通過負載均衡器的對外IP+Port來訪問此服務,每個Service都有一個全域唯一的虛擬ClusterIP,這樣每個服務就變成了具備唯一IP地址的“通信節點”,服務呼叫就變成了最基礎的TCP網路通信問題,

Volume:是Pod中能夠被多個容器訪問的共享目錄,定義在Pod之上,被一個Pod里的多個容器掛載到具體的檔案目錄之下;Volume與Pod生命周期相同,Volume可以讓一個Pod里的多個容器共享檔案、讓容器的資料寫到宿主機的磁盤上或者寫檔案到 網路存盤中,具體如下圖所示:

在kubernetes1.2的時候,RC就由Replication Controller升級成Replica Set,“下一代RC”,命令兼容適用,Replica Set主要被Deployment這個更高層的資源物件所使用,從而形成一套Pod創建、洗掉、更新的編排機制,當我們使用Deployment時,無需關心它是如何創建和維護ReplicaSet的,這一切是自動發生的,

Docker: 既然k8s是基于容器的,那么就不得不提到docker,2013年初,docker橫空出世,孕育著新思想的“容器”,Docker選擇容器作為核心和基礎,以容器為資源分割和調度的基本單位,封裝整個軟體運行時環境,為開發者和系統管理員設計,用于構建、發布和運行分布式應用的平臺,是一個跨平臺、可移植并且簡單易用的容器解決方案,通過作業系統內核技術(namespaces、cgroups等)為容器提供資源隔離與安全保障,

上圖是一個image的簡單使用,我們可以通過一個dockerfile來build自己的image,可以把image上傳(push)到自己的私有鏡像倉庫,也可以從私有倉庫pull到本地進行使用,可以單獨使用命令列,直接run container,可以對container進行stop、start、restart操作,也可以對image進行save保存操作以及加載load操作,大家具體可以根據自己的使用,選擇不同的操作即可,

Docker資源隔離技術
Docker選擇容器作為核心和基礎,以容器為資源分割和調度的基本單位,封裝整個軟體運行時環境,為開發者和系統管理員設計,用于構建、發布和運行分布式應用的平臺,Docker是一個跨平臺、可移植并且簡單易用的容器解決方案, 通過作業系統內核技術(namespaces、cgroups等)為容器提供資源隔離與安全保障,

Docker監控
cAdvisor(Container Advisor)是Google開發的用于分析運行中容器的資源占用和性能指標的開源工具,cAdvisor是一個運行時的守護行程,負責收集、聚合、處理和輸出運行中容器的資訊,對于每個容器,cAdvisor都有資源隔離引數、資源使用歷史情況以及完整的歷史資源使用和網路統計資訊的柱狀圖,cAdvisor不但可以為用戶提供監控服務,還可以結合其他應用為用戶提供良好的服務移植和定制,包括結合InfluxDB對資料進行存盤,以及結合Grafana提供web控制臺,自定義查詢指標,并進行展示:

當下配合Kubernetes集群比較成熟的監控方案是: Prometheus +Grafana

五、Kubernetes集群里容器之間的通訊方式

Kubernetes集群里面容器是存在于pod里面的,所以容器之間通訊,一般分為三種型別:
->  pod內部容器之間
->  pod與pod容器之間
->  pod訪問service服務

1)pod內部容器之間
這種情況下容器通訊比較簡單,因為k8s pod內部容器是共享網路空間的,所以容器直接可以使用localhost訪問其他容器,k8s在啟動容器的時候會先啟動一個pause容器,這個容器就是實作這個功能的,

2)pod與pod容器之間
這種型別又可以分為兩種情況:
->  兩個pod在同一臺主機上面
->  兩個pod分布在不同主機之上
第一種情況,就比較簡單了,就是docker默認的docker網橋互連容器,
第二種情況需要更為復雜的網路模型了,k8s官方推薦的是使用flannel組建一個大二層扁平網路,pod的ip分配由flannel統一分配,通訊程序也是走flannel的網橋,比如:

1 2 # docker --daemon --bip=172.17.18.1/24 注意,這其中的"--bip=172.17.18.1/24"這個引數,它限制了所在節點容器獲得的IP范圍,

每個node上面都會創建一個flannel0虛擬網卡,用于跨node之間通訊,所以容器直接可以直接使用pod id進行通訊,跨節點通訊時,發送端資料會從docker0路由到flannel0虛擬網卡,接收端資料會從flannel0路由到docker0,這是因為flannel會添加一個路由,

1 2 3 4 5 6 7 8 發送端: # route -n 172.17.0.0    0.0.0.0    255.255.0.0      U  0  0  0   flannel0 172.17.13.0   0.0.0.0    255.255.255.0    U  0  0  0   docker0   接收端: 172.18.0.0    0.0.0.0    255.255.0.0      U  0  0  0   flannel0 172.17.12.0   0.0.0.0    255.255.255.0    U  0  0  0   docker0

例如現在有一個資料包要從IP為172.17.13.2的容器發到IP為172.17.12.2的容器,根據資料發送節點的路由表,它只與172.17.0.0/16匹配這條記錄匹配,因此資料從docker0出來以后就被投遞到了flannel0,同理在目標節點,由于投遞的地址是一個容器,因此目的地址一定會落在docker0對于的172.17.12.0/24這個記錄上,自然的被投遞到了docker0網卡,

flannel的原理: 是將網路包封裝在udp里面,所以發送端和接收端需要裝包和解包,對性能有一定的影響,除了flannel,k8s也支持其他的網路模型,比較有名的還有calico,

3)pod 訪問service服務
這里涉及到k8s里面一個重要的概念service,它是一個服務的抽象,通過label(k8s會根據service和pod直接的關系創建endpoint,可以通過“kubectl get ep”查看)關聯到后端的pod容器,Service分配的ip叫cluster ip是一個虛擬ip(相對固定,除非洗掉service),這個ip只能在k8s集群內部使用,如果service需要對外提供,只能使用Nodeport方式映射到主機上,使用主機的ip和埠對外提供服務,(另外還可以使用LoadBalance方式,但這種方式是在gce這樣的云環境里面使用的 ),

節點上面有個kube-proxy行程,這個行程從master apiserver獲取資訊,感知service和endpoint的創建,然后做下面兩個事情:
-> 為每個service 在集群中每個節點上面創建一個隨機埠,任何該埠上面的連接會代理到相應的pod
-> 集群中每個節點安裝iptables規則,用于clusterip + port路由到上一步定義的隨機埠上面,所以集群中每個node上面都有service的轉發規則:

1 2 3 4 KUBE-PORTALS-CONTAINER 從容器中通過service cluster ip和埠訪問service的請求 KUBE-PORTALS-HOST 從主機中通過service cluster ip和埠訪問service的請求 KUBE-NODEPORT-CONTAINER 從容器中通過service nodeport埠訪問service的請求 KUBE-NODEPORT-HOST 從主機中通過service nodeport埠訪問service的請求,

比如下面是一個測驗環境內容:

1 2 3 4 -A KUBE-NODEPORT-CONTAINER -p tcp -m comment --comment "smart/ccdb:port1521"  -m tcp --dport 50171 -j REDIRECT --to-ports 52244 -A KUBE-NODEPORT-HOST -p tcp -m comment --comment "smart/ccdb:port1521" -m tcp --dport 50171 -j DNAT --to-destination 10.45.25.227:52244 -A KUBE-PORTALS-CONTAINER -d 10.254.120.169/32 -p tcp -m comment --comment "smart/ccdb:port1521" -m tcp --dport 1521 -j REDIRECT --to-ports 52244 -A KUBE-PORTALS-HOST -d 10.254.120.169/32 -p tcp -m comment --comment "smart/ccdb:port1521" -m tcp --dport 1521 -j DNAT --to-destination 10.45.25.227:5224452244

這些就是kube-proxy針對service “"smart/ccdb:port1521"” 在節點上面監聽的埠,

六、Kubernetes日常維護命令

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 一. 查看集群資訊 ============================================================================================================= [root@k8s-master01 ~]# kubectl cluster-info [root@k8s-master01 ~]# kubectl cluster-info dump      二. 查看各組件狀態 ============================================================================================================= [root@k8s-master01 ~]# kubectl -s http://localhost:8080 get componentstatuses NAME                 STATUS    MESSAGE             ERROR controller-manager   Healthy   ok               scheduler            Healthy   ok               etcd-0               Healthy   {"health":"true"}      或者 [root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get componentstatuses NAME                 STATUS    MESSAGE             ERROR scheduler            Healthy   ok               controller-manager   Healthy   ok               etcd-0               Healthy   {"health":"true"}      三. GET資訊 ============================================================================================================= 1) 查看節點 (k8s-master01 對應的是 172.16.60.220的主機名) [root@k8s-master01 ~]# kubectl get node                                #將命令中的node變為nodes也是可以的 NAME         STATUS    AGE k8s-node01   Ready     1d k8s-node02   Ready     1d      [root@k8s-master01 ~]# kubectl -s http://k8s-master01:8080 get node    #將命令中的node變為nodes也是可以的 NAME         STATUS    AGE k8s-node01   Ready     1d k8s-node02   Ready     1d      2) 查看pods清單(查看pod ip地址,下面命令加上"-o wide" [root@k8s-master01 ~]# kubectl get pod                           #將pod變為pods也可以,如果有namespace,需要跟上"-n namespace名字" 或 "--all-namespaces"             NAME                      READY     STATUS    RESTARTS   AGE nginx-controller-d97wj    1/1       Running   0          1h nginx-controller-lf11n    1/1       Running   0          1h tomcat-controller-35kzb   1/1       Running   0          18m tomcat-controller-lsph4   1/1       Running   0          18m      [root@k8s-master01 ~]# kubectl -s http://k8s-master01:8080 get pod          #將命令中的pod變為pods也是可以的 NAME                      READY     STATUS    RESTARTS   AGE nginx-controller-d97wj    1/1       Running   0          1h nginx-controller-lf11n    1/1       Running   0          1h tomcat-controller-35kzb   1/1       Running   0          18m tomcat-controller-lsph4   1/1       Running   0          18m      3) 查看service清單 [root@k8s-master01 ~]# kubectl get service                                             #將命令中的service變為services也是可以的 NAME                       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE kubernetes                 172.16.0.1       <none>        443/TCP          1d nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         1h nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   59m tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         14m tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   11m      [root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get service               #將命令中的service變為services也是可以的 NAME                       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE kubernetes                 172.16.0.1       <none>        443/TCP          1d nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         1h nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   1h tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         17m tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   14m      或者  (后面的sed表示 列印奇數行) [root@k8s-master01 ~]# kubectl get services -o json|grep '"name":'|sed -n '1~2p'                 "name""kubernetes",                 "name""nginx-service-clusterip",                 "name""nginx-service-nodeport",                 "name""tomcat-service-clusterip",                 "name""tomcat-service-nodeport",      4) 查看replicationControllers清單 (同理可以將命令中的replicationControllers變為replicationController也是可以的) [root@k8s-master01 ~]# kubectl get replicationControllers NAME                DESIRED   CURRENT   READY     AGE nginx-controller    2         2         2         2h tomcat-controller   2         2         2         1h      [root@k8s-master01 ~]# kubectl -s http://172.16.60.220:8080 get replicationControllers NAME                DESIRED   CURRENT   READY     AGE nginx-controller    2         2         2         2h tomcat-controller   2         2         2         1h      5) 查看rc和namespace [root@k8s-master01 ~]# kubectl get rc,namespace NAME                   DESIRED   CURRENT   READY     AGE rc/nginx-controller    2         2         2         2h rc/tomcat-controller   2         2         2         1h      NAME             STATUS    AGE ns/default       Active    1d ns/kube-system   Active    1d      6) 查看pod和svc(和service一樣) [root@k8s-master01 ~]# kubectl get pods,svc NAME                         READY     STATUS    RESTARTS   AGE po/nginx-controller-d97wj    1/1       Running   0          2h po/nginx-controller-lf11n    1/1       Running   0          2h po/tomcat-controller-35kzb   1/1       Running   0          1h po/tomcat-controller-lsph4   1/1       Running   0          1h      NAME                           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE svc/kubernetes                 172.16.0.1       <none>        443/TCP          1d svc/nginx-service-clusterip    172.16.77.193    <none>        8001/TCP         2h svc/nginx-service-nodeport     172.16.234.94    <nodes>       8000:32172/TCP   2h svc/tomcat-service-clusterip   172.16.144.116   <none>        8801/TCP         1h svc/tomcat-service-nodeport    172.16.183.234   <nodes>       8880:31960/TCP   1h      7) 以jison格式輸出pod的詳細資訊. [root@k8s-master01 ~]# kubectl get pods NAME                      READY     STATUS    RESTARTS   AGE nginx-controller-d97wj    1/1       Running   0          2h nginx-controller-lf11n    1/1       Running   0          2h tomcat-controller-35kzb   1/1       Running   0          1h tomcat-controller-lsph4   1/1       Running   0          1h      注意下面命令中的pods的名稱可以通過上面命令查看 [root@k8s-master01 ~]# kubectl get po nginx-controller-d97wj -o json {     "apiVersion""v1",     "kind""Pod",     "metadata": {         "annotations": { ................... ...................         "hostIP""172.16.60.222",         "phase""Running",         "podIP""192.168.100.2",         "startTime""2019-03-15T14:40:18Z"     } }      還可以輸出其它格式和方法(kubectl get -h查看幫助) [root@k8s-master01 ~]# kubectl get -h      8) 查看指定pod跑在哪個node上 [root@k8s-master01 ~]# kubectl get po nginx-controller-d97wj -o wide  NAME                     READY     STATUS    RESTARTS   AGE       IP              NODE nginx-controller-d97wj   1/1       Running   0          2h        192.168.100.2   k8s-node02      9) 獲取指定json或ymal格式的KEY資料,custom-columns=XXXXX(自定義列名):.status.hostIP(以“點開始”,然后寫路徑就可以) 注意: 下面命令中的nginx-controller-d97wj是pod單元名稱 (kubectl get pods 可以查看pods) [root@k8s-master01 ~]# kubectl get po nginx-controller-d97wj -o custom-columns=HOST-IP:.status.hostIP,POD-IP:.status.podIP  HOST-IP         POD-IP 172.16.60.222   192.168.100.2      10) describe方法 describe類似于get,同樣用于獲取resource的相關資訊,不同的是,get獲得的是更詳細的resource個性的詳細資訊,describe獲得的是resource集群相關的資訊, describe命令同get類似,但是describe不支持-o選項,對于同一型別resource,describe輸出的資訊格式,內容域相同,       需要注意:  如果發現是查詢某個resource的資訊,使用get命令能夠獲取更加詳盡的資訊,但是如果想要查詢某個resource的狀態,如某個pod并不是在running狀態, 這時需要獲取更詳盡的狀態資訊時,就應該使用describe命令,      [root@k8s-master01 ~]# kubectl describe po nginx-controller-d97wj Name:           nginx-controller-d97wj Namespace:      default Node:           k8s-node02/172.16.60.222 Start Time:     Fri, 15 Mar 2019 22:40:18 +0800 Labels:         name=nginx Status:         Running IP:             192.168.100.2 Controllers:    ReplicationController/nginx-controller Containers:   nginx:     Container ID:               docker://8ae4502b4e62120322de98aa532e653d3d2e058ffbb0b842e0f265621bebbe61     Image:                      172.16.60.220:5000/nginx     Image ID:                   docker-pullable://172.16.60.220:5000/nginx@sha256:7734a210432278817f8097acf2f72d20e2ccc7402a0509810c44b3a8bfe0094a     Port:                       80/TCP     State:                      Running       Started:                  Fri, 15 Mar 2019 22:40:19 +0800     Ready:                      True     Restart Count:              0     Volume Mounts:              <none>     Environment Variables:      <none> Conditions:   Type          Status   Initialized   True   Ready         True   PodScheduled  True No volumes. QoS Class:      BestEffort Tolerations:    <none> No events.      11) create創建 kubectl命令用于根據檔案或輸入創建集群resource,如果已經定義了相應resource的yaml或son檔案,直接kubectl create -f filename即可創建檔案內定義的 resource,也可以直接只用子命令[namespace/secret/configmap/serviceaccount]等直接創建相應的resource,從追蹤和維護的角度出發,建議使用json或 yaml的方式定義資源,       命令格式: # kubectl create -f 檔案名       12) replace更新替換資源 replace命令用于對已有資源進行更新、替換,如前面create中創建的nginx,當我們需要更新resource的一些屬性的時候,如果修改副本數量,增加、修改label, 更改image版本,修改埠等,都可以直接修改原yaml檔案,然后執行replace命令,       需要注意: 名字不能被更更新,另外,如果是更新label,原有標簽的pod將會與更新label后的rc斷開聯系,有新label的rc將會創建指定副本數的新的pod,但是默認 并不會洗掉原來的pod,所以此時如果使用get po將會發現pod數翻倍,進一步check會發現原來的pod已經不會被新rc控制,此處只介紹命令不詳談此問題,好奇者可自行實驗,       命令格式: # kubectl replace -f nginx-rc.yaml       13) patch 如果一個容器已經在運行,這時需要對一些容器屬性進行修改,又不想洗掉容器,或不方便通過replace的方式進行更新,kubernetes還提供了一種在容器運行時,直接 對容器進行修改的方式,就是patch命令, 如創建pod的label是app=nginx-2,如果在運行程序中,需要把其label改為app=nginx-3, 這個patch命令如下: [root@k8s-master01 ~]# kubectl patch pod nginx-controller-d97wj -p '{"metadata":{"labels":{"app":"nginx-3"}}}' "nginx-controller-d97wj" patched      14) edit edit提供了另一種更新resource源的操作,通過edit能夠靈活的在一個common的resource基礎上,發展出更過的significant resource, 例如,使用edit直接更新前面創建的pod的命令為: # kubectl edit po nginx-controller-d97wj       上面命令的效果等效于: # kubectl get po nginx-controller-d97wj -o yaml >> /tmp/nginx-tmp.yaml # vim /tmp/nginx-tmp.yaml             // 這此檔案里做一些修改 # kubectl replace -f /tmp/nginx-tmp.yaml       15) Delete 根據resource名或label洗掉resource, # kubectl delete -f nginx-rc.yaml # kubectl delete po nginx-controller-d97wj # kubectl delete po nginx-controller-lf11n       16) apply apply命令提供了比patch,edit等更嚴格的更新resource的方式,通過apply,用戶可以將resource的configuration使用source control的方式維護在版本庫中, 每次有更新時,將組態檔push到server,然后使用kubectl apply將更新應用到resource,kubernetes會在參考更新前將當前組態檔中的配置同已經應用的配置 做比較,并只更新更改的部分,而不會主動更改任何用戶未指定的部分,       apply命令的使用方式同replace相同,不同的是,apply不會洗掉原有resource,然后創建新的,apply直接在原有resource的基礎上進行更新,同時kubectl apply 還會resource中添加一條注釋,標記當前的apply,類似于git操作,       17) logs logs命令用于顯示pod運行中,容器內程式輸出到標準輸出的內容,跟docker的logs命令類似,如果要獲得tail -f 的方式,也可以使用-f選項, # kubectl logs nginx-controller-d97wj       18) rolling-update rolling-update是一個非常重要的命令,對于已經部署并且正在運行的業務,rolling-update提供了不中斷業務的更新方式,rolling-update每次起一個新的pod, 等新pod完全起來后洗掉一個舊的pod,然后再起一個新的pod替換舊的pod,直到替換掉所有的pod,       rolling-update需要確保新的版本有不同的name,Version和label,否則會報錯 , # kubectl rolling-update nginx-controller -f nginx-rc.yaml       如果在升級程序中,發現有問題還可以中途停止update,并回滾到前面版本 # kubectl rolling-update nginx-controller --rollback       rolling-update還有很多其他選項提供豐富的功能,如--update-period指定間隔周期,使用時可以使用-h查看help資訊.       19) scale  (注意下面的nginx-controller 是在nginx-rc.yaml檔案中定義的name名稱) scale用于程式在負載加重或縮小時副本進行擴容或縮小,如前面創建的nginx有兩個副本,可以輕松的使用scale命令對副本數進行擴展或縮小, 擴展副本數到4: # kubectl scale rc nginx-controller --replicas=4       重新縮減副本數到2: # kubectl scale rc nginx-controller --replicas=2       20) autoscale scale雖然能夠很方便的對副本數進行擴展或縮小,但是仍然需要人工介入,不能實時自動的根據系統負載對副本數進行擴、縮,autoscale命令提供了自動根據pod負載 對其副本進行擴縮的功能,       autoscale命令會給一個rc指定一個副本數的范圍,在實際運行中根據pod中運行的程式的負載自動在指定的范圍內對pod進行擴容或縮容,如前面創建的nginx,可以用 如下命令指定副本范圍在1~4 # kubectl autoscale rc nginx-controller --min=1 --max=4       21) attach attach命令類似于docker的attach命令,可以直接查看容器中以daemon形式運行的行程的輸出,效果類似于logs -f,退出查看使用ctrl-c,如果一個pod中有多個容器, 要查看具體的某個容器的的輸出,需要在pod名后使用-c containers name指定運行的容器,如下示例的命令為查看kube-system namespace中的kube-dns-v9-rcfuk pod 中的skydns容器的輸出, # kubectl attach kube-dns-v9-rcfuk -c skydns --namespace=kube-system       22) exec exec命令同樣類似于docker的exec命令,為在一個已經運行的容器中執行一條shell命令,如果一個pod容器中,有多個容器,需要使用-c選項指定容器,       23) run 類似于docker的run命令,直接運行一個image,       24) cordon, drain, uncordon 這三個命令是正式release的1.2新加入的命令,三個命令一起介紹,是因為三個命令配合使用可以實作節點的維護,在1.2之前,因為沒有相應的命令支持,如果要維護一個 節點,只能stop該節點上的kubelet將該節點退出集群,是集群不在將新的pod調度到該節點上,如果該節點上本生就沒有pod在運行,則不會對業務有任何影響,如果該節 點上有pod正在運行,kubelet停止后,master會發現該節點不可達,而將該節點標記為notReady狀態,不會將新的節點調度到該節點上,同時,會在其他節點上創建新的 pod替換該節點上的pod,這種方式雖然能夠保證集群的健壯性,但是任然有些暴力,如果業務只有一個副本,而且該副本正好運行在被維護節點上的話,可能仍然會造成業 務的短暫中斷,       1.2中新加入的這3個命令可以保證維護節點時,平滑的將被維護節點上的業務遷移到其他節點上,保證業務不受影響,如下圖所示是一個整個的節點維護的流程(為了方便 demo增加了一些查看節點資訊的操作): 1- 首先查看當前集群所有節點狀態,可以看到共四個節點都處于ready狀態; 2- 查看當前nginx兩個副本分別運行在d-node1和k-node2兩個節點上; 3- 使用cordon命令將d-node1標記為不可調度; 4- 再使用kubectl get nodes查看節點狀態,發現d-node1雖然還處于Ready狀態,但是同時還被禁能了調度,這意味著新的pod將不會被調度到d-node1上, 5- 再查看nginx狀態,沒有任何變化,兩個副本仍運行在d-node1和k-node2上; 6- 執行drain命令,將運行在d-node1上運行的pod平滑的趕到其他節點上; 7- 再查看nginx的狀態發現,d-node1上的副本已經被遷移到k-node1上;這時候就可以對d-node1進行一些節點維護的操作,如升級內核,升級Docker等; 8- 節點維護完后,使用uncordon命令解鎖d-node1,使其重新變得可調度;8)檢查節點狀態,發現d-node1重新變回Ready狀態       # kubectl get nodes # kubectl get po -o wide # kubectl cordon d-node1 # kubectl get nodes # kubectl get po -o wide # kubectl drain d-node1 # kubectl get po -o wide # kubectl uncordon # kubectl uncordon d-node1 # kubectl get nodes       25) 查看某個pod重啟次數(這個是參考) # kubectl get pod nginx-controller-d97wj --template="{{range .status.containerStatuses}}{{.name}}:{{.restartCount}}{{end}}"       26) 查看pod生命周期 [root@k8s-master01 ~]# kubectl get pod nginx-controller-d97wj --template="{{.status.phase}}" Running     四、日常維護命令 ============================================================================================================= kubectl get pods kubectl get rc kubectl get service kubectl get componentstatuses kubectl get endpoints kubectl cluster-info kubectl create -f redis-master-controller.yaml kubectl delete -f redis-master-controller.yaml kubectl delete pod nginx-772ai kubectl logs -f pods/heapster-xxxxx -n kube-system                     #查看日志 kubectl scale rc redis-slave --replicas=3                              #修改RC的副本數量,來實作Pod的動態縮放 etcdctl cluster-health                                                 #檢查網路集群健康狀態 etcdctl --endpoints=http://172.16.60.220:2379 cluster-health           #帶有安全認證檢查網路集群健康狀態 etcdctl member list etcdctl set /k8s/network/config '{ "Network": "10.1.0.0/16" }' etcdctl get /k8s/network/config        五、基礎進階 ============================================================================================================= kubectl get services kubernetes-dashboard -n kube-system           #查看所有service kubectl get deployment kubernetes-dashboard -n kube-system         #查看所有發布 kubectl get pods --all-namespaces                                  #查看所有pod kubectl get pods -o wide --all-namespaces                          #查看所有pod的IP及節點 kubectl get pods -n kube-system | grep dashboard kubectl describe service/kubernetes-dashboard --namespace="kube-system" kubectl describe pods/kubernetes-dashboard-349859023-g6q8c --namespace="kube-system"       #指定型別查看 kubectl describe pod nginx-772ai                                   #查看pod詳細資訊 kubectl scale rc nginx --replicas=5                                #動態伸縮 kubectl scale deployment redis-slave --replicas=5                  #動態伸縮 kubectl scale --replicas=2 -f redis-slave-deployment.yaml          #動態伸縮 kubectl exec -it tomcat-controller-35kzb /bin/bash                 #進入容器 kubectl label nodes k8s-node01 zone=north                #增加節點lable值 spec.nodeSelector: zone: north, 指定pod在哪個節點 kubectl get nodes -lzone                                 #獲取zone的節點 kubectl label pod tomcat-controller-35kzb role=master    #增加lable值 [key]=[value] kubectl label pod tomcat-controller-35kzb role-                       #洗掉lable值 kubectl label pod tomcat-controller-35kzb role=backend --overwrite    #修改lable值 kubectl rolling-update redis-master -f redis-master-controller-v2.yaml      #組態檔滾動升級 kubectl rolling-update redis-master --image=redis-master:2.0                #命令升級 kubectl rolling-update redis-master --image=redis-master:1.0 --rollback     #pod版本回滾    六、yaml使用及命令 ============================================================================================================= kubectl create -f nginx-deployment.yaml   #創建deployment資源 kubectl get deploy      #查看deployment kubectl get rs          #查看ReplicaSet kubectl get pods --show-labels   #查看pods所有標簽,可以添加"-all-namespaces" 或者 "-n kube-system"表示查看所有命名空間或某一命名空間里pods的標簽 kubectl get pods -l app=nginx    #根據標簽查看pods    kubectl set image deployment/nginx-deployment nginx=nginx:1.11     #滾動更新鏡像 或者 kubectl edit deployment/nginx-deployment 或者 kubectl apply -f nginx-deployment.yaml                             #也表示對yaml修改后進行更新操作,更新到kubernetes集群配置中    kubectl rollout status deployment/nginx-deployment                 #實時觀察發布狀態:    kubectl rollout history deployment/nginx-deployment                #查看deployment歷史修訂版本 kubectl rollout history deployment/nginx-deployment --revision=3    kubectl rollout undo deployment/nginx-deployment                   #回滾到以前版本 kubectl rollout undo deployment/nginx-deployment --to-revision=3    kubectl scale deployment nginx-deployment --replicas=10            #擴容deployment的Pod副本數量    kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80     #設定啟動擴容/縮容    七、命名空間 ============================================================================================================= kubectl get namespace                            #獲取k8s的命名空間 kubectl get pod --namespace =[命令空間名稱]        #獲取對應命名空間內的pod,"--namespace"可以寫成"-c" kubectl --namespace [命令空間名稱] logs [pod名稱] -c 容器名稱    #獲取對應namespace中對應pod的日志,如果不加"-c 容器名稱",則默認查看的是該pod下第一個容器的日志    pod維護示例: 查看某個命令空間下的pod # kubectl get pods -n namespace      在沒有pod 的yaml檔案時,強制重啟某個pod # kubectl get pod podname -n namespace -o yaml | kubectl replace --force -f -     查看某個pod重啟次數(這個是參考) # kubectl get pod podname -n namespace --template="{{range .status.containerStatuses}}{{.name}}:{{.restartCount}}{{end}}"     查看pod生命周期 # kubectl get pod podname --template="{{.status.phase}}"     查看kube-space命令空間下的pod [root@m7-autocv-gpu01 ~]# kubectl get pods -n kube-system -o wide|grep -E 'elasticsearch|fluentd|kibana' elasticsearch-logging-0                  1/1     Running   0          5h9m    172.30.104.6   m7-autocv-gpu03   <none> elasticsearch-logging-1                  1/1     Running   0          4h59m   172.30.232.8   m7-autocv-gpu02   <none> fluentd-es-v2.2.0-mkkcf                  1/1     Running   0          5h9m    172.30.104.7   m7-autocv-gpu03   <none> kibana-logging-f6fc77549-nlxfg           1/1     Running   0          42s     172.30.96.7    m7-autocv-gpu01   <none>     [root@m7-autocv-gpu01 ~]# kubectl get pod kibana-logging-f6fc77549-nlxfg -n kube-system -o yaml | kubectl replace --force -f - pod "kibana-logging-f6fc77549-d47nc" deleted pod/kibana-logging-f6fc77549-d47nc replaced     [root@m7-autocv-gpu01 ~]#  kubectl get pod kibana-logging-f6fc77549-nlxfg -n kube-system --template="{{range .status.containerStatuses}}{{.name}}:{{.restartCount}}{{end}}" kibana-logging:0     [root@m7-autocv-gpu01 ~]# kubectl get pod kibana-logging-f6fc77549-nlxfg -n kube-system --template="{{.status.phase}}" Running   八、進入pod內的容器 ============================================================================================================= kubernetes中登錄pod中的容器,如下,kevintest-f857f78ff-dlp24是pod名稱,webha是命名空間 # kubectl -n webha exec -it kevintest-f857f78ff-dlp24 -- bash       #登錄后終端資訊中顯示主機名 # kubectl -n webha exec -it kevintest-f857f78ff-dlp24 sh            #登錄后終端資訊中不顯示主機名   如果pod中有多個容器,則默認登錄到第一個容器中, 也可以通過-c引數制定登錄到哪個容器中, 比如進入kevintest-f857f78ff-dlp24的nginx_bo容器 # kubectl -n webha exec -it kevintest-f857f78ff-dlp24 -c nginx_bo -- bash

七、Kubernetes集群部署失敗的一般原因

1. 錯誤的容器鏡像/非法的倉庫權限
其中兩個最普遍的問題是:a) 指定了錯誤的容器鏡像;b) 使用私有鏡像卻不提供倉庫認證資訊,這在首次使用 Kubernetes 或者系結 CI/CD 環境時尤其棘手,看個例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 首先我們創建一個名為 fail 的 deployment,它指向一個不存在的 Docker 鏡像: $ kubectl run fail --image=rosskukulinski/dne:v1.0.0   然后我們查看 Pods,可以看到有一個狀態為 ErrImagePull 或者 ImagePullBackOff 的 Pod: $ kubectl get pods NAME                    READY     STATUS             RESTARTS   AGE fail-1036623984-hxoas   0/1       ImagePullBackOff   0          2m   想查看更多資訊,可以 describe 這個失敗的 Pod: $ kubectl describe pod fail-1036623984-hxoas   查看 describe 命令的輸出中 Events 這部分,我們可以看到如下內容: Events: FirstSeen    LastSeen    Count   From                        SubObjectPath       Type        Reason      Message ---------    --------    -----   ----                        -------------       --------    ------      ------- 5m        5m      1   {default-scheduler }                            Normal      Scheduled   Successfully assigned fail-1036623984-hxoas to gke-nrhk-1-default-pool-a101b974-wfp7 5m        2m      5   {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} spec.containers{fail}   Normal      Pulling     pulling image "rosskukulinski/dne:v1.0.0" 5m        2m      5   {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} spec.containers{fail}   Warning     Failed      Failed to pull image "rosskukulinski/dne:v1.0.0": Error: image rosskukulinski/dne not found 5m        2m      5   {kubelet gke-nrhk-1-default-pool-a101b974-wfp7}             Warning     FailedSync  Error syncing pod, skipping: failed to "StartContainer" for "fail" with ErrImagePull: "Error: image rosskukulinski/dne not found"   5m    11s 19  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7} spec.containers{fail}   Normal  BackOff     Back-off pulling image "rosskukulinski/dne:v1.0.0" 5m    11s 19  {kubelet gke-nrhk-1-default-pool-a101b974-wfp7}             Warning FailedSync  Error syncing pod, skipping: failed to "StartContainer" for "fail" with ImagePullBackOff: "Back-off pulling image \"rosskukulinski/dne:v1.0.0\""   顯示錯誤的那句話:Failed to pull image "rosskukulinski/dne:v1.0.0": Error: image rosskukulinski/dne not found 告訴我們 Kubernetes無法找到鏡像 rosskukulinski/dne:v1.0.0,   因此問題變成:為什么 Kubernetes 拉不下來鏡像?   除了網路連接問題外,還有三個主要元兇: - 鏡像 tag 不正確 - 鏡像不存在(或者是在另一個倉庫) - Kubernetes 沒有權限去拉那個鏡像   如果你沒有注意到你的鏡像 tag 的拼寫錯誤,那么最好就用你本地機器測驗一下,   通常我會在本地開發機上,用 docker pull 命令,帶上 完全相同的鏡像 tag,來跑一下,比如上面的情況,我會運行命令 docker pull rosskukulinski/dne:v1.0.0, 如果這成功了,那么很可能 Kubernetes 沒有權限去拉取這個鏡像,參考鏡像拉取 Secrets 來解決這個問題, 如果失敗了,那么我會繼續用不顯式帶 tag 的鏡像測驗 - docker pull rosskukulinski/dne - 這會嘗試拉取 tag 為 latest 的鏡像,如果這樣成功,表明原來指定的 tag 不存在,這可能是人為原因,拼寫錯誤,或者 CI/CD 的配置錯誤,   如果 docker pull rosskukulinski/dne(不指定 tag)也失敗了,那么我們碰到了一個更大的問題:我們所有的鏡像倉庫中都沒有這個鏡像,默認情況下,Kubernetes 使用 Dockerhub 鏡像倉庫,如果你在使用 Quay.io,AWS ECR,或者 Google Container Registry,你要在鏡像地址中指定這個倉庫的 URL,比如使用 Quay,鏡像地址就變成 quay.io/rosskukulinski/dne:v1.0.0,   如果你在使用 Dockerhub,那你應該再次確認你發布鏡像到 Dockerhub 的系統,確保名字和 tag 匹配你的 deployment 正在使用的鏡像,   注意:觀察 Pod 狀態的時候,鏡像缺失和倉庫權限不正確是沒法區分的,其它情況下,Kubernetes 將報告一個 ErrImagePull 狀態,

2. 應用啟動之后又掛掉
無論你是在 Kubernetes 上啟動新應用,還是遷移應用到已存在的平臺,應用在啟動之后就掛掉都是一個比較常見的現象,看個例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 我們創建一個 deployment,它的應用會在1秒后掛掉: $ kubectl run crasher --image=rosskukulinski/crashing-app   我們看一下 Pods 的狀態: $ kubectl get pods NAME                       READY     STATUS             RESTARTS   AGE crasher-2443551393-vuehs   0/1       CrashLoopBackOff   2          54s   CrashLoopBackOff 告訴我們,Kubernetes 正在盡力啟動這個 Pod,但是一個或多個容器已經掛了,或者正被洗掉,   讓我們 describe 這個 Pod 去獲取更多資訊: $ kubectl describe pod crasher-2443551393-vuehs Name:        crasher-2443551393-vuehs Namespace:    fail Node:        gke-nrhk-1-default-pool-a101b974-wfp7/10.142.0.2 Start Time:    Fri, 10 Feb 2017 14:20:29 -0500 Labels:        pod-template-hash=2443551393     run=crasher Status:        Running IP:        10.0.0.74 Controllers:    ReplicaSet/crasher-2443551393 Containers: crasher: Container ID:    docker://51c940ab32016e6d6b5ed28075357661fef3282cb3569117b0f815a199d01c60 Image:        rosskukulinski/crashing-app Image ID:        docker://sha256:cf7452191b34d7797a07403d47a1ccf5254741d4bb356577b8a5de40864653a5 Port:        State:        Terminated   Reason:        Error   Exit Code:    1   Started:        Fri, 10 Feb 2017 14:22:24 -0500   Finished:        Fri, 10 Feb 2017 14:22:26 -0500 Last State:        Terminated   Reason:        Error   Exit Code:    1   Started:        Fri, 10 Feb 2017 14:21:39 -0500   Finished:        Fri, 10 Feb 2017 14:21:40 -0500 Ready:        False Restart Count:    4 ...   好可怕,Kubernetes 告訴我們這個 Pod 正被 Terminated,因為容器里的應用掛了,我們還可以看到應用的 Exit Code 是 1,后面我們可能還會看到一個 OOMKilled 錯誤,   我們的應用正在掛掉?為什么?   首先我們查看應用日志,假定你發送應用日志到 stdout(事實上你也應該這么做),你可以使用 kubectl logs 看到應用日志: $ kubectl logs crasher-2443551393-vuehs   不幸的是,這個 Pod 沒有任何日志,這可能是因為我們正在查看一個新起的應用實體,因此我們應該查看前一個容器: $ kubectl logs crasher-2443551393-vuehs --previous   什么!我們的應用仍然不給我們任何東西,這個時候我們應該給應用加點啟動日志了,以幫助我們定位這個問題,我們也可以本地運行一下這個容器,以確定是否缺失環境變數或者掛載卷,

3. 缺失 ConfigMap 或者 Secret
Kubernetes 最佳實踐建議通過 ConfigMaps 或者 Secrets 傳遞應用的運行時配置,這些資料可以包含資料庫認證資訊,API endpoints,或者其它配置資訊,一個常見的錯誤是,創建的 deployment 中參考的 ConfigMaps 或者 Secrets 的屬性不存在,有時候甚至參考的 ConfigMaps 或者 Secrets 本身就不存在,

缺失 ConfigMap
第一個例子,我們將嘗試創建一個 Pod,它加載 ConfigMap 資料作為環境變數:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 # configmap-pod.yaml apiVersion: v1 kind: Pod metadata: name: configmap-pod spec: containers: - name: test-container   image: gcr.io/google_containers/busybox   command: [ "/bin/sh""-c""env" ]   env:     - name: SPECIAL_LEVEL_KEY       valueFrom:         configMapKeyRef:           name: special-config           key: special.how     讓我們創建一個 Pod:kubectl create -f configmap-pod.yaml,在等待幾分鐘之后,我們可以查看我們的 Pod: $ kubectl get pods NAME            READY     STATUS              RESTARTS   AGE configmap-pod   0/1       RunContainerError   0          3s   Pod 狀態是 RunContainerError ,我們可以使用 kubectl describe 了解更多: $ kubectl describe pod configmap-pod [...] Events: FirstSeen    LastSeen    Count   From                        SubObjectPath           Type        Reason      Message ---------    --------    -----   ----                        -------------           --------    ------      ------- 20s        20s     1   {default-scheduler }                                Normal      Scheduled   Successfully assigned configmap-pod to gke-ctm-1-sysdig2-35e99c16-tgfm 19s        2s      3   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Pulling     pulling image "gcr.io/google_containers/busybox" 18s        2s      3   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Pulled      Successfully pulled image "gcr.io/google_containers/busybox" 18s        2s      3   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}                   Warning     FailedSync  Error syncing pod, skipping: failed to "StartContainer" for "test-container" with RunContainerError: "GenerateRunContainerOptions: configmaps \"special-config\" not found"   Events 章節的最后一條告訴我們什么地方錯了,Pod 嘗試訪問名為 special-config 的 ConfigMap,但是在該 namespace 下找不到,一旦我們創建這個 ConfigMap,Pod 應該重啟并能成功拉取運行時資料,   在 Pod 規格說明中訪問 Secrets 作為環境變數會產生相似的錯誤,就像我們在這里看到的 ConfigMap錯誤一樣,

但是假如你通過 Volume 來訪問 Secrets 或者 ConfigMap會發生什么呢?

缺失 Secrets
下面是一個pod規格說明,它參考了名為 myothersecret 的 Secrets,并嘗試把它掛為卷:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # missing-secret.yaml apiVersion: v1 kind: Pod metadata: name: secret-pod spec: containers: - name: test-container   image: gcr.io/google_containers/busybox   command: [ "/bin/sh""-c""env" ]   volumeMounts:     - mountPath: /etc/secret/       name: myothersecret restartPolicy: Never volumes: - name: myothersecret   secret:     secretName: myothersecret   讓我們用 kubectl create -f missing-secret.yaml 來創建一個 Pod,   幾分鐘后,我們 get Pods,可以看到 Pod 仍處于 ContainerCreating 狀態: $ kubectl get pods NAME            READY     STATUS              RESTARTS   AGE secret-pod   0/1       ContainerCreating   0          4h   這就奇怪了,我們 describe 一下,看看到底發生了什么: $ kubectl describe pod secret-pod Name:        secret-pod Namespace:    fail Node:        gke-ctm-1-sysdig2-35e99c16-tgfm/10.128.0.2 Start Time:    Sat, 11 Feb 2017 14:07:13 -0500 Labels:        Status:        Pending IP:        Controllers:      [...]   Events: FirstSeen    LastSeen    Count   From                        SubObjectPath   Type        Reason      Message ---------    --------    -----   ----                        -------------   --------    ------      ------- 18s        18s     1   {default-scheduler }                        Normal      Scheduled   Successfully assigned secret-pod to gke-ctm-1-sysdig2-35e99c16-tgfm 18s        2s      6   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}           Warning     FailedMount MountVolume.SetUp failed for volume "kubernetes.io/secret/337281e7-f065-11e6-bd01-42010af0012c-myothersecret" (spec.Name: "myothersecret") pod "337281e7-f065-11e6-bd01-42010af0012c" (UID: "337281e7-f065-11e6-bd01-42010af0012c") with: secrets "myothersecret" not found   Events 章節再次解釋了問題的原因,它告訴我們 Kubelet 無法從名為 myothersecret 的 Secret 掛卷,為了解決這個問題,我們可以創建 myothersecret ,它包含必要的安全認證資訊,一旦 myothersecret 創建完成,容器也將正確啟動,

4. 活躍度/就緒狀態探測失敗
在 Kubernetes 中處理容器問題時,需要注意的是:你的容器應用是 running 狀態,不代表它在作業!?

Kubernetes 提供了兩個基本特性,稱作活躍度探測就緒狀態探測,本質上來說,活躍度/就緒狀態探測將定期地執行一個操作(例如發送一個 HTTP 請求,打開一個 tcp 連接,或者在你的容器內運行一個命令),以確認你的應用和你預想的一樣在作業,

如果活躍度探測失敗,Kubernetes 將殺掉你的容器并重新創建一個,如果就緒狀態探測失敗,這個 Pod 將不會作為一個服務的后端 endpoint,也就是說不會流量導到這個 Pod,直到它變成 Ready,

如果你試圖部署變更你的活躍度/就緒狀態探測失敗的應用,滾動部署將一直懸掛,因為它將等待你的所有 Pod 都變成 Ready,

這個實際是怎樣的情況?以下是一個 Pod 規格說明,它定義了活躍度/就緒狀態探測方法,都是基于8080埠對 /healthy 路由進行健康檢查:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 apiVersion: v1 kind: Pod metadata: name: liveness-pod spec: containers: - name: test-container   image: rosskukulinski/leaking-app   livenessProbe:     httpGet:       path: /healthz       port: 8080     initialDelaySeconds: 3     periodSeconds: 3   readinessProbe:     httpGet:       path: /healthz       port: 8080     initialDelaySeconds: 3     periodSeconds: 3   讓我們創建這個 Pod:kubectl create -f liveness.yaml,過幾分鐘后查看發生了什么: $ kubectl get pods NAME           READY     STATUS    RESTARTS   AGE liveness-pod   0/1       Running   4          2m   2分鐘以后,我們發現 Pod 仍然沒處于 Ready 狀態,并且它已被重啟了4次,讓我們 describe 一下查看更多資訊: $ kubectl describe pod liveness-pod Name:        liveness-pod Namespace:    fail Node:        gke-ctm-1-sysdig2-35e99c16-tgfm/10.128.0.2 Start Time:    Sat, 11 Feb 2017 14:32:36 -0500 Labels:        Status:        Running IP:        10.108.88.40 Controllers:    Containers: test-container: Container ID:    docker://8fa6f99e6fda6e56221683249bae322ed864d686965dc44acffda6f7cf186c7b Image:        rosskukulinski/leaking-app Image ID:        docker://sha256:7bba8c34dad4ea155420f856cd8de37ba9026048bd81f3a25d222fd1d53da8b7 Port:        State:        Running   Started:        Sat, 11 Feb 2017 14:40:34 -0500 Last State:        Terminated   Reason:        Error   Exit Code:    137   Started:        Sat, 11 Feb 2017 14:37:10 -0500   Finished:        Sat, 11 Feb 2017 14:37:45 -0500 [...] Events: FirstSeen    LastSeen    Count   From                        SubObjectPath           Type        Reason      Message ---------    --------    -----   ----                        -------------           --------    ------      ------- 8m        8m      1   {default-scheduler }                                Normal      Scheduled   Successfully assigned liveness-pod to gke-ctm-1-sysdig2-35e99c16-tgfm 8m        8m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Created     Created container with docker id 0fb5f1a56ea0; Security:[seccomp=unconfined] 8m        8m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Started     Started container with docker id 0fb5f1a56ea0 7m        7m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Created     Created container with docker id 3f2392e9ead9; Security:[seccomp=unconfined] 7m        7m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Normal      Killing     Killing container with docker id 0fb5f1a56ea0: pod "liveness-pod_fail(d75469d8-f090-11e6-bd01-42010af0012c)" container "test-container" is unhealthy, it will be killed and re-created. 8m    16s 10  {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Warning Unhealthy   Liveness probe failed: Get http://10.108.88.40:8080/healthz: dial tcp 10.108.88.40:8080: getsockopt: connection refused 8m    1s  85  {kubelet gke-ctm-1-sysdig2-35e99c16-tgfm}   spec.containers{test-container} Warning Unhealthy   Readiness probe failed: Get http://10.108.88.40:8080/healthz: dial tcp 10.108.88.40:8080: getsockopt: connection refused   Events 章節再次救了我們,我們可以看到活躍度探測和就緒狀態探測都失敗了,關鍵的一句話是 container "test-container" is unhealthy, it will be killed and re-created,這告訴我們 Kubernetes 正在殺這個容器,因為容器的活躍度探測失敗了,   這里有三種可能性: - 你的探測不正確,健康檢查的 URL 是否改變了? - 你的探測太敏感了, 你的應用是否要過一會才能啟動或者回應? - 你的應用永遠不會對探測做出正確回應,你的資料庫是否配置錯了   查看 Pod 日志是一個開始調測的好地方,一旦你解決了這個問題,新的 deployment 應該就能成功了,

5. 超出CPU/記憶體的限制
Kubernetes 賦予集群管理員限制 Pod 和容器的 CPU 或記憶體數量的能力,作為應用開發者,你可能不清楚這個限制,導致 deployment 失敗的時候一臉困惑,我們試圖部署一個未知 CPU/memory 請求限額的 deployment:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 # gateway.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: gateway spec: template: metadata:   labels:     app: gateway spec:   containers:     - name: test-container       image: nginx       resources:         requests:           memory: 5Gi   你會看到我們設了 5Gi 的資源請求,讓我們創建這個 deployment:kubectl create -f gateway.yaml,   現在我們可以看到我們的 Pod: $ kubectl get pods No resources found.     為啥,讓我們用 describe 來觀察一下我們的 deployment: $ kubectl describe deployment/gateway Name:            gateway Namespace:        fail CreationTimestamp:    Sat, 11 Feb 2017 15:03:34 -0500 Labels:            app=gateway Selector:        app=gateway Replicas:        0 updated | 1 total | 0 available | 1 unavailable StrategyType:        RollingUpdate MinReadySeconds:    0 RollingUpdateStrategy:    0 max unavailable, 1 max surge OldReplicaSets:        NewReplicaSet:        gateway-764140025 (0/1 replicas created) Events: FirstSeen    LastSeen    Count   From                SubObjectPath   Type        Reason          Message ---------    --------    -----   ----                -------------   --------    ------          ------- 4m        4m      1   {deployment-controller }            Normal      ScalingReplicaSet   Scaled up replica set gateway-764140025 to 1   基于最后一行,我們的 deployment 創建了一個 ReplicaSet(gateway-764140025) 并把它擴展到 1,這個是用來管理 Pod 生命周期的物體,我們可以 describe 這個 ReplicaSet: $ kubectl describe rs/gateway-764140025 Name:        gateway-764140025 Namespace:    fail Image(s):    nginx Selector:    app=gateway,pod-template-hash=764140025 Labels:        app=gateway     pod-template-hash=764140025 Replicas:    0 current / 1 desired Pods Status:    0 Running / 0 Waiting / 0 Succeeded / 0 Failed No volumes. Events: FirstSeen    LastSeen    Count   From                SubObjectPath   Type        Reason      Message ---------    --------    -----   ----                -------------   --------    ------      ------- 6m        28s     15  {replicaset-controller }            Warning     FailedCreate    Error creating: pods "gateway-764140025-" is forbidden: [maximum memory usage per Pod is 100Mi, but request is 5368709120., maximum memory usage per Container is 100Mi, but request is 5Gi.]

上面可知,集群管理員設定了每個 Pod 的最大記憶體使用量為 100Mi,你可以運行 kubectl describe limitrange 來查看當前租戶的限制,

那么現在就有3個選擇:
- 要求你的集群管理員提升限額;
- 減少 deployment 的請求或者限額設定;
- 直接編輯限額;

6. 資源配額
和資源限額類似,Kubernetes 也允許管理員給每個 namespace 設定資源配額,這些配額可以在 Pods,Deployments,PersistentVolumes,CPU,記憶體等資源上設定軟性或者硬性限制,讓我們看看超出資源配額后會發生什么,以下是我們的 deployment 例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 # test-quota.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: gateway-quota spec: template: spec:   containers:     - name: test-container       image: nginx   我們可用 kubectl create -f test-quota.yaml 創建,然后觀察我們的 Pods: $ kubectl get pods NAME                            READY     STATUS    RESTARTS   AGE gateway-quota-551394438-pix5d   1/1       Running   0          16s   看起來很好,現在讓我們擴展到 3 個副本:kubectl scale deploy/gateway-quota --replicas=3,然后再次觀察 Pods: $ kubectl get pods NAME                            READY     STATUS    RESTARTS   AGE gateway-quota-551394438-pix5d   1/1       Running   0          9m   啊,我們的pod去哪了?讓我們觀察一下 deployment: $ kubectl describe deploy/gateway-quota Name:            gateway-quota Namespace:        fail CreationTimestamp:    Sat, 11 Feb 2017 16:33:16 -0500 Labels:            app=gateway Selector:        app=gateway Replicas:        1 updated | 3 total | 1 available | 2 unavailable StrategyType:        RollingUpdate MinReadySeconds:    0 RollingUpdateStrategy:    1 max unavailable, 1 max surge OldReplicaSets:        NewReplicaSet:        gateway-quota-551394438 (1/3 replicas created) Events: FirstSeen    LastSeen    Count   From                SubObjectPath   Type        Reason          Message ---------    --------    -----   ----                -------------   --------    ------          ------- 9m        9m      1   {deployment-controller }            Normal      ScalingReplicaSet   Scaled up replica set gateway-quota-551394438 to 1 5m        5m      1   {deployment-controller }            Normal      ScalingReplicaSet   Scaled up replica set gateway-quota-551394438 to 3   在最后一行,我們可以看到 ReplicaSet 被告知擴展到 3 ,我們用 describe 來觀察一下這個 ReplicaSet 以了解更多資訊: kubectl describe replicaset gateway-quota-551394438 Name:        gateway-quota-551394438 Namespace:    fail Image(s):    nginx Selector:    app=gateway,pod-template-hash=551394438 Labels:        app=gateway     pod-template-hash=551394438 Replicas:    1 current / 3 desired Pods Status:    1 Running / 0 Waiting / 0 Succeeded / 0 Failed No volumes. Events: FirstSeen    LastSeen    Count   From                SubObjectPath   Type        Reason          Message ---------    --------    -----   ----                -------------   --------    ------          ------- 11m        11m     1   {replicaset-controller }            Normal      SuccessfulCreate    Created pod: gateway-quota-551394438-pix5d 11m        30s     33  {replicaset-controller }            Warning     FailedCreate        Error creating: pods "gateway-quota-551394438-" is forbidden: exceeded quota: compute-resources, requested: pods=1, used: pods=1, limited: pods=1

上面可以看出,我們的 ReplicaSet 無法創建更多的 pods 了,因為配額限制了:exceeded quota: compute-resources, requested: pods=1, used: pods=1, limited: pods=1,

和資源限額類似,我們現在也有3個選項:
- 要求集群管理員提升該 namespace 的配額
- 洗掉或者收縮該 namespace 下其它的 deployment
- 直接編輯配額

7. 集群資源不足
除非你的集群開通了集群自動伸縮功能,否則總有一天你的集群中 CPU 和記憶體資源會耗盡,這不是說 CPU 和記憶體被完全使用了,而是指它們被 Kubernetes 調度器完全使用了,如同我們在第 5 點看到的,集群管理員可以限制開發者能夠申請分配給 pod 或者容器的 CPU 或者記憶體的數量,聰明的管理員也會設定一個默認的 CPU/記憶體 申請數量,在開發者未提供申請額度時使用,

如果你所有的作業都在 default 這個 namespace 下作業,你很可能有個默認值 100m 的容器 CPU申請額度,對此你甚至可能都不清楚,運行 kubectl describe ns default 檢查一下是否如此,我們假定你的 Kubernetes 集群只有一個包含 CPU 的節點,你的 Kubernetes 集群有 1000m 的可調度 CPU,當前忽略其它的系統 pods(kubectl -n kube-system get pods),你的單節點集群能部署 10 個 pod(每個 pod 都只有一個包含 100m 的容器),

10 Pods * (1 Container * 100m) = 1000m == Cluster CPUs

當你擴大到 11 個的時候,會發生什么?下面是一個申請 1CPU(1000m)的 deployment 例子

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 # cpu-scale.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: cpu-scale spec: template: metadata:   labels:     app: cpu-scale spec:   containers:     - name: test-container       image: nginx       resources:         requests:           cpu: 1   我把這個應用部署到有 2 個可用 CPU 的集群,除了我的 cpu-scale 應用,Kubernetes 內部服務也在消耗 CPU 和記憶體,   我們可以用 kubectl create -f cpu-scale.yaml 部署這個應用,并觀察 pods: $ kubectl get pods NAME                        READY     STATUS    RESTARTS   AGE cpu-scale-908056305-xstti   1/1       Running   0          5m   第一個 pod 被調度并運行了,我們看看擴展一個會發生什么: $ kubectl scale deploy/cpu-scale --replicas=2 deployment "cpu-scale" scaled $ kubectl get pods NAME                        READY     STATUS    RESTARTS   AGE cpu-scale-908056305-phb4j   0/1       Pending   0          4m cpu-scale-908056305-xstti   1/1       Running   0          5m   我們的第二個pod一直處于 Pending,被阻塞了,我們可以 describe 這第二個 pod 查看更多的資訊: $ kubectl describe pod cpu-scale-908056305-phb4j Name:        cpu-scale-908056305-phb4j Namespace:    fail Node:        gke-ctm-1-sysdig2-35e99c16-qwds/10.128.0.4 Start Time:    Sun, 12 Feb 2017 08:57:51 -0500 Labels:        app=cpu-scale     pod-template-hash=908056305 Status:        Pending IP:        Controllers:    ReplicaSet/cpu-scale-908056305 [...] Events: FirstSeen    LastSeen    Count   From            SubObjectPath   Type        Reason          Message ---------    --------    -----   ----            -------------   --------    ------          ------- 3m        3m      1   {default-scheduler }            Warning     FailedScheduling    pod (cpu-scale-908056305-phb4j) failed to fit in any node fit failure on node (gke-ctm-1-sysdig2-35e99c16-wx0s): Insufficient cpu fit failure on node (gke-ctm-1-sysdig2-35e99c16-tgfm): Insufficient cpu fit failure on node (gke-ctm-1-sysdig2-35e99c16-qwds): Insufficient cpu

Events 模塊告訴我們 Kubernetes 調度器(default-scheduler)無法調度這個 pod 因為它無法匹配任何節點,它甚至告訴我們每個節點哪個擴展點失敗了(Insufficient cpu),

那么我們如何解決這個問題?如果你太渴望你申請的 CPU/記憶體 的大小,你可以減少申請的大小并重新部署,當然,你也可以請求你的集群管理員擴展這個集群(因為很可能你不是唯一一個碰到這個問題的人),

現在你可能會想:我們的 Kubernetes 節點是在我們的云提供商的自動伸縮群組里,為什么他們沒有生效呢?原因是,你的云提供商沒有深入理解 Kubernetes 調度器是做啥的,利用 Kubernetes 的集群自動伸縮能力允許你的集群根據調度器的需求自動伸縮它自身,如果你在使用 GCE,集群伸縮能力是一個 beta 特性,

8. 持久化卷掛載失敗
另一個常見錯誤是創建了一個參考不存在的持久化卷(PersistentVolumes)的 deployment,不論你是使用 PersistentVolumeClaims(你應該使用這個!),還是直接訪問持久化磁盤,最終結果都是類似的,

下面是我們的測驗 deployment,它想使用一個名為 my-data-disk 的 GCE 持久化卷:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 # volume-test.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: volume-test spec: template: metadata:   labels:     app: volume-test spec:   containers:     - name: test-container       image: nginx       volumeMounts:       - mountPath: /test         name: test-volume   volumes:   - name: test-volume     # This GCE PD must already exist (oops!)     gcePersistentDisk:       pdName: my-data-disk       fsType: ext4   讓我們創建這個 deployment:kubectl create -f volume-test.yaml,過幾分鐘后查看 pod: kubectl get pods NAME                           READY     STATUS              RESTARTS   AGE volume-test-3922807804-33nux   0/1       ContainerCreating   0          3m   3 分鐘的等待容器創建時間是很長了,讓我們用 describe 來查看這個 pod,看看到底發生了什么: $ kubectl describe pod volume-test-3922807804-33nux Name:        volume-test-3922807804-33nux Namespace:    fail Node:        gke-ctm-1-sysdig2-35e99c16-qwds/10.128.0.4 Start Time:    Sun, 12 Feb 2017 09:24:50 -0500 Labels:        app=volume-test     pod-template-hash=3922807804 Status:        Pending IP:        Controllers:    ReplicaSet/volume-test-3922807804 [...] Volumes: test-volume: Type:    GCEPersistentDisk (a Persistent Disk resource in Google Compute Engine) PDName:    my-data-disk FSType:    ext4 Partition:    0 ReadOnly:    false [...] Events: FirstSeen    LastSeen    Count   From                        SubObjectPath   Type        Reason      Message ---------    --------    -----   ----                        -------------   --------    ------      ------- 4m        4m      1   {default-scheduler }                        Normal      Scheduled   Successfully assigned volume-test-3922807804-33nux to gke-ctm-1-sysdig2-35e99c16-qwds 1m        1m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-qwds}           Warning     FailedMount Unable to mount volumes for pod "volume-test-3922807804-33nux_fail(e2180d94-f12e-11e6-bd01-42010af0012c)": timeout expired waiting for volumes to attach/mount for pod "volume-test-3922807804-33nux"/"fail". list of unattached/unmounted volumes=[test-volume] 1m        1m      1   {kubelet gke-ctm-1-sysdig2-35e99c16-qwds}           Warning     FailedSync  Error syncing pod, skipping: timeout expired waiting for volumes to attach/mount for pod "volume-test-3922807804-33nux"/"fail". list of unattached/unmounted volumes=[test-volume] 3m        50s     3   {controller-manager }                       Warning     FailedMount Failed to attach volume "test-volume" on node "gke-ctm-1-sysdig2-35e99c16-qwds" with: GCE persistent disk not found: diskName="my-data-disk" zone="us-central1-a"

Events 模塊留有我們一直在尋找的線索,我們的 pod 被正確調度到了一個節點(Successfully assigned volume-test-3922807804-33nux to gke-ctm-1-sysdig2-35e99c16-qwds),但是那個節點上的 kubelet 無法掛載期望的卷 test-volume,那個卷本應該在持久化磁盤被關聯到這個節點的時候就被創建了,但是,正如我們看到的,controller-manager 失敗了:Failed to attach volume "test-volume" on node "gke-ctm-1-sysdig2-35e99c16-qwds" with: GCE persistent disk not found: diskName="my-data-disk" zone="us-central1-a",

最后一條資訊相當清楚了:為了解決這個問題,我們需要在 GKE 的 us-central1-a 區中創建一個名為 my-data-disk 的持久化卷,一旦這個磁盤創建完成,controller-manager 將掛載這塊磁盤,并啟動容器創建程序,

9. 校驗錯誤
看著整個 build-test-deploy 任務到了 deploy 步驟卻失敗了,原因竟是 Kubernetes 物件不合法,還有什么比這更讓人沮喪的!

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 你可能之前也碰到過這種錯誤: $ kubectl create -f test-application.deploy.yaml error: error validating "test-application.deploy.yaml": error validating data: found invalid field resources for v1.PodSpec; if you choose to ignore these errors, turn validation off with --validate=false   在這個例子中,我嘗試創建以下 deployment: # test-application.deploy.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: test-app spec: template: metadata:   labels:     app: test-app spec:   containers:   - image: nginx     name: nginx   resources:     limits:       cpu: 100m       memory: 200Mi     requests:       cpu: 100m       memory: 100Mi

一眼望去,這個 YAML 檔案是正確的,但錯誤訊息會證明是有用的,錯誤說的是 found invalid field resources for v1.PodSpec,再仔細看一下 v1.PodSpec, 我們可以看到 resource 物件變成了 v1.PodSpec的一個子物件,事實上它應該是 v1.Container 的子物件,在把 resource 物件縮進一層后,這個 deployment 物件就可以正常作業了,

除了查找縮進錯誤,另一個常見的錯誤是寫錯了物件名(比如 peristentVolumeClaim 寫成了 persistentVolumeClaim),這樣的錯誤有時會很費你的時間!

為了能在早期就發現這些錯誤,我推薦在 pre-commit 鉤子或者構建的測驗階段添加一些校驗步驟,例如,你可以:
1. 用 python -c 'import yaml,sys;yaml.safe_load(sys.stdin)' < test-application.deployment.yaml 驗證 YAML 格式
2. 使用標識 --dry-run 來驗證 Kubernetes API 物件,比如這樣:kubectl create -f test-application.deploy.yaml --dry-run --validate=true

重要提醒:校驗 Kubernetes 物件的機制是在服務端的校驗,這意味著 kubectl 必須有一個在作業的 Kubernetes 集群與之通信,不幸的是,當前 kubectl 還沒有客戶端的校驗選項,但是已經有 issue(kubernetes/kubernetes #29410 和 kubernetes/kubernetes #11488)在跟蹤這個缺失的特性了,

10. 容器鏡像沒有更新
可能使用 Kubernetes 的大多數人都碰到過這個問題,它也確實是一個難題,

這個場景就像下面這樣:
1. 使用一個鏡像 tag(比如:rosskulinski/myapplication:v1) 創建一個 deployment
2. 注意到 myapplication 鏡像中存在一個 bug
3. 構建了一個新的鏡像,并推送到了相同的 tag(rosskukulinski/myapplication:v1)
4. 洗掉了所有 myapplication 的 pods,新的實體被 deployment 創建出了
5. 發現 bug 仍然存在
6. 重復 3-5 步直到你抓狂為止

這個問題關系到 Kubernetes 在啟動 pod 內的容器時是如何決策是否做 docker pull 動作的,

在 v1.Container 說明中,有一個選項 ImagePullPolicy:

1 Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.

因為我們把我們的鏡像 tag 標記為 :v1,默認的鏡像拉取策略是 IfNotPresent,Kubelet 在本地已經有一份 rosskukulinski/myapplication:v1 的拷貝了,因此它就不會在做 docker pull 動作了,當新的 pod 出現的時候,它仍然使用了老的有問題的鏡像,

有三個方法來解決這個問題:
1. 切成 :latest tag(千萬不要這么做!)
2. deployment 中指定 ImagePullPolicy: Always
3. 使用唯一的 tag(比如基于你的代碼版本控制器的 commit id)

在開發階段或者要快速驗證原型的時候,我會指定 ImagePullPolicy: Always 這樣我可以使用相同的 tag 來構建和推送,然而,在我的產品部署階段,我使用基于 Git SHA-1 的唯一 tag,這樣很容易查到產品部署的應用使用的源代碼,

所以說,當使用kubernetes時,我們有這么多地方要當心,一般來說,大部分常見的部署失敗都可以用下面的命令定位出來:
1. kubectl describe deployment/<deployname>
2. kubectl describe replicaset/<rsname>
3. kubectl get pods
4. kubectl describe pod/<podname>
5. kubectl logs <podname> --previous

下面是一個bash腳本,它在 CI/CD 的部署程序中任何失敗的時候,都可以跑,在 Jenkins等的構建輸出中,將顯示有用的 Kubernetes 資訊,幫助開發者快速找到任何明顯的問題,

 1 #!/bin/bash
 2  
 3 if [ -z "$1" ]
 4 then
 5   echo "ERROR: No deployment specified"
 6   exit 1
 7 fi
 8  
 9 DEPLOY=${1}
10 NAMESPACE=${2:=default}
11  
12 printf "\n\nOk - Let's figure out why this deployment might have failed"
13  
14 printf "\n\n------------------------------\n\n"
15  
16 printf "> kubectl describe deployment ${DEPLOY} --namespace=${NAMESPACE}\n\n"
17 kubectl describe deployment ${DEPLOY} --namespace=${NAMESPACE}
18  
19 printf "\n\n------------------------------\n\n"
20  
21 CURRENT_GEN=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.metadata.generation}')
22 OBS_GEN=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.observedGeneration}')
23 REPLICAS=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.replicas}')
24 UPDATED_REPLICAS=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.updatedReplicas}')
25 AVAILABLE_REPLICAS=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.availableReplicas}')
26  
27 if [ "$AVAILABLE_REPLICAS" == "$REPLICAS" ] && \
28    [ "$UPDATED_REPLICAS" == "$REPLICAS" ] ; then
29  
30   printf "Available Replicas (${AVAILABLE_REPLICAS}) equals Current Replicas (${REPLICAS}) \n"
31   printf "Updated Replicas (${UPDATED_REPLICAS}) equals Current Replicas (${REPLICAS}). \n"
32   printf "Are you sure the deploy failed?\n\n"
33   exit 0
34 fi
35  
36 if [ "$AVAILABLE_REPLICAS" != "$REPLICAS" ] ; then
37   printf "Available Replicas (${AVAILABLE_REPLICAS}) does not equal Current Replicas (${REPLICAS}) \n"
38 fi
39  
40 if [ "$UPDATED_REPLICAS" != "$REPLICAS" ] ; then
41   printf "Updated Replicas (${UPDATED_REPLICAS}) does not equal Current Replicas (${REPLICAS}) \n"
42 fi
43  
44 printf "\n\n------------------------------\n\n"
45  
46 NEW_RS=$(kubectl describe deploy ${DEPLOY} --namespace=${NAMESPACE} | grep "NewReplicaSet" | awk '{print $2}')
47 POD_HASH=$(kubectl get rs ${NEW_RS} --namespace=${NAMESPACE} -o jsonpath='{.metadata.labels.pod-template-hash}')
48  
49 printf "Pods for this deployment:\n\n"
50 printf "> kubectl get pods  --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}\n\n"
51 kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}
52  
53 printf "\n\n------------------------------\n\n"
54  
55 printf "Detailed pods for this deployment:\n\n"
56  
57 printf "> kubectl describe pods  --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}\n\n"
58 kubectl describe pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}
59  
60 printf "\n\n------------------------------\n\n"
61 printf "Containers that are currently 'waiting':\n\n"
62 printf "> kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} -o jsonpath='...'\n"
63 kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} -o jsonpath='{"\n"}{range .items[*]}{@.metadata.name}:{"\n"}{range @.status.conditions[*]}{"\t"}{@.lastTransitionTime}: {@.type}={@.status}{"\n"}{end}{"\n"}{"\tWaiting Containers\n"}{range @.status.containerStatuses[?(@.state.waiting)]}{"\t\tName: "}{@.name}{"\n\t\tImage: "}{@.image}{"\n\t\tState: Waiting"}{"\n\t\tMessage: "}{@.state.waiting.message}{"\n\t\tReason: "}{@.state.waiting.reason}{end}{"\n"}{end}'
64  
65 printf "\n\n------------------------------\n\n"
66  
67 printf "Pods with Terminated state\n\n"
68  
69 printf "> kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} -o jsonpath='...'\n"
70 kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} -o jsonpath='{"\n"}{range .items[*]}{"\n"}{@.metadata.name}:{"\n"}{"\n\tTerminated Containers\n"}{range @.status.containerStatuses[?(@.lastState.terminated)]}{"\t\tName: "}{@.name}{"\n\t\tImage: "}{@.image}{"\n\t\texitCode: "}{@.lastState.terminated.exitCode}{"\n\t\tReason: "}{@.lastState.terminated.reason}{"\n"}{end}{"\n"}{end}'
71  
72 printf "\n\n------------------------------\n\n"
73  
74 printf "Trying to get previous logs from each Terminated pod\n\n"
75  
76 kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} --no-headers | awk '{print $1}' | xargs -I pod sh -c "printf \"pod\n\n\"; kubectl --namespace=${NAMESPACE} logs --previous --tail=100 --timestamps pod; printf \
   
*************** 當你發現自己的才華撐不起野心時,就請安靜下來學習吧!***************
   

*************** 當你發現自己的才華撐不起野心時,就請安靜下來學習吧!***************

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/205081.html

標籤:其他

上一篇:關于“On the eigenvectors of p-Laplacian”目標函式的優化問題

下一篇:萬萬沒想到,曾經以為的 VSCode 專屬代碼工具,竟然可以這樣…

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more