Kubernetes之Pod使用

 

1、什麼是Pod
kubernetes中的一切均可以理解爲是一種資源對象,pod,rc,service,均可以理解是 一種資源對象。pod的組成示意圖以下,由一個叫」pause「的根容器,加上一個或多個用戶自定義的容器構造。pause的狀態帶便了這一組容器的狀態,pod裏多個業務容器共享pod的Ip和數據卷。在kubernetes環境下,pod是容器的載體,全部的容器都是在pod中被管理,一個或多個容器放在pod裏做爲一個單元方便管理。php

pod是kubernetes能夠部署和管理的最小單元,若是想要運行一個容器,先要爲這個容器建立一個pod。同時一個pod也能夠包含多個容器,之因此多個容器包含在一個pod裏,每每是因爲業務上的緊密耦合。須要注意】這裏說的場景都非必須把不一樣的容器放在同一個pod裏,可是這樣每每更便於管理,甚至後面會講到的,緊密耦合的業務容器放置在同一個容器裏通訊效率更高。具體怎麼使用還要看實際狀況,綜合權衡。html

在Kubrenetes集羣中Pod有以下兩種使用方式:
a)一個Pod中運行一個容器。這是最多見用法。在這種方式中,你能夠把Pod想象成是單個容器的封裝,kuberentes管理的是Pod而不是直接管理容器。
b)在一個Pod中同時運行多個容器。當多個應用之間是緊耦合的關係時,能夠將多個應用一塊兒放在一個Pod中,同個Pod中的多個容器之間互相訪問能夠經過localhost來通訊(能夠把Pod理解成一個虛擬機,共享網絡和存儲卷)。也就是說一個Pod中也能夠同時封裝幾個須要緊密耦合互相協做的容器,它們之間共享資源。這些在同一個Pod中的容器能夠互相協做成爲一個service單位 (即一個容器共享文件),另外一個「sidecar」容器來更新這些文件。Pod將這些容器的存儲資源做爲一個實體來管理。node

就像每一個應用容器,pod被認爲是臨時實體。在Pod的生命週期中,pod被建立後,被分配一個惟一的ID(UID),調度到節點上,並一致維持指望的狀態直到被終結(根據重啓策略)或者被刪除。若是node死掉了,分配到了這個node上的pod,在通過一個超時時間後會被從新調度到其餘node節點上。一個給定的pod(如UID定義的)不會被「從新調度」到新的節點上,而是被一個一樣的pod取代,若是指望的話甚至能夠是相同的名字,可是會有一個新的UID(查看replication controller獲取詳情)。python

kubernetes爲何使用pod做爲最小單元,而不是container
直接部署一個容器看起來更簡單,可是這裏也有更好的緣由爲何在容器基礎上抽象一層呢?根本緣由是爲了管理容器,kubernetes須要更多的信息,好比重啓策略,它定義了容器終止後要採起的策略;或者是一個可用性探針,從應用程序的角度去探測是否一個進程還存活着。基於這些緣由,kubernetes架構師決定使用一個新的實體,也就是pod,而不是重載容器的信息添加更多屬性,用來在邏輯上包裝一個或者多個容器的管理所須要的信息。nginx

kubernetes爲何容許一個pod裏有多個容器
pod裏的容器運行在一個邏輯上的"主機"上,它們使用相同的網絡名稱空間 (即同一pod裏的容器使用相同的ip和相同的端口段區間) 和相同的IPC名稱空間。它們也能夠共享存儲卷。這些特性使它們能夠更有效的通訊,而且pod可使你把緊密耦合的應用容器做爲一個單元來管理。也就是說當多個應用之間是緊耦合關係時,能夠將多個應用一塊兒放在一個Pod中,同個Pod中的多個容器之間互相訪問能夠經過localhost來通訊(能夠把Pod理解成一個虛擬機,共享網絡和存儲卷)。git

所以當一個應用若是須要多個運行在同一主機上的容器時,爲何不把它們放在同一個容器裏呢?首先,這樣何故違反了一個容器只負責一個應用的原則。這點很是重要,若是咱們把多個應用放在同一個容器裏,這將使解決問題變得很是麻煩,由於它們的日誌記錄混合在了一塊兒,而且它們的生命週期也很難管理。所以一個應用使用多個容器將更簡單,更透明,而且使應用依賴解偶。而且粒度更小的容器更便於不一樣的開發團隊共享和複用。web

【須要注意】這裏說到爲了解偶把應用分別放在不一樣容器裏,前面咱們也強調爲了便於管理管緊耦合的應用把它們的容器放在同一個pod裏。一會強調耦合,一個強調解偶看似矛盾,實際上廣泛存在,高內聚低耦合是咱們的追求,然而一個應用的業務邏輯模塊不可能徹底完獨立不存在耦合,這就須要咱們從實際上來考量,作出決策。redis

由於,雖然可使用一個pod來承載一個多層應用,可是更建議使用不一樣的pod來承載不一樣的層,因這這樣你能夠爲每個層單獨擴容而且把它們分佈到集羣的不一樣節點上。算法

Pod中如何管理多個容器
Pod中能夠同時運行多個進程(做爲容器運行)協同工做,同一個Pod中的容器會自動的分配到同一個 node 上,同一個Pod中的容器共享資源、網絡環境和依賴,它們老是被同時調度。須要注意:一個Pod中同時運行多個容器是一種比較高級的用法。只有當你的容器須要緊密配合協做的時候才考慮用這種模式。docker

Pod中共享的環境包括Linux的namespace,cgroup和其餘可能的隔絕環境,這一點跟Docker容器一致。在Pod的環境中,每一個容器中可能還有更小的子隔離環境。Pod中的容器共享IP地址和端口號,它們之間能夠經過localhost互相發現。它們之間能夠經過進程間通訊,須要明白的是同一個Pod下的容器是經過lo網卡進行通訊。例如SystemV信號或者POSIX共享內存。不一樣Pod之間的容器具備不一樣的IP地址,不能直接經過IPC通訊。Pod中的容器也有訪問共享volume的權限,這些volume會被定義成pod的一部分並掛載到應用容器的文件系統中。

總而言之。Pod中能夠共享兩種資源:網絡 和 存儲
1. 網絡:每一個Pod都會被分配一個惟一的IP地址。Pod中的全部容器共享網絡空間,包括IP地址和端口。Pod內部的容器可使用localhost互相通訊。Pod中的容器與外界通訊時,必須分配共享網絡資源(例如使用宿主機的端口映射)。
2. 存儲:能夠Pod指定多個共享的Volume。Pod中的全部容器均可以訪問共享的volume。Volume也能夠用來持久化Pod中的存儲資源,以防容器重啓後文件丟失。

容器的依賴關係和啓動順序
當前,同一個pod裏的全部容器都是並行啓動而且沒有辦法肯定哪個容器必須早於哪個容器啓動。若是要想確保第一個容器早於第二個容器啓動,那麼就要使用到"init container"了。

同一pod的容器間網絡通訊
同一pod下的容器使用相同的網絡名稱空間,這就意味着他們能夠經過"localhost"來進行通訊,它們共享同一個Ip和相同的端口空間。

同一個pod暴露多個容器
一般pod裏的容器監聽不一樣的端口,想要被外部訪問都須要暴露出去.你能夠經過在一個服務裏暴露多個端口或者使用不一樣的服務來暴露不一樣的端口來實現。

2、如何使用Pod
一般把Pod分爲兩類:
-  自主式Pod :這種Pod自己是不能自我修復的,當Pod被建立後(不管是由你直接建立仍是被其餘Controller),都會被Kuberentes調度到集羣的Node上。直到Pod的進程終止、被刪掉、由於缺乏資源而被驅逐、或者Node故障以前這個Pod都會一直保持在那個Node上。Pod不會自愈。若是Pod運行的Node故障,或者是調度器自己故障,這個Pod就會被刪除。一樣的,若是Pod所在Node缺乏資源或者Pod處於維護狀態,Pod也會被驅逐。
-  控制器管理的Pod:Kubernetes使用更高級的稱爲Controller的抽象層,來管理Pod實例。Controller能夠建立和管理多個Pod,提供副本管理、滾動升級和集羣級別的自愈能力。例如,若是一個Node故障,Controller就能自動將該節點上的Pod調度到其餘健康的Node上。雖然能夠直接使用Pod,可是在Kubernetes中一般是使用Controller來管理Pod的。以下圖:

每一個Pod都有一個特殊的被稱爲"根容器"的Pause 容器。 Pause容器對應的鏡像屬於Kubernetes平臺的一部分,除了Pause容器,每一個Pod還包含一個或者多個緊密相關的用戶業務容器。

Kubernetes設計這樣的Pod概念和特殊組成結構有什麼用意呢?
緣由一:在一組容器做爲一個單元的狀況下,難以對總體的容器簡單地進行判斷及有效地進行行動。好比一個容器死亡了,此時是算總體掛了麼?那麼引入與業務無關的Pause容器做爲Pod的根容器,以它的狀態表明着整個容器組的狀態,這樣就能夠解決該問題。
緣由二:Pod裏的多個業務容器共享Pause容器的IP,共享Pause容器掛載的Volume,這樣簡化了業務容器之間的通訊問題,也解決了容器之間的文件共享問題。

1. Pod的持久性和終止
-  Pod的持久性
Pod在設計上就不是做爲持久化實體的。在調度失敗、節點故障、缺乏資源或者節點維護的狀態下都會死掉會被驅逐。一般,用戶不須要手動直接建立Pod,而是應該使用controller(例如Deployments),即便是在建立單個Pod的狀況下。Controller能夠提供集羣級別的自愈功能、複製和升級管理。

-  Pod的終止
由於Pod做爲在集羣的節點上運行的進程,因此在再也不須要的時候可以優雅的終止掉是十分必要的(比起使用發送KILL信號這種暴力的方式)。用戶須要可以放鬆刪除請求,而且知道它們什麼時候會被終止,是否被正確的刪除。用戶想終止程序時發送刪除pod的請求,在pod能夠被強制刪除前會有一個寬限期,會發送一個TERM請求到每一個容器的主進程。一旦超時,將向主進程發送KILL信號並從API server中刪除。若是kubelet或者container manager在等待進程終止的過程當中重啓,在重啓後仍然會重試完整的寬限期。

