2.k8sPod、控制器、service

xiaokantianse發表於2020-12-25

一、Pod生命週期

  • Pod是k8s中最小的管理單元(邏輯上存在,實際不存在),是一組容器的集合
  • 同一個Pod中的容器共享網路和儲存(通過pause容器實現),由一個統一的IP向叢集內部提供服務
  • Pod分為自主式(死亡之後就會消失)和被控制器控制的(死亡之後會被控制器拉起來保持Pod的副本數量)
  • Pod的生命週期是短暫的,Pod死亡之後,重新建立的Pod和原來的Pod完全不一樣

1、init c

  • init c 即 init container (初始化容器)

  • Pod能夠具有多個容器,應用執行在容器裡面,但是它也可能有一個或多個先於應用容器啟動的Init容器

  • Init容器與普通的容器非常像,除了如下兩點

    • Init容器總是執行到成功完成為止
    • 每個Init容器都必須在下一個Init容器啟動之前成功完成
  • 如果Pod的Init容器失敗, Kubernetes 會不斷地重啟該Pod,直到Init容器成功為止。然而,如果Pod對應的 restartPolicy為Never,他不會重新啟動

2、init c的作用

  • 因為Init容器具有與應用程式容器分離的單獨映象,所以它們的啟動相關程式碼具有如下優勢
    • 它們可以包含並執行實用工具,但是出於安全考慮,是不建議在應用程式容器映象中包含這些實用工具的
    • 它們可以包含使用工具和定製化程式碼來安裝,但是不能出現在應用程式映象中。例如,建立映象沒必要FROM另一個映象,只需要在安裝過程中使用類似 sed 、 awk 、 python或 dig這樣的工具
    • 應用程式映象可以分離出建立和部署的角色,而沒有必要聯合它們構建一個單獨的映象
    • Init容器使用 Linux Namespace ,所以相對應用程式容器來說具有不同的檔案系統檢視。因此,它們能夠具有訪問 Secret 的許可權,而應用程式容器則不能
    • 它們必須在應用程式容器啟動之前執行完成,而應用程式容器是並行執行的,所以Init容器能夠提供了一種簡單的阻塞或延遲應用容器的啟動的方法,直到滿足了一組先決條件
  • 也就是說在主容器啟動之前使用init c可以做一些其他事情
    • 讀取配置檔案等等(為保證主容器執行)
    • 讀取私密檔案等等(保證系統的安全,init c一旦成功結束,就會刪除,而讀取私密檔案放在主容器中,可能會出現安全隱患,因為主容器時一直執行)
    • 但是不建議用init c去監測其他容器(比如,我們的服務mysql應該先啟動,用init c去監測mysql的容器時是正常的,init c成功結束,但是主容器啟動時mysql容器可能又掛了,所以這種方法不太準確)

3、特殊說明

  • 在Pod啟動過程中,Init容器會按順序在網路和資料卷初始化之後啟動。每個容器必須在下一個容器啟動之前成功退出,init c按順序執行,上一個執行成功之後才會執行下一個
  • 如果由於執行時或失敗退出,將導致容器啟動失敗,它會根據Pod的restartPolicy指定的策略進行重試。然而,如果Pod的restartPolicy設定為Always ,Init容器失敗時會使用RestartPolicy策略
  • 在所有的Init容器沒有成功之前,Pod將不會變成Ready狀態。Init容器的埠將不會在Service中進行聚集。正在初始化中的 Pod處於 Pending 狀態,但應該會將 Initializing狀態設定為 true
  • 如果Pod重啟,所有Init容器必須重新執行
  • 對Init容器spec的修改被限制在容器image欄位,修改其他欄位都不會生效。更改Init容器的image欄位,等價於重啟該Pod
  • Init容器具有應用容器的所有欄位。除了 readinessProbe ,因為Init容器無法定義不同於完成(completion)的就緒(readiness)之外的其他狀態。這會在驗證過程中強制執行
  • 在Pod中的每個app和Init容器的名稱必須唯一;與任何其它容器共享同一個名稱,會在驗證時丟擲錯誤
  • start和stop會在main c 啟動之後和結束之前做一些事情

4、init c資源清單示例

  • # pod 資源清單
    apiVersion: v1
    kind: Pod
    metadata:
      name: initc-pod
      labels:
        app: myapp
    spec:
      containers:
      - name: initc-container
        image: busybox
        command: ['sh', '-c', 'echo The app is running! && sleep 3600']
      # 以下為init c容器
      initContainers:
      # 第一個init c容器
      - name: init-myservice
        image: busybox
        # 監測service 看叢集中有無名為myservice的service 直至監測到才會成功推出(直接寫service名稱可能不能解析,ping不同,解決方法如下)
        command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2;done;']
      # 第一個init c容器
      - name: init-mydb
        image: busybox
        # 監測service 看叢集中有無名為mydb的service 直至監測到才會成功推出
        command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
    
  • # 檢視pod狀態
    [root@k8smaster pods]# kubectl get pod
    NAME        READY   STATUS     RESTARTS   AGE
    initc-pod   0/1     Init:0/2   0          43s
    
    # 檢視pod日誌
    [root@k8smaster pods]# kubectl logs initc-pod -c  init-myservice
    ;; connection timed out; no servers could be reached
    
    waiting for myservice
    ;; connection timed out; no servers could be reached
    
    waiting for myservice
    ;; connection timed out; no servers could be reached
    
    ......
    
    
  • # service 資源清單 簡寫svc
    kind: Service
    apiVersion: v1
    metadata:
      name: myservice
    spec:
      ports:
      	# 使用的協議 TCP和UDP可選
        - protocol: TCP
          # 暴露給k8s內部叢集的埠
          port: 80
          # 也監聽就是sevice的pod下容器 並且容器暴露埠為9376
          targetPort: 9376
    # 用--- 分開表示一個yaml檔案
    ---
    kind: Service
    apiVersion: v1
    metadata:
      name: mydb
    spec:
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9377
    
  • # 啟動名為myservice的service 檢視pod狀態
    [root@k8smaster pods]# kubectl get pod
    NAME                   READY   STATUS     RESTARTS   AGE
    initc-pod              0/1     Init:1/2   0          57s
    
    # 啟動名為mydb的service 檢視pod狀態
    [root@k8smaster pods]# kubectl get pod
    NAME                   READY   STATUS    RESTARTS   AGE
    initc-pod              1/1     Running   0          2m34s
    
  • 解決pod內不能通過域名進行訪問其他servce或者pod(ping 不同 不管是clusterIp,還是名稱)

    • 問題原因:kubeadm安裝的k8s的kube-proxy使用的iptables,需要修改為ipvs

    • 1.開啟ipvs支援(每個節點執行)

      • yum -y install ipvsadm ipset (好像可省略)
      • 永久生效:cat > /etc/sysconfig/modules/ipvs.modules <<EOF
        modprobe -- ip_vs
        modprobe -- ip_vs_rr
        modprobe -- ip_vs_wrr
        modprobe -- ip_vs_sh
        modprobe -- nf_conntrack_ipv4
        EOF
      • 臨時生效:modprobe -- ip_vs
        modprobe -- ip_vs_rr
        modprobe -- ip_vs_wrr
        modprobe -- ip_vs_sh
        modprobe -- nf_conntrack_ipv4
    • 2.修改許可權(每個節點執行,好像可省略)

      • chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
    • 3.修改kube-proxy的configMap

      • kubectl edit cm kube-proxy -n kube-system

      • ipvs:
              excludeCIDRs: null
              minSyncPeriod: 0s
              scheduler: ""
              strictARP: false
              syncPeriod: 30s
            kind: KubeProxyConfiguration
            metricsBindAddress: 127.0.0.1:10249
            mode: "ipvs" # 修改此處為ipvs
        
    • 4.重啟kube-proxy

      • kubectl get pod -n kube-system | grep kube-proxy |awk '{system("kubectl delete pod "$1" -n kube-system")}'
    • 5.檢視kube-proxy啟動日誌,確認是否為ipvs

      • kubectl logs -n kube-system kube-proxy-ff74q(自己的kube-proxy pod名)

      • # 有Using ipvs Proxier則配置成功
        I1215 09:18:42.852942       1 server_others.go:259] Using ipvs Proxier.
        
    • 6.進入Pod中使用ping 命令測試,如果不行重啟k8s叢集后再試

