Pod
pod在之前說過,pod是kubernetes集群中是最小的調度單元,pod中可以運行多個容器,而node又可以包含多個pod,關系如下圖:
在對pod的用法進行說明之前,有必要先對docker容器進行說明
在使用docker時,可以使用docker run命令創建一個容器,而在kubernetes集群中對長時間運行容器的要求是:
- 主程序需要一直在前台執行
如果我們創建的docker鏡像的啟動命令是后台執行程序,例如Linux腳本:
nohup ./start.sh &
那么kubelet在創建這個Pod執行完這個命令后,會認為這個Pod執行完畢,從而進行銷毀pod,如果pod定義了ReplicationController,那么銷毀了還會在創建,繼續執行上述的命令然后又銷毀,然后又重建,這樣就會陷入惡性循環,這也是為什么執行的命令要運行在前台的原因,這一點一定要注意
Pod生命周期和重啟策略
Pod的重啟策略(RestartPolicy):
- Always:當容器失敗時,由kubelet自動重啟該容器
- OnFailure:當容器終止運行且退出代碼不為0的時候,由kubelet自動重啟
- Never:不論容器運行狀態如何,kubelet永遠都不會重啟該容器
Pod的重啟策略和控制器息息相關,每種控制器對Pod的重啟策略如下:
- RC和DaemonSet:必須設置為Always,需要保證該容器持續運行
- Job:OnFailure和Never,確保容器執行完畢后不在重啟
- kubelet:在Pod失效時自動重啟它,不論將RestartPolicy設置成什么值,也不會對Pod進行健康檢查
我們簡單介紹完kubernetes的邏輯結構之后我們來看一下k8s控制器資源,k8s控制器資源分很多種,有replication controller,deployment,Horizontal pod autoscaler,statefulSet,daemonSet,job等...,接下來我們來詳細分析一下下面資源的使用場景和區別
replication Controller控制器
replication controller簡稱RC,是kubernetes系統中的核心概念之一,簡單來說,它其實定義了一個期望的場景,即聲明某種pod的副本數量在任意時刻都復合某個預期值,所以RC的定義包含以下部分:
- pod期待的副本數量
- 用於篩選目標pod的Label Selector
- 當pod的副本數量小於期望值時,用於創建新的pod的pod模板(template)
下面是一個完整的RC定義的例子,即確保擁有app=mynginx標簽的這個pod在整個kubernetes集群中始終只有一個副本,紅色字體一定要一直,因為我們的控制器就是根據標簽篩選出來的(因為pod的ip和pod名字都會變化):
[root@master ~]# vim nginx.yaml apiVersion: v1 kind: ReplicationController metadata: name: myweb namespace: default spec: replicas: 1 selector: app: mynginx template: metadata: labels: app: mynginx spec: containers: - name: mycontainer image: lizhaoqwe/nginx:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 80
在我們定義了一個RC並將其提交到kubernetes集群中后,master上的controller Manager組件就得到了通知,定期巡檢系統中當前存貨的目標pod,並確保目標pod實力數量剛好等於此RC的期望值,如果多余期望值,則停掉一些,如果少於期望值會再創建一些。
以下面3個Node節點的集群為例,說明kubernetes是如何通過RC來實現pod副本數量自動控制的機制,我們創建2個pod運行redis-slave,系統可能會再兩個節點上創建pod,如下圖
假設Node2上的pod意外終止,則根據RC定義的replicas數量1,kubernetes將會自動創建並啟動兩個新的pod,以保證在整個集群中始終有兩個redis-slave Pod運行,如下圖所示,系統可能選擇Node3或者Node1來創建新的pod
此外,我們還可以通過修改RC的副本數量,來實現Pod的動態擴容和縮容
[root@master ~]# kubectl scale --replicas=3 rc myweb replicationcontroller/myweb scaled
結果如下
這里需要注意的是,刪除RC並不會影響通過該RC已創建好的pod,為了刪除所有pod,可以設置replicas的值為0,然后更新該RC,另外,kubectl提供了stop和delete命令來一次性刪除RC和RC控制的全部Pod
ReplicaSet
上一階段講過了ReplicationController控制器之后,相信大家對其已經了解了,ReplicaSet控制器其實就是ReplicationController的升級版,官網解釋為下一代的“RC”,ReplicationController和ReplicaSet的唯一區別是ReplicaSet支持基於集合的Label selector,而RC只支持基於等式的Label Selector,這使得ReplicaSet的功能更強,下面等價於之前的RC例子的ReokucaSet的定義
[root@master ~]# vim replicaSet.yaml apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: name: myweb namespace: default spec: replicas: 1 selector: matchLabels: app: mynginx matchExpressions: - {key: app, operator: In, values: [mynginx]} template: metadata: labels: app: mynginx spec: containers: - name: mycontainer image: lizhaoqwe/nginx:v1 imagePullPolicy: IfNotPresent ports: - containerPort: 80
kubectl命令行工具適用於RC的絕大部分命令同樣適用於ReplicaSet,此外,我們當前很少單獨適用ReplicaSet,它主要被Deployment這個更高層的資源對象所使用,從而形成一整套Pod創建,刪除,更新的編排機制,我們在使用Deployment時無需關心它是如何維護和創建ReplicaSet的,這一切都是自動發生的
最后,總結一下RC(ReplicaSet)的一些特性和作用:
- 在絕大多數情況下,我們通過定義一個RC實現Pod的創建及副本數量的自動控制
- 在RC里包括完整的Pod定義模板
- RC通過Label Selector機制實現對Pod副本的自動控制
- 通過改變RC里的Pod副本數量,可以實現Pod的擴容和縮容
- 通過改變RC里Pod模板中的鏡像版本,可以實現滾動升級
Deployment
Deployment是kubernetes在1.2版本中引入的新概念,用於更好的解決Pod的編排問題,為此,Deployment在內部使用了ReplicaSet來實現目的,我們可以把Deployment理解為ReplicaSet的一次升級,兩者的相似度超過90%
Deployment的使用場景有以下幾個:
- 創建一個Deployment對象來生成對應的ReplicaSet並完成Pod副本的創建
- 檢查Deployment的狀態來看部署動作是否完成(Pod副本數量是否達到了預期的值)
- 更新Deployment以創建新的Pod(比如鏡像升級)
- 如果當前Deployment不穩定,可以回滾到一個早先的Deployment版本
- 暫停Deployment以便於一次性修改多個PodTemplateSpec的配置項,之后在恢復Deployment,進行新的發布
- 擴展Deployment以應對高負載
- 查看Deployment的狀態,以此作為發布是否成功的紙幣哦
- 清理不在需要的舊版本ReplicaSet
除了API生命與Kind類型有區別,Deployment的定義與Replica Set的定義很類似,我們這里還是以上面為例子
apiVersion: extensions/v1beta1 apiVersion: apps/v1
kind: ReplicaSet kind: Deployment
執行文件
[root@master ~]# kubectl apply -f deploy.yaml
deployment.apps/myweb created
查看結果
[root@master ~]# kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE myweb 1/1 1 1 5m5s
解釋一下上面的顯示
NAME:你的deployment控制器的名字
READY:已經准備好的Pod個數/所期望的Pod個數
UP-TO-DATE:最新版本的Pod數量,用於在執行滾動升級時,有多少個Pod副本已經成功升級
AVAILABLE:當前集群中可用的Pod數量,也就是集群中存活的Pod數量
StatefulSet
在kubernetes系統中,Pod的管理對象RC,Deployment,DaemonSet和Job都面向無狀態的服務,但現實中有很多服務時有狀態的,比如一些集群服務,例如mysql集群,集群一般都會有這四個特點:
- 每個節點都是有固定的身份ID,集群中的成員可以相互發現並通信
- 集群的規模是比較固定的,集群規模不能隨意變動
- 集群中的每個節點都是有狀態的,通常會持久化數據到永久存儲中
- 如果磁盤損壞,則集群里的某個節點無法正常運行,集群功能受損
如果你通過RC或Deployment控制Pod副本數量來實現上述有狀態的集群,就會發現第一點是無法滿足的,因為Pod名稱和ip是隨機產生的,並且各Pod中的共享存儲中的數據不能都動,因此StatefulSet在這種情況下就派上用場了,那么StatefulSet具有以下特性:
- StatefulSet里的每個Pod都有穩定,唯一的網絡標識,可以用來發現集群內的其它成員,假設,StatefulSet的名稱為fengzi,那么第1個Pod叫fengzi-0,第2個叫fengzi-1,以此類推
- StatefulSet控制的Pod副本的啟停順序是受控的,操作第N個Pod時,前N-1個Pod已經是運行且准備狀態
- StatefulSet里的Pod采用穩定的持久化存儲卷,通過PV或PVC來實現,刪除Pod時默認不會刪除與StatefulSet相關的存儲卷(為了保證數據的安全)
StatefulSet除了要與PV卷捆綁使用以存儲Pod的狀態數據,還要與Headless,Service配合使用,每個StatefulSet定義中都要生命它屬於哪個Handless Service,Handless Service與普通Service的關鍵區別在於,它沒有Cluster IP
一、創建pv
[root@master ~]# cat createpv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv01 spec: nfs: path: /share_v1 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv02 spec: nfs: path: /share_v2 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv03 spec: nfs: path: /share_v3 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 1Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv04 spec: nfs: path: /share_v4 server: 192.168.254.14 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv05 spec: nfs: path: /share_v5 server: 192.168.254.11 accessModes: - ReadWriteMany - ReadWriteOnce capacity: storage: 2Gi
二、創建pvc和statfulset控制器管理的pod
[root@master ~]# cat state.yaml apiVersion: v1 kind: Service metadata: name: myapp-svc namespace: default labels: app: myapp spec: ports: - name: http port: 80 clusterIP: None selector: app: myapp-pod --- apiVersion: apps/v1 kind: StatefulSet metadata: name: myapp spec: serviceName: myapp-svc replicas: 3 selector: matchLabels: app: myapp-pod template: metadata: labels: app: myapp-pod spec: containers: - name: myapp image: liwang7314/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: myappdata mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: myappdata spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
三、創建完畢后觀察
[root@master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-0 1/1 Running 0 8s myapp-1 1/1 Running 0 5s myapp-2 1/1 Running 0 3s
DemonSet
在每一個node節點上只調度一個Pod,因此無需指定replicas的個數,比如:
- 在每個node上都運行一個日志采集程序,負責收集node節點本身和node節點之上的各個Pod所產生的日志
- 在每個node上都運行一個性能監控程序,采集該node的運行性能數據
[root@localhost ~]# cat daemon.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-cloud-logging namespace: kube-system labels: k8s-app: fluentd-cloud-logging spec: selector: matchLabels: k8s-app: fluentd-cloud-logging template: metadata: namespace: kube-system labels: k8s-app: fluentd-cloud-logging spec: containers: - name: fluentd-cloud-logging image: kayrus/fluentd-elasticsearch:1.20 resources: limits: cpu: 100m memory: 200Mi env: - name: FLUENTD_ARGS value: -q volumeMounts: - name: varlog mountPath: /var/log readOnly: false - name: containers mountPath: /var/lib/docker/containers readOnly: false volumes: - name: containers hostPath: path: /usr - name: varlog hostPath: path: /usr/sbin
查看pod所在的節點
[root@localhost ~]# kubectl get pods -n kube-system -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-bccdc95cf-8sqzn 1/1 Running 2 2d7h 10.244.0.6 master <none> <none> coredns-bccdc95cf-vt8nz 1/1 Running 2 2d7h 10.244.0.7 master <none> <none> etcd-master 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> fluentd-cloud-logging-6xx4l 1/1 Running 0 4h24m 10.244.2.7 node2 <none> <none> fluentd-cloud-logging-qrgg6 1/1 Rruning 0 4h24m 10.244.1.7 node1 <none> <none> kube-apiserver-master 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-controller-manager-master 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-flannel-ds-amd64-c97wh 1/1 Running 0 2d7h 192.168.254.12 node1 <none> <none> kube-flannel-ds-amd64-gl6wg 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-flannel-ds-amd64-npsqf 1/1 Running 0 2d7h 192.168.254.10 node2 <none> <none> kube-proxy-gwmx8 1/1 Running 1 2d7h 192.168.254.13 master <none> <none> kube-proxy-phqk2 1/1 Running 0 2d7h 192.168.254.12 node1 <none> <none> kube-proxy-qtt4b 1/1 Running 0 2d7h 192.168.254.10 node2 <none> <none> kube-scheduler-master 1/1 Running 2 2d7h 192.168.254.13 master <none> <none>