Kubernetes學習筆記(二):部署託管的Pod -- 存活探針、ReplicationController、ReplicaSet、DaemonSet、Job、CronJob

蝦敏四把刀發表於2020-05-21

存活探針

Kubernetes可以通過存活探針(liveness probe)檢查容器是否存活。如果探測失敗,Kubernetes將定期執行探針並重新啟動容器。
官方文件請見:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
存活探針分為三種:

  • exec:在容器內執行任意命令,並檢查命令的退出狀態碼,為0則成功,否則失敗。
  • httpGet:執行http get請求,http code大於等於200並且小於400表示成功,否則失敗。
  • tcp:嘗試與指定埠建立socket連線,建立成功則探測成功,否則失敗。

exec探針

從官網複製個yaml,但是要將image從k8s.gcr.io/busybox更改為busybox。

-> [root@kube0.vm] [~] cat exec-liveness.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

執行然後30秒後檢視

-> [root@kube0.vm] [~] k create -f exec-liveness.yaml
pod/liveness-exec created

-> [root@kube0.vm] [~] k describe po liveness-exec
.......
    Liveness:       exec [cat /tmp/healthy] delay=5s timeout=1s period=5s #success=1 #failure=3
.......
Events:
  Type     Reason     Age                            From               Message
  ----     ------     ----                           ----               -------
  Normal   Scheduled  <unknown>                      default-scheduler  Successfully assigned default/liveness-exec to kube1.vm
  Normal   Pulling    <invalid>                      kubelet, kube1.vm  Pulling image "busybox"
  Normal   Pulled     <invalid>                      kubelet, kube1.vm  Successfully pulled image "busybox"
  Normal   Created    <invalid>                      kubelet, kube1.vm  Created container liveness
  Normal   Started    <invalid>                      kubelet, kube1.vm  Started container liveness
  Warning  Unhealthy  <invalid> (x3 over <invalid>)  kubelet, kube1.vm  Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
  Normal   Killing    <invalid>                      kubelet, kube1.vm  Container liveness failed liveness probe, will be restarted

前30秒,/tmp/healthy是存在的,探針返回成功。30秒後,因為/tmp/healthy不存在了,所以一直失敗,直到失敗3次後重啟。

livenessProbe下的欄位

這時候kubectl explain po.spec.containers.livenessProbe就派上了用場。

  • exec:exec探針配置
  • httpGet:httpGet探針配置
  • tcpSocket:tcpSocket探針配置
  • initialDelaySeconds:初始延遲時間,在此時間內不進行探測,給程式預留啟動時間。
  • periodSeconds:探測週期,預設10s
  • timeoutSeconds:超時時間,預設1s
  • failureThreshold:被認為失活的最小連續失敗次數,預設3次
  • successThreshold:失敗後被認為存活的最小連續成功次數,預設為1次。

這些引數在上面的kubectl describe的結果中的Liveness欄位有:

exec [cat /tmp/healthy] delay=5s timeout=1s period=5s #success=1 #failure=3

注意事項

  • 一定要檢查程式的內部,而沒有外界因素影響。
    例如,當伺服器無法連線到資料庫時,前端服務的探針不應返回失敗。因為問題在資料庫,重啟前端服務也無法解決問題。
  • 無須再探針中實現重試
  • 探針要輕量

ReplicationController

ReplicationController是一種Kubernetes資源,可以確保他的pod始終處於執行狀態。

ReplicationController描述檔案分為三大部分:

  • label selector(標籤選擇器):用於確定ReplicationController管理哪些pod
  • replica count(副本個數):指定執行pod的數量
  • pod template(pod模板):用於建立pod

ReplicationController的目的就是建立和管理若干個pod副本,它會持續監控正在執行的pod列表,保證標籤選擇器匹配的pod數量與副本個數一致。如果少於副本數量,就要根據pod模板建立新的副本;如果多餘副本數量,就要刪除多餘的pod。

數量少的原因可能是:

  • 增加了副本個數
  • pod所在的工作節點出現故障
  • pod的標籤更改了,導致pod與ReplicationController的標籤選擇器不再匹配了。此時,如果它不被任何ReplicationController的標籤選擇器匹配,它就稱為一個孤兒了。

數量多的原因可能是:

  • 減少了副本個數
  • 新建的pod與該ReplicationController的標籤選擇器匹配
  • 已存在的pod標籤修改後與該ReplicationController的標籤選擇器匹配

