1、yaml格式的Pod配置文件內容及注解
深入Pod之前,首先我們來了解下Pod的yaml整體文件內容及功能注解。
如下:
# yaml格式的pod定義文件完整內容: apiVersion: v1 #必選,版本號,例如v1 kind: Pod #必選,Pod metadata: #必選,元數據 name: string #必選,Pod名稱 namespace: string #必選,Pod所屬的命名空間 labels: #自定義標簽 - name: string #自定義標簽名字 annotations: #自定義注釋列表 - name: string spec: #必選,Pod中容器的詳細定義 containers: #必選,Pod中容器列表 - name: string #必選,容器名稱 image: string #必選,容器的鏡像名稱 imagePullPolicy: [Always | Never | IfNotPresent] #獲取鏡像的策略 Alawys表示下載鏡像 IfnotPresent表示優先使用本地鏡像,否則下載鏡像,Nerver表示僅使用本地鏡像 command: [string] #容器的啟動命令列表,如不指定,使用打包時使用的啟動命令 args: [string] #容器的啟動命令參數列表 workingDir: string #容器的工作目錄 volumeMounts: #掛載到容器內部的存儲卷配置 - name: string #引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名 mountPath: string #存儲卷在容器內mount的絕對路徑,應少於512字符 readOnly: boolean #是否為只讀模式 ports: #需要暴露的端口庫號列表 - name: string #端口號名稱 containerPort: int #容器需要監聽的端口號 hostPort: int #容器所在主機需要監聽的端口號,默認與Container相同 protocol: string #端口協議,支持TCP和UDP,默認TCP env: #容器運行前需設置的環境變量列表 - name: string #環境變量名稱 value: string #環境變量的值 resources: #資源限制和請求的設置 limits: #資源限制的設置 cpu: string #Cpu的限制,單位為core數,將用於docker run --cpu-shares參數 memory: string #內存限制,單位可以為Mib/Gib,將用於docker run --memory參數 requests: #資源請求的設置 cpu: string #Cpu請求,容器啟動的初始可用數量 memory: string #內存清楚,容器啟動的初始可用數量 livenessProbe: #對Pod內個容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器,檢查方法有exec、httpGet和tcpSocket,對一個容器只需設置其中一種方法即可 exec: #對Pod容器內檢查方式設置為exec方式 command: [string] #exec方式需要制定的命令或腳本 httpGet: #對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、port path: string port: number host: string scheme: string HttpHeaders: - name: string value: string tcpSocket: #對Pod內個容器健康檢查方式設置為tcpSocket方式 port: number initialDelaySeconds: 0 #容器啟動完成后首次探測的時間,單位為秒 timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒 periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次 successThreshold: 0 failureThreshold: 0 securityContext: privileged: false restartPolicy: [Always | Never | OnFailure] #Pod的重啟策略,Always表示一旦不管以何種方式終止運行,kubelet都將重啟,OnFailure表示只有Pod以非0退出碼退出才重啟,Nerver表示不再重啟該Pod nodeSelector: obeject #設置NodeSelector表示將該Pod調度到包含這個label的node上,以key:value的格式指定 imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定 - name: string hostNetwork: false #是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡 volumes: #在該pod上定義共享存儲卷列表 - name: string #共享存儲卷名稱 (volumes類型有很多種) emptyDir: {} #類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值 hostPath: string #類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄 path: string #Pod所在宿主機的目錄,將被用於同期中mount的目錄 secret: #類型為secret的存儲卷,掛載集群與定義的secre對象到容器內部 scretname: string items: - key: string path: string configMap: #類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部 name: string items: - key: string path: string
2、Pod基本用法:
在使用docker時,我們可以使用docker run命令創建並啟動一個容器,而在Kubernetes系統中對長時間運行的容器要求是:其主程序需要一直在前台運行。如果我們創建的docker鏡像的啟動命令是后台執行程序,例如Linux腳本:
nohup ./startup.sh &
則kubelet創建包含這個容器的pod后運行完該命令,即認為Pod執行結束,之后根據RC中定義的pod的replicas副本數量生產一個新的pod,而一旦創建出新的pod,將在執行完命令后陷入無限循環的過程中,這就是Kubernetes需要我們創建的docker鏡像以一個前台命令作為啟動命令的原因。
對於無法改造為前台執行的應用,也可以使用開源工具supervisor輔助進行前台運行的功能。
****Pod可以由一個或多個容器組合而成
例如:兩個容器應用的前端frontend和redis為緊耦合的關系,應該組合成一個整體對外提供服務,則應該將這兩個打包為一個pod.
配置文件frontend-localredis-pod.yaml如下:
apiVersion:v1 kind: Pod metadata: name: redis-php label: name: redis-php spec: containers: - name: frontend image: kubeguide/guestbook-php-frontend:localredis ports: - containersPort: 80 - name: redis-php image:kubeguide/redis-master ports: - containersPort: 6379
屬於一個Pod的多個容器應用之間相互訪問只需要通過localhost就可以通信,這一組容器被綁定在一個環境中。
使用kubectl create創建該Pod后,get Pod信息可以看到如下圖:
#kubectl get gods NAME READY STATUS RESTATS AGE redis-php 2/2 Running 0 10m
可以看到READY信息為2/2,表示Pod中的兩個容器都成功運行了.
查看pod的詳細信息,可以看到兩個容器的定義和創建過程。
[root@kubernetes-master ~]# kubectl describe redis-php the server doesn't have a resource type "redis-php" [root@kubernetes-master ~]# kubectl describe pod redis-php Name: redis-php Namespace: default Node: kubernetes-minion/10.0.0.23 Start Time: Wed, 12 Apr 2017 09:14:58 +0800 Labels: name=redis-php Status: Running IP: 10.1.24.2 Controllers: <none> Containers: nginx: Container ID: docker://d05b743c200dff7cf3b60b7373a45666be2ebb48b7b8b31ce0ece9be4546ce77 Image: nginx Image ID: docker-pullable://docker.io/nginx@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582 Port: 80/TCP State: Running Started: Wed, 12 Apr 2017 09:19:31 +0800
3、靜態Pod
靜態pod是由kubelet進行管理的僅存在於特定Node的Pod上,他們不能通過API Server進行管理,無法與ReplicationController、Deployment或者DaemonSet進行關聯,並且kubelet無法對他們進行健康檢查。靜態Pod總是由kubelet進行創建,並且總是在kubelet所在的Node上運行。
創建靜態Pod有兩種方式:配置文件或者HTTP方式
1)配置文件方式
首先,需要設置kubelet的啟動參數"--config",指定kubelet需要監控的配置文件所在的目錄,kubelet會定期掃描該目錄,冰根據目錄中的 .yaml或 .json文件進行創建操作
假設配置目錄為/etc/kubelet.d/配置啟動參數:--config=/etc/kubelet.d/,然后重啟kubelet服務后,再宿主機受用docker ps或者在Kubernetes Master上都可以看到指定的容器在列表中
由於靜態pod無法通過API Server直接管理,所以在master節點嘗試刪除該pod,會將其變為pending狀態,也不會被刪除
#kubetctl delete pod static-web-node1 pod "static-web-node1" deleted #kubectl get pods NAME READY STATUS RESTARTS AGE static-web-node1 0/1 Pending 0 1s
要刪除該pod的操作只能在其所在的Node上操作,將其定義的.yaml文件從/etc/kubelet.d/目錄下刪除
#rm -f /etc/kubelet.d/static-web.yaml #docker ps
4、Pod容器共享Volume
Volume類型包括:emtyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、scsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume等等,可以定義多個Volume,每個Volume的name保持唯一。在同一個pod中的多個容器能夠共享pod級別的存儲卷Volume。Volume可以定義為各種類型,多個容器各自進行掛載操作,講一個Volume掛載為容器內需要的目錄。
如下圖:

如上圖中的Pod中包含兩個容器:tomcat和busybox,在pod級別設置Volume “app-logs”,用於tomcat想其中寫日志文件,busybox讀日志文件。
配置文件如下:
apiVersion:v1 kind: Pod metadata: name: redis-php label: name: volume-pod spec: containers: - name: tomcat image: tomcat ports: - containersPort: 8080 volumeMounts: - name: app-logs mountPath: /usr/local/tomcat/logs - name: busybox image:busybox command: ["sh","-C","tail -f /logs/catalina*.log"] volumes: - name: app-logs emptyDir:{}
busybox容器可以通過kubectl logs查看輸出內容
#kubectl logs volume-pod -c busybox
tomcat容器生成的日志文件可以登錄容器查看
#kubectl exec -ti volume-pod -c tomcat -- ls /usr/local/tomcat/logs
5.Pod的配置管理
應用部署的一個最佳實踐是將應用所需的配置信息於程序進行分離,這樣可以使得應用程序被更好的復用,通過不用配置文件也能實現更靈活的功能。將應用打包為容器鏡像后,可以通過環境變量或外掛文件的方式在創建容器時進行配置注入。ConfigMap是Kubernetes v1.2版本開始提供的一種統一集群配置管理方案。
5.1 ConfigMap:容器應用的配置管理
容器使用ConfigMap的典型用法如下:
(1)生產為容器的環境變量。
(2)設置容器啟動命令的啟動參數(需設置為環境變量)。
(3)以Volume的形式掛載為容器內部的文件或目錄。
ConfigMap以一個或多個key:value的形式保存在Kubernetes系統中共應用使用,既可以用於表示一個變量的值,也可以表示一個完整的配置文件內容。
通過yuaml配置文件或者直接使用kubelet create configmap 命令的方式來創建ConfigMap
5.2 ConfigMap的創建
舉個小例子cm-appvars.yaml來描述將幾個應用所需的變量定義為ConfigMap的用法:
# vim cm-appvars.yaml apiVersion: v1 kind: ConfigMap metadata: name: cm-appvars data: apploglevel: info appdatadir: /var/data
執行kubectl create命令創建該ConfigMap
#kubectl create -f cm-appvars.yaml configmap "cm-appvars.yaml" created
查看建立好的ConfigMap:
#kubectl get configmap NAME DATA AGE cm-appvars 2 3s [root@kubernetes-master ~]# kubectl describe configmap cm-appvars Name: cm-appvars Namespace: default Labels: <none> Annotations: <none> Data ==== appdatadir: 9 bytes apploglevel: 4 bytes [root@kubernetes-master ~]# kubectl get configmap cm-appvars -o yaml apiVersion: v1 data: appdatadir: /var/data apploglevel: info kind: ConfigMap metadata: creationTimestamp: 2017-04-14T06:03:36Z name: cm-appvars namespace: default resourceVersion: "571221" selfLink: /api/v1/namespaces/default/configmaps/cm-appvars uid: 190323cb-20d8-11e7-94ec-000c29ac8d83
另:創建一個cm-appconfigfile.yaml描述將兩個配置文件server.xml和logging.properties定義為configmap的用法,設置key為配置文件的別名,value則是配置文件的文本內容:
apiVersion: v1 kind: ConfigMap metadata: name: cm-appvars data: key-serverxml: <?xml Version='1.0' encoding='utf-8'?> <Server port="8005" shutdown="SHUTDOWN"> ..... </service> </Server> key-loggingproperties: "handlers=lcatalina.org.apache.juli.FileHandler, ...."
在pod "cm-test-app"定義中,將configmap "cm-appconfigfile"中的內容以文件形式mount到容器內部configfiles目錄中。
Pod配置文件cm-test-app.yaml內容如下:
#vim cm-test-app.yaml apiVersion: v1 kind: Pod metadata: name: cm-test-app spec: containers: - name: cm-test-app image: tomcat-app:v1 ports: - containerPort: 8080 volumeMounts: - name: serverxml #引用volume名 mountPath: /configfiles #掛載到容器內部目錄 configMap: name: cm-test-appconfigfile #使用configmap定義的的cm-appconfigfile items: - key: key-serverxml #將key=key-serverxml path: server.xml #value將server.xml文件名進行掛載 - key: key-loggingproperties #將key=key-loggingproperties path: logging.properties #value將logging.properties文件名進行掛載
創建該Pod:
#kubectl create -f cm-test-app.yaml Pod "cm-test-app" created
登錄容器查看configfiles目錄下的server.xml和logging.properties文件,他們的內容就是configmap “cm-appconfigfile”中定義的兩個key的內容
#kubectl exec -ti cm-test-app -- bash root@cm-rest-app:/# cat /configfiles/server.xml root@cm-rest-app:/# cat /configfiles/logging.properties
5.3使用ConfigMap的條件限制
使用configmap的限制條件如下:
-
- configmap必須在pod之間創建
- configmap也可以定義為屬於某個Namespace,只有處於相同namespaces中的pod可以引用
- configmap中配額管理還未能實現
- kubelet只支持被api server管理的pod使用configmap,靜態pod無法引用
- 在pod對configmap進行掛載操作時,容器內部職能掛載為目錄,無法掛載文件。
6.Pod生命周期和重啟策略
Pod在整個生命周期過程中被定義為各種狀態,熟悉Pod的各種狀態有助於理解如何設置Pod的調度策略、重啟策略
Pod的狀態包含以下幾種,如圖:

Pod的重啟策略(RestartPolicy)應用於Pod內所有的容器,並且僅在Pod所處的Node上由kubelet進行判斷和重啟操作。當某哥容器異常退出或者健康檢查石柏師,kubelet將根據RestartPolicy的設置進行相應的操作
Pod的重啟策略包括Always、OnFailure及Nerver,默認值為Always。
kubelet重啟失效容器的時間間隔以sync-frequency乘以2n來計算,例如1、2、4、8倍等,最長延時5分鍾,並且成功重啟后的10分鍾后重置該事件。
Pod的重啟策略和控制方式息息相關,當前可用於管理Pod的控制器寶庫ReplicationController、Job、DaemonSet及直接通過kubelet管理(靜態Pod),每種控制器對Pod的重啟策略要求如下:
-
- RC和DaemonSet:必須設置為Always,需要保證該容器持續運行
- Job:OnFailure或Nerver,確保容器執行完成后不再重啟
- kubelet:在Pod失效時重啟他,不論RestartPolicy設置什么值,並且也不會對Pod進行健康檢查
7、Pod健康檢查
對Pod的健康檢查可以通過兩類探針來檢查:LivenessProbe和ReadinessProbe
-
- LivenessProbe探針:用於判斷容器是否存活(running狀態),如果LivenessProbe探針探測到容器不健康,則kubelet殺掉該容器,並根據容器的重啟策略做響應處理
- ReadinessProbe探針:用於判斷容器是否啟動完成(ready狀態),可以接受請求。如果ReadinessProbe探針探測失敗,則Pod的狀態被修改。Endpoint Controller將從service的Endpoint中刪除包含該容器所在的Pod的Endpoint。
kubelet定制執行LivenessProbe探針來診斷容器的健康狀況。LivenessProbe有三種事項方式。
(1)ExecAction:在容器內部執行一個命令,如果該命令的返回值為0,則表示容器健康
例:
apiVersion:v1 kind: Pod metadata: name: liveness-exec label: name: liveness spec: containers: - name: tomcat image: grc.io/google_containers/tomcat args: - /bin/sh - -c - echo ok >/tmp.health;sleep 10; rm -fr /tmp/health;sleep 600 livenessProbe: exec: command: - cat - /tmp/health initianDelaySeconds:15 timeoutSeconds:1
(2)TCPSocketAction:通過容器ip地址和端口號執行TCP檢查,如果能夠建立tcp連接表明容器健康
例:
kind: Pod metadata: name: pod-with-healthcheck spec: containers: - name: nginx image: nginx livenessProbe: tcpSocket: port: 80 initianDelaySeconds:30 timeoutSeconds:1
(3)HTTPGetAction:通過容器Ip地址、端口號及路徑調用http get方法,如果響應的狀態嗎大於200且小於400,則認為容器健康
例:
apiVersion:v1 kind: Pod metadata: name: pod-with-healthcheck spec: containers: - name: nginx image: nginx livenessProbe: httpGet: path: /_status/healthz port: 80 initianDelaySeconds:30 timeoutSeconds:1
對於每種探針方式,都需要設置initialDelaySeconds和timeoutSeconds兩個參數,它們含義如下:
- initialDelaySeconds:啟動容器后首次監控檢查的等待時間,單位秒
- timeouSeconds:健康檢查發送請求后等待響應的超時時間,單位秒。當發生超時就被認為容器無法提供服務無,該容器將被重啟
8.玩轉Pod調度
在Kubernetes系統中,Pod在大部分場景下都只是容器的載體而已,通常需要通過RC、Deployment、DaemonSet、Job等對象來完成Pod的調度和自動控制功能。
8.1 RC、Deployment:全自動調度
RC的主要功能之一就是自動部署容器應用的多份副本,以及持續監控副本的數量,在集群內始終維護用戶指定的副本數量。
在調度策略上,除了使用系統內置的調度算法選擇合適的Node進行調度,也可以在Pod的定義中使用NodeSelector或NodeAffinity來指定滿足條件的Node進行調度。
1)NodeSelector:定向調度
Kubernetes Master上的scheduler服務(kube-Scheduler進程)負責實現Pod的調度,整個過程通過一系列復雜的算法,最終為每個Pod計算出一個最佳的目標節點,通常我們無法知道Pod最終會被調度到哪個節點上。實際情況中,我們需要將Pod調度到我們指定的節點上,可以通過Node的標簽和pod的nodeSelector屬性相匹配來達到目的。
(1)首先通過kubectl label命令給目標Node打上標簽
kubectl label nodes <node-name> <label-key>=<label-value>
例:
#kubectllabel nodes k8s-node-1 zonenorth
(2)然后在Pod定義中加上nodeSelector的設置
例:
apiVersion:v1 kind: Pod metadata: name: redis-master label: name: redis-master spec: replicas: 1 selector: name: redis-master template: metadata: labels: name: redis-master spec: containers: - name: redis-master images: kubeguide/redis-master ports: - containerPort: 6379 nodeSelector: zone: north
運行kubectl create -f命令創建Pod,scheduler就會將該Pod調度到擁有zone=north標簽的Node上。 如果多個Node擁有該標簽,則會根據調度算法在該組Node上選一個可用的進行Pod調度。
需要注意的是:如果集群中沒有擁有該標簽的Node,則這個Pod也無法被成功調度。
2)NodeAffinity:親和性調度
該調度策略是將來替換NodeSelector的新一代調度策略。由於NodeSelector通過Node的Label進行精確匹配,所有NodeAffinity增加了In、NotIn、Exists、DoesNotexist、Gt、Lt等操作符來選擇Node。調度側露更加靈活。
8.2 DaemonSet:特定場景調度
DaemonSet用於管理集群中每個Node上僅運行一份Pod的副本實例,如圖