5、健康檢查(探針)

  • 探針是由kubelet對容器執行的定期診斷。要執行診斷,kubelet呼叫由容器實現的Handler 。有三種型別的處理程式

    • ExecAction :在容器內執行指定命令。如果命令退出時返回碼為 0 則認為診斷成功
    • TCPSocketAction :對指定埠上的容器的IP地址進行TCP檢查。如果埠開啟,則診斷被認為是成功的
    • HTTPGetAction :對指定的埠和路徑上的容器的 IP地址執行 HTTP Get請求。如果響應的狀態碼大於等於200且小於400 ,則診斷被認為是成功的,可以防止埠是開的但是服務已經掛掉
  • 每次探測都將獲得以下三種結果之一

    • 成功:容器通過了診斷
    • 失敗:容器未通過診斷
    • 未知:診斷失敗,因此不會採取任何行動
  • 探針分類

    • readinessProbe :指示容器是否準備好服務請求。如果就緒探測失敗,端點控制器將從與Pod匹配的所有Service的端點中刪除該Pod的IP地址。初始延遲之前的就緒狀態預設為Failure 。如果容器不提供就緒探針,則預設狀態為 Success,也就是在主容器建立時,檢測該主容器是否已經準備就緒,可以向外界提供服務了,防止出現,容器啟動了,但是容器中的服務還沒有啟動,這個是否向外界提供服務就會出現異常

      • readinessProbe隻影響Realy狀態,不會重啟pod,重啟pod會重新執行readinessProbe

      • 資源清單示例

        • apiVersion: v1
          kind: Pod
          metadata:
            name: readiness-httpget-pod
            namespace: default
          spec:
            containers:
            - name: readiness-httpget-container
              image: nginx
              # 映象拉取方式,如果本地有就用本地
              imagePullPolicy: IfNotPresent
              # 就緒檢查方式採用httpGet,預設的nginx肯定沒有index1.html
              readinessProbe:
                httpGet:
                  port: 80
                  path: /index1.html
                # 開始檢測延遲時間1s
                initialDelaySeconds: 1
                # 檢測間隔時間3s
                periodSeconds: 3
          
      • 啟動readiness-httpget-pod並檢視

        • # pod的狀態為Running,但是READY沒準備好
          [root@k8smaster pods]# kubectl get pod
          NAME                    READY   STATUS    RESTARTS   AGE
          readiness-httpget-pod   0/1     Running   0          5m51s
          
          # 檢視pod的描述 就緒檢測失敗
          [root@k8smaster pods]# kubectl describe pod  readiness-httpget-pod
          Warning  Unhealthy  4m26s (x100 over 9m23s)  kubelet, k8snode2  Readiness probe failed: HTTP probe failed with statuscode: 404
          
      • 進入Pod中新增index1.html

        • # 向/usr/share/nginx/html 中新增index1.html 後檢視pod狀態已經就緒
          # kubectl exec -it pod名 -c 容器名(pod中只有一個容器可不指定) -- /bin/sh(執行的命令)
          [root@k8smaster pods]# kubectl exec -it readiness-httpget-pod -c readiness-httpget-container -- /bin/sh
          # cd /usr/share/nginx/html
          # echo "123" >> index1.html
          # ls
          50x.html  index.html  index1.html
          # exit
          
          [root@k8smaster pods]# kubectl get pod
          NAME                    READY   STATUS    RESTARTS   AGE
          readiness-httpget-pod   1/1     Running   0          16m
          
    • livenessProbe :指示容器是否正在執行。如果存活探測失敗,則kubelet會殺死容器,並且容器將受到其重啟策略的影響。如果容器不提供存活探針,則預設狀態為Success,也就是為了防止殭屍程式存在,程式還在,不能提供服務

      • livenessProbe檢測失敗會重啟pod,重啟pod會重新執行readinessProbe

      • 資源清單示例一:exec方式

        • apiVersion: v1
          kind: Pod
          metadata:
            name: liveness-exec-pod
            namespace: default
          spec:
            containers:
            - name: liveness-exec-container
              image: busybox
              imagePullPolicy: IfNotPresent
              # 容器啟動的命令 在/tmp下建立live檔案 過30s刪除
              command: ["/bin/sh","-c","touch /tmp/live ; sleep 30; rm -rf /tmp/live; sleep 3600"]
              # 存活檢查採用 命令的方式 看/tmp下建立live檔案存不存在 不存在則檢測失敗,使用Pod的重啟策略
              livenessProbe:
                exec:
                  command: ["test","-e","/tmp/live"]
                initialDelaySeconds: 1
                periodSeconds: 3
          
        • # 檢視pod 在30s的時候進行了重啟(也不一定是30s,因為容器啟動也會浪費時間)
          [root@k8smaster pods]# kubectl get pod
          NAME                   READY   STATUS    RESTARTS   AGE
          liveness-exec-pod      1/1     Running   0          29s
          web-5dcb957ccc-r74d6   1/1     Running   0          28m
          
          [root@k8smaster pods]# kubectl get pod
          NAME                   READY   STATUS    RESTARTS   AGE
          liveness-exec-pod      1/1     Running   1          32s
          web-5dcb957ccc-r74d6   1/1     Running   0          28m
          
      • 資源清單示例二:httpGet方式

        • apiVersion: v1
          kind: Pod
          metadata:
            name: liveness-httpget-pod
            namespace: default
          spec:
            containers:
            - name: liveness-httpget-container
              image: nginx
              imagePullPolicy: IfNotPresent
              ports:
              - name: http
                # 該Pod監聽其下容器的埠
                containerPort: 80
              # 存活檢查採用 httpGet的方式 訪問/index.html存不存在
              livenessProbe:
                httpGet:
                  port: http
                  path: /index.html
                initialDelaySeconds: 1
                periodSeconds: 3
                # 超時時間 超時代表失敗
                timeoutSeconds: 10
          
        • # 檢視pod狀態
          [root@k8smaster pods]# kubectl get pod
          NAME                   READY   STATUS    RESTARTS   AGE
          liveness-httpget-pod   1/1     Running   0          5m12s
          
          # 進入pod刪除index.html
          [root@k8smaster pods]# kubectl exec -it liveness-httpget-pod -c readiness-httpget-container -- /bin/sh
          # cd /usr/share/nginx/html
          # rm index.html
          
          # 檢視pod狀態
          [root@k8smaster pods]# kubectl get pod
          NAME                   READY   STATUS    RESTARTS   AGE
          liveness-httpget-pod   1/1     Running   1          9m43s
          
      • 資源清單示例三:tcp方式

        • apiVersion: v1
          kind: Pod
          metadata:
            name: liveness-tcp-pod
          spec:
            containers:
            - name: liveness-tcp-container
              image: nginx
              # 存活檢查採用 tcp的方式 檢查8080埠
              livenessProbe:
                initialDelaySeconds: 5
                timeoutSeconds: 1
                periodSeconds: 3
                tcpSocket:
                  port: 8080
          
        • # 檢視pod狀態 一直重啟 因為8080埠一直不存在
          NAME                   READY   STATUS    RESTARTS   AGE
          liveness-tcp-pod       1/1     Running   4          104s
          
    • readinessProbe和livenessProbe 同時使用

      • 資源清單示例

        • apiVersion: v1
          kind: Pod
          metadata:
            name: liveness-httpget-pod
            namespace: default
          spec:
            containers:
            - name: liveness-httpget-container
              image: nginx
              imagePullPolicy: IfNotPresent
              ports:
              - name: http
                # 該Pod監聽其下容器的埠
                containerPort: 80
              readinessProbe:
                httpGet:
                  port: 80
                  path: /index1.html
                # 開始檢測延遲時間1s
                initialDelaySeconds: 1
                # 檢測間隔時間3s
                periodSeconds: 3
              # 存活檢查採用 httpGet的方式 訪問/index.html存不存在
              livenessProbe:
                httpGet:
                  port: http
                  path: /index.html
                initialDelaySeconds: 1
                periodSeconds: 3
                # 超時時間 超時代表失敗
                timeoutSeconds: 10
          

