kubernetes 實踐四:Pod詳解

本篇是關於k8s的Pod,主要包括Pod和容器的使用、Pod的控制和調度管理、應用配置管理等內容。node

Pod的定義

Pod是k8s的核心概念一直,就名字同樣,是k8s中一個邏輯概念。Pod是docekr容器的集合,每一個Pod中至少有一個Pause容器和業務容器。和docker容器關注單個可用的資源不一樣,Pod更多在應用層的角度,將多個docker容器組合來實現做爲一個應用,它是k8s中最小的資源單位。linux

結合docker自己容器的特性,Pod中全部容器都是共享資源,如磁盤、網絡、CPU、內存等,同時,一個Pod共用一個網絡。nginx

如下的yaml格式的Pod定義文件:redis

apiVersion: v1
kind: Pod
metadata:
  name: string
  namespace: string
  labels:
    - name: string
  annotations:
    - name: string
spec:
  containers:
    - name: string
      image: string
      imagePullPolicy: [Always | Never | IfNotPresent]
      command: [string]
      args: [string]
      workingDir: string
      volumeMounts: string
        - 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
              scheme: string
              httpHeaders:
                - name: string
                  value: string
            tcpSocket:
              port: number
            initialDelaySeconds: 0
            timeoutSeconds: 0
            periodSeconds: 0
            successThreshold: 0
            failureThreshold: 0
          securityContext:
            privileged: false
        restartPolicy: [Always | Never | OnFailure]
        nodeSelector: object
        imagePullSecrets:
          - name: string
        hostNetwork: false
        volumes:
          - name: string
            emptyDir: {}
            hostPath:
              path: string
            secret:
              secretName: string
              items:
                - key: string
                  value: string
            configMap:
              name: string
              items:
                - key: string
                - path: string

Pod定義文件模板中各屬性的說明以下: 算法

注:k8s的Pod啓動命令不能是後臺執行的,否則k8s會不斷建立新的Pod而陷入無限循環中。若是docker鏡像的命令沒法改造爲前臺執行,可使用開源工具Supervisor。或是 && tail -f xx 這樣的組合命令。docker

靜態Pod

靜態Pod是由Kubelet進行管理的的僅存在於特定Node上的Pod。它們不能經過API Server進行管理,也沒法和ReplicationController、Deployment或者DaemonSet進行關聯,kubelet也不乏對它們進行健康檢查。靜態Pod老是由kubelet建立,也由kubelet來銷燬。同時也只運行在該kubelet所在的Node上。json

建立靜態Pod由兩種方式:配置文件和HTTP。api

1.配置文件方式bash

靜態文件存放的位置在kubelet的配置文件中定義,由參數staticPodPath指定,若是k8s集羣由kubeadm搭建,那默認存儲在目錄/etc/kubernetes/manifests下。咱們定義配置文件static-nginx.yaml網絡

apiVersion: v1
kind: Pod
metadata:
  name: static-nginx
  labels:
    name: static-nginx
spec:
  containers:
    - name: static-nginx
      image: nginx
      ports:
        - name: nginx
          containerPort: 80

不須要使用命令建立,等他一會,kubelet會自動建立Pod

[root@k8s-master ~]# kubectl get pods
static-nginx-k8s-master   1/1     Running   0          12m

注:若是一段時間仍是沒有生成Pod,能夠查看日誌文件 /var/log/messages

刪除Pod不是使用命令kubelet delete ...,而是直接刪除/etc/kubernetes/manifests/static-nginx.yaml,kubelet自動會刪除Pod。

2.HTTP方式

kubelet會定時根據參數--manifest-url來下載鏡像並生成靜態Pod。

注:比較巧妙地方的是kubeadm安裝地管理節點 kube-apiserver、kube-sheduler、kube-controller-manager 組件都是靜態的Pod。

Pod共享volume

以前說過,同一個Pod中的容器能共享Volume,那怎麼將Volume共享給Pod呢。

關鍵在於配置文件中的spec.containers[].volumeMountsspec.volumes[]參數,例如Pod中容器之間共享一個emptyDir的目錄,名爲logs,配置文件就能夠這樣:

apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - name: logs
          mountPath: /usr/local/nginx/logs
    - name: busybox
      image: busybox
      volumeMounts:
        - name: logs
          mountPath: /logs
  volumes:
    - name: logs
      emptyDir: {}

這樣一來,容器nginx和busybox就共享一個目錄,且各自掛載的路徑也不一樣。重點是用volumes定義要共享的volume,再在containers上使用volumeMounts參數來使用。

Pod ConfigMap

k8s 在 1.2版本時提供了一種統一的集羣配置管理方案,就是ConfigMap,利用不一樣配置和不一樣容器分離開的方式,讓複雜容器管理簡單化。

ConfigMap的用法

ConfigMap供容器使用的典型用法以下:

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

ConfigMap的建立

1.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 -o json

或者key:value也可使用配置文件的別名和文件的內容。

data:
  key-serverxml: |
    <?xml ?>
    ......
    ......
    <?xml ... ?>

  key-properties: "key=...
    ....
    "

注意格式問題。

2.kubelet命令行建立 直接經過kubectl create configmap也能夠建立ConfigMap,可使用參數--from-file--from-literal指定內容。

經過--from-file參數從文件中建立,能夠指定key的名稱,能夠在一個命令中建立包含多個key的ConfigMap,語法爲:

kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source

經過--from-file參數從目錄中進行建立,該目錄的每一個配置文件名都被設置爲key,文件的內容被設置爲value,語法爲:

kubectl create configmap NAME --from-file=config-files-dir

經過--file-literal從文本中進行建立、直接將指定的key#=value#建立爲ConfigMap的內容,語法爲:

kubectl create configmap NAME --from-literal=key1=value1 --from-literal=key2=value2

ConfigMap的使用

ConfigMap的使用也有幾種方式。

1.環境變量

apiVersion: v1
kind: Pod
metadata: 
  name: cm-test-pod
spec:
  containers:
    - name: cm-test
      image: buysbox
      env:
        - name: APPLOGLEVEL    # 定義環境變量名稱
          valueFrom:                       # key "apploglevel" 對應的值
            configMapKeyRef:
              name: cm-appvars       # 環境變量的值取自 cm-appvars 中
              key: apploglevel           # key 爲 "apploglevel"  
        - name: APPDATADIR
          valueFrom:
            configMapKeyRef:
              name: cm-appvars
              key: appdatadir
restartPolicy: Never

要點是環境變量中使用valueFrom參數指定configMapKeyRef

2.volumeMount的方式

...
spec:
  containers:
    - name: cm-test-app
      image: busybox
      volumeMounts:
        - name: serverxml           # 引用volume名 
          mountPath: /configfiles # 掛載到容器內的目錄
  volumes:
    - name: serverxml
      configMap:
        name: cm-appconfigfiles       # 使用 ConfigMap "cm-appconfigfiles"
        items:
          - key: key-serverxml           # key=key-serverxml
            path: server.xml                # value將server.xml文件名進行掛載

若是在引用 ConfigMap 時不指定 items,則使用 volumeMount 方式在容器內的目錄中爲每一個 item 生成一個文件名爲 key 的文件。

ConfigMap 的限制條件

使用 ConfigMap 的限制條件以下:

  • ConfigMap 必須在 Pod 以前建立。
  • ConfigMap 也能夠定義爲屬於某個 Namespace。 只有處於相同 Namespace 中的 Pod 能夠引用它。
  • ConfigMap 中的配額管理還未能實現。
  • 靜態Pod沒法引用 ConfigMap。
  • 在 Pod 對 ConfgMap 進行掛載(volumeMount)操做時,容器內部只能掛載爲「目錄」,沒法掛載爲「文件」。在掛載到容器內部後,目錄中將包含 ConfigMap 定義的每一個 item,若是該目錄下原先還有其餘文件,則容器內的該目錄將會被掛載的 ConfigMap 進行覆蓋。若是應用程序須要保留原來的其餘文件,則須要進行額外的處理。能夠經過將 ConfigMap 掛載到容器內部的臨時目錄,在經過啓動腳本將配置文件複製或者連接到應用所用的實例配置目錄下。

Pod生命週期和重啓策略

Pod的狀態