這種用法適合一些有下列需求的應用:
- 在每個Node上運行個以GlusterFS存儲或者ceph存儲的daemon進程
- 在每個Node上運行一個日志采集程序,例如fluentd或者logstach
- 在每個Node上運行一個健康程序,采集Node的性能數據。
DaemonSet的Pod調度策略類似於RC,除了使用系統內置的算法在每台Node上進行調度,也可以在Pod的定義中使用NodeSelector或NodeAffinity來指定滿足條件的Node范圍來進行調度。
8.3 批處理調度
9.Pod的擴容和縮榮
在實際生產環境中,我們經常遇到某個服務需要擴容的場景,也有可能因為資源精確需要縮減資源而需要減少服務實例數量,此時我們可以Kubernetes中RC提供scale機制來完成這些工作。
以redis-slave RC為例,已定義的最初副本數量為2,通過kubectl scale命令可以將Pod副本數量重新調整
#kubectl scale rc redis-slave --replicas=3 ReplicationController "redis-slave" scaled #kubectl get pods NAME READY STATUS RESTARTS AGE redis-slave-1sf23 1/1 Running 0 1h redis-slave-54wfk 1/1 Running 0 1h redis-slave-3da5y 1/1 Running 0 1h
除了可以手工通過kubectl scale命令完成Pod的擴容和縮容操作以外,新版本新增加了Horizontal Podautoscaler(HPA)的控制器,用於實現基於CPU使用路進行啟動Pod擴容縮容的功能。該控制器基於Mastger的kube-controller-manager服務啟動參數 --horizontal-pod-autoscler-sync-period定義的時長(默認30秒),周期性監控目標Pod的Cpu使用率並在滿足條件時對ReplicationController或Deployment中的Pod副本數量進行調整,以符合用戶定義的平均Pod Cpu使用率,Pod Cpu使用率來源於heapster組件,所以需預先安裝好heapster。
10.Pod的滾動升級
當集群中的某個服務需要升級時,我們需要停止目前與該服務相關的所有Pod,然后重新拉取鏡像並啟動。如果集群規模較大,因服務全部停止后升級的方式將導致長時間的服務不可用。由此,Kubernetes提供了rolling-update(滾動升級)功能來解決該問題。
滾動升級通過執行kubectl rolling-update命令一鍵完成,該命令創建一個新的RC,然后自動控制舊版本的Pod數量逐漸減少到0,同時新的RC中的Pod副本數量從0逐步增加到目標值,最終實現Pod的升級。需要注意的是,系統要求新的RC需要與舊的RC在相同的Namespace內,即不能把別人的資產轉到到自家名下。
例:將redis-master從1.0版本升級到2.0
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: labels: name: redis-master Version: v2 spec: containers: - name: master images: kubeguide/redis-master:2.0 ports: - containerPort: 6379
需要注意的點:
(1)RC的name不能與舊的RC名字相同
(2)在sele中應至少有一個label與舊的RC的label不同,以標識為新的RC。本例中新增了一個名為version的label與舊的RC區分
運行kubectl rolling-update來完成Pod的滾動升級:
#kubectl rolling-update redis-master -f redis-master-controller-v2.yaml
另一種方法就是不使用配置文件,直接用kubectl rolling-update加上--image參數指定新版鏡像名來完成Pod的滾動升級
#kubectl rolling-update redis-master --image=redis-master:2.0
與使用配置文件的方式不同的是,執行的結果是舊的RC被刪除,新的RC仍然使用就的RC的名字。
如果在更新過程總發現配置有誤,則用戶可以中斷更新操作,並通過執行kubectl rolling-update-rollback完成Pod版本的回滾。