示例流程以下:
用戶發送刪除pod的命令,默認寬限期是30秒;
在Pod超過該寬限期後API server就會更新Pod的狀態爲"dead";
在客戶端命令行上顯示的Pod狀態爲"terminating";
跟第三步同時,當kubelet發現pod被標記爲"terminating"狀態時,開始中止pod進程:
1. 若是在pod中定義了preStop hook,在中止pod前會被調用。若是在寬限期事後,preStop hook依然在運行,第二步會再增長2秒的寬限期;
2. 向Pod中的進程發送TERM信號;
- 跟第三步同時,該Pod將從該service的端點列表中刪除,再也不是replication controller的一部分。關閉的慢的pod將繼續處理load balancer轉發的流量;
- 過了寬限期後,將向Pod中依然運行的進程發送SIGKILL信號而殺掉進程。
- Kublete會在API server中完成Pod的的刪除,經過將優雅週期設置爲0(當即刪除)。Pod在API中消失,而且在客戶端也不可見。

刪除寬限期默認是30秒。 kubectl delete命令支持 --grace-period=<seconds> 選項,容許用戶設置本身的寬限期若是設置爲0將強制刪除pod。在kubectl>=1.5版本的命令中,你必須同時使用 --force 和 --grace-period=0 來強制刪除pod

Pod的強制刪除是經過在集羣和etcd中將其定義爲刪除狀態。當執行強制刪除命令時,API server不會等待該pod所運行在節點上的kubelet確認,就會當即將該pod從API server中移除,這時就能夠建立跟原pod同名的pod了。這時,在節點上的pod會被當即設置爲terminating狀態,不過在被強制刪除以前依然有一小段優雅刪除週期【須要注意:若是刪除一個pod後,再次查看發現pod還在,這是由於在deployment.yaml文件中定義了副本數量!還須要刪除deployment才行。即:"kubectl delete pod pod-name -n namespace" && "kubectl delete deployment deployment-name -n namespace"】

2.  Pause容器
Pause容器,又叫Infra容器。咱們檢查node節點的時候會發現每一個node節點上都運行了不少的pause容器,例如以下:

[root@k8s-node01 ~]# docker ps |grep pause
0cbf85d4af9e    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_myapp-848b5b879b-ksgnv_default_0af41a40-a771-11e8-84d2-000c2972dc1f_0
d6e4d77960a7    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_myapp-848b5b879b-5f69p_default_09bc0ba1-a771-11e8-84d2-000c2972dc1f_0
5f7777c55d2a    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_kube-flannel-ds-pgpr7_kube-system_23dc27e3-a5af-11e8-84d2-000c2972dc1f_1
8e56ef2564c2    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_client2_default_17dad486-a769-11e8-84d2-000c2972dc1f_1
7815c0d69e99    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_nginx-deploy-5b595999-872c7_default_7e9df9f3-a6b6-11e8-84d2-000c2972dc1f_2
b4e806fa7083    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_kube-proxy-vxckf_kube-system_23dc0141-a5af-11e8-84d2-000c2972dc1f_2

kubernetes中的pause容器主要爲每一個業務容器提供如下功能:
在pod中擔任Linux命名空間共享的基礎
啓用pid命名空間,開啓init進程

示例以下:
[root@k8s-node01 ~]# docker run -d --name pause -p 8880:80 k8s.gcr.io/pause:3.1

[root@k8s-node01 ~]# docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx

[root@k8s-node01 ~]#  docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost

如今訪問http://****:8880/就能夠看到ghost博客的界面了。

解析說明:
pause容器將內部的80端口映射到宿主機的8880端口,pause容器在宿主機上設置好了網絡namespace後,nginx容器加入到該網絡namespace中,咱們看到nginx容器啓動的時候指定了
--net=container:pause,ghost容器一樣加入到了該網絡namespace中,這樣三個容器就共享了網絡,互相之間就可使用localhost直接通訊,
--ipc=contianer:pause --pid=container:pause就是三個容器處於同一個namespace中,init進程爲pause

這時咱們進入到ghost容器中查看進程狀況。
[root@k8s-node01 ~]# docker exec -it ghost /bin/bash
root@d3057ceb54bc:/var/lib/ghost# ps axu 
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0   1012     4 ?        Ss   03:48   0:00 /pause
root          6  0.0  0.0  32472   780 ?        Ss   03:53   0:00 nginx: master process nginx -g daemon off;
systemd+     11  0.0  0.1  32932  1700 ?        S    03:53   0:00 nginx: worker process
node         12  0.4  7.5 1259816 74868 ?       Ssl  04:00   0:07 node current/index.js
root         77  0.6  0.1  20240  1896 pts/0    Ss   04:29   0:00 /bin/bash
root         82  0.0  0.1  17496  1156 pts/0    R+   04:29   0:00 ps axu

在ghost容器中同時能夠看到pause和nginx容器的進程,而且pause容器的PID是1。而在kubernetes中容器的PID=1的進程即爲容器自己的業務進程。

3.  Init容器
Pod 可以具備多個容器,應用運行在容器裏面,可是它也可能有一個或多個先於應用容器啓動的 Init 容器。init容器是一種專用的容器,在應用容器啓動以前運行,能夠包含普通容器映像中不存在的應用程序或安裝腳本。init容器會優先啓動,待裏面的任務完成後容器就會退出。    init容器配置示例以下:

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  # These containers are run during pod initialization
  initContainers:
  - name: install
    image: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://kubernetes.io
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}

1.  理解init容器
- 它們老是運行到完成。
- 每一個都必須在下一個啓動以前成功完成。
- 若是 Pod 的 Init 容器失敗,Kubernetes 會不斷地重啓該 Pod,直到 Init 容器成功爲止。然而,若是 Pod 對應的 restartPolicy 爲 Never,它不會從新啓動。
- Init 容器支持應用容器的所有字段和特性,但不支持 Readiness Probe由於它們必須在 Pod 就緒以前運行完成
- 若是爲一個 Pod 指定了多個 Init 容器,那些容器會按順序一次運行一個。 每一個 Init 容器必須運行成功,下一個纔可以運行。
- 由於 Init 容器可能會被重啓、重試或者從新執行,因此 Init 容器的代碼應該是冪等的。 特別地,被寫到 EmptyDirs 中文件的代碼,應該對輸出文件可能已經存在作好準備。
- 在 Pod 上使用 activeDeadlineSeconds,在容器上使用 livenessProbe,這樣可以避免 Init 容器一直失敗。 這就爲 Init 容器活躍設置了一個期限。
- 在 Pod 中的每一個 app 和 Init 容器的名稱必須惟一;與任何其它容器共享同一個名稱,會在驗證時拋出錯誤。
- 對 Init 容器 spec 的修改,被限制在容器 image 字段中。 更改 Init 容器的 image 字段,等價於重啓該 Pod。

一個pod能夠包含多個普通容器和多個init容器,在Pod中全部容器的名稱必須惟一,init容器在普通容器啓動前順序執行,若是init容器失敗,則認爲pod失敗,K8S會根據pod的重啓策略來重啓這個容器,直到成功

Init容器須要在pod.spec中的initContainers數組中定義(與3pod.spec.containers數組類似)。init容器的狀態在.status.initcontainerStatus字段中做爲容器狀態的數組返回(與status.containerStatus字段相似)。init容器支持普通容器的全部字段和功能,除了readinessprobe。Init 容器只能修改image 字段,修改image 字段等於重啓 Pod,Pod 重啓全部Init 容器必須從新執行。 

若是Pod的Init容器失敗,Kubernetes會不斷地重啓該Pod,直到Init容器成功爲止。然而若是Pod對應的restartPolicy爲Never,則它不會從新啓動。因此在Pod上使用activeDeadlineSeconds,在容器上使用livenessProbe,至關於爲Init容器活躍設置了一個期限,可以避免Init容器一直失敗

2.  Init容器與普通容器的不一樣之處
Init 容器與普通的容器很是像,除了以下兩點:
- Init 容器老是運行到成功完成爲止。
- 每一個 Init 容器都必須在下一個 Init 容器啓動以前成功完成。

Init 容器支持應用容器的所有字段和特性,包括資源限制、數據卷和安全設置。 然而,Init 容器對資源請求和限制的處理稍有不一樣, 並且 Init 容器不支持 Readiness Probe,由於它們必須在 Pod 就緒以前運行完成。若是爲一個 Pod 指定了多個 Init 容器,那些容器會按順序一次運行一個。 每一個 Init 容器必須運行成功,下一個纔可以運行。 當全部的 Init 容器運行完成時,Kubernetes 初始化 Pod 並像日常同樣運行應用容器。

3.  Init 容器能作什麼
由於 Init 容器具備與應用容器分離的單獨鏡像,它們的啓動相關代碼具備以下優點:
- 它們能夠包含並運行實用工具,處於安全考慮,是不建議在應用容器鏡像中包含這些實用工具的。
- 它們能夠包含實用工具和定製化代碼來安裝,但不能出如今應用鏡像中。例如建立鏡像不必FROM另外一個鏡像,只須要在安裝中使用相似sed,awk、 python 或dig這樣的工具。
- 應用鏡像能夠分離出建立和部署的角色,而沒有必要聯合它們構建一個單獨的鏡像。
- 它們使用 Linux Namespace,因此對應用容器具備不一樣的文件系統視圖。所以,它們可以具備訪問 Secret 的權限,而應用容器不可以訪問。
- 它們在應用容器啓動以前運行完成,然而應用容器並行運行,因此 Init 容器提供了一種簡單的方式來阻塞或延遲應用容器的啓動,直到知足了一組先決條件。

4.  靜態pod
靜態Pod是由kubelet進行管理,僅存在於特定Node上的Pod。它們不能經過API Server進行管理,沒法與ReplicationController、Deployment或DaemonSet進行關聯,而且kubelet也沒法對其健康檢查。靜態Pod老是由kubelet建立,而且總在kubelet所在的Node上運行。建立靜態Pod的方式:使用配置文件方式 HTTP方式。通常常使用的是配置文件方式。