狀態值 描述
Peding API Server 已經建立該Pod,但Pod內還有一個或多個容器的鏡像沒有建立,包括正在下載鏡像的過程
Running Pod內全部容器均已建立,且至少有一個容器處於運行狀態、正在啓動狀態或正在重啓狀態
Succeeded Pod內全部容器均成功執行退出,且不會再重啓
Failed Pod內全部容器均已退出,但至少有一個容器退出爲失敗狀態
Unknown 因爲某種緣由沒法獲取該Pod的狀態,可能因爲容器通訊不順暢致使

Pod的RestartPolicy重啓策略:

  • Always:當容器失效時,有Kubelet自動重啓容器。
  • OnFailure:當容器終止運行且退出碼不爲0,由kubelet自動重啓該容器。
  • Never:不論容器運行狀態如何,kubelet都不會啓動該容器。

每種控制器對Pod的重啓策略不一樣:

  • RC和DaemonSet:必須設置爲 Always,須要保證該容器持續運行。
  • Job:OnFailure或Never,確保容器執行完成後再也不重啓。
  • kubelet:在Pod失效時自動重啓它,不論RestartPolicy設置爲何值,而且也不會對Pod進行健康檢查。

Pod健康檢查

Pod的健康狀態檢查能夠經過兩類探針來檢查:LivenessProbe 和 ReadinessProbe。

  • LivenessProbe:用於判斷容器是否存活(running 狀態),若是LivenessProbe探針探測到容器不健康,則kubelet將殺掉該容器,並根據容器的重啓策略作相應的處理。若是一個容器不包含LivenessProbe探針,那麼kubelet認爲該容器的LivenessProbe探針返回值永遠是「Success」。
  • ReadinessProbe:用於判斷容器是否自動完成(ready 狀態),能夠接收請求。 若是ReadinessProbe探針檢測到失敗,則Pod的狀態將被修改。Endpoint Controller將從Service的Endpoint中刪除包含該容器所在Pod的Endpoint。

kubelet按期執行LivenessProbe探針來診斷容器的健康情況。LivenessProbe有三種實現方式。

ExecAction:在容器內部執行一個命令,若是該命令的返回碼爲0,則代表容器健康。

...
spec:
  containers:
    - name: liveness
      ...
      livenessProbe:
        exec:
          command:
            - cat
            - /tmp/health
        initialDelaySeconds: 15   # 探針初始化檢測時間間隔,單位爲秒
        timeoutSeconds: 1          # 返回超時時間,單位爲秒。若是超時kubelet會重啓容器

TCPSocketAction:經過容器的IP地址和端口號執行TCP檢查,若是可以創建TCP鏈接。則代表容器健康。

...
spec:
  containers:
    - name: liveness
      ...
      livenessProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 15   # 探針初始化檢測時間間隔,單位爲秒
        timeoutSeconds: 1          # 返回超時時間,單位爲秒

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

...
spec:
  containers:
    - name: liveness
      ...
      livenessProbe:
        httpGet:
          path: /_status/healthz
          port: 80
        initialDelaySeconds: 15   # 探針初始化檢測時間間隔,單位爲秒
        timeoutSeconds: 1          # 返回超時時間,單位爲秒

Pod調度

在k8s中,Pod在大部分場景在都只是容器的載體而已,一般須要經過RC、Deployment、DaemonSet、Job等對象來完成Pod的調度與自動控制功能。

RC、Deployment全自動調度

RC的主要功能之一就是自動部署一個容器應用的多份副本,以及持續監控副本的數量,在集羣內始終保持用戶指定的副本數量。

Pod的調度策略除了有系統內置的Node調度算法,還能夠在Pod的定義中使用NodeSelector或者NodeAffinity來指定知足條件的Node進行調度。

1.NodeSelector:定向調度

k8s上的服務Scheduler服務負責實現Pod的調度,整個調度經過執行一系列複雜的算法,最終爲每一個Pod計算出一個最佳的目標節點,這一過程是自動完成的。NodeSelector 調度就是經過給Node打上Label,使用Pod的NodeSelector屬性來匹配的。

首先經過kubectl label命令給目標Node打上一些標籤:

kubectl label node <node-name> <label-key>=<label-value>

