Kubernetes 運維學習筆記

 1、Kubernetes 介紹vue

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

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

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

2、Kubernetes主要功能nginx

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

數據卷: Pod中容器之間共享數據,可使用數據卷。web

應用程序健康檢查: 容器內服務可能進程堵塞沒法處理請求,能夠設置監控檢查策略保證應用健壯性。redis

複製應用程序實例: 控制器維護着Pod副本數量,保證一個Pod或一組同類的Pod數量始終可用。算法

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

服務發現: 使用環境變量或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調用這些外部的有狀態應用。

3、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、集羣自身的配置和管理等;

4、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

5、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的網橋。好比:

# 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會添加一個路由。

發送端:
# 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的轉發規則:

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的請求。

好比下面是一個測試環境內容:

-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"」 在節點上面監聽的端口。

6、Kubernetes平常維護命令

一. 查看集羣信息
=============================================================================================================
[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
  
4、平常維護命令
=============================================================================================================
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
  
 
5、基礎進階
=============================================================================================================
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版本回滾
 
6、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     #設置啓動擴容/縮容
 
7、命名空間
=============================================================================================================
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

8、進入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

7、Kubernetes集羣部署失敗的通常緣由

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

首先咱們建立一個名爲 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 上啓動新應用,仍是遷移應用到已存在的平臺,應用在啓動以後就掛掉都是一個比較常見的現象。看個例子:

咱們建立一個 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 數據做爲環境變量:

# 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,並嘗試把它掛爲卷:

# 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 路由進行健康檢查:

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:

# 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 例子:

# 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 例子

# 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 持久化卷:

# 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 對象不合法。還有什麼比這更讓人沮喪的!

你可能以前也碰到過這種錯誤:
$ 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:

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 信息,幫助開發者快速找到任何明顯的問題。

#!/bin/bash

if [ -z "$1" ]
then
  echo "ERROR: No deployment specified"
  exit 1
fi

DEPLOY=${1}
NAMESPACE=${2:=default}

printf "\n\nOk - Let's figure out why this deployment might have failed"

printf "\n\n------------------------------\n\n"

printf "> kubectl describe deployment ${DEPLOY} --namespace=${NAMESPACE}\n\n"
kubectl describe deployment ${DEPLOY} --namespace=${NAMESPACE}

printf "\n\n------------------------------\n\n"

CURRENT_GEN=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.metadata.generation}')
OBS_GEN=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.observedGeneration}')
REPLICAS=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.replicas}')
UPDATED_REPLICAS=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.updatedReplicas}')
AVAILABLE_REPLICAS=$(kubectl get deployment ${DEPLOY} --namespace=${NAMESPACE} -o jsonpath='{.status.availableReplicas}')

if [ "$AVAILABLE_REPLICAS" == "$REPLICAS" ] && \
   [ "$UPDATED_REPLICAS" == "$REPLICAS" ] ; then

  printf "Available Replicas (${AVAILABLE_REPLICAS}) equals Current Replicas (${REPLICAS}) \n"
  printf "Updated Replicas (${UPDATED_REPLICAS}) equals Current Replicas (${REPLICAS}). \n"
  printf "Are you sure the deploy failed?\n\n"
  exit 0
fi

if [ "$AVAILABLE_REPLICAS" != "$REPLICAS" ] ; then
  printf "Available Replicas (${AVAILABLE_REPLICAS}) does not equal Current Replicas (${REPLICAS}) \n"
fi

if [ "$UPDATED_REPLICAS" != "$REPLICAS" ] ; then
  printf "Updated Replicas (${UPDATED_REPLICAS}) does not equal Current Replicas (${REPLICAS}) \n"
fi

printf "\n\n------------------------------\n\n"

NEW_RS=$(kubectl describe deploy ${DEPLOY} --namespace=${NAMESPACE} | grep "NewReplicaSet" | awk '{print $2}')
POD_HASH=$(kubectl get rs ${NEW_RS} --namespace=${NAMESPACE} -o jsonpath='{.metadata.labels.pod-template-hash}')

printf "Pods for this deployment:\n\n"
printf "> kubectl get pods  --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}\n\n"
kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}

printf "\n\n------------------------------\n\n"

printf "Detailed pods for this deployment:\n\n"

printf "> kubectl describe pods  --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}\n\n"
kubectl describe pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH}

printf "\n\n------------------------------\n\n"
printf "Containers that are currently 'waiting':\n\n"
printf "> kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} -o jsonpath='...'\n"
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}'

printf "\n\n------------------------------\n\n"

printf "Pods with Terminated state\n\n"

printf "> kubectl get pods --namespace=${NAMESPACE} -l pod-template-hash=${POD_HASH} -o jsonpath='...'\n"
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}'

printf "\n\n------------------------------\n\n"

printf "Trying to get previous logs from each Terminated pod\n\n"

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 \"\n\n\""
相關文章
相關標籤/搜索