kubernetes之pod超詳細解讀--第一篇(三)

   小編在這裏向各位博友道個歉,上篇文章確實寫的有些應付,但怎麼說,部署確實因人而異,而且很少有剛剛進公司就讓你搭建一個集羣,一般公司都有自己的集羣,所以小編覺得,側重點不應該在安裝,應該在維護!雖然有些牽強,但小編保證,這一篇絕對有質量!希望看了小編的博客,大家對pod有更深入的認識。
   這篇文章,小編打算介紹關於pod的11個重要的知識點,大家要有耐心的看下去哦!雖然內容比較多,有興趣的朋友可以細細閱讀,小編會儘可能的用比較容易理解的話和圖,去介紹比較重要並且難以理解的地方。

1. pod的基本定義和用法

  首先我們通過yaml定義的方式看看pod中可以定義哪些內容:

apiVersion: v1  #版本號
kind: Pod       #資源對象類型
metadata:       #元數據
  name: string  #pod的名稱
  namespace: string  #pod所屬的命名空間
  labels:  #自定義標籤列表
    - name: string
  annotations:  #自定義註解列表
    - name: string
spec:  #pod的容器的詳細定義
  containers:  
  - name: string   #容器的名稱
    image: string  #容器的鏡像
    imagePullPolicy: [Always|Never|IfNotPresent]  #鏡像獲取策略
    command: [string]   #容器的啓動命令列表
    args: [string]      #啓動命令參數
    workingDir: string  #容器的工作目錄
    volumeMounts:       #掛載到容器內部的存儲卷配置
    - name: string
      mountPath: string #掛載的目錄
      readOnly: boolean #是否只讀掛載
    ports:  #容器暴露的端口列表
    - name: string  #端口名稱
      containerPort: int  #容器監聽的端口
      hostPort: int  #容器所在主機需要監聽的端口
      protocol: string  #端口協議
    env: #容器中的環境變量
    - name: string 
      value: string
    resources: #資源限制設置
      limits: #最大使用資源
        cpu: string
        memory: string
      requests: #請求時資源設置
        cpu: string
        memory: string
    livenessProbe:  #對容器的健康檢查
      exec:  #通過命令的返回值
        command: [string]
      httpGet: #通過訪問容器的端口的返回的狀態碼
        path: string
        port: number
        host: string
        scheme: string
        httpHeaders:
        - name: string
          value: string
      tcpSocket:  #通過tcpSocket
        port: number
      initialDelaySeconds: 0 #首次健康檢查時間
      timeoutSeconds: 0  #健康檢查的超時時間
      periodSeconds: 0  #每次健康檢查的時間間隔
      successThreshold: 0 
      failureThreshold: 0
    securityContext:
      privileged: false
  restartPolicy: [Always|Never|OnFailure] #pod的重啓策略
  nodeSelector: object  #指定的運行pod的node標籤
  imagePullSecrets:  
  - name: string
  hostNetwork: false  #是否使用主機網絡模式
  volumes:  #該pod上定義的共享存儲卷列表
  - name: string
    emptyDir: {}  #臨時掛載
    hostPath:  #掛載宿主機目錄
      path: string
    secret: #類型爲secret存儲卷
      secretName: string
      items:
      - key: string
        path: string
    configMap: #類型爲configMap的存儲卷
      name: string
      items:
      - key: string
        path: string