nginx-rc.yaml

API伺服器會檢查模板中的標籤是否與selector匹配,如果不匹配是無法建立的。可以不指定selector,它會根據模板中的labels自動配置。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-rc
spec:
  replicas: 3	# 副本數量
  selector:	# pod選擇器,決定RC的操作物件
    app: nginx
  template:	# pod模板
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80

使用ReplicationController

建立ReplicationController

-> [root@kube0.vm] [~] k create -f nginx-rc.yaml
replicationcontroller/nginx-rc created

檢視pod,確實新建了3個pod。

-> [root@kube0.vm] [~] k get po --show-labels
NAME             READY   STATUS              RESTARTS   AGE   LABELS
nginx-rc-2c47w   0/1     ContainerCreating   0          5s    app=nginx
nginx-rc-jrcl2   0/1     ContainerCreating   0          5s    app=nginx
nginx-rc-qgchh   0/1     ContainerCreating   0          5s    app=nginx

檢視ReplicationController,rc是ReplicationController的簡寫。

-> [root@kube0.vm] [~] k get rc
NAME       DESIRED   CURRENT   READY   AGE
nginx-rc   3         3         3       5m58s

檢視詳情

-> [root@kube0.vm] [~] k describe rc nginx-rc
Name:         nginx-rc
Namespace:    default
Selector:     app=nginx
Labels:       app=nginx
Annotations:  <none>
Replicas:     3 current / 3 desired				# 當前數量、期望數量
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed	# 各種狀態下的數量
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age    From                    Message
  ----    ------            ----   ----                    -------
  Normal  SuccessfulCreate  7m26s  replication-controller  Created pod: nginx-rc-2c47w
  Normal  SuccessfulCreate  7m26s  replication-controller  Created pod: nginx-rc-jrcl2
  Normal  SuccessfulCreate  7m26s  replication-controller  Created pod: nginx-rc-qgchh

可以搞一些破壞,比如更改、刪除pod的標籤,或者乾脆刪除pod。ReplicationController會因為當前數量與期望數量不符而建立新的副本。

控制器如何建立的pod

控制器通過建立一個新的副本代替被刪除的副本時,它並沒有對刪除本身做出反應,而是針對由此產生的狀態——副本數量不足做出反應。
雖然控制器會立即受到pod被刪除的通知,但這並不是它建立新副本的原因。該通知會觸發控制器檢查實際的副本數量並採取相應措施。

kubectl edit

kubectl edit rc nginx-rc可以在編輯器中開啟nginx-rc的yaml配置,修改在儲存後會立刻做出改變。

-> [root@kube0.vm] [~] k edit rc nginx-rc
replicationcontroller/nginx-rc edited
通過KUBE_EDITOR環境變數可以指定用什麼編輯器開啟。

kubectl scale

可以使用kubectl scale命令進行擴縮容。

-> [root@kube0.vm] [~] k scale rc nginx-rc --replicas=6
replicationcontroller/nginx-rc scaled

以上操作會修改spec.replicas欄位,就像通過kubectl edit修改一樣。

刪除ReplicationController

使用 kubectl delete 刪除ReplicationController時,pod也會被刪除。但由於pod是被ReplicationController建立的而不是它的組成部分,所以可以通過指定--cascade=false而不刪除pod。

注意事項

  • pod永遠不會被重新安置到另一個節點。
  • 雖然一個pod沒有繫結在ReplicationController上,但該pod在metadata.ownerReferences引用它。

ReplicaSet

ReplicationController最終將要被棄用,代替它的是ReplicaSet。它們的行為完全相同,但是ReplicaSet的選擇器更具有表達力。

nginx-rs.yaml

讓我們來看一個ReplicaSet的描述檔案:

apiVersion: apps/v1	# api版本與ReplicationController不同
kind: ReplicaSet
metadata:
  name: nginx-rs
spec:
  replicas: 3
  selector:		# ReplicaSet的pod選擇器有matchLabels和matchExpressions,與ReplicationController的是相似的,但更強大
    matchLabels:
      app: nginx
  template:		# 模板內容是一致的
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80

主要區別在於pod選擇器。ReplicaSet的建立於ReplicationController一樣,縮寫是rs,就不再贅敘了。