-  經過配置文件建立
配置文件只是特定目錄中json或yaml格式的標準pod定義。它經過在kubelet守護進程中添加配置參數--pod-manifest-path=<the directory> 來運行靜態Pod,kubelet常常會它按期掃描目錄;例如,如何將一個簡單web服務做爲靜態pod啓動?

選擇運行靜態pod的節點服務器,不必定是node節點,只要有kubelet進程所在的節點均可以運行靜態pod。能夠在某個節點上建立一個放置一個Web服務器pod定義的描述文件文件夾,例如/etc/kubelet.d/static-web.yaml

# mkdir /etc/kubelet.d/
# vim /etc/kubelet.d/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
#ls /etc/kubelet.d/ static-web.yaml

經過使用--pod-manifest-path=/etc/kubelet.d/參數運行它,在節點上配置個人kubelet守護程序以使用此目錄。好比這裏kubelet啓動參數位/etc/systemd/system/kubelet.service.d/10-kubelet.conf, 修改配置,而後將參數加入到現有參數配置項中(安裝方式不盡相同,可是道理同樣)。

# vim /etc/systemd/system/kubelet.service.d/10-kubelet.conf
······
······
Environment="KUBELET_EXTRA_ARGS=--cluster-dns=10.96.0.10 --cluster-domain=cluster.local --pod-manifest-path=/etc/kubelet.d/"
······
······

保存退出,reload一下systemd daeomon ,重啓kubelet服務進程

#systemctl daemon-reload
# systemctl restart kubelet

前面說了,當kubelet啓動時,它會自動啓動在指定的目錄–pod-manifest-path=或–manifest-url=參數中定義的全部pod ,即咱們的static-web。接着在該節點上檢查是否建立成功:

# kubectl get pods -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP            NODE    
static-web-k8s-m1   1/1       Running   0          2m        10.244.2.32   k8s-m1

上面也提到了,它不歸任何部署方式來管理,即便咱們嘗試kubelet命令去刪除

# kubectl delete pod static-web-k8s-m1
pod "static-web-k8s-m1" deleted

# kubectl get pods -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP        NODE      NOMINATED NODE
static-web-k8s-m1   0/1       Pending   0          2s        <none>    k8s-m1    <none>

能夠看出靜態pod經過這種方式是無法刪除的

那我如何去刪除或者說是動態的添加一個pod呢?這種機制已經知道,kubelet進程會按期掃描配置的目錄(/etc/kubelet.d在個人示例)以進行更改,並在文件出現/消失在此目錄中時添加/刪除pod。

5. Pod容器共享Volume
同一個Pod中的多個容器能夠共享Pod級別的存儲卷Volume,Volume能夠定義爲各類類型,多個容器各自進行掛載,將Pod的Volume掛載爲容器內部須要的目錄。例如:Pod級別的Volume:"app-logs",用於tomcat向其中寫日誌文件,busybox讀日誌文件。

pod-volumes-applogs.yaml文件的配置內容

apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
    image: tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  - name: busybox
    image: busybox
    command: ["sh","-c","tailf /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:
  - name: app-logs
    emptuDir: {}

查看日誌
# kubectl logs <pod_name> -c <container_name>
# kubectl exec -it <pod_name> -c <container_name> – tail /usr/local/tomcat/logs/catalina.xx.log

6. Pod的配置管理
Kubernetes v1.2的版本提供統一的集羣配置管理方案 – ConfigMap:容器應用的配置管理

ConfigMap使用場景:
-  生成爲容器內的環境變量。
-  設置容器啓動命令的啓動參數(需設置爲環境變量)。
-  以Volume的形式掛載爲容器內部的文件或目錄。

ConfigMap以一個或多個key:value的形式保存在kubernetes系統中供應用使用,既能夠表示一個變量的值(例如:apploglevel=info),也能夠表示完整配置文件的內容(例如:server.xml=<?xml…>…)。能夠經過yaml配置文件或者使用kubectl create configmap命令的方式建立ConfigMap。

3.1)建立ConfigMap
經過yaml文件方式
cm-appvars.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir: /var/data

經常使用命令
# kubectl create -f cm-appvars.yaml
# kubectl get configmap
# kubectl describe configmap cm-appvars
# kubectl get configmap cm-appvars -o yaml

經過kubectl命令行方式
經過kubectl create configmap建立,使用參數–from-file或–from-literal指定內容,能夠在一行中指定多個參數。

1)經過–from-file參數從文件中進行建立,能夠指定key的名稱,也能夠在一個命令行中建立包含多個key的ConfigMap。
# kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source

2)經過–from-file參數從目錄中進行建立,該目錄下的每一個配置文件名被設置爲key,文件內容被設置爲value。
# kubectl create configmap NAME --from-file=config-files-dir

3)經過–from-literal從文本中進行建立,直接將指定的key=value建立爲ConfigMap的內容。
# kubectl create configmap NAME --from-literal=key1=value1 --from-literal=key2=value2

容器應用對ConfigMap的使用有兩種方法:
- 經過環境變量獲取ConfigMap中的內容。
- 經過Volume掛載的方式將ConfigMap中的內容掛載爲容器內部的文件或目錄。

經過環境變量的方式
ConfigMap的yaml文件: cm-appvars.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appvars
data:
  apploglevel: info
  appdatadir: /var/data

Pod的yaml文件:cm-test-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: cm-test
    image: busybox
    command: ["/bin/sh","-c","env|grep APP"]
    env:
    - name: APPLOGLEVEL
      valueFrom:
        configMapKeyRef:
          name: cm-appvars
          key: apploglevel
    - name: APPDATADIR
      valueFrom:
        configMapKeyRef:
          name: cm-appvars
          key: appdatadir

建立命令:
# kubectl create -f cm-test-pod.yaml
# kubectl get pods --show-all
# kubectl logs cm-test-pod

使用ConfigMap的限制條件
-  ConfigMap必須在Pod以前建立
-  ConfigMap也能夠定義爲屬於某個Namespace。只有處於相同Namespace中的Pod能夠引用它。
-  kubelet只支持能夠被API Server管理的Pod使用ConfigMap。靜態Pod沒法引用。
-  在Pod對ConfigMap進行掛載操做時,容器內只能掛載爲「目錄」,沒法掛載爲文件。

7. Pod的生命週期

-  Pod的狀態
pod從建立到最後的建立成功會分別處於不一樣的階段,下面是Pod的生命週期示意圖,從圖中能夠看到Pod狀態的變化:

掛起或等待中 (Pending):API Server建立了Pod資源對象並已經存入了etcd中,可是它並未被調度完成,或者仍然處於從倉庫下載鏡像的過程當中。這時候Pod 已被 Kubernetes 系統接受,但有一個或者多個容器鏡像還沒有建立。等待時間包括調度 Pod 的時間和經過網絡下載鏡像的時間,這可能須要花點時間。建立pod的請求已經被k8s接受,可是容器並無啓動成功,可能處在:寫數據到etcd,調度,pull鏡像,啓動容器這四個階段中的任何一個階段,pending伴隨的事件一般會有:ADDED, Modified這兩個事件的產生。
運行中 (Running):該 Pod 已經被調度到了一個node節點上,Pod 中全部的容器都已被kubelet建立完成。至少有一個容器正在運行,或者正處於啓動或重啓狀態。
正常終止 (Succeeded):pod中的全部的容器已經正常的自行退出,而且k8s永遠不會自動重啓這些容器,通常會是在部署job的時候會出現。
異常中止 (Failed):Pod 中的全部容器都已終止了,而且至少有一個容器是由於失敗終止。也就是說,容器以非0狀態退出或者被系統終止。
未知狀態 (Unkonwn):出於某種緣由,沒法得到Pod的狀態,一般是因爲與Pod主機通訊時出錯。

-  Pod的建立過程
Pod是Kubernetes的基礎單元,瞭解其建立的過程,更有助於理解系統的運做。建立Pod的整個流程的時序圖以下:

① 用戶經過kubectl客戶端提交Pod Spec給API Server。
② API Server嘗試將Pod對象的相關信息存儲到etcd中,等待寫入操做完成,API Server返回確認信息到客戶端。
③ API Server開始反映etcd中的狀態變化。
④ 全部的Kubernetes組件經過"watch"機制跟蹤檢查API Server上的相關信息變更。
⑤ kube-scheduler(調度器)經過其"watcher"檢測到API Server建立了新的Pod對象可是沒有綁定到任何工做節點。
⑥ kube-scheduler爲Pod對象挑選一個工做節點並將結果信息更新到API Server。
⑦ 調度結果新消息由API Server更新到etcd,而且API Server也開始反饋該Pod對象的調度結果。
⑧ Pod被調度到目標工做節點上的kubelet嘗試在當前節點上調用docker engine進行啓動容器,並將容器的狀態結果返回到API Server。
⑨ API Server將Pod信息存儲到etcd系統中。
⑩ 在etcd確認寫入操做完成,API Server將確認信息發送到相關的kubelet。

Pod常規的排查:見這裏

一個pod的完整建立,一般會伴隨着各類事件的產生,kubernetes事件的種類總共只有4種:
Added EventType = "ADDED"
Modified EventType = "MODIFIED"
Deleted EventType = "DELETED"
Error EventType = "ERROR"

PodStatus 有一組PodConditions。 PodCondition中的ConditionStatus,它表明了當前pod是否處於某一個階段(PodScheduled,Ready,Initialized,Unschedulable),"true" 表示處於,"false"表示不處於。PodCondition數組的每一個元素都有一個類型字段和一個狀態字段。

類型字段 PodConditionType  是一個字符串,可能的值是:
PodScheduled:pod正處於調度中,剛開始調度的時候,hostip還沒綁定上,持續調度以後,有合適的節點就會綁定hostip,而後更新etcd數據
Ready: pod 已經能夠開始服務,譬如被加到負載均衡裏面
Initialized:全部pod 中的初始化容器已經完成了
Unschedulable:限制不能被調度,譬如如今資源不足

狀態字段 ConditionStatus  是一個字符串,可能的值爲True,False和Unknown