是不是一下子看了這麼多配置,感覺有點濛濛的,不要着急,先苦後甜,小編接下來會一一介紹如何使用這些配置,以及這些配置有什麼作用。
Pod的基本用法:
  當然這裏小編要強調的是,如果自己定義的image,並且image運行的程序的腳本是後臺調度運行的例:nohup ./start.sh,類似這樣的,如果是在docker中可以使用docker run的方式創建並啓動這個容器,當時在kubernetes中,類似這樣後臺的程序,kubelet創建這個容器的pod之後,運行完該命令,就認爲pod執行結束,將立刻銷燬這個pod,如果給這個pod綁定了RC,那麼系統將會根據RC中的pod的副本數重新啓動這個pod,這樣下去會進入無限的死循環中,當然問題出現了肯定會有解決辦法,這裏小編向大家介紹一個服務supervisor,說實話,小編也不是很懂它,有時間再把它玩一玩,這裏小編先介紹一下它是如何讓這些後臺啓動的容器持續運行,並滿足kubernetes對容器啓動的要求:supervisor提供了一種可以同時啓動多個後臺進程,並保持supervisor自身在前臺執行的機制。(好吧我知道是廢話,但是很通俗易懂,能就這樣,強行安慰自己,感覺很nice)。
  接下來創建幾個pod,看看效果,這裏pod可以由一個或者多個container組成,先創建一個只有一個container的:

#frontend-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: frontend
  labels:
    name: frontend
spec:
  containers:
  - name: frontend
    image: docker.io/kubeguide/guestbook-php-frontend
    env:
    - name: GET_HOSTS_FROM
      value: env
    ports: 
    - containerPort: 80
  hostNetwork: true
[root@zy yaml_file]# kubectl create -f frontend-pod.yaml  #創建這個pod 

創建一個兩個container組合成一個整體的pod對外提供服務:
kubernetes之pod超詳細解讀--第一篇(三)

apiVersion: v1
kind: Pod
metadata:
  name: redis-php
  labels:
    name: redis-php
spec:
  containers:
  - name: frontend
    image: docker.io/kubeguide/guestbook-php-frontend:localredis
    ports: 
    - containerPort: 80
  - name: redis
    image: docker.io/kubeguide/redis-master
    ports:
    - containerPort: 6379

kubernetes之pod超詳細解讀--第一篇(三)
  這裏有一個container,死了,可能是鏡像出了問題,當然重點不在這裏,主要是演示如何運行2個容器在一個pod中有興趣的可以解決一下這個問題:
kubernetes之pod超詳細解讀--第一篇(三)

2. 靜態pod

  所謂的靜態的pod就是通過kubelet管理並且僅存在於特定的node上的pod,它們不能通過API server 進行管理,也無法與RC、deployment、daemonSet進行關聯,並且kubelet也不能對其進行健康檢查。他們總是由kubelet創建,並且總在kubelet所在node上運行。其中創建靜態的pod有兩種方式:配置文件方式HTTP方式
① 配置文件方式
這裏我的kubelet的配置文件在/etc/kubernetes/kubelet
kubernetes之pod超詳細解讀--第一篇(三)
然後在配置文件中加入:--config=/k8s/yaml_file/static-web.yaml

#static-web.yaml
apiVersion: v1
kind: Pod
metadata: 
  name: static-web
  labels:
    name: static-web
spec:
  containers:
  - name: static-web
    image: nginx
    ports:
    - name: web
      containerPort: 80
[root@zy yaml_file]# systemctl restart kubelet  #重啓kubelet服務
[root@zy yaml_file]# docker ps #查看本機的容器:docker ps

kubernetes之pod超詳細解讀--第一篇(三)
kubernetes之pod超詳細解讀--第一篇(三)
  此時這個靜態的pod就正在創建,並且使用kubectl delete pod pod_name刪除不了這個pod的,只能讓這個pod處於pending狀態,當然只要將該node上的定義這個pod的文件刪除,就能將這個pod從這個node上刪除了。
② HTTP方式:因爲簡單這裏就不在演示,只要在kubelet的配置文件中加入:--manifest-url=xxx,kubelet就會定期的從指定的URL中下載pod的定義文件(yaml|json),然後根據定義在自己的node上創建pod。

3.pod容器共享volume

  這裏的配置管理,就是將程序和配置分離,使程序更加靈活,一般的我們在打包應用程序爲鏡像,可以通過環境變量或者外掛volume的方式在創建容器的時候進行配置注入,但是如果機器規模比較大的時候,對多容器進行不同的配置注入將十分複雜,所以這裏我們介紹一種便捷方式configMap。