matchExpressions

上面描述檔案的選擇器使用matchExpressions可以改為:

  selector:
    matchExpressions:
      - key: app
        operator: In
        values:
          - nginx

每個表示式都必須包含key、operator(運算子),有可能包含values(取決於運算子),運算子有以下四種:

  • In:Label的值必須與values中的一個相等
  • NotIn:Label的值不能與values中的任何一個相等
  • Exists:Pod必須包含一個指定名稱的標籤,values無所謂
  • DoesNotExists:Pod不得包含指定名稱的標籤,values不能指定

DaemonSet

DaemonSet與ReplicaSet的不同在於,DaemonSet可以讓pod在叢集的每個節點上執行,有且只有一個。

如果節點下線,DaemonSet不會在其他地方重建pod;如果叢集新加入了一個節點,DaemonSet會立刻部署一個新的pod。如果有人刪除了pod,DaemonSet會建立一個新的。

這些pod通常用於執行系統級別或基礎結構相關的工作,如日誌收集和資源監控。典型的例子就是Kubernetes自己的kube-proxy。

如果想讓DaemonSet只在特定節點執行pod,需要通過pod模板中的nodeSelector屬性指定。

DaemonSet中沒有期望副本數的概念,它不需要。因為它的工作是確保有一個匹配它選擇器的pod在節點上執行。

nginx-ds.yaml

可能這裡用繼續用nginx作為例子不太恰當,但目前我們關注的重點是DaemonSet,繼續吧。。。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-ds
spec:			# 去掉了replicas,因為不需要
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeSelector:	# 新增了一個節點的標籤選擇器,選擇只部署地區在在北京的節點上
        region: "beijing"
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80

建立DaemonSet

先給kube1.vm打個標籤

-> [root@kube0.vm] [~] k label node kube1.vm region=beijing
node/kube1.vm labeled

-> [root@kube0.vm] [~] k get node -L region
NAME       STATUS   ROLES    AGE   VERSION   REGION
kube0.vm   Ready    master   40h   v1.17.3
kube1.vm   Ready    <none>   40h   v1.17.3   beijing
kube2.vm   Ready    <none>   40h   v1.17.3

提交描述檔案

-> [root@kube0.vm] [~] k create -f nginx-ds.yaml
daemonset.apps/nginx-ds created

-> [root@kube0.vm] [~] k get ds,po -o wide
NAME                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR    AGE   CONTAINERS   IMAGES   SELECTOR
daemonset.apps/nginx-ds   1         1         1       1            1           region=beijing   52s   nginx        nginx    app=nginx

NAME                 READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
pod/nginx-ds-5z95c   1/1     Running   0          52s   10.244.1.24   kube1.vm   <none>           <none>

給kube2.vm也打個標籤

-> [root@kube0.vm] [~] k label node kube2.vm region=beijing
node/kube2.vm labeled

-> [root@kube0.vm] [~] k get ds,po -o wide
NAME                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR    AGE    CONTAINERS   IMAGES   SELECTOR
daemonset.apps/nginx-ds   2         2         1       2            1           region=beijing   113s   nginx        nginx    app=nginx

NAME                 READY   STATUS              RESTARTS   AGE    IP            NODE       NOMINATED NODE   READINESS GATES
pod/nginx-ds-5z95c   1/1     Running             0          113s   10.244.1.24   kube1.vm   <none>           <none>
pod/nginx-ds-b46lb   0/1     ContainerCreating   0          3s     <none>        kube2.vm   <none>           <none>

Job

Job可以執行一種pod,在該pod內的程式成功結束時,不再重啟。一旦任務完成,pod就被認為處於完成狀態。

比如可以使用在將一些資料匯出到其他地方,這個工作可能會持續幾個小時。

我們基於busybox映象,sleep一段時間用於模擬這個操作。

job.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: job
spec:
  template:			# 沒有指定selector,會根據pod標籤中自動建立
    metadata:
      labels:
        app: job
    spec:
      restartPolicy: OnFailure	# 執行遇到異常則重啟,預設為Always。
      containers:
        - name: sleepbusybox
          image: busybox
          command: ["/bin/sh","-c","sleep 60;echo this is busybox"]

restartPolicy表示pod的重啟策略,預設Always,其他兩項為OnFailure和Never。

執行檢視

建立job