6、start和stop

  • Pod hook(鉤子)是由 Kubernetes管理的kubelet發起的,當容器中的程式啟動前或者容器中的程式終止之前執行,這是包含在容器的生命週期之中。可以同時為Pod中的所有容器都配置hookHook

  • Hook 的型別包括兩種

    • exec :執行一段命令
    • HTTP :傳送 HTTP 請求
  • 資源清單示例

    • apiVersion: v1
      kind: Pod
      metadata:
        name: lifecycle-demo
      spec:
        containers:
        - name: lifecycle-demo-container
          image: nginx
          lifecycle:
            # 容器啟動後執行
            postStart:
              exec:
                command: ["/bin/sh", "-c", "echo Hello from the postStart handler >/usr/share/message"]
            # 容器關閉前執行
            preStop:
              exec:
                command: ["/bin/sh", "-c", "echo Hello from the poststop handler >/usr/share/message"]
      
    • # 進入容器檢視
      [root@k8smaster pods]# kubectl exec -it lifecycle-demo -- /bin/sh
      # cat /usr/share/message
      Hello from the postStart handler
      

7、pod狀態

  • 掛起( Pending ): Pod 已被 Kubernetes 系統接受,但有一個或者多個容器映象尚未建立。等待時間包括排程 Pod
  • 執行中( Running ):該 Pod 已經繫結到了一個節點上, Pod 中所有的容器都已被建立。至少有一個容器正在執行,或者正處於啟動或重啟狀態
  • 成功( Succeeded ): Pod 中的所有容器都被成功終止,並且不會再重啟 常見job和cronjob
  • 失敗( Failed ): Pod 中的所有容器都已終止了,並且至少有一個容器是因為失敗終止。也就是說,容器以非 0
  • 未知( Unknown ):因為某些原因無法取得 Pod 的狀態,通常是因為與 Pod 所在主機通訊失敗

二、控制器

  • Kubernetes中內建了很多controller(控制器),這些相當於一個狀態機,用來控制Pod的具體狀態和行為,也就是說pod是最小的排程單元,而控制器用來管理pod,由控制器管理的pod死亡後,會被拉起,而沒被管理的pod不會被管(也就是自主式pod)

1、Replication Controller(RC)

  • Replication Controller(RC)是Kubernetes系統中核心概念之一,當我們定義了一個 RC並提交到Kubernetes叢集中以後,Master節點上的Controller Manager元件就得到通知,定期檢查系統中存活的Pod,並確保目標Pod例項的數量剛好等於RC的預期值,如果有過多或過少的Pod執行,系統就會停掉或建立一些Pod.此外我們也可以通過修改RC副本數量,來實現Pod的動態縮放功能,也就是說RC用來確保容器應用的副本數始終保持在使用者定義的副本數,即如果有容器異常退出,會自動建立新的Pod來替代;而如果異常多出來的容器也會自動回收

  • 資源清單示例

    • apiVersion: v1
      kind: ReplicationController
      metadata:
        name: rcdemo
      spec:
        # pod需要保持的副本數
        replicas: 3
        # RC的標籤選擇器
        selector:
          # 所管理的Pod上有tier=frontend 這個標籤,也就是說RC是通過labels來區分自己要管理的Pod,不支援集合式的selector
          tier: frontend
        # template 相當於定義的Pod
        template:
          metadata:
            # pod的標籤key=value的形式 如果跟RC所需要的一致,就會被匹配到的RC管理
            labels:
              tier: frontend
          spec:
            # pod中的容器
            containers:
            - name: mynginx
              image: nginx
              # 容器內部新增環境變數
              env:
              - name: GET_HOSTS_FROM
                value: dns
              ports:
              - containerPort: 80
      
    • # 建立RC 
      [root@k8smaster deployments]# kubectl apply -f rcdemo.yaml
      replicationcontroller/rcdemo created
      
      # 檢視rc DESIRED:設計數量 CURRENT:當前數量 READY:準備的數量
      [root@k8smaster deployments]# kubectl get rc
      NAME     DESIRED   CURRENT   READY   AGE
      rcdemo   3         3         3       11m
      
      並檢視Pod Pod名稱規則(控制器name+隨機字串)
      [root@k8smaster deployments]# kubectl get pod
      NAME           READY   STATUS    RESTARTS   AGE
      rcdemo-4sgkd   1/1     Running   0          37s
      rcdemo-hkhfw   1/1     Running   0          37s
      rcdemo-xnr2n   1/1     Running   0          37s
      
      # 刪除所有pod 在檢視pod是否被控制器拉起 
      [root@k8smaster deployments]# kubectl delete pod --all
      pod "rcdemo-4sgkd" deleted
      pod "rcdemo-hkhfw" deleted
      pod "rcdemo-xnr2n" deleted
      
      # --show-labels(顯示標籤) 被拉起的Pod和原來的完全不一樣 說明是重新建立的
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME           READY   STATUS    RESTARTS   AGE   LABELS
      rcdemo-bxtzt   1/1     Running   0          62s   tier=frontend
      rcdemo-f2pnn   1/1     Running   0          62s   tier=frontend
      rcdemo-gbxt4   1/1     Running   0          62s   tier=frontend
      
      # 修改其中一個Pod的label 觀察Pod情況 發現多了一個Pod(說明了控制器是通過label來控制Pod的)
      [root@k8smaster deployments]# kubectl label pod rcdemo-bxtzt tier=frontend123 --overwrite
      pod/rcdemo-bxtzt labeled
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME           READY   STATUS    RESTARTS   AGE     LABELS
      rcdemo-8kt4h   1/1     Running   0          14s     tier=frontend
      rcdemo-bxtzt   1/1     Running   0          7m34s   tier=frontend123
      rcdemo-f2pnn   1/1     Running   0          7m34s   tier=frontend
      rcdemo-gbxt4   1/1     Running   0          7m34s   tier=frontend
      

2、ReplicaSet(RS)

  • Kubernetes 官方建議使用 RS(ReplicaSet ) 替代 RC (ReplicationController ) 進行部署,RS 跟 RC 沒有本質的不同,只是名字不一樣,並且 RS 支援集合式的 selector

  • 資源清單示例

    • apiVersion: apps/v1
      kind: ReplicaSet
      metadata:
        name: rsdemo
      spec:
        # pod需要保持的副本數
        replicas: 3
        # RC的標籤選擇器
        selector:
          # 所管理的Pod上需要有tier=frontend 這個標籤,也就是說RS是通過labels來區分自己要管理的Pod,支援集合式的selector matchLabels等(RC不支援)
          matchLabels:
            tier: frontend
        # template 相當於定義的Pod
        template:
          metadata:
            # pod的標籤key=value的形式 如果跟RC所需要的一致,就會被匹配到的RS管理
            labels:
              tier: frontend
          spec:
            # pod中的容器
            containers:
            - name: mynginx
              image: nginx
              # 容器內部新增環境變數
              env:
              - name: GET_HOSTS_FROM
                value: dns
              ports:
              - containerPort: 80
      
    • # 使用效果和RC一樣
      