Pod的ERROR事件的狀況大概有:
CrashLoopBackOff: 容器退出,kubelet正在將它重啓
InvalidImageName: 沒法解析鏡像名稱
ImageInspectError: 沒法校驗鏡像
ErrImageNeverPull: 策略禁止拉取鏡像
ImagePullBackOff: 正在重試拉取
RegistryUnavailable: 鏈接不到鏡像中心
ErrImagePull: 通用的拉取鏡像出錯
CreateContainerConfigError: 不能建立kubelet使用的容器配置
CreateContainerError: 建立容器失敗
m.internalLifecycle.PreStartContainer  執行hook報錯
RunContainerError: 啓動容器失敗
PostStartHookError: 執行hook報錯
ContainersNotInitialized: 容器沒有初始化完畢
ContainersNotReady: 容器沒有準備完畢
ContainerCreating:容器建立中
PodInitializing:pod 初始化中
DockerDaemonNotReady:docker尚未徹底啓動
NetworkPluginNotReady: 網絡插件尚未徹底啓動

-  Pod的重啓策略
PodSpec 中有一個 restartPolicy 字段,可能的值爲 AlwaysOnFailure Never。默認爲 Always。 restartPolicy 適用於 Pod 中的全部容器。restartPolicy 僅指經過同一節點上的 kubelet 從新啓動容器。失敗的容器由 kubelet 以五分鐘爲上限的指數退避延遲(10秒,20秒,40秒...)從新啓動,並在成功執行十分鐘後重置。pod一旦綁定到一個節點,Pod 將永遠不會從新綁定到另外一個節點(除非刪除這個pod,或pod所在的node節點發生故障或該node從集羣中退出,則pod纔會被調度到其餘node節點上)

說明: 能夠管理Pod的控制器有Replication Controller,Job,DaemonSet,及kubelet(靜態Pod)。
RC和DaemonSet:必須設置爲Always,須要保證該容器持續運行。
Job:OnFailure或Never,確保容器執行完後再也不重啓。
kubelet:在Pod失效的時候重啓它,不論RestartPolicy設置爲何值,而且不會對Pod進行健康檢查。

-  常見的狀態轉換場景

8.  Pod健康檢查 (存活性探測)
在pod生命週期中能夠作的一些事情。主容器啓動前能夠完成初始化容器,初始化容器能夠有多個,他們是串行執行的,執行完成後就推出了,在主程序剛剛啓動的時候能夠指定一個post start 主程序啓動開始後執行一些操做,在主程序結束前能夠指定一個 pre stop 表示主程序結束前執行的一些操做。Pod啓動後的健康狀態能夠由兩類探針來檢測:Liveness Probe(存活性探測) Readiness Probe(就緒性探測)。以下圖:

-  Liveness Probe
1. 用於判斷容器是否存活(running狀態)。
2. 若是LivenessProbe探針探測到容器非健康,則kubelet將殺掉該容器,並根據容器的重啓策略作相應處理。
3. 若是容器不包含LivenessProbe探針,則kubelet認爲該探針的返回值永遠爲「success」。

livenessProbe:指示容器是否正在運行。若是存活探測失敗,則 kubelet 會殺死容器,而且容器將受到其 重啓策略 的影響。若是容器不提供存活探針,則默認狀態爲 Success。Kubelet使用liveness probe(存活探針)來肯定什麼時候重啓容器。例如,當應用程序處於運行狀態但沒法作進一步操做,liveness探針將捕獲到deadlock,重啓處於該狀態下的容器,使應用程序在存在bug的狀況下依然可以繼續運行下去(誰的程序還沒幾個bug呢)。

-  Readiness Probe
1. 用於判斷容器是否啓動完成(read狀態),能夠接受請求。
2. 若是ReadnessProbe探針檢測失敗,則Pod的狀態將被修改。Endpoint Controller將從Service的Endpoint中刪除包含該容器所在Pod的Endpoint。

readinessProbe:指示容器是否準備好服務請求。若是就緒探測失敗,端點控制器將從與 Pod 匹配的全部 Service 的端點中刪除該 Pod 的 IP 地址。初始延遲以前的就緒狀態默認爲 Failure。若是容器不提供就緒探針,則默認狀態爲 Success。Kubelet使用readiness probe(就緒探針)來肯定容器是否已經就緒能夠接受流量。只有當Pod中的容器都處於就緒狀態時kubelet纔會認定該Pod處於就緒狀態。該信號的做用是控制哪些Pod應該做爲service的後端。若是Pod處於非就緒狀態,那麼它們將會被從service的load balancer中移除。

Kubelet 能夠選擇是否執行在容器上運行的兩種探針執行和作出反應,每次探測都將得到如下三種結果之一:
成功:容器經過了診斷。
失敗:容器未經過診斷。
未知:診斷失敗,所以不會採起任何行動。

探針是由 kubelet 對容器執行的按期診斷。要執行診斷,kubelet 調用由容器實現的Handler。其存活性探測的方法有如下三種:
- ExecAction:在容器內執行指定命令。若是命令退出時返回碼爲 0 則認爲診斷成功。
- TCPSocketAction:對指定端口上的容器的 IP 地址進行 TCP 檢查。若是端口打開,則診斷被認爲是成功的。
- HTTPGetAction:對指定的端口和路徑上的容器的 IP 地址執行 HTTP Get 請求。若是響應的狀態碼大於等於200 且小於 400,則診斷被認爲是成功的。

-  定義LivenessProbe命令
許多長時間運行的應用程序最終會轉換到broken狀態,除非從新啓動,不然沒法恢復。Kubernetes提供了Liveness Probe來檢測和補救這種狀況。LivenessProbe三種實現方式:

1)ExecAction:在一個容器內部執行一個命令,若是該命令狀態返回值爲0,則代表容器健康。(即定義Exec liveness探針)

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-exec
  name: liveness-exec
spec:
  containers:
  - name: liveness-exec-demo
    image: busybox
    args: ["/bin/sh","-c","touch /tmp/healthy;sleep 60;rm -rf /tmp/healthy;"sleep 600]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]
      initialDelaySeconds: 5
      periodSeconds: 5

上面的資源清單中定義了一個Pod 對象, 基於 busybox 鏡像 啓動 一個 運行「 touch/ tmp/ healthy; sleep 60; rm- rf/ tmp/ healthy; sleep 600」 命令 的 容器, 此 命令 在 容器 啓動 時 建立/ tmp/ healthy 文件, 並於 60 秒 以後 將其 刪除。 periodSeconds 規定kubelet要每隔5秒執行一次liveness probe, initialDelaySeconds 告訴kubelet在第一次執行probe以前要的等待5秒鐘。存活性探針探針檢測命令是在容器中執行 "test -e /tmp/healthy"命令檢查/ tmp/healthy 文件的存在性。若是命令執行成功,將返回0,表示 成功 經過 測試,則kubelet就會認爲該容器是活着的而且很健康。若是返回非0值,kubelet就會殺掉這個容器並重啓它。

2)TCPSocketAction:經過容器IP地址和端口號執行TCP檢查,若是可以創建TCP鏈接,則代表容器健康。這種方式使用TCP Socket,使用此配置,kubelet將嘗試在指定端口上打開容器的套接字。 若是能夠創建鏈接,容器被認爲是健康的,若是不能就認爲是失敗的。(即定義TCP liveness探針)

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-tcp
  name: liveness-tcp
spec:
  containers:
  - name: liveness-tcp-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: http

上面的資源清單文件,向Pod IP的80/tcp端口發起鏈接請求,並根據鏈接創建的狀態判斷Pod存活狀態。

3)HTTPGetAction:經過容器IP地址、端口號及路徑調用HTTP Get方法,若是響應的狀態碼大於等於200且小於等於400,則認爲容器健康。(即定義HTTP請求的liveness探針)

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-http
  name: liveness-http
spec:
  containers:
  - name: liveness-http-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh","-c","echo healthy > /usr/share/nginx/html/healthy"]
    livenessProbe:
      httpGet:
        path: /healthy
        port: http
        scheme: HTTP
    initialDelaySeconds: 3
    periodSeconds: 3

上面 清單 文件 中 定義 的 httpGet 測試 中, 請求 的 資源 路徑 爲「/ healthy」, 地址 默認 爲 Pod IP, 端口 使用 了 容器 中 定義 的 端口 名稱 HTTP, 這也 是 明確 爲 容器 指明 要 暴露 的 端口 的 用途 之一。livenessProbe 指定kubelete須要每隔3秒執行一次liveness probe。initialDelaySeconds 指定kubelet在該執行第一次探測以前須要等待3秒鐘。該探針將向容器中的server的默認http端口發送一個HTTP GET請求。若是server的/healthz路徑的handler返回一個成功的返回碼,kubelet就會認定該容器是活着的而且很健康。若是返回失敗的返回碼,kubelet將殺掉該容器並重啓它。任何大於200小於400的返回碼都會認定是成功的返回碼。其餘返回碼都會被認爲是失敗的返回碼。

-  定義ReadinessProbe命令
有時,應用程序暫時沒法對外部流量提供服務。 例如,應用程序可能須要在啓動期間加載大量數據或配置文件。 在這種狀況下,你不想殺死應用程序,但你也不想發送請求。 Kubernetes提供了readiness probe來檢測和減輕這些狀況。 Pod中的容器能夠報告本身尚未準備,不能處理Kubernetes服務發送過來的流量。Readiness probe的配置跟liveness probe很像。惟一的不一樣是使用 readinessProbe而不是livenessProbe。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: readiness-exec
  name: readiness-exec
spec:
  containers:
  - name: readiness-demo
    image: busybox
    args: ["/bin/sh","-c","touch /tmp/healthy;sleep 60;rm -rf /tmp/healthy;"sleep 600]
    readinessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]
      initialDelaySeconds: 5
      periodSeconds: 5

上面定義的是一個exec的Readiness探針,另外Readiness probe的HTTP和TCP的探測器配置跟liveness probe同樣。Readiness和livenss probe能夠並行用於同一容器。 使用二者能夠確保流量沒法到達未準備好的容器,而且容器在失敗時從新啓動。