而後,在Pod的定義中加上nodeSelector的設置

...
template:
  ...
  spec:
    ...
    nodeSelector:
      label-key: label-value

注:若是有多個節點都定義了相同的label,scheduler就會根據調度算法從這組Node進行Pod調度,可是若是Pod上定義了nodeSelector參數,可是Node上沒法找到對應的Node,則Pod沒法被調度成功。

2.NodeAffinity:親和性調度

NodeAffinity意爲親和性的策略調度,是一種更加靈活的調度策略。增長了In、NotIn、Exists、DoesNotExists、Gt、Lt等操做符來選擇Node。同時還添加一些信息來設置親和性調度策略:

  • RequiredDuringSchedulingRequiredDuringExecution:相似於 NodeSelector,但在 Node不知足時,系統將從該Node上移除以前調度上的Pod。
  • RequiredDuringSchedulingRequiredIgnoredExecution:在Node不知足條件時,系統不必定從該Node上移除以前調度上的Pod。
  • PreferredDuringSchedulingRequiredIgnoredExecution:指定在知足條件的Node中,哪些Node應更優先地進行調度。同時在Node不知足條件時,系統不必定從該Node上移除以前調度地Pod。

NodeAffinity 對應地還有 PodAffinity 和 PodAntiAffinity。

下面來個實例,指定Pod運行到kubernetes.io/e2e-az-name值爲e2e-az1 或e2e-az2的節點上面

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
  annotations:
    scheduler.alpha.kubernetes.io/affinity: >
      {
        "nodeAffinity": {
          "requiredDuringSchedulingIgnoredDuringExecution": {
            "nodeSelectorTerms": [
              {
                "matchExpressions": [
                  {
                    "key": "kubernetes.io/e2e-az-name",
                    "operator": "In",
                    "values": ["e2e-az1", "e2e-az2"]
                  }
                ]
              }
            ]
          }
        }
      }
    another-annotation-key: another-annotation-value
spec:
  containers:
  - name: with-node-affinity
    image: gcr.io/google_containers/pause:2.0

2.DaemonSet:特定場景調度

DaemonSet是k8s1.2版本新增地一種資源對象,用於在集羣中每一個Node上僅運行一份Pod的副本實例。它具備如下的應用實例:

  • 在每一個 Node 上運行一個 GlusterFS 存儲或者 Ceph 存儲的 daemon 進程。
  • 在每一個 Node 上運行一個日誌採集程序,例如fluented或者logstach。
  • 在每一個 Node 上運行一個健康程序,採集該Node的運行性能數據。

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

批處理調度

k8s在1.2版本之後,開始支持批處理類型的應用,咱們能夠經過k8s Job資源對象來定義並啓動一個批處理任務。批處理任務一般並行或串行啓動多個計算進程去處理一批工做項(work item)。

apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    metadata:
      name: myjob
    spec:
      containers:
        - name: hello
          image: busybox
          command: ["echo", "hello world"]
      restartPolicy: Never
  • batch/v1是當前job的Version
  • 指定當前資源的類型時Job
  • restartPolicy是指當前的重啓策略。對於 Job,只能設置爲 Never 或者 OnFailure。對於其餘 controller(好比 Deployment)能夠設置爲 Always 。

查看批處理任務:

[root@k8s-master ~]# kubectl get jobs
NAME    COMPLETIONS   DURATION   AGE
myjob   1/1           21s        4m44s

同時也能夠指定批處理的並行個數和重複次數

...
spec:
  completions: 6   # 重複次數
  parallelism: 3    # 並行個數
...

同時k8s還支持定時任務,相似linux的Crontab。利用 CronJob 資源對象表示:

apiVersion: batch/v2alpha1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command: ["echo","hello k8s job!"]
          restartPolicy: Never
  • batch/v2alpha1 是當前 CronJob 的 apiVersion。
  • 指明當前資源的類型爲 CronJob。
  • schedule 指定何時運行 Job,其格式與 Linux cron 一致。這裏 */1 * * * * 的含義是每一分鐘啓動一次。
  • jobTemplate 定義 Job 的模板,格式與前面 Job 一致。

可是建立CronJob會報錯:

[root@k8s-master ~]# kubectl apply -f job/cronJob.yaml 
error: unable to recognize "job/cronJob.yaml": no matches for kind "CronJob" in version "batch/v2alpha1"

修改kube-apiserver配置文件,/etc/kubernetes/manifests/kube-apiserver.yaml

...
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.10.20
    - --runtime-config=batch/v2alpha1=true          # 添加版本
...

重啓kubelet

systemctl restart kubelet

從新建立CronJob

kubectl apply -f job/cronJob.yaml

Pod 的擴容和縮容

k8s RC的Scale機制能讓咱們在運行中修改Pod的數量。經過命令:

[root@k8s-master ~]# kubectl get rc
NAME           DESIRED   CURRENT   READY   AGE
redis-master   1         1         1       4d11h
redis-slave    2         2         2       4d11h
[root@k8s-master ~]# kubectl scale rc redis-slave --replicas=3
replicationcontroller/redis-slave scaled

關鍵在於指定參數--replicas的值,若是該值大於當前rc對應的Pod的值,就添加Pod;反之,則殺死。

除了使用命令kubectl scale以外,k8s還支持 HPA(Horizontal Pod Autoscaler)用於實現基於CPU使用率進行自動Pod擴容縮容的功能。HPA針對RC或Department對象,且Pod必須定義resource.request.cpu。HPA控制器基於Master的kube-controller-manager服務啓動參數--horizontal-pod-autoscaler-sync-period定義的探測週期(默認值爲15s),週期性地監測目標Pod地資源性能指標,並與HPA資源對象中地擴縮容條件進行對比,在知足條件時對Pod副本數量進行調整。

須要自動調整的RC或Deployment配置以下:

apiVersion: v1
kind: Service
metadata:
  name: svc-hpa
  namespace: default
spec:
  selector:
    app: myapp
  type: NodePort  ##注意這裏是NodePort,下面壓力測試要用到。
  ports:
  - name: http
    port: 80
    nodePort: 31111
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp-demo
      namespace: default
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80
        resources:
          requests:
            cpu: 50m
            memory: 50Mi
          limits:
            cpu: 50m
            memory: 50Mi

HPA配置以下:

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa-v2
  namespace: default
spec:
  minReplicas: 1         ##至少1個副本
  maxReplicas: 8         ##最多8個副本
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 50  ##注意此時是根據使用率,也能夠根據使用量:targetAverageValue
  - type: Resource
    resource:
      name: memory
      targetAverageUtilization: 50  ##注意此時是根據使用率,也能夠根據使用量:targetAverageValue

其中關鍵的參數:

  • scaleTargetRef:目標做用對象,能夠是Deployment、ReplicationController或ReplicaSet
  • minReplicas和maxReplicas:Pod副本數量的最小值和最大值,系統將在這個範圍內進行自動擴縮容操做,並維持每一個Pod的CPU使用率爲50%。
  • metrics:目標指標值。

Pod的升級和回滾

k8s支持對如下資源的升級和回滾:

  • Deployment
  • Daemonset
  • Statefulset

Deployment 的升級

以Deployment爲例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

使用升級命令:

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

或者使用kubectl edit命令修改nginx版本爲1.9.1。

一旦鏡像名(或Pod的定義)發生修改,則將觸發系統完成Deployment全部運行Pod的滾動升級操做,使用命令查看:

kubectl rollout status deployment/nginx-deployment

Deployment的回滾

首先可使用命令來查看可回滾的版本:

kubectl rollout history deployment/nginx-deployment

而後使用命令進行回滾:

kubectl rollout undo deployment/nginx-deployment

或者使用指定回滾版本:

kubectl rollout undo deployment/nginx-deployment  --to-revision=3

Deployment 暫停和恢復回滾

若是Deployment的恢復比較複雜或者在回滾過程當中臨時須要修改,就能夠先暫停回滾,當修改完成後再恢復。

暫停回滾使用命令

kubectl rollout pause deployment/nginx-deployment

暫停回滾後咱們能夠對deployment進行任意次的修改,如更新容器的資源限制:

kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi

修改完成後再恢復回滾:

kubectl rollout resume deployment/nginx-deployment
相關文章
相關標籤/搜索