3、Deployment

  • Deployment 為 Pod 和 ReplicaSet 提供了一個宣告式定義 (declarative) 方法,用來替代以前的ReplicationController來方便的管理應用,Kubenetes v1.2 引入的新概念,引入的目的是為了更好的解決 Pod 的編排問題,Deployment 內部使用了 Replica Set 來實現。Deployment 的定義與 Replica Set 的定義很類似

  • 應用場景

    • RS和RC只能維持Pod的副本數量,功能薄弱
    • 定義 Deployment 來建立 Pod 和 ReplicaSet
    • 滾動升級和回滾應用:可以動態的切換版本,底層使用不同的RS來實現的
    • 擴容和縮容:增加或減少副本的數量
    • 暫停和繼續 Deployment
    • 滾動升級和回滾
      • 說明:從V1版升級到V2版 共有三個Pod副本

        • 1.建立新的RS1,建立一個(數量可控)新的V2Pod,當新的V2Pod可以正常使用後,刪除一箇舊的V1Pod,保證k8s叢集中該中Pod的數量不會變,直至所有的V1Pod換成V2Pod,並且舊的RS不會被刪除(用於回滾)
        • 2.回滾的過程和上述相反,啟動舊的RS
  • 資源清單示例

    • apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: nginx-deployment
      spec:
        replicas: 3
        # 這個版本的Deployment不能省去selector
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            labels:
              app: nginx
          spec:
            containers:
            - name: nginx
              image: nginx:1.7.9
              ports:
              - containerPort: 80
      
    • # 啟動Deployment 檢視 Deployment 、rs(rs名稱規則:Deployment的name+隨機字串) 和 pod (pod名稱規則:rs的name+隨機字串)
      # --record 方便檢視版本
      [root@k8smaster deployments]# kubectl apply -f nginx-deployment.yaml --record
      deployment.apps/nginx-deployment created
      
      [root@k8smaster deployments]# kubectl get deployment
      NAME               READY   UP-TO-DATE   AVAILABLE   AGE
      nginx-deployment   3/3     3            3           42s
      
      [root@k8smaster deployments]# kubectl get rs
      NAME                          DESIRED   CURRENT   READY   AGE
      nginx-deployment-5bf87f5f59   3         3         3       47s
      
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS    RESTARTS   AGE   LABELS
      nginx-deployment-5bf87f5f59-hxj25   1/1     Running   0          93s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-wgw7w   1/1     Running   0          93s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-zcf82   1/1     Running   0          93s   app=nginx,pod-template-hash=5bf87f5f59
      
      # 動態擴容到10個Pod副本
      [root@k8smaster deployments]# kubectl scale deployment nginx-deployment --replicas 10
      deployment.apps/nginx-deployment scaled
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS    RESTARTS   AGE     LABELS
      nginx-deployment-5bf87f5f59-58vtn   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-dmrrs   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-f7cl6   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-g5n6j   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-gsvpd   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-hfmjh   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-hxj25   1/1     Running   0          2m30s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-wgw7w   1/1     Running   0          2m30s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-wzjtm   1/1     Running   0          29s     app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-zcf82   1/1     Running   0          2m30s   app=nginx,pod-template-hash=5bf87f5f59
      
      # 動態縮減到3個Pod副本 發現還是原來的那3個 說明會保留存活時間長的
      [root@k8smaster deployments]# kubectl scale deployment nginx-deployment --replicas 3
      deployment.apps/nginx-deployment scaled
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS    RESTARTS   AGE     LABELS
      nginx-deployment-5bf87f5f59-hxj25   1/1     Running   0          3m11s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-wgw7w   1/1     Running   0          3m11s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-zcf82   1/1     Running   0          3m11s   app=nginx,pod-template-hash=5bf87f5f59
      [root@k8smaster deployments]#
      
      # 動態擴容和縮減都不會重新建立新的RS
      [root@k8smaster deployments]# kubectl get rs
      NAME                          DESIRED   CURRENT   READY   AGE
      nginx-deployment-5bf87f5f59   3         3         3       5m9s
      
      
    • # 滾動更新 更新映象版本 檢視 RS 和 pod
      [root@k8smaster deployments]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
      deployment.apps/nginx-deployment image updated
      # 已經建立了新的RS
      [root@k8smaster deployments]# kubectl get rs
      NAME                          DESIRED   CURRENT   READY   AGE
      nginx-deployment-5bf87f5f59   3         3         3       9m46s
      nginx-deployment-678645bf77   1         1         0       3s
      # 已經啟動的新Pod
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS              RESTARTS   AGE     LABELS
      nginx-deployment-5bf87f5f59-hxj25   1/1     Running             0          9m49s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-wgw7w   1/1     Running             0          9m49s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-zcf82   1/1     Running             0          9m49s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-678645bf77-4k5ls   0/1     ContainerCreating   0          6s      app=nginx,pod-template-hash=678645bf77
      
      # 持續觀察 RS 和 pod READY的數量始終會保持在我們需要的副本數量
      [root@k8smaster deployments]# kubectl get rs
      NAME                          DESIRED   CURRENT   READY   AGE
      nginx-deployment-5bf87f5f59   1         1         1       10m
      nginx-deployment-678645bf77   3         3         2       41s
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS              RESTARTS   AGE   LABELS
      nginx-deployment-5bf87f5f59-wgw7w   1/1     Running             0          10m   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-678645bf77-4k5ls   1/1     Running             0          48s   app=nginx,pod-template-hash=678645bf77
      nginx-deployment-678645bf77-szk6f   1/1     Running             0          18s   app=nginx,pod-template-hash=678645bf77
      nginx-deployment-678645bf77-tm4jl   0/1     ContainerCreating   0          16s   app=nginx,pod-template-hash=678645bf77
      
      # 最終結果 舊的RS並沒有刪除
      [root@k8smaster deployments]# kubectl get rs -o wide
      NAME                          DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES        SELECTOR
      nginx-deployment-5bf87f5f59   0         0         0       23m   nginx        nginx:1.7.9   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-678645bf77   3         3         3       13m   nginx        nginx:1.9.1   app=nginx,pod-template-hash=678645bf77
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS    RESTARTS   AGE   LABELS
      nginx-deployment-678645bf77-4k5ls   1/1     Running   0          67s   app=nginx,pod-template-hash=678645bf77
      nginx-deployment-678645bf77-szk6f   1/1     Running   0          37s   app=nginx,pod-template-hash=678645bf77
      nginx-deployment-678645bf77-tm4jl   1/1     Running   0          35s   app=nginx,pod-template-hash=678645bf77
      
      # 使用 kubectl edit deployment/nginx-deployment 可以編輯資源的yaml
      
    • # 回滾到上一個版本 啟用上一個版本對應的RS
      [root@k8smaster deployments]# kubectl rollout undo deployment/nginx-deployment
      deployment.apps/nginx-deployment rolled back
      [root@k8smaster deployments]# kubectl get rs -o wide
      NAME                          DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES        SELECTOR
      nginx-deployment-5bf87f5f59   2         2         1       27m   nginx        nginx:1.7.9   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-678645bf77   2         2         2       17m   nginx        nginx:1.9.1   app=nginx,pod-template-hash=678645bf77
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS        RESTARTS   AGE   LABELS
      nginx-deployment-5bf87f5f59-97lds   1/1     Running       0          9s    app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-lb7mm   1/1     Running       0          13s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-zltbv   1/1     Running       0          11s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-678645bf77-4k5ls   0/1     Terminating   0          18m   app=nginx,pod-template-hash=678645bf77
      nginx-deployment-678645bf77-szk6f   0/1     Terminating   0          17m   app=nginx,pod-template-hash=678645bf77
      nginx-deployment-678645bf77-tm4jl   0/1     Terminating   0          17m   app=nginx,pod-template-hash=678645bf77
      [root@k8smaster deployments]# kubectl get rs -o wide
      NAME                          DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES        SELECTOR
      nginx-deployment-5bf87f5f59   3         3         3       28m   nginx        nginx:1.7.9   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-678645bf77   0         0         0       18m   nginx        nginx:1.9.1   app=nginx,pod-template-hash=678645bf77
      [root@k8smaster deployments]# kubectl get pod --show-labels
      NAME                                READY   STATUS    RESTARTS   AGE   LABELS
      nginx-deployment-5bf87f5f59-97lds   1/1     Running   0          26s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-lb7mm   1/1     Running   0          30s   app=nginx,pod-template-hash=5bf87f5f59
      nginx-deployment-5bf87f5f59-zltbv   1/1     Running   0          28s   app=nginx,pod-template-hash=5bf87f5f59
      
      # 檢視回滾狀態如果 rollout 成功完成, kubectl rollout status 將返回一個0值的 Exit Code
      [root@k8smaster deployments]# kubectl rollout status deployment/nginx-deployment
      deployment "nginx-deployment" successfully rolled out
      [root@k8smaster deployments]# echo $?
      0
      
      # 檢視版本 前面的REVISION可用於回退於指定版本
      [root@k8smaster deployments]# kubectl rollout history deployment/nginx-deployment
      
      deployment.apps/nginx-deployment
      REVISION  CHANGE-CAUSE
      2         kubectl apply --filename=nginx-deployment.yaml --record=true
      3         kubectl apply --filename=nginx-deployment.yaml --record=true
      
      
    • # Deployment 更新策略
      # Deployment 可以保證在升級時只有一定數量的 Pod 是 down 的。預設的,它會確保至少有比期望的Pod數量少一個是up狀態(最多一個不可用)
      # Deployment 同時也可以確保只建立出超過期望數量的一定數量的 Pod。預設的,它會確保最多比期望的Pod數量多一個的 Pod 是 up 的(最多1個 surge )
      [root@k8smaster deployments]# kubectl describe deployments
      Name:                   nginx-deployment
      Namespace:              default
      CreationTimestamp:      Wed, 16 Dec 2020 10:57:54 +0800
      Labels:                 <none>
      Annotations:            deployment.kubernetes.io/revision: 3
                              kubernetes.io/change-cause: kubectl apply --filename=nginx-deployment.yaml --record=true
      Selector:               app=nginx
      Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
      StrategyType:           RollingUpdate
      MinReadySeconds:        0
      RollingUpdateStrategy:  25% max unavailable, 25% max surge
      
      # 未來的 Kuberentes 版本中,將從1-1變成25%-25%
      
    • Rollover(多個rollout並行)

      • 假如您建立了一個有5個niginx:1.7.9 replica的Deployment,但是當還只有3個nginx:1.7.9 的 replica 建立出來的時候您就開始更新含有5個 nginx:1.9.1 replica 的 Deployment。在這種情況下,Deployment 會立即殺掉已建立的3個 nginx:1.7.9 的 Pod,並開始建立 nginx:1.9.1 的 Pod。它不會等到所有的5個 nginx:1.7.9 的Pod 都建立完成後才開始改變航道
    • 回退Deployment

      • # 檢視版本歷史記錄 REVISION可用於回退於指定版本
        kubectl rollout history deployment/nginx-deployment
        [root@k8smaster deployments]# kubectl rollout history deployment/nginx-deployment
        
        deployment.apps/nginx-deployment
        REVISION  CHANGE-CAUSE
        2         kubectl apply --filename=nginx-deployment.yaml --record=true
        3         kubectl apply --filename=nginx-deployment.yaml --record=true
        
        # 回退到當前的上一個版本
        kubectl rollout undo deployment/nginx-deployment
        
        # 回退到指定版本 可以使用 --revision引數指定某個歷史版本
        kubectl rollout undo deployment/nginx-deployment --to-revision=2
        
        # 暫停 deployment 的更新
        kubectl rollout pause deployment/nginx-deployment
        
        # 通過設定.spec.revisonHistoryLimit來指定deployment最多保留多少revision歷史記錄。預設會保留所有revision;如果將該項設定為0,Deployment 就不能回退
        