-  配置Probe
Probe中有不少精確和詳細的配置,經過它們你能準確的控制liveness和readiness檢查:
initialDelaySeconds:容器啓動後第一次執行探測是須要等待多少秒。即啓動容器後首次進行健康檢查的等待時間,單位爲秒。
periodSeconds:執行探測的頻率。默認是10秒,最小1秒。
timeoutSeconds:探測超時時間。默認1秒,最小1秒。即健康檢查發送請求後等待響應的時間,若是超時響應kubelet則認爲容器非健康,重啓該容器,單位爲秒。
successThreshold:探測失敗後,最少連續探測成功多少次才被認定爲成功。默認是1。對於liveness必須是1。最小值是1。
failureThreshold:探測成功後,最少連續探測失敗多少次才被認定爲失敗。默認是3。最小值是1。

HTTP probe中能夠給 httpGet設置其餘配置項:
host:鏈接的主機名,默認鏈接到pod的IP。你可能想在http header中設置」Host」而不是使用IP。
scheme:鏈接使用的schema,默認HTTP。
path: 訪問的HTTP server的path。
httpHeaders:自定義請求的header。HTTP運行重複的header。
port:訪問的容器的端口名字或者端口號。端口號必須介於1和65525之間。

對於HTTP探測器,kubelet向指定的路徑和端口發送HTTP請求以執行檢查。 Kubelet將probe發送到容器的IP地址,除非地址被httpGet中的可選host字段覆蓋。 在大多數狀況下,不想設置主機字段。 有一種狀況下能夠設置它, 假設容器在127.0.0.1上偵聽,而且Pod的hostNetwork字段爲true。 而後,在httpGet下的host應該設置爲127.0.0.1。 若是你的pod依賴於虛擬主機,這多是更常見的狀況,你不該該是用host,而是應該在httpHeaders中設置Host頭。

-  Liveness Probe和Readiness Probe使用場景
-  若是容器中的進程可以在遇到問題或不健康的狀況下自行崩潰,則不必定須要存活探針; kubelet 將根據 Pod 的restartPolicy 自動執行正確的操做。
-  若是但願容器在探測失敗時被殺死並從新啓動,那麼請指定一個存活探針,並指定restartPolicy 爲 Always 或 OnFailure。
-  若是要僅在探測成功時纔開始向 Pod 發送流量,請指定就緒探針。在這種狀況下,就緒探針可能與存活探針相同,可是 spec 中的就緒探針的存在乎味着 Pod 將在沒有接收到任何流量的狀況下啓動,而且只有在探針探測成功後纔開始接收流量。
-  若是你但願容器可以自行維護,您能夠指定一個就緒探針,該探針檢查與存活探針不一樣的端點。

請注意:若是你只想在 Pod 被刪除時可以排除請求,則不必定須要使用就緒探針;在刪除 Pod 時,Pod 會自動將自身置於未完成狀態,不管就緒探針是否存在。當等待 Pod 中的容器中止時,Pod 仍處於未完成狀態。

9.  Pod調度
在kubernetes集羣中,Pod(container)是應用的載體,通常經過RC、Deployment、DaemonSet、Job等對象來完成Pod的調度與自愈功能

0.  Pod的生命
通常來講,Pod 不會消失,直到人爲銷燬它們。這多是一我的或控制器。這個規則的惟一例外是成功或失敗的 phase 超過一段時間(由 master 肯定)的Pod將過時並被自動銷燬。有三種可用的控制器:
使用 Job 運行預期會終止的 Pod,例如批量計算。Job 僅適用於重啓策略爲 OnFailure 或 Never 的 Pod。
對預期不會終止的 Pod 使用 ReplicationController、ReplicaSet 和 Deployment ,例如 Web 服務器。 ReplicationController 僅適用於具備 restartPolicy 爲 Always 的 Pod。
提供特定於機器的系統服務,使用 DaemonSet 爲每臺機器運行一個 Pod

全部這三種類型的控制器都包含一個 PodTemplate。建議建立適當的控制器,讓它們來建立 Pod,而不是直接本身建立 Pod這是由於單獨的 Pod 在機器故障的狀況下沒有辦法自動復原,而控制器卻能夠。若是節點死亡或與集羣的其他部分斷開鏈接,則 Kubernetes 將應用一個策略將丟失節點上的全部 Pod 的 phase 設置爲 Failed。

1.  RC、Deployment:全自動調度
RC的功能即保持集羣中始終運行着指定個數的Pod。在調度策略上主要有:
-   系統內置調度算法  [最優Node]
-   NodeSelector   [定向調度]
-   NodeAffinity  [親和性調度]

-  NodeSelector  [定向調度]
kubernetes中kube-scheduler負責實現Pod的調度,內部系統經過一系列算法最終計算出最佳的目標節點。若是須要將Pod調度到指定Node上,則能夠經過Node的標籤(Label)和Pod的nodeSelector屬性相匹配來達到目的。

1. kubectl label nodes {node-name} {label-key}={label-value}
2. nodeSelector:
{label-key}:{label-value}

若是給多個Node打了相同的標籤,則scheduler會根據調度算法從這組Node中選擇一個可用的Node來調度。
若是Pod的nodeSelector的標籤在Node中沒有對應的標籤,則該Pod沒法被調度成功。

Node標籤的使用場景:
對集羣中不一樣類型的Node打上不一樣的標籤,可控制應用運行Node的範圍。例如:role=frontend;role=backend;role=database。

-  NodeAffinity [親和性調度]
NodeAffinity意爲Node親和性調度策略,NodeSelector爲精確匹配,NodeAffinity爲條件範圍匹配,經過In(屬於)、NotIn(不屬於)、Exists(存在一個條件)、DoesNotExist(不存在)、Gt(大於)、Lt(小於)等操做符來選擇Node,使調度更加靈活。

1. RequiredDuringSchedulingRequiredDuringExecution:相似於NodeSelector,但在Node不知足條件時,系統將從該Node上移除以前調度上的Pod。
2. RequiredDuringSchedulingIgnoredDuringExecution:與上一個相似,區別是在Node不知足條件時,系統不必定從該Node上移除以前調度上的Pod。
3. PreferredDuringSchedulingIgnoredDuringExecution:指定在知足調度條件的Node中,哪些Node應更優先地進行調度。同時在Node不知足條件時,系統不必定從該Node上移除以前調度上的Pod。

若是同時設置了NodeSelector和NodeAffinity,則系統將須要同時知足二者的設置才能進行調度。

2.  DaemonSet:特定場景調度
DaemonSet是kubernetes1.2版本新增的一種資源對象,用於管理在集羣中每一個Node上僅運行一份Pod的副本實例

該用法適用的應用場景:
1.  在每一個Node上運行一個GlusterFS存儲或者Ceph存儲的daemon進程。
2.  在每一個Node上運行一個日誌採集程序:fluentd或logstach。
3.  在每一個Node上運行一個健康程序,採集該Node的運行性能數據,例如:Prometheus Node Exportor、collectd、New Relic agent或Ganglia gmond等。

DaemonSet的Pod調度策略與RC相似,除了使用系統內置算法在每臺Node上進行調度,也能夠經過NodeSelector或NodeAffinity來指定知足條件的Node範圍進行調度。

3.  Job:批處理調度
kubernetes從1.2版本開始支持批處理類型的應用,能夠經過kubernetes Job資源對象來定義並啓動一個批處理任務。批處理任務一般並行(或串行)啓動多個計算進程去處理一批工做項(work item),處理完後,整個批處理任務結束。

批處理的三種模式:

批處理按任務實現方式不一樣分爲如下幾種模式:
1. Job Template Expansion模式
一個Job對象對應一個待處理的Work item,有幾個Work item就產生幾個獨立的Job,經過適用於Work item數量少,每一個Work item要處理的數據量比較大的場景。例若有10個文件(Work item),每一個文件(Work item)爲100G。
2. Queue with Pod Per Work Item
採用一個任務隊列存放Work item,一個Job對象做爲消費者去完成這些Work item,其中Job會啓動N個Pod,每一個Pod對應一個Work item。
3. Queue with Variable Pod Count
採用一個任務隊列存放Work item,一個Job對象做爲消費者去完成這些Work item,其中Job會啓動N個Pod,每一個Pod對應一個Work item。但Pod的數量是可變的。

Job的三種類型
1. Non-parallel Jobs
一般一個Job只啓動一個Pod,除非Pod異常纔會重啓該Pod,一旦此Pod正常結束,Job將結束。
2. Parallel Jobs with a fixed completion count
並行Job會啓動多個Pod,此時須要設定Job的.spec.completions參數爲一個正數,當正常結束的Pod數量達到該值則Job結束。
3. Parallel Jobs with a work queue
任務隊列方式的並行Job須要一個獨立的Queue,Work item都在一個Queue中存放,不能設置Job的.spec.completions參數。

此時Job的特性:
-  每一個Pod能獨立判斷和決定是否還有任務項須要處理;
-  若是某個Pod正常結束,則Job不會再啓動新的Pod;
-  若是一個Pod成功結束,則此時應該不存在其餘Pod還在幹活的狀況,它們應該都處於即將結束、退出的狀態;
-  若是全部的Pod都結束了,且至少一個Pod成功結束,則整個Job算是成功結束;

10.  Pod伸縮
kubernetes中RC是用來保持集羣中始終運行指定數目的實例,經過RC的scale機制能夠完成Pod的擴容和縮容(伸縮)。

1.  手動伸縮(scale)

# kubectl scale rc redis-slave --replicas=3

2.  自動伸縮(HPA)
Horizontal Pod Autoscaler(HPA)控制器用於實現基於CPU使用率進行自動Pod伸縮的功能。HPA控制器基於Master的kube-controller-manager服務啓動參數--horizontal-pod-autoscaler-sync-period定義是時長(默認30秒),週期性監控目標Pod的CPU使用率,並在知足條件時對ReplicationController或Deployment中的Pod副本數進行調整,以符合用戶定義的平均Pod CPU使用率。Pod CPU使用率來源於heapster組件,所以需安裝該組件。

HPA能夠經過kubectl autoscale命令進行快速建立或者使用yaml配置文件進行建立。建立以前需已存在一個RC或Deployment對象,而且該RC或Deployment中的Pod必須定義resources.requests.cpu的資源請求值,以便heapster採集到該Pod的CPU。

-  經過kubectl autoscale建立。

例如php-apache-rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: php-apache
spec:
  replicas: 1
  template:
    metadata:
      name: php-apache
      labels:
        app: php-apache
    spec:
      containers:
      - name: php-apache
        image: gcr.io/google_containers/hpa-example
        resources:
          requests:
            cpu: 200m
        ports:
        - containerPort: 80