-> [root@kube0.vm] [~] k create -f job.yaml
job.batch/job created

60秒內檢視:

-> [root@kube0.vm] [~] k get job,pod
NAME            COMPLETIONS   DURATION   AGE
job.batch/job   0/1           14s        14s

NAME            READY   STATUS    RESTARTS   AGE
pod/job-4sfv4   1/1     Running   0          14s

60秒後檢視:

-> [root@kube0.vm] [~] k get job,pod
NAME            COMPLETIONS   DURATION   AGE
job.batch/job   1/1           68s        71s

NAME            READY   STATUS      RESTARTS   AGE
pod/job-4sfv4   0/1     Completed   0          71s

使用jsonpath獲取 pod name 檢視log

-> [root@kube0.vm] [~] k logs $(k get po --selector=app=job --output=jsonpath={.items..metadata.name})
this is busybox

completions與parallelism

job描述檔案下的spec有兩個欄位:

  • completions:job要確保完成多少個pod,預設為1。
  • parallelism:最多並行執行多少個pod,預設為1。

job-batch.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: job-batch
spec:
  completions: 5
  parallelism: 2
  template:
    # 與job.yaml相同

activeDeadlineSeconds

job.spec.activeDeadlineSeconds屬性來限制Job在叢集中的存活時間,超過此值,所有的pod都被終止。Job的status變為 type: Failed,reason: DeadlineExceeded

CronJob

與Linux的crontab類似,使pod可以在特定時間執行,或者週期執行。

cronjob.yaml

這個描述檔案是個套娃。。

CronJob包含job的模板,也就是jobTemplate;job裡還包含pod的模板,也就是template。

schedule欄位中的值是"分 小時 每月第幾天 月份 星期幾",詳情請參考Linux crontab。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cron-job
spec:
  schedule: "*/3 * * * * "	# 每三分鐘執行一次
  jobTemplate:			# job的模板
    spec:
      template:			# pod的模板
        metadata:
          labels:
            app: job-in-cron
        spec:
          restartPolicy: OnFailure
          containers:
            - name: sleepbusybox
              image: busybox
              command: ["/bin/sh","-c","sleep 60;echo this is busybox"]

執行

建立cron-job

-> [root@kube0.vm] [~] k create -f cronjob.yaml
cronjob.batch/cron-job created

等了幾分鐘

-> [root@kube0.vm] [~] k get all
NAME                            READY   STATUS      RESTARTS   AGE
pod/cron-job-1590053040-pqhhh   0/1     Completed   0          97s

NAME                            COMPLETIONS   DURATION   AGE
job.batch/cron-job-1590053040   1/1           68s        97s

NAME                     SCHEDULE       SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/cron-job   */3 * * * *    False     0        105s            4m39s

又等了幾分鐘

-> [root@kube0.vm] [~] k get all
NAME                            READY   STATUS              RESTARTS   AGE
pod/cron-job-1590053040-pqhhh   0/1     Completed           0          3m4s
pod/cron-job-1590053220-whflp   0/1     ContainerCreating   0          3s

NAME                            COMPLETIONS   DURATION   AGE
job.batch/cron-job-1590053040   1/1           68s        3m4s
job.batch/cron-job-1590053220   0/1           3s         3s

NAME                     SCHEDULE       SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/cron-job   */3 * * * *    False     1        12s             6m6s

可以看到就當前的描述檔案而言,cron-job每次建立一個job,job建立一個pod的執行。

startingDeadlineSeconds

cronjob.spec.startingDeadlineSeconds規定pod必須預定時間之後的startingDeadlineSeconds秒內執行,超過此時間則不執行,並將其顯示未Failed。

小結

  • 使用存活探針檢查容器是否存活。
  • ReplicationController建立並管理Pod,但並不是繫結關係。它始終保持期望數量的副本正在執行。
  • ReplicationController將被棄用,ReplicaSet擁有更強的標籤選擇器。
  • DaemonSet會在每個節點上保持執行有且只有一個Pod,可以通過nodeSelector讓其只在特定節點執行。
  • Job建立Pod執行完成後,Pod為為Completed狀態。completions與parallelism指定需要完成的數量與並行度。
  • restartPolicy:預設Always,還有OnFailure、Never。
  • CronJob建立Job,Job建立Pod。
  • 命令:edit、scale

相關文章