4、DaemonSet

  • DaemonSet 確保全部(或者一些)Node上執行一個Pod 的副本。當有Node加入叢集時,也會為他們新增一個Pod 。當有Node從叢集移除時,這些Pod也會被回收。刪除DaemonSet將會刪除它建立的所有Pod,也就是說由DaemoSet控制的Pod會在每個Node上都會執行一個,相當於一個守護模式,當有新的Node加入到叢集時,新的Node也會有這種Pod,當有Node退出叢集時,這個Node上的Pod就會刪除

  • 應用場景

    • 執行叢集儲存 daemon,例如在每個 Node 上執行 glusterd 、 ceph
    • 在每個 Node 上執行日誌收集 daemon,例如 fluentd 、 logstash
    • 在每個 Node 上執行監控 daemon,例如 Prometheus Node Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
  • 資源清單示例

    • apiVersion: apps/v1
      kind: DaemonSet
      metadata:
        name: deamonset-example
        labels:
          app: daemonset
      spec:
        selector:
          matchLabels:
            name: deamonset-example
        template:
          metadata:
            labels:
              name: deamonset-example
          spec:
            containers:
            - name: daemonset-example
              image: nginx
      
    • # 啟動deamonset-example
      [root@k8smaster deployments]# kubectl apply -f deamonset-example.yaml
      daemonset.apps/deamonset-example created
      
      # 檢視 DaemonSet
      [root@k8smaster deployments]# kubectl get daemonset
      NAME                DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
      deamonset-example   2         2         2       2            2           <none>          71s
      
      # 檢視 Pod node1和node2節點上各一個
      [root@k8smaster deployments]# kubectl get pod -o wide
      NAME                                READY   STATUS    RESTARTS   AGE    IP            NODE       NOMINATED NODE   READINESS GATES
      deamonset-example-5l9f6             1/1     Running   0          28s    10.244.2.31   k8snode1   <none>           <none>
      deamonset-example-lwhrl             1/1     Running   0          28s    10.244.1.35   k8snode2   <none>           <none>
      

5、StatefulSet

  • StatefulSet作為Controller為Pod提供唯一的標識。它可以保證部署和scale的順序

  • StatefulSet是為了解決有狀態服務的問題(對應Deployments和ReplicaSets是為無狀態服務而設計)例如mysql

  • 應用場景

    • 定的持久化儲存,即Pod重新排程後還是能訪問到相同的持久化資料,基於PVC來實現
    • 穩定的網路標誌,即Pod重新排程後其PodName和HostName不變,基於Headless Service(即沒有Cluster IP的Service)來實現
    • 有序部署,有序擴充套件,即Pod是有順序的,在部署或者擴充套件的時候要依據定義的順序依次依次進行(即從0到N-1,在下一個Pod執行之前所有之前的Pod必須都是Running和Ready狀態),基於init containers來實現
    • 有序收縮,有序刪除(即從N-1到0)
  • 需要用到的知識有service,pvc儲存等,可先跳過,先學習下前置知識

  • 在PVC模組詳細介紹

6、Job

  • Job 負責批處理任務,即僅執行一次的任務,它保證批處理任務的一個或多個 Pod 成功結束

  • Job Spec

    • spec.template格式同Pod
    • RestartPolicy僅支援Never或OnFailure(僅執行成功一次所以不能用Always)
    • 單個Pod時,預設Pod成功執行後Job即結束
    • .spec.completions 標誌Job結束需要成功執行的Pod個數,預設為1 (Pod的返回碼為0時成功一次)
    • .spec.parallelism 標誌並行執行的Pod的個數,預設為1
    • .spec.activeDeadlineSeconds 標誌失敗Pod的重試最大時間,超過這個時間不會繼續重試
  • 資源清單示例

    • apiVersion: batch/v1
      kind: Job
      metadata:
        name: pi
      spec:
        template:
          metadata:
            name: pi
          spec:
            containers:
            - name: pi
              # 該映象用於計算圓周率 CMD是將2000位的圓周率輸出
              image: perl
              command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
            restartPolicy: Never
      
    • # 建立job
      [root@k8smaster deployments]# kubectl apply -f job-test.yaml
      job.batch/pi created
      
      # 檢視job 已經成功結束
      [root@k8smaster deployments]# kubectl get job
      NAME   COMPLETIONS   DURATION   AGE
      pi     1/1           90s        109s
      
      # 檢視pod 已經成功結束
      [root@k8smaster deployments]# kubectl get pod
      NAME                                READY   STATUS      RESTARTS   AGE
      pi-ljl4g                            0/1     Completed   0          101s
      
      # 檢視pod日誌
      [root@k8smaster deployments]# kubectl logs pi-ljl4g
      3.1415926535897932384626433832795028841971693993751058209...以後位數省略
      