configMap典型使用場景:
 生成容器內部的環境變量
 設置容器的啓動命令的參數
 以volume的形式掛載爲容器內部的文件或者目錄
在同一個pod中的多個容器之間能夠共享pod級別的存儲卷volume,volume可以被定義爲各種類型,多個容器之間進行各自掛載,將一個volume掛載爲容器內部的目錄:
kubernetes之pod超詳細解讀--第一篇(三)
上圖就是一個pod中有兩個容器同時掛載一個volume共享,其中實現的功能爲:tomcat用於向其中寫日誌,busybox用於向其中讀取日誌:

#pod-volume-applogs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
imagePullPolicy: IfNotPresent
    image: docker.io/tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  - name: logreader
image: docker.io/busybox
imagePullPolicy: IfNotPresent
    command: ["sh","-c","tail -f /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:
  - name: app-logs
    emptyDir: {}

注意 :這裏名稱爲volume-pod的pod中的兩個容器,同時掛載了類型爲emptyDir這種臨時目錄,並且在容器logreader中打印Tomcat容器生成的日誌,我們通過命令查看:

[root@zy yaml_file]# kubectl logs volume-pod -c logreader

kubernetes之pod超詳細解讀--第一篇(三)

4. pod的配置管理

  這裏的配置管理,就是將程序和配置分離,使程序更加靈活,一般的我們在打包應用程序爲鏡像,可以通過環境變量或者外掛volume的方式在創建容器的時候進行配置注入,但是如果機器規模比較大的時候,對多容器進行不同的配置注入將十分複雜,所以這裏我們介紹一種便捷方式configMap。
configMap典型使用場景:
  生成容器內部的環境變量
  設置容器的啓動命令的參數
  以volume的形式掛載爲容器內部的文件或者目錄
configMap用法:configMap以key-value的形式定義,這裏的value可以是string也可以是一個文件內容,通過kubectl create configmap 的方式創建configMap。
這裏小編通過這個幾個方面去介紹configMap的用法:
  configMap的兩種創建方式:基於yaml文件或者基於命令
  configMap的兩種使用方式:環境變量volume掛載
configMap的創建:
① 基於yaml文件

#cm-appvars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-aoovars
data:
  apploglevel: info
  appdatadir: /var/data
[root@zy yaml_file]# kubectl create  -f  cm-appvars.yaml #創建configmap
[root@zy yaml_file]# kubectl get configmap  #查看創建的configmap

kubernetes之pod超詳細解讀--第一篇(三)

[root@zy yaml_file]# kubectl describe configmap cm-aoovars  #查看configmap定義的內容

kubernetes之pod超詳細解讀--第一篇(三)

#cm-appconfigfiles.yaml(value爲文件內容)
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appconfigfiles
data:
  key-serverxml: |
    xml文件內容
  key-loggingproperties: "配置文件內容"
[root@zy yaml_file]# kubectl get configmap cm-appconfigfiles -o yaml  #查看詳細內容

kubernetes之pod超詳細解讀--第一篇(三)
② 基於命令

[root@zy yaml_file]# kubectl create configmap cm-figmap --from-file=server.xml

注意:這裏會將文件的名稱爲key,內容爲value,如果目錄下有多個文件,則同時創建,並且文件的名稱爲key,內容爲value。

[root@zy yaml_file]# kubectl create configmap cm-figmap --from-literal=loglevel=info --fromliteral=appdatadir=/var/data

注意:這種方式是指定具體的key-value創建configmap
configMap的使用:
① 通過環境變量的方式使用configmap

apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: busybox
    image: docker.io/busybox
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","env|grep APP"]
    env:
    - name: APPLOGLEVEL  #定義環境變量
      valueFrom:
        configMapKeyRef: 
          name: configMapNAME
          key: configMap_KEY
    - name: APPLOGLEVEL  #定義環境變量
      valueFrom:
        configMapKeyRef: 
          name: configMapNAME
          key: configMap_KEY

在kubernetes1.6之後,有一種簡單的定義env的方式:

envFrom:
- configMapRef:
  name: configMapNAME

② 通過volumeMount使用configmap
上面我們定義了一個基於文件的configmap,接下來看如何使用基於文件的configmap,掛載到具體的pod的容器的目錄下:

#cm-test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: busybox
    image: kubeguide/tomcat-app:v1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: serverxml  #引用volumes名稱
      mountPath: /configfiles  #將confimap中的文件掛載到容器的目錄中
  volumes:
  - name: serverxml  
    configMap:
      name: configMapName  #定義的configmap的名稱
      items:  #需要掛載的文件的列表
      - key: key-serverxml  #configmap中定義的key
        path: server.xml   #掛載後的文件名
      - key: key-loggingproperties
        path: logging.properties

注意:如果在定義volume中的configmap時不指定items,那麼volumeMount會在容器的指定目錄下爲每一個items生產一個文件名爲key,內容爲value的文件。
ConfigMap的使用限制說明:
  Configmap必須在pod創建之間創建
  ConfigMap受到namespace的限制,只有同一個命名空間下才能引用
  靜態的pod無法使用ConfigMap
  在使用volumeMount掛載的時候,configMap基於items創建的文件會整體的將掛載數據卷的容器的目錄下的文件全部覆蓋

5.在容器中獲取pod的信息

  在我們創建pod後,系統就會給其分配唯一的名字、IP、並且處於某個namspace下面,那麼這些信息我們如何獲取呢?通過Downward API的方式。
Downward API可以通過以下得到兩種方式將pod的信息導入到container中:
  環境變量,用於單個變量,可以將pod信息和container信息注入到容器中
  Volume掛載:將數組類信息生成文件掛載到容器內部。
實例:
① 環境變量的方式

#將pod信息注入爲環境變量
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","env"]
      env:
        - name: MY_POD_NAME
          valueFrom:
            fieldRef: 
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef: 
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef: 
              fieldPath: status.podIP
[root@zy yaml_file]# kubectl log dapi-test-pod  #查看

kubernetes之pod超詳細解讀--第一篇(三)

#daip-test-pod-container-vars.yaml將容器資源信息注入爲環境變量
apiVersion: v1
kind: Pod
metadata:
  name: daip-test-pod-container-vars
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c"]
      args:
      - while true; do
          echo -en "\n";
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 60;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "40Mi"
          cpu: "130m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: limits.memory
[root@zy yaml_file]# kubectl logs daip-test-pod-container-vars

kubernetes之pod超詳細解讀--第一篇(三)

② Volume掛載方式

![](https://s1.51cto.com/images/blog/201905/18/33b1db246d451ae70c8bc2738341fea9.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)#dapi-test-pod-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c"]
      args:
      - while true;do
          if [[ -e /etc/labels ]];then
            echo -en "\n\n";cat /etc/labels; fi;
          if [[ -e /etc/annotations ]];then
            echo -en "\n\n";cat /etc/annotations; fi;
          sleep 60;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc
          readOnly: false
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

注意:通過設置items,將會以path的名稱生成文件。

[root@zy yaml_file]# kubectl logs dapi-test-pod-volume

kubernetes之pod超詳細解讀--第一篇(三)
Downward API的作用:在某些集羣中,集羣中的節點需要將自身標識以及進程綁定的IP地址等信息預習寫入配置文件,進程啓動時讀取這些信息,然後發佈到類似於服務註冊中心中,做集羣的節點的自動發現功能。那麼Downward API就大有用處,它可以先編寫一個預啓動腳本或者init container,通過環境變量或者文件的方式獲取pod自身的名稱、IP地址等信息,然後寫入主程序配置中。

6.pod的聲明週期和重啓策略

  pod在整個聲明週期中有很多狀態,瞭解其各種狀態對於集羣排錯大有幫助:
kubernetes之pod超詳細解讀--第一篇(三)
  當某個pod異常退出或者健康檢查失敗的時候,kubelet會根據pod定義中的spec.RestartPolicy的設置來進行相應的操作。
 Pod的重啓策略包括:
   Always:當容器失效時,由kubelet自動重啓
   OnFailure:當容器終止運行且退出碼不爲0時,由kubelet自動重啓
   Nerver:永遠不重啓
 Pod的重啓策略,與管理其的資源對象息息相關,RC、job、Daemonset以及通過kubelet直接管理的靜態的pod,每一種控制器對pod的重啓策略都有要求:
   RC和Daemonset:必須設置爲Always。
   Job:OnFailure或者Nerver,確保容器執行完成不在重啓
   靜態pod:在pod失效時自動重啓。(注意靜態pod不會被健康檢查)

7 .Pod的健康檢查

  就像HDFS中的DataNode會定時發送心跳給namenode一樣,k8s集羣中通過探針的方式給Pod定時做健康檢查,一旦符合定義的情況,就執行相應的重啓策略。
 健康檢查分爲以下兩種探針:
   LivenessProbe探針: 判斷容器是否存活(runnning狀態),如果探測到容器不健康,則kubelet殺死該容器,並且根據重啓策略做相應處理,如果容器中沒有LivenessProbe探針,那麼kubelet認爲該容器的LivenessProbe探針一直返回“Success”
   ReadinessProbe探針: 用於判斷容器是否啓動完成(ready狀態),可以接受請求。如果ReadinessProbe探針探測到失敗,則pod的狀態被修改。並且EndPoint Controller將從service的EndPoint中刪除包含該容器所在pod的EndPoint。
 接下來我們重點介紹一下LivenessProbe探針如何判斷pod是否健康,這裏有三種實現方式:
   ExecAction:在容器中執行一個命令,如果該命令返回的狀態碼爲0,表示該容器正常
   TCPSocketAction:通過容器的IP地址和端口執行TCP檢查,能夠建立TCP連接,表示該容器正常
   HTTPGetAction:通過容器的IP和端口以及路徑調用HTTP get方法,如果相應的狀態碼大於等於200並且小於400,則認爲該容器正常
實例演示:
① ExecAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test:liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    images: gcr.io/google_containers/busybox
    args:
    - /bin/sh
    - -c
    - echo ok > /tmp/health; sleep 10 ;rm -rf /tmp/health;sleep 10 ;
    livenessProbe:
      exec:
        command:
        - cat 
        - /tmp/health
      initialDelaySeconds: 15  #初次健康檢查時間
      timeoutSeconds: 1        #超時時間

這裏注意:我們設置了livenessProbe探針初次檢查時間爲15秒,但是程序中將10之後刪除檢查的文件,這裏cat /tmp/health 這個命令執行失敗,狀態碼返回不爲0,導致kubelet殺掉該進程,並根據相應的重啓策略進行操作。
② TCPSocketAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test:liveness
  name: liveness-exec
spec:
  containers:
  - name: nginx
    images: nginx
    ports:
    - containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 15  #初次健康檢查時間
      timeoutSeconds: 1        #超時時間

③ HTTPGetAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test:liveness
  name: liveness-exec
spec:
  containers:
  - name: nginx
    images: nginx
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: /_status/helthz
        port: 80
      initialDelaySeconds: 30  #初次健康檢查時間
      timeoutSeconds: 1        #超時時間

這裏小編強調一下:
initialDelaySeconds:啓動容器後首次健康檢查的時間
timeoutSeconds:健康檢查發送請求後等待相應的時間

看到這裏大家一定腦子都炸了,這裏小編想說一下,後面還有很多,這裏小編把pod介紹分爲兩篇博文向大家介紹,

後續部分URL:

文章參考至《kubernetes權威指南》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章