建立php-apache的RC

kubectl create -f php-apache-rc.yaml

php-apache-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: php-apache
spec:
  ports:
  - port: 80
  selector:
    app: php-apache

建立php-apache的Service

kubectl create -f php-apache-svc.yaml

建立HPA控制器

kubectl autoscale rc php-apache --min=1 --max=10 --cpu-percent=50

-  經過yaml配置文件建立

hpa-php-apache.yaml

apiVersion: v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: v1
    kind: ReplicationController
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

建立hpa

kubectl create -f hpa-php-apache.yaml

查看hpa

kubectl get hpa

11. Pod滾動升級和回滾
Kubernetes是一個很好的容器應用集羣管理工具,尤爲是採用ReplicationController這種自動維護應用生命週期事件的對象後,將容器應用管理的技巧發揮得淋漓盡致。在容器應用管理的諸多特性中,有一個特性是最能體現Kubernetes強大的集羣應用管理能力的,那就是滾動升級。

滾動升級的精髓在於升級過程當中依然可以保持服務的連續性,使外界對於升級的過程是無感知的。整個過程當中會有三個狀態:所有舊實例,新舊實例皆有,所有新實例。舊實例個數逐漸減小,新實例個數逐漸增長,最終達到舊實例個數爲0,新實例個數達到理想的目標值。

1.  使用kubectl rolling-update命令完成RC的滾動升級 和 回滾
kubernetes中的RC的滾動升級經過執行 kubectl rolling-update 命令完成,該命令建立一個新的RC(與舊的RC在同一個命名空間中),而後自動控制舊的RC中的Pod副本數逐漸減小爲0,同時新的RC中的Pod副本數從0逐漸增長到目標值,來完成Pod的升級。 須要注意的是:新舊RC要再同一個命名空間內。但滾動升級中Pod副本數(包括新Pod和舊Pod)保持原預期值。

1.1  經過配置文件實現
redis-master-controller-v2.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-master-v2
  labels:
    name: redis-master
    version: v2
spec:
  replicas: 1
  selector:
    name: redis-master
    version: v2
  template:
    metadata:
      labels:
        name: redis-master
        version: v2
    spec:
      containers:
      - name: master
        image: kubeguide/redis-master:2.0
        ports:
        - containerPort: 6379

注意事項:
-  RC的名字(name)不能與舊RC的名字相同
-  在selector中應至少有一個Label與舊的RC的Label不一樣,以標識其爲新的RC。例如本例中新增了version的Label。

運行kubectl rolling-update

kubectl rolling-update redis-master -f redis-master-controller-v2.yaml

1.2  經過kubectl rolling-update命令實現

kubectl rolling-update redis-master --image=redis-master:2.0

與使用配置文件實現不一樣在於,該執行結果舊的RC被刪除,新的RC仍使用舊的RC的名字。

1.3  經過kubectl rolling-update加參數--rollback實現回滾操做

kubectl rolling-update redis-master --image=kubeguide/redis-master:2.0 --rollback

rollback原理很簡單,kubernetes記錄了各個版本的PodTemplate,把舊的PodTemplate覆蓋新的Template便可。 

2.  經過Deployment的滾動升級 和 回滾
採用RS來管理Pod實例。若是當前集羣中的Pod實例數少於目標值,RS會拉起新的Pod,反之,則根據策略刪除多餘的Pod。Deployment正是利用了這樣的特性,經過控制兩個RS裏面的Pod,從而實現升級。滾動升級是一種平滑過渡式的升級,在升級過程當中,服務仍然可用,這是kubernetes做爲應用服務化管理的關鍵一步!!服務無處不在,而且按需使用。Kubernetes Deployment滾動更新機制不一樣於ReplicationController rolling update,Deployment rollout還提供了滾動進度查詢,滾動歷史記錄,回滾等能力,無疑是使用Kubernetes進行應用滾動發佈的首選。配置示例以下:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec: 
  replicas: 3
  template: 
    metadata:
      labels:
        app: nginx
    spec:
      containers: 
      - name: nginx
        images: nginx:1.7.9
        ports:
        - containerPort: 80

2.1  經過kubectl set image命令爲Deployment設置新的鏡像名稱

kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

2.2  使用kubectl edit命令修改Deployment的配置
將spec.template.spec.containers[0].images 從nginx:1.7.9 更改成1.9.1;  保存退出後,kubernetes會自動升級鏡像。

2.3  經過"kubectl rollout status"能夠查看deployment的更新過程

在Deployment的定義中,能夠經過spec.strategy指定Pod更新的策略:
- Recreate(重建): 設置spec.strategy.type=Recreate,表示Deployment在更新Pod時,會先殺掉全部正在運行的Pod,而後建立新的Pod.
- RollingUpdate(滾動更新):以滾動更新的方式來逐個更新Pod,能夠經過設置spec.strategy.rollingUpdate下的兩個參數(maxUnavailable和maxSurge)來控制滾動更新的過程。

一般來講,不鼓勵更新Deployment的標籤選擇器,由於這樣會致使Deployment選擇的Pod列表發生變化,也可能與其它控制器產生衝突。

Deployment滾動升級的過程大體爲:
- 查找新的RS和舊的RS,並計算出新的Revision(這是Revision的最大值);
- 對新的RS進行擴容操做;
- 對舊的RS進行縮容操做;
- 完成以後,刪掉舊的RS;
- 經過Deployment狀態到etcd;

2.4  Deployment的回滾
全部Deployment的發佈歷史記錄都保留在系統中,若是要進行回滾:
-  用 kubectl rollout history 命令檢查這個Deployment部署的歷史記錄
-  用 kubectl rollout undo deployment/nginx-deployment 撤銷本次發佈回滾到上一個部署版本
-  用 kubectl rollout undo deployment/nginx-deployment --to-revision=2 回滾到指定版本

2.5  暫停和恢復Deployment的部署操做,以完成複雜的修改
對應一次複雜的Deployment配置修改,爲了不頻繁觸發Deployment的更新操做,能夠暫停Deployment的更新操做,而後進行配置修改,再回復Deployment.一次性觸發完整的更新操做。使用命令:kubectl rollout pause deployment/nginx-deployment

Kubernetes滾動升級和回滾操做分享 (Deployment的rollout方式)

產品部署完成上線以後,常常遇到須要升級服務的要求(只考慮更新鏡像),以往比較粗糙的操做流程大體以下:
方式一:找到 master具體調度到的全部目標node,刪除其對應的鏡像文件
方式二:修改 file.yaml中鏡像拉取策略爲Always
 
刪掉舊pod,並從新建立
# kubectl delete -f /path/file.yaml
# kubectl create -f /path/file.yaml

可是這樣有一個比較棘手的問題,就是若是升級失敗的回滾策略。所以想到利用kubernetes自身的滾動升級的工具,部署及升級流程以下:

1)在初次建立的時候,儘可能加入參數--record,這樣k8s會記錄下本次啓動的腳本 。
# kubectl create -f /path/file.yaml --record

2)執行查看發佈的歷史記錄,會顯示如今已經記錄的腳本及其序號。
查看歷史記錄
# kubectl rollout history deployment deploy-apigw

3)升級命令執行後會輸出"xxx image updated"
升級鏡像
# kubectl set image deployment/deploy-name containerName=newIMG:version
# kubectl set image controllerType/controllerInstanceName underInstanceContainerName=image:version

4)查看pod狀態,若是失敗須要回滾操做
回滾到上一個操做版本
# kubectl rollout undo deployment/deploy-name

回滾到指定版本,版本號由第二步查看得到
# kubectl rollout undo deployment/deploy-name --to-revision=3

須要注意:執行rollout undo操做以後,版本號會移動,須要確認版本號無誤再undo

3.  其它管理對象的更新策略
3.1  DaemonSet的更新策略
- OnDelete: 默認配置。只有舊的Pod被用戶手動刪除後,才觸發新建操做。
- RollingUpdate: 舊版本的Pod將被自動殺掉,而後自動建立新版本的DaemonSet Pod.

3.2  StatefulSet的更新策略
StatefulSet的更新策略正逐漸向Deployment和DaemonSet的更新策略看齊。

12.  資源需求和資源限制
在Docker的範疇內,咱們知道能夠對運行的容器進行請求或消耗的資源進行限制。而在Kubernetes中也有一樣的機制,容器或Pod能夠進行申請和消耗的計算資源就是CPU和內存,這也是目前僅有的受支持的兩種類型。相比較而言,CPU屬於可壓縮資源,即資源額度可按需收縮;而內存則是不可壓縮型資源,對其執行收縮操做可能會致使某種程度的問題

資源的隔離目前是屬於容器級別,CPU和內存資源的配置須要Pod中的容器spec字段下進行定義。其具體字段,可使用"requests"進行定義請求的確保資源可用量。也就是說容器的運行可能用不到這樣的資源量,可是必須確保有這麼多的資源供給。而"limits"是用於限制資源可用的最大值,屬於硬限制。

在Kubernetes中,1個單位的CPU至關於虛擬機的1顆虛擬CPU(vCPU)或者是物理機上一個超線程的CPU,它支持分數計量方式,一個核心(1core)至關於1000個微核心(millicores),所以500m至關因而0.5個核心,即二分之一個核心。內存的計量方式也是同樣的,默認的單位是字節,也可使用E、P、T、G、M和K做爲單位後綴,或者是Ei、Pi、Ti、Gi、Mi、Ki等形式單位後綴。

-  容器的資源需求,資源限制
requests:需求,最低保障;
limits:限制,硬限制;

-  CPU
1 顆邏輯 CPU
1=1000,millicores (微核心)
500m=0.5CPU

-  資源需求
自主式pod要求爲stress容器確保128M的內存及五分之一個cpu核心資源可用,它運行stress-ng鏡像啓動一個進程進行內存性能壓力測試,滿載測試時它也會盡量多地佔用cpu資源,另外再啓動一個專用的cpu壓力測試進程。stress-ng是一個多功能系統壓力測試工具,master/worker模型,master爲主進程,負責生成和控制子進程,worker是負責執行各種特定測試的子進程。