7、CronJob

  • CronJob管理基於時間的 Job

    • 在給定時間點只執行一次
    • 週期性地在給定時間點執行
    • 使用條件:當前使用的 Kubernetes 叢集,版本 >= 1.8(對 CronJob)
    • CronJob通過建立Job來進行管理
  • 應用場景

    • 在給定的時間點排程 Job 執行
    • 建週期性執行的 Job,例如:資料庫備份、傳送郵件
  • CronJob Spec

    • .spec.schedule指定任務執行週期,格式同Cron
    • .spec.jobTemplate指定需要執行的任務,格式同Job,包含了.spec.completions、.spec.parallelism、.spec.activeDeadlineSeconds
    • .spec.startingDeadlineSeconds :啟動 Job 的期限(秒級別),該欄位是可選的。如果因為任何原因而錯過了被排程的時間,那麼錯過執行時間的 Job 將被認為是失敗的。如果沒有指定,則沒有期限
    • .spec.concurrencyPolicy :併發策略,該欄位也是可選的。它指定了如何處理被 Cron Job 建立的 Job 的併發執行。只允許指定下面策略中的一種:
      • Allow (預設):允許併發執行 Job
      • Forbid :禁止併發執行,如果前一個還沒有完成,則直接跳過下一個
      • Replace :取消當前正在執行的 Job,用一個新的來替換
      • 注意,當前策略只能應用於同一個 Cron Job 建立的 Job。如果存在多個 Cron Job,它們建立的 Job 之間總是允許併發執行
    • .spec.suspend :掛起,該欄位也是可選的。如果設定為true ,後續所有執行都會被掛起。它對已經開始執行的Job不起作用。預設值為 false
    • .spec.successfulJobsHistoryLimit和.spec.failedJobsHistoryLimit :歷史限制,是可選的欄位。它們指定了可以保留多少完成和失敗的 Job。預設情況下,它們分別設定為3和1 。設定限制的值為0 ,相關型別的Job完成後將不會被保留
    • .spec.startingDeadlineSeconds指定任務開始的截止期限
  • 資源清單示例

    • apiVersion: batch/v1beta1
      kind: CronJob
      metadata:
        name: hello
      spec:
        # 一分鐘建立一個Job去執行 (分鐘 小時 天 月 周)
        schedule: "*/1 * * * *"
        jobTemplate:
          spec:
            template:
              spec:
                containers:
                - name: hello
                  image: busybox
                  args:
                  - /bin/sh
                  - -c
                  - date; echo Hello from the Kubernetes cluster
                restartPolicy: OnFailure
      
    • # 啟動cronjob
      [root@k8smaster deployments]# kubectl apply -f cronjob-test.yaml
      cronjob.batch/hello created
      
      # 檢視cronjob
      [root@k8smaster deployments]# kubectl get cronjob
      NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
      hello   */1 * * * *   False     0        <none>          12s
      
      # 等待幾分鐘檢視job和pod 預設只保留3條
      [root@k8smaster deployments]# kubectl get job
      NAME               COMPLETIONS   DURATION   AGE
      hello-1608103680   1/1           16s        2m49s
      hello-1608103740   1/1           17s        108s
      hello-1608103800   1/1           17s        48s
      [root@k8smaster deployments]# kubectl get pod
      NAME                     READY   STATUS      RESTARTS   AGE
      hello-1608103680-68c5t   0/1     Completed   0          2m54s
      hello-1608103740-9rwpl   0/1     Completed   0          114s
      hello-1608103800-fjv7z   0/1     Completed   0          54s
      
      
    • 注意事項:

      • 建立Job操作應該是冪等的(防止後面的執行結果影響前面的結果)
      • cronjob不能連線到job的成功,只會週期的建立job,但是job是否執行成功可以監測到

三、Service

  • Kubernetes Service定義了這樣一種抽象:一個Pod的邏輯分組,一種可以訪問它們的策略 —— 通常稱為微服務。 這一組Pod能夠被Service訪問到,通常是通過Label Selector,標籤選擇不到建立的是空叢集

  • Kubernetes Service可以為一組具有相同功能的容器應用提供一個統一的入口地址,並且將請求負載分發到後端的各個容器應用上,提供了服務註冊與發現(因為Pod掛掉之後,新建立的Pod的原來的完全不一樣,防止Pod失聯),相當於註冊中心

  • Service能夠提供負載均衡的能力,但是在使用上有以下限制

    • 只提供 4 層負載均衡能力,而沒有7層功能,但有時我們可能需要更多的匹配規則來轉發請求,這點上 4 層負載均衡是不支援的,僅支援輪詢演算法
    • 四層負載均衡:通過Ip和埠進行轉發
    • 七層負載均衡:主機和域名進行轉發(k8s通過ingress可以實現)
  • service原理:

  • service分類

    • ClusterIp:預設型別,自動分配一個僅 Cluster 內部可以訪問的虛擬 IP
    • NodePort:在 ClusterIP 基礎上為 Service 在每臺機器上繫結一個埠,這樣就可以通過 : NodePort 來訪問該服務
    • LoadBalancer:在 NodePort 的基礎上,藉助 cloud provider 建立一個外部負載均衡器,並將請求轉發到: NodePort
    • ExternalName:把叢集外部的服務引入到叢集內部來,在叢集內部直接使用。沒有任何型別代理被建立,這隻有 kubernetes 1.7 或更高版本的 kube-dns 才支援
  • VIP和Service代理

    • 在 Kubernetes 叢集中,每個 Node 執行一個kube-proxy 程式。kube-proxy負責為Service實現了一種VIP(虛擬 IP)的形式,而不是ExternalName的形式。 在Kubernetes v1.0 版本,代理完全在 userspace。在Kubernetes v1.1 版本,新增了 iptables 代理,但並不是預設的執行模式。 從 Kubernetes v1.2 起,預設就是iptables 代理。 在 Kubernetes v1.8.0-beta.0 中,新增了 ipvs 代理
      在 Kubernetes 1.14 版本開始預設使用 ipvs 代理
    • 在 Kubernetes v1.0 版本, Service 是 “4層”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用來表示 “7層”(HTTP)服務
    • 為什麼不使用DNS代理
      • 因為DNS有快取,不能實現負載均衡
  • 代理模式分類

    • userspace 代理模式

    • iptables 代理模式

    • ipvs 代理模式

      • 這種模式,kube-proxy 會監視 Kubernetes Service 物件和 Endpoints ,呼叫 netlink 介面以相應地建立ipvs 規則並定期與 Kubernetes Service 物件和 Endpoints 物件同步 ipvs 規則,以確保 ipvs 狀態與期望一致。訪問服務時,流量將被重定向到其中一個後端 Pod與 iptables 類似,ipvs 於 netfilter 的 hook 功能,但使用雜湊表作為底層資料結構並在核心空間中工作。這意味著 ipvs 可以更快地重定向流量,並且在同步代理規則時具有更好的效能。此外,ipvs 為負載均衡演算法提供了更多選項,例如:

        • rr :輪詢排程
        • lc :最小連線數
        • dh :目標雜湊
        • sh :源雜湊
        • sed :最短期望延遲
        • nq : 不排隊排程

1、ClusterIp

  • 預設型別,自動分配一個僅Cluster內部可以訪問的虛擬IP(自身節點也可訪問)

  • clusterIP 主要在每個 node 節點使用 iptables或ipvs,將發向 clusterIP 對應埠的資料,轉發到 kube-proxy 中。然後 kube-proxy 自己內部實現有負載均衡的方法,並可以查詢到這個 service 下對應 pod 的地址和埠,進而把資料轉發給對應的 pod 的地址和埠

    • apiserver 使用者通過kubectl命令向apiserver傳送建立service的命令,apiserver接收到請求後將資料儲存到etcd中
    • kube-proxy kubernetes的每個節點中都有一個叫做kube-porxy的程式,這個程式負責感知service,pod的變化,並將變化的資訊寫入本地的iptables規則中
    • iptables 使用NAT等技術將virtualIP的流量轉至endpoint中
    • 服務支援tcp和UDP,但是預設的是TCP
  • 資源清單示例

    • # deployment 的資源清單
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: myapp-deploy
        namespace: default
      spec:
        replicas: 3
        selector:
          # 需要匹配的標籤
          matchLabels:
            app: myapp
            release: stabel
        template:
          metadata:
            # Pod攜帶的標籤
            labels:
              app: myapp
              release: stabel
              env: test
          spec:
            containers:
            - name: myapp
              image: nginx
              imagePullPolicy: IfNotPresent
              ports:
              - name: http
                containerPort: 80
      
    • # ClusterIP service 的資源清單
      apiVersion: v1
      kind: Service
      metadata:
        name: myapp
        namespace: default
      spec:
        # service 的型別
        type: ClusterIP
        # 通過標籤來選擇pod,需全部滿足
        selector:
          app: myapp
          release: stabel
        # 暴露埠
        ports:
        # name 是pod的一個埠
        - name: http
          # 叢集內部使用的埠
          port: 80
          # 轉發到其下Pod的容器埠 負載均衡演算法是輪詢
          targetPort: 80
      
    • # 建立myapp-deploy的deployment 並檢視Pod
      [root@k8smaster services]# kubectl apply -f myapp-deploy.yaml
      deployment.apps/myapp-deploy created
      [root@k8smaster services]# kubectl get deployment
      NAME           READY   UP-TO-DATE   AVAILABLE   AGE
      myapp-deploy   3/3     3            3           14s
      [root@k8smaster services]# kubectl get pod
      NAME                            READY   STATUS    RESTARTS   AGE
      myapp-deploy-7c4dbc97b9-qk62v   1/1     Running   0          20s
      myapp-deploy-7c4dbc97b9-qvx4h   1/1     Running   0          20s
      myapp-deploy-7c4dbc97b9-svsww   1/1     Running   0          20s
      
      # 建立service並檢視
      [root@k8smaster services]# kubectl apply -f clusterip.yaml
      service/myapp created
      [root@k8smaster services]# kubectl get svc -o wide
      NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE     SELECTOR
      kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP   20h     <none>
      myapp        ClusterIP   10.110.54.87   <none>        80/TCP    6m28s   app=myapp,release=stabel
      
      # 使用 CLUSTER-IP 進行訪問
      [root@k8smaster services]# curl 10.110.54.87:80
      <!DOCTYPE html>
      <html>
      <head>
      <title>Welcome to nginx!</title>
      <style>
          body {
              width: 35em;
              margin: 0 auto;
              font-family: Tahoma, Verdana, Arial, sans-serif;
          }
      </style>
      </head>
      <body>
      <h1>Welcome to nginx!</h1>
      <p>If you see this page, the nginx web server is successfully installed and
      working. Further configuration is required.</p>
      
      <p>For online documentation and support please refer to
      <a href="http://nginx.org/">nginx.org</a>.<br/>
      Commercial support is available at
      <a href="http://nginx.com/">nginx.com</a>.</p>
      
      <p><em>Thank you for using nginx.</em></p>
      </body>
      </html>
      

2、Headless

  • 有時不需要或不想要負載均衡,以及單獨的 Service IP 。遇到這種情況,可以通過指定 ClusterIP(spec.clusterIP) 的值為 “None” 來建立 Headless Service 。這類 Service 並不會分配 Cluster IP, kube-proxy 不會處理它們,而且平臺也不會為它們進行負載均衡和路由,簡單點說CLUSTER-IP為None 部署有狀態服務時必須使用

  • 資源清單示例

    • apiVersion: v1
      kind: Service
      metadata:
        name: myapp-headless
        namespace: default
      spec:
        selector:
          app: myapp
        clusterIP: "None"
        ports:
        - port: 80
          targetPort: 80
      
    • # 建立無頭service並檢視
      [root@k8smaster services]# kubectl apply -f headless.yaml
      service/myapp-headless created
      [root@k8smaster services]# kubectl get svc -o wide
      NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
      kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP   21h   <none>
      myapp-headless   ClusterIP   None         <none>        80/TCP    4s    app=myapp
      
      # 通過endpoints檢視其關聯的Pod
      [root@k8smaster services]# kubectl get endpoints
      NAME             ENDPOINTS                                      AGE
      kubernetes       192.168.47.150:6443                            66s
      myapp-headless   10.244.1.76:80,10.244.2.73:80,10.244.2.74:80   8s
      
  • 通過解析域名的方式檢視Headless所關聯的Pod(也就意味著沒有CLUSTER-IP,還能使用域名的方式去訪問Headless)

    • # 檢視系統Pod 其中 coredns 就是負責域名解析的
      [root@k8smaster services]# kubectl get pod -n  kube-system -o wide
      NAME                                READY   STATUS    RESTARTS   AGE     IP               NODE        NOMINATED NODE   READINESS GATES
      coredns-7ff77c879f-494fb            1/1     Running   2          41h     10.244.2.14      k8snode1    <none>           <none>
      coredns-7ff77c879f-tvgrz            1/1     Running   2          42h     10.244.1.20      k8snode2    <none>           <none>
      etcd-k8smaster                      1/1     Running   3          6d14h   192.168.47.150   k8smaster   <none>           <none>
      kube-apiserver-k8smaster            1/1     Running   3          6d14h   192.168.47.150   k8smaster   <none>           <none>
      kube-controller-manager-k8smaster   1/1     Running   15         6d14h   192.168.47.150   k8smaster   <none>           <none>
      kube-flannel-ds-amd64-hflj8         1/1     Running   4          6d1h    192.168.47.150   k8smaster   <none>           <none>
      kube-flannel-ds-amd64-s9xhk         1/1     Running   3          6d1h    192.168.47.162   k8snode2    <none>           <none>
      kube-flannel-ds-amd64-wp7mp         1/1     Running   4          6d1h    192.168.47.161   k8snode1    <none>           <none>
      kube-proxy-5l8kb                    1/1     Running   2          42h     192.168.47.150   k8smaster   <none>           <none>
      kube-proxy-6n8vp                    1/1     Running   2          42h     192.168.47.161   k8snode1    <none>           <none>
      kube-proxy-lgcxp                    1/1     Running   2          42h     192.168.47.162   k8snode2    <none>           <none>
      kube-scheduler-k8smaster            1/1     Running   16         6d14h   192.168.47.150   k8smaster   <none>           <none>
      
      # 使用dig 命令 解析域名 dig -t A service名.名稱空間名.svc.cluster.local.(預設叢集域名) @coredns的Ip(任意一個即可)
      # 如果沒有dig 命令 安裝即可 yum -y install bind-utils
      # 我們發現 其中所關聯的Pod都顯示了出來
      [root@k8smaster services]# dig -t A myapp-headless.default.svc.cluster.local. @10.244.2.14
      
      ; <<>> DiG 9.11.13-RedHat-9.11.13-3.el8 <<>> -t A myapp-headless.default.svc.cluster.local. @10.244.2.14
      ;; global options: +cmd
      ;; Got answer:
      ;; WARNING: .local is reserved for Multicast DNS
      ;; You are currently testing what happens when an mDNS query is leaked to DNS
      ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37833
      ;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
      ;; WARNING: recursion requested but not available
      
      ;; OPT PSEUDOSECTION:
      ; EDNS: version: 0, flags:; udp: 4096
      ; COOKIE: 96e732da9566602d (echoed)
      ;; QUESTION SECTION:
      ;myapp-headless.default.svc.cluster.local. IN A
      
      ;; ANSWER SECTION:
      myapp-headless.default.svc.cluster.local. 30 IN A 10.244.2.73
      myapp-headless.default.svc.cluster.local. 30 IN A 10.244.2.74
      myapp-headless.default.svc.cluster.local. 30 IN A 10.244.1.76
      
      ;; Query time: 2 msec
      ;; SERVER: 10.244.2.14#53(10.244.2.14)
      ;; WHEN: Thu Dec 17 11:02:40 CST 2020
      ;; MSG SIZE  rcvd: 249
      
      

3、NodePort

  • nodePort的原理在於在node上開了一個埠,將向該埠的流量匯入到kube-proxy,然後由kube-proxy進一步到給對應的pod

  • 將服務暴露給叢集外部使用,可指定埠

  • NodePort,Kubernetes master會分配一個區域範圍內,(預設是30000-32767),並且,每一個node,都會代理(proxy)這個埠到你的服務中,我們可以在spec.ports[*].nodePort 找到具體的值,如果我們向指定一個埠,我們可以直接寫在nodePort上,系統就會給你指派指定埠,但是這個值必須是指定範圍內的

  • 資源清單示例

    • apiVersion: v1
      kind: Service
      metadata:
        name: myapp-nodeport
        namespace: default
      spec:
        type: NodePort
        selector:
          app: myapp
          release: stabel
        ports:
        - name: http
          port: 80
          targetPort: 80
          # node暴露的埠 外界使用任意nodeIp:該埠  不指定隨機分配
          nodePort: 30001
      
    • # 建立myapp-nodeport並檢視service
      [root@k8smaster services]# kubectl apply -f nodeport.yaml
      service/myapp-nodeport created
      [root@k8smaster services]# kubectl get svc -o wide
      NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
      kubernetes       ClusterIP   10.96.0.1      <none>        443/TCP        21h   <none>
      myapp-nodeport   NodePort    10.111.91.90   <none>        80:30001/TCP   4s    app=myapp,release=stabel
      
      # 經過測試 在外界使用任意nodeIp:30001 都可訪問成功
      
      # iptables -t nat -nvL  查詢轉發流程
      # ipvsadm -Ln 查詢轉發流程 
      [root@k8smaster services]# ipvsadm -Ln
      IP Virtual Server version 1.2.1 (size=4096)
      Prot LocalAddress:Port Scheduler Flags
        -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
      TCP  10.96.0.1:443 rr
        -> 192.168.47.150:6443          Masq    1      1          0
      TCP  10.96.0.10:53 rr
        -> 10.244.1.20:53               Masq    1      0          0
        -> 10.244.2.14:53               Masq    1      0          0
      