集羣中的每一個節點都擁有定量的cpu和內存資源,調度pod時,僅那些被請求資源的餘量可容納當前調度的pod的請求量的節點纔可做爲目標節點。也就是說,kubernetes的調度器會根據容器的requests屬性中定義的資源需求量來斷定僅哪些節點可接受運行相關的pod資源,而對於一個節點的資源來講,每運行一個pod對象,其requestes中定義的請求量都要被預留,直到被全部pod對象瓜分完畢爲止。

資源需求配置示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        memory: "128Mi"
        cpu: "200m"

上面的配置清單中,nginx請求的CPU資源大小爲200m,這意味着一個CPU核心足以知足nginx以最快的方式運行,其中對內存的指望可用大小爲128Mi,實際運行時不必定會用到這麼多的資源。考慮到內存的資源類型,在超出指定大小運行時存在會被OOM killer殺死的可能性,因而該請求值屬於理想中使用的內存上限。

-  資源限制
容器的資源需求僅能達到爲其保證可用的最少資源量的目的,它並不會限制容器的可用資源上限,所以對因應用程序自身存在bug等多種緣由而致使的系統資源被長期佔用的狀況則機關用盡,這就須要經過limits屬性定義資源的最大可用量。資源分配時,可壓縮型資源cpu的控制閾可自由調節,容器進程沒法得到超出其cpu配額的可用時間。不過,若是進程申請分配超出其limits屬性定義的硬限制的內存資源時,它將被OOM killer殺死。不過,隨後可能會被其控制進程所重啓。例如,容器進程的pod對象會被殺死並重啓(重啓策略爲always或onfailure時),或者是容器進程的子進程被其父進程所重啓。也就是說,CPU是屬於可壓縮資源,可進行自由地調節。內存屬於硬限制性資源,當進程申請分配超過limit屬性定義的內存大小時,該Pod將被OOM killer殺死。

與requests不一樣的是,limits並不會影響pod的調度結果也就是說,一個節點上的全部pod對象的limits數量之和能夠大於節點所擁有的資源量,即支持資源的過載使用。不過,這麼一來一旦資源耗盡,尤爲是內存資源耗盡,則必然會有容器因OOMKilled而終止。另外,kubernetes僅會確保pod可以得到他們請求的cpu時間額度,他們可否得到額外的cpu時間,則取決於其餘正在運行的做業對cpu資源的佔用狀況。例如,對於總數爲1000m的cpu來講,容器a請求使用200m,容器b請求使用500m,在不超出它們各自的最大限額的前提下,餘下的300m在雙方都須要時會以2:5的方式進行配置。

資源限制配置示例:

[root@k8s-master ~]# vim memleak-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memleak-pod
  labels:
    app: memleak
spec:
  containers:
  - name: simmemleak
    image: saadali/simmemleak
    resources:
      requests:
        memory: "64Mi"
        cpu: "1"
      limits:
        memory: "64Mi"
        cpu: "1"

[root@k8s-master ~]# kubectl apply -f memleak-pod.yaml 
pod/memleak-pod created
[root@k8s-master ~]# kubectl get pods -l app=memleak
NAME          READY     STATUS      RESTARTS   AGE
memleak-pod   0/1       OOMKilled   2          12s
[root@k8s-master ~]# kubectl get pods -l app=memleak
NAME          READY     STATUS             RESTARTS   AGE
memleak-pod   0/1       CrashLoopBackOff   2          28s

Pod資源默認的重啓策略爲Always,在上面例子中memleak由於內存限制而終止會當即重啓,此時該Pod會被OOM killer殺死,在屢次重複由於內存資源耗盡重啓會觸發Kunernetes系統的重啓延遲,每次重啓的時間會不斷拉長,後面看到的Pod的狀態一般爲"CrashLoopBackOff"

-  容器的可見資源
對於容器中運行top等命令觀察資源可用量信息時,即使定義了requests和limits屬性,雖然其可用資源受限於此兩個屬性的定義,但容器中可見資源量依然是節點級別可用總量。

-  Pod的服務質量類別(QoS)
這裏還須要明確的是,kubernetes容許節點資源對limits的過載使用,這意味着節點沒法同時知足其上的全部pod對象以資源滿載的方式運行。在一個Kubernetes集羣上,運行的Pod衆多,那麼當node節點都沒法知足多個Pod對象的資源使用時 (節點內存資源緊缺時),應該按照什麼樣的順序去終止這些Pod對象呢?kubernetes沒法自行對此作出決策,它須要藉助於pod對象的優先級來斷定終止Pod的優先問題。根據pod對象的requests和limits屬性,kubernetes將pod對象歸類到BestEffortBurstableGuaranteed三個服務質量類別:
Guaranteed:每一個容器都爲cpu資源設置了具備相同值的requests和limits屬性,以及每一個容器都爲內存資源設置了具備相同值的requests和limits屬性的pod資源會自動歸屬於此類別,這類pod資源具備最高優先級.
Burstable:至少有一個容器設置了cpu或內存資源的requests屬性,但不知足Guaranteed類別要求的pod資源將自動歸屬此類別,它們具備中等優先級。
BestEffort:未爲任何一個容器設置requests和limits屬性的pod資源將自動歸屬於此類別,它們的優先級爲最低級別。

內存資源緊缺時,BestEfford類別的容器將首當其衝地終止,由於系統不爲其提供任何級別的資源保證,但換來的好處是:它們可以在可用時作到儘量多地佔用資源。若已然不存在BestEfford類別的容器,則接下來是有着中等優先級的Burstable類別的pod被終止。Guaranteed類別的容器擁有最高優先級,它們不會被殺死,除非其內存資源需求超限,或者OOM時沒有其餘更低優先級的pod資源存在。

每一個運行狀態的容器都有其OOM得分,得分越高越會被優先殺死。OOM得分主要根據兩個維度進行計算:由QoS類別繼承而來的默認分值和容器的可用內存資源比例。同等類別的pod資源的默認分值相同。同等級別優先級的pod資源在OOM時,與自身requests屬性相比,其內存佔用比例最大的pod對象將被首先殺死。須要特別說明的是,OOM是內存耗盡時的處理機制,它們與可壓縮型資源cpu無關,所以cpu資源的需求沒法獲得保證時,pod僅僅是暫時獲取不到相應的資源而已。

查看 Qos
[root@k8s-master01 ~]# kubectl describe pod/prometheus-858989bcfb-ml5gk -n kube-system|grep "QoS Class"
QoS Class:       Burstable

13.  Pod持久存儲方式
volume是kubernetes Pod中多個容器訪問的共享目錄。volume被定義在pod上,被這個pod的多個容器掛載到相同或不一樣的路徑下。volume的生命週期與pod的生命週期相同,pod內的容器中止和重啓時通常不會影響volume中的數據。因此通常volume被用於持久化pod產生的數據。Kubernetes提供了衆多的volume類型,包括emptyDir、hostPath、nfs、glusterfs、cephfs、ceph rbd等。

1.  emptyDir
emptyDir類型的volume在pod分配到node上時被建立,kubernetes會在node上自動分配 一個目錄,所以無需指定宿主機node上對應的目錄文件。這個目錄的初始內容爲空,當Pod從node上移除時,emptyDir中的數據會被永久刪除。emptyDir Volume主要用於某些應用程序無需永久保存的臨時目錄,多個容器的共享目錄等。下面是pod掛載emptyDir的示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

2.  hostPath
hostPath Volume爲pod掛載宿主機上的目錄或文件,使得容器可使用宿主機的高速文件系統進行存儲。缺點是,在k8s中,pod都是動態在各node節點上調度。當一個pod在當前node節點上啓動並經過hostPath存儲了文件到本地之後,下次調度到另外一個節點上啓動時,就沒法使用在以前節點上存儲的文件。下面是pod掛載hostPath的示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data

3.  pod持久存儲
方式一: pod直接掛載nfs-server

volumes:
  - name: nfs
    nfs:
      server: 192.168.1.1
      path:"/"

靜態提供:管理員手動建立多個PV,供PVC使用。
動態提供:動態建立PVC特定的PV,並綁定。

方式二: 手動建立PV
Persistent Volume(持久化卷)簡稱PV,是一個Kubernetes資源對象,咱們能夠單首創建一個PV,它不和Pod直接發生關係,而是經過Persistent Volume Claim,簡稱PVC來實現動態綁定, 咱們會在Pod定義裏指定建立好的PVC, 而後PVC會根據Pod的要求去自動綁定合適的PV給Pod使用。

持久化卷下PV和PVC概念
Persistent Volume(PV)是由管理員設置的存儲,它是羣集的一部分。就像節點是集羣中的資源同樣,PV 也是集羣中的資源。 PV 是 Volume 之類的卷插件,但具備獨立於使用 PV 的 Pod 的生命週期。此 API 對象包含存儲實現的細節,即 NFS、iSCSI 或特定於雲供應商的存儲系統。

PersistentVolumeClaim(PVC)是用戶存儲的請求。它與 Pod 類似,Pod 消耗節點資源,PVC 消耗 PV 資源。Pod 能夠請求特定級別的資源(CPU 和內存)。PVC聲明能夠請求特定的大小和訪問模式(例如,能夠以讀/寫一次或只讀屢次模式掛載)。

它和普通Volume的區別是什麼呢?
普通Volume和使用它的Pod之間是一種靜態綁定關係,在定義Pod的文件裏,同時定義了它使用的Volume。Volume是Pod的附屬品,咱們沒法單首創建一個Volume,由於它不是一個獨立的Kubernetes資源對象。

配置示例:

pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /somepath
    server: 192.168.1.1

查看PV

# kubectl get pv
NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                             STORAGECLASS     REASON    AGE
nfs-pv-heketi                              300Mi      ROX           Retain          Bound     default/nfs-pvc-heketi                                       7d
pvc-02b8a30d-8e28-11e7-a07a-025622f1d9fa   50Gi       RWX           Retain          Bound     kube-public/jenkins-pvc           heketi-storage             5d

PV能夠設置三種回收策略:保留(Retain),回收(Recycle)和刪除(Delete)。
保留策略:容許人工處理保留的數據。
刪除策略:將刪除pv和外部關聯的存儲資源,須要插件支持。
回收策略:將執行清除操做,以後能夠被新的pvc使用,須要插件支持。