4、LoadBalancer

  • loadBalancer 和 nodePort 其實是同一種方式。區別在於 loadBalancer 比 nodePort 多了一步,就是可以呼叫cloud provider 去建立LB來向節點導流(nodePort使用的是nginx等其他)
  • 雲服務商收費

5、ExternalName

  • 這種型別的 Service 通過返回 CNAME 和它的值,可以將服務對映到 externalName 欄位的內容( 例如:www.baidu.com )。ExternalName Service 是 Service 的特例,它沒有 selector,也沒有定義任何的埠和Endpoint。相反的,對於執行在叢集外部的服務,它通過返回該外部服務的別名這種方式來提供服務

  • 在叢集內部訪問叢集外部的服務

  • 資源清單示例

    • apiVersion: v1
      kind: Service
      metadata:
        # 名稱externalName-1、externalName1 為什麼不合法 不能使用駝峰 只能用-連線單詞 並且還要小寫
        name: external1
        namespace: default
      spec:
        type: ExternalName
        # 連線的外界域名
        externalName: www.baidu.com
      
    • #  檢視ExternalName型別的service
      [root@k8smaster services]# kubectl get svc
      NAME             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
      external1        ExternalName   <none>          www.baidu.com   <none>         15h
      
  • 當查詢主機 my-service.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )時,叢集的DNS 服務將返回一個值 my.database.example.com 的 CNAME 記錄。訪問這個服務的工作方式和其他的相同,唯一不同的是重定向發生在 DNS 層,而且不會進行代理或轉發

6、Multi-Port

  • 可能很多服務需要開發不止一個埠,為了滿足這樣的情況,Kubernetes允許在定義時候指定多個埠,當我們使用多個埠的時候,我們需要指定所有埠的名稱,這樣endpoints才能清楚

  • 資源清單示例

    • apiVersion: v1
      kind: Service
      metadata:
        name: multiPort
      spec:
        type: NodePort
        selector:
          app: myapp
          release: stabel
        ports:
        - name: http
          protocol: TCP
          port: 80
          targetPort: 9376
        - name: https
          protocol: TCP
          port: 443
          targetPort: 9377
      
    • # 檢視service和endpoints
      [root@k8smaster services]# kubectl get svc
      NAME             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
      kubernetes       ClusterIP      10.96.0.1       <none>          443/TCP                      16h
      
      [root@k8smaster services]# kubectl get endpoints
      NAME             ENDPOINTS                                                        AGE
      multi-port       10.244.1.76:9376,10.244.2.73:9376,10.244.2.74:9376 + 3 more...   48s
      
      
      

7、Endpoints

  • 當我們建立一個service之後,我們可以使用這個service對Pod進行訪問,而service是通過標籤的選擇來確定訪問那些Pod,Kubernete提供了一個簡單的Endpoints API,這個Endpoints api的作用就是當一個服務中的pod發生變化時,Endpoints API隨之變化,對於哪些不是原生的程式,Kubernetes提供了一個基於虛擬IP的網橋的服務,這個服務會將請求轉發到對應的後臺pod,也就是service和pod的對應關係,通過Endpoints可以找到,當我們建立一個service之後,k8s就會建立一個同名的Endpoints(沒有選擇器的情況下 值為None),ExternalName是沒有Endpoints的

  • 資源清單示例

    • apiVersion: v1
      kind: Service
      metadata:
        name: myapp1
        namespace: default
      spec:
        type: ClusterIP
        # 通過標籤來選擇pod,需全部滿足 當前環境下並沒有攜帶app=myapp1的Pod,也就是該service連線不到任何Pod 所以Endpoints為null
        selector:
          app: myapp1
          release: stabel
        # 暴露埠
        ports:
        - name: http
          # 叢集內部使用的埠
          port: 80
          # 轉發到其下Pod的容器埠
          targetPort: 80
      
    • # 檢視service
      [root@k8smaster services]# kubectl get svc
      NAME             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
      multi-port       NodePort       10.102.72.19    <none>          80:31277/TCP,443:31584/TCP   11s
      myapp1           ClusterIP      10.102.0.48     <none>          80/TCP                       17m
      
      # 檢視endpoints 發現myapp1的為<none> 前面的Ip為Pod的ip,埠為targetPort
      [root@k8smaster services]# kubectl get endpoints -o wide
      NAME             ENDPOINTS                                                        AGE
      multi-port       10.244.1.76:9376,10.244.2.73:9376,10.244.2.74:9376 + 3 more...   63s
      myapp1           <none>                                                           18m
      
      # 檢視pod 發現和endpoints中的ip一致
      [root@k8smaster services]# kubectl get pod -o wide
      NAME                            READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
      myapp-deploy-7c4dbc97b9-7cdzf   1/1     Running   0          16h   10.244.2.74   k8snode1   <none>           <none>
      myapp-deploy-7c4dbc97b9-k44kg   1/1     Running   0          16h   10.244.2.73   k8snode1   <none>           <none>
      myapp-deploy-7c4dbc97b9-lglrv   1/1     Running   0          16h   10.244.1.76   k8snode2   <none>           <none>
      
      
  • 這種情況下,我們需要手動建立Endpoints,將service和Pod進行關聯

    • 資源清單示例

      • apiVersion: v1
        kind: Endpoints
        metadata:
          # 連線到的servcie名稱 如果沒有該service,會建立一個新的service 不會對原來的service進行更新
          name: myapp1
        subsets:
        # 連線Pod的Ip和port 外部的也可以
        - addresses:
          - IP: 10.244.2.74
          ports:
          - port: 80
        
      • # 建立endpoints 並檢視
        [root@k8smaster services]# kubectl apply -f myapp1endpoints.yaml
        Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
        endpoints/myapp1 configured
        
        # 發現名為myapp1 的endpoints的ENDPOINTS 不在為<none> 為我們設定的值
        [root@k8smaster services]# kubectl get endpoints
        NAME             ENDPOINTS                                                        AGE
        multi-port       10.244.1.76:9376,10.244.2.73:9376,10.244.2.74:9376 + 3 more...   63m
        myapp1           10.244.2.74:80                                                   80m
        

8、service轉發

  • 我們使用上述NodePort的資源清單建立service 檢視其轉發規則

  • 這就說明了建立NodePort的service,使用任意node:埠都能訪問的原因(包含master),在每個node上都開啟了該埠

  • # 建立service
    [root@k8smaster services]# kubectl apply -f nodeport.yaml
    service/myapp-nodeport created
    
    # 檢視service
    [root@k8smaster services]# kubectl get svc
    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP        53s
    myapp-nodeport   NodePort    10.109.117.16   <none>        80:30001/TCP   10s
    
    # 檢視service ipvs的轉發 ipvsadm -Ln
    # 可以發現使用本機IP和127.0.0.1的30001埠 都會轉發到任意Pod的80埠上
    # 使用CLUSTER-IP:80 都會轉發到任意Pod的80埠上
    [root@k8smaster services]# ipvsadm -Ln
    IP Virtual Server version 1.2.1 (size=4096)
    Prot LocalAddress:Port Scheduler Flags
      -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
      
    TCP  192.168.47.150:30001 rr
      -> 10.244.1.76:80               Masq    1      0          0
      -> 10.244.2.73:80               Masq    1      0          0
      -> 10.244.2.74:80               Masq    1      0          0
    
    TCP  10.109.117.16:80 rr
      -> 10.244.1.76:80               Masq    1      0          0
      -> 10.244.2.73:80               Masq    1      0          0
      -> 10.244.2.74:80               Masq    1      0          0
    
    TCP  127.0.0.1:30001 rr
      -> 10.244.1.76:80               Masq    1      0          0
      -> 10.244.2.73:80               Masq    1      0          0
      -> 10.244.2.74:80               Masq    1      0          0
      
    UDP  10.96.0.10:53 rr
      -> 10.244.1.20:53               Masq    1      0          0
      -> 10.244.2.14:53               Masq    1      0          0
    
    # 還可以使用netstat -ano | grep 30001 檢視
    

相關文章