PV的狀態:
Available :資源還沒有被claim使用
Bound :已經綁定到某個pvc上
Released : 對應的pvc被刪除,可是資源尚未被集羣回收
Failed : 自動回收失敗

PV訪問權限
ReadWriteOnce : 被單個節點mount爲讀寫rw模式
ReadOnlyMany : 被多個節點mount爲只讀ro模式
ReadWriteMany : 被多個節點mount爲讀寫rw模式

配置示例

pv的配置定義
# pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

pod配置文件中應用pv
# mypod.yaml
volumes:
  - name: mypod
    persistentVolumeClaim:
      claimName: myclaim

kubernetes 快速批量建立 PV & PVC 腳本
-  快速批量建立nfs pv

for i in {3..6}; do
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv00${i}
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce  #這裏根據須要配置ReadWriteOnce或者ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /volume1/harbor/nfs${i}
    server: 192.168.2.4
EOF
done          

-  快速批量建立nfs pvc

for i in {3..6}; do
cat <<EOF | kubectl delete -f -
kind: PersistentVolumeClaim 
apiVersion: v1 
metadata:  
  name: pvc00${i}-claim 
spec:  
  accessModes:   
    - ReadWriteOnce 
  resources:    
    requests:      
      storage: 100Gi
EOF
done

14.  Pod水平自動擴展(HPA)
Kubernetes有一個強大的功能,它能在運行的服務上進行編碼並配置彈性伸縮。若是沒有彈性伸縮功能,就很難適應部署的擴展和知足SLAs。這一功能稱爲Horizontal Pod Autoscaler (HPA),這是kubernetes的一個很重要的資源對象。HPA是Kubernetes中彈性伸縮API組下的一個API資源。當前穩定的版本是autoscaling/v1,它只提供了對CPU自動縮放的支持

Horizontal Pod Autoscaling,即pod的水平自動擴展。自動擴展主要分爲兩種,其一爲水平擴展,針對於實例數目的增減;其二爲垂直擴展,即單個實例可使用的資源的增減。HPA屬於水平自動擴展。HPA的操做對象是RC、RS或Deployment對應的Pod,根據觀察到的CPU等實際使用量與用戶的指望值進行比對,作出是否須要增減實例數量的決策。

1.  爲何使用HPA
使用HPA,能夠根據資源的使用狀況或者自定義的指標,實現部署的自動擴展和縮減,讓部署的規模接近於實際服務的負載。HPA能夠爲您的服務帶來兩個直接的幫助:
- 在須要計算和內存資源時提供資源,在不須要時釋放它們
- 按需增長/下降性能以實現SLA

2.  HPA原理
它根據Pod當前系統的負載來自動水平擴容,若是系統負載超過預約值,就開始增長Pod的個數,若是低於某個值,就自動減小Pod的個數。目前Kubernetes的HPA只能根據CPU等資源使用狀況去度量系統的負載。HPA會根據監測到的CPU/內存利用率(資源指標),或基於第三方指標應用程序(如Prometheus等)提供的自定義指標,自動調整副本控制器、部署或者副本集合的pods數量(定義最小和最大pods數)。HPA是一種控制迴路,它的週期由Kubernetes的controller manager 的--horizontal-pod-autoscaler-sync-period標誌控制(默認值是30s

在通常狀況下HPA是由kubectl來提供支持的。可使用kubectl進行建立、管理和刪除:
建立HPA
- 帶有manifest: "kubectl create -f <HPA_MANIFEST>"
- 沒有manifest(只支持CPU):"kubectl autoscale deployment hello-world –min=2 --man=5 –-cpu-percent=50"

獲取hpa信息
- 基本信息: "kubectl get hpa hello-world"
- 細節描述: "kubectl describe hpa hello-world"

刪除hpa
# kubectl delete hpa hello-world

下面是一個HPA manifest定義的例子:

這裏使用了autoscaling/v2beta1版本,用到了cpu和內存指標
控制hello-world項目部署的自動縮放
定義了副本的最小值1
定義了副本的最大值10
當知足時調整大小:
- CPU使用率超過50%
- 內存使用超過100Mi

3.  HPA條件
HPA經過按期(按期輪詢的時間經過--horizontal-pod-autoscaler-sync-period選項來設置,默認的時間爲30秒)經過Status.PodSelector來查詢pods的狀態,得到pod的CPU使用率。而後,經過現有pods的CPU使用率的平均值(計算方式是最近的pod使用量(最近一分鐘的平均值,從heapster中得到)除以設定的每一個Pod的CPU使用率限額)跟目標使用率進行比較,而且在擴容時,還要遵循預先設定的副本數限制:MinReplicas <= Replicas <= MaxReplicas。

計算擴容後Pod的個數:sum(最近一分鐘內某個Pod的CPU使用率的平均值)/CPU使用上限的整數+1

4.  HPA流程
- 建立HPA資源,設定目標CPU使用率限額,以及最大、最小實例數
- 收集一組中(PodSelector)每一個Pod最近一分鐘內的CPU使用率,並計算平均值
- 讀取HPA中設定的CPU使用限額
- 計算:平均值之和/限額,求出目標調整的實例個數
- 目標調整的實例數不能超過1中設定的最大、最小實例數,若是沒有超過,則擴容;超過,則擴容至最大的實例個數
- 回到2,不斷循環

5.  HPA例外
考慮到自動擴展的決策可能須要一段時間纔會生效,甚至在短期內會引入一些噪聲。例如當pod所須要的CPU負荷過大,從而運行一個新的pod進行分流,在建立過程當中,系統的CPU使用量可能會有一個攀升的過程。因此,在每一次做出決策後的一段時間內,將再也不進行擴展決策。對於ScaleUp (縱向擴展)而言,這個時間段爲3分鐘,Scaledown爲5分鐘。

HPA容許必定範圍內的CPU使用量的不穩定,只有 avg(CurrentPodsConsumption) / Target 小於90%或者大於110%時纔會觸發擴容或縮容,避免頻繁擴容、縮容形成顛簸。

【擴展】
Scale Up (縱向擴展) :主要是利用現有的存儲系統,經過不斷增長存儲容量來知足數據增加的需求。可是這種方式只增長了容量,而帶寬和計算能力並無相應的增長。因此,整個存儲系統很快就會達到性能瓶頸,須要繼續擴展。

Scale-out (橫向擴展):一般是以節點爲單位,每一個節點每每將包含容量、處理能力和I / O帶寬。一個節點被添加到存儲系統,系統中的三種資源將同時升級。這種方式容量增加和性能擴展(即增長額外的控制器)是同時進行。並且,Scale-out架構的存儲系統在擴展以後,從用戶的視角看起來仍然是一個單一的系統,這一點與咱們將多個相互獨立的存儲系統簡單的疊加在一個機櫃中是徹底不一樣的。因此scale out方式使得存儲系統升級工做大大簡化,用戶可以真正實現按需購買,下降TCO。

6.  爲何HPA選擇相對比率
爲了簡便,選用了相對比率(90%的CPU資源)而不是0.6個CPU core來描述擴容、縮容條件。若是選擇使用絕對度量,用戶須要保證目標(限額)要比請求使用的低,不然,過載的Pod未必可以消耗那麼多,從而自動擴容永遠不會被觸發:假設設置CPU爲1個核,那麼這個pod只能使用1個核,可能Pod在過載的狀況下也不能徹底利用這個核,因此擴容不會發生。在修改申請資源時,還有同時調整擴容的條件,好比將1個core變爲1.2core,那麼擴容條件應該同步改成1.2core,這樣的話,就真是太麻煩了,與自動擴容的目標相悖。

7.  安裝需求
在HPA能夠在Kubernetes集羣上使用以前,有一些元素須要在系統中安裝和配置。檢查肯定Kubernetes集羣服務正在運行而且至少包含了這些標誌:
kube-api:requestheader-client-ca-file
kubelet:read-only-port 在端口10255
kube-controller:可選,只在須要和默認值不一樣時使用
horizontal-pod-autoscaler-downscale-delay:」5m0s」
horizontal-pod-autoscaler-upscale-delay:」3m0s」
horizontal-pod-autoscaler-sync-period: 「30s」

HPA的實例說明:

1)建立Deployment
[root@k8s-master01 ~]# cat << EOF > lykops-hpa-deploy.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: lykops-hpa-deploy
  labels:
    software: apache
    project: lykops
    app: hpa
    version: v1      
spec:
  replicas: 1 
  selector:
    matchLabels:
      name: lykops-hpa-deploy
      software: apache
      project: lykops
      app: hpa
      version: v1
  template:
    metadata:
      labels:
        name: lykops-hpa-deploy
        software: apache
        project: lykops
        app: hpa
        version: v1
    spec:
      containers:
      - name: lykops-hpa-deploy
        image: web:apache
        command: [ "sh", "/etc/run.sh" ]
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        resources:
          requests:
            cpu: 0.001
            memory: 4Mi
          limits:
            cpu: 0.01
            memory: 16Mi
EOF

建立這個實例
[root@k8s-master01 ~]# kubectl create -f lykops-hpa-deploy.yaml --record

2)建立service
[root@k8s-master01 ~]# 
cat << EOF > lykops-hpa-deploy-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: lykops-hpa-svc
  labels:
    software: apache
    project: lykops
    app: hpa
    version: v1
spec:
  selector:
    software: apache
    project: lykops
    app: hpa
    version: v1
    name: lykops-hpa-deploy
  ports:
  - name: http
    port: 80
    protocol: TCP
EOF

建立這個service
[root@k8s-master01 ~]# kubectl create -f lykops-hpa-deploy-svc.yaml

3)建立HPA
[root@k8s-master01 ~]# cat << EOF > lykops-hpa.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: lykops-hpa
  labels:
    software: apache
    project: lykops
    app: hpa
    version: v1
spec:
  scaleTargetRef:
    apiVersion: v1
    kind: Deployment
    name: lykops-hpa-deploy
    #這裏只能爲這三項
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 5
EOF

建立這個HPA
[root@k8s-master01 ~]# kubectl create -f lykops-hpa.yaml

4)測試
多臺機器不斷訪問service的clusterIP地址,而後能夠看出是否增長pod數了
相關文章
相關標籤/搜索