kubernetes權威指南第四版讀書筆記


yum list kubelet --showduplicates | sort -r

kubernetes權威指南第四版讀書筆記

                                   第一章、第二章

1.  HPA與之前的RC、Deployment一樣,也屬於一種Kubernetes資源對象。通過追蹤分析指定RC控制的所有目標Pod的負載變化情況,來確定是否需要有針對性地調整目標Pod的副本數量,這是HPA的實現原理。HPA有以下兩種方式作為Pod負載的度量指標:CPUUtilizationPercentage 應用程序自定義的度量指標,比如服務在每秒內的相應請求數(TPS或QPS)。CPUUtilizationPercentage是一個算術平均值,即目標Pod所有副本自 身的CPU利用率的平均值。一個Pod自身的CPU利用率是該Pod當前CPU 的使用量除以它的Pod Request的值,比如定義一個Pod的Pod Request為 0.4,而當前Pod的CPU使用量為0.2,則它的CPU使用率為50%,這樣就 可以算出一個RC控制的所有Pod副本的CPU利用率的算術平均值了。在CPUUtilizationPercentage計算過程中使用到的Pod的CPU使用量通常是1min內的平均值

2.  StatefulSet特性:StatefulSet里的每個Pod都有穩定、唯一的網絡標識,可以用來 發現集群內的其他成員。StatefulSet控制的Pod副本的啟停順序是受控的,操作第n個Pod 時,前n-1個Pod已經是運行且准備好的狀態。StatefulSet里的Pod采用穩定的持久化存儲卷,通過PV或PVC來 實現,刪除Pod時默認不會刪除與StatefulSet相關的存儲卷(為了保證數 據的安全)StatefulSet除了要與PV卷捆綁使用以存儲Pod的狀態數據,還要與 Headless Service配合使用,即在每個StatefulSet定義中都要聲明它屬於 哪個Headless Service。Headless Service與普通Service的關鍵區別在於, 它沒有Cluster IP,如果解析Headless Service的DNS域名,則返回的是該 Service對應的全部Pod的Endpoint列表。StatefulSet在Headless Service的 基礎上又為StatefulSet控制的每個Pod實例都創建了一個DNS域名:podname.headless.service name

3.  PV: accessModes屬性,目前有以下類型:  ReadWriteOnce:讀寫權限,並且只能被單個Node掛載  ReadOnlyMany:只讀權限,允許被多個Node掛載  ReadWriteMany:讀寫權限,允許被多個Node掛載

PV是有狀態的對象,它的狀態有以下幾種:Available:空閑狀態。Bound:已經綁定到某個PVC上。Released:對應的PVC已經被刪除,但資源還沒有被集群收 回。Failed:PV自動回收失敗。Reclaiming:用戶刪除PVC釋放對PV的占用后,系統根據PV的"reclaim policy"決定對PV執行何種回收操作。 目前,"reclaim policy"有三種方式:Retained、Recycled、Deleted。Retained:保護被PVC釋放的PV及其上數據,並將PV狀態改成"released",不將被其它PVC綁定。集群管理員手動通過如下步驟釋放存儲資源:手動刪除PV,但與其相關的后端存儲資源如(AWS EBS, GCE PD, Azure Disk, or Cinder volume)仍然存在。手動清空后端存儲volume上的數據。手動刪除后端存儲volume,或者重復使用后端volume,為其創建新的PV     Delete:刪除被PVC釋放的PV及其后端存儲volume。對於動態PV其"reclaim policy"繼承自其"storage class",默認是Delete。集群管理員負責將"storage class"的"reclaim policy"設置成用戶期望的形式,否則需要用戶手動為創建后的動態PV編輯"reclaim policy"

 4.  kubelet的職責在於通過RPC管理容器的生命周期,實現容器生命周 期的鈎子,存活和健康監測,以及執行Pod的重啟策略等

5.  目前還有一個潛在問題是,kubelet處理所有的請求連接,使其有成 為Node通信瓶頸的可能。在設計CRI時,要讓容器運行時能夠跳過中間 過程。容器運行時可以啟動一個單獨的流式服務來處理請求(還能對 Pod的資源使用情況進行記錄),並將服務地址返回給kubelet。這樣 kubelet就能反饋信息給API Server,使之可以直接連接到容器運行時提 供的服務,並連接到客戶端

6.  目前已經有多款開源CRI項目可用於Kubernetes:Docker、CRI-O、 Containerd、frakti(基於Hypervisor的容器運行時),各CRI運行時的安 裝手冊可參考官網https://kubernetes.io/docs/setup/cri/的說明

 

                                  第三章

1.  kubelet只支持可以被API Server管理的Pod使用ConfigMap。 kubelet在本Node上通過 --manifest-url或--config自動創建的靜態Pod將無 法引用ConfigMap

2.  在Pod對ConfigMap進行掛載(volumeMount)操作時,在容器 內部只能掛載為“目錄”,無法掛載為“文件”。在掛載到容器內部后,在 目錄下將包含ConfigMap定義的每個item,如果在該目錄下原來還有其 他文件,則容器內的該目錄將被掛載的ConfigMap覆蓋

3.  將Pod信息注入為環境變量:

 

apiVersion: v1
kind: pod
metadata:
  name: {{ template "module.name" . }}
  namespace: {{ $.Release.Namespace }}
spec:
  containers:
        - name: {{ .Chart.Name }}
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          env:
            - name: MY_POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: MY_POD_NAMESPACE
              valueFrom:
                 fieldRef:
                   fieldPath: metadata.namespace
            - name: MY_POD_IP
              valueFrom:
                fieldPath: matadata.podIP      

4.  將容器資源信息注入為環 境變量

apiVersion: v1
kind: pod
metadata:
  name: {{ template "module.name" . }}
  namespace: {{ $.Release.Namespace }}
spec:
  containers:
        - name: {{ .Chart.Name }}
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          resources:
            requests:
              memory: "32Mi"
              cpu: "125m"
            limits:
             memory: "64Mi"
             cpu: "250m"
          env:
            - name: MY_CPU_REQUEST
              valueFrom:
                resourceFieldRef:
                  containerName: test-container
                  resource: request.cpu
      

5.  通過Downward API將Pod的Label、Annotation列表通過 Volume掛載為容器中的一個文件

apiVersion: v1
kind: pod
metadata:
  name: {{ template "module.name" . }}
  namespace: {{ $.Release.Namespace }}
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
    annotations:
      build: two
      builder: john-doe
spec:
  containers:
        - name: {{ .Chart.Name }}
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          resources:
            requests:
              memory: "32Mi"
              cpu: "125m"
            limits:
             memory: "64Mi"
             cpu: "250m"
          volumeMounts:
- name: pidinfo
mountPath: /etc
readOnly: false
volumes:
- name: podinfo
downloadAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations

 這里要注意“volumes”字段中downwardAPI的特殊語法,通過items 的設置,系統會根據path的名稱生成文件。根據上例的設置,系統將在 容器內生成/etc/labels和/etc/annotations兩個文件。Downward API的價值.在某些集群中,集群中的每個節點都需要將自身的標識(ID)及進程綁定的IP地址等信息事先寫入配置文件中,進程在啟動時會讀取這些 信息,然后將這些信息發布到某個類似服務注冊中心的地方,以實現集 群節點的自動發現功能

6.  pod的生命周期:kubelet重啟失效容器的時間間隔以sync-frequency乘以2n來計算,例 如1、2、4、8倍等,最長延時5min,並且在成功重啟后的10min后重置該時間. 

7.  NodeAffinity:Node親和性調度  NodeAffinity意為Node親和性的調度策略,是用於替換NodeSelector 的全新調度策略。目前有兩種節點親和性表達:◎ RequiredDuringSchedulingIgnoredDuringExecution:必須滿足指定的規則才可以調度Pod到Node上(功能與nodeSelector很像,但是使用 的是不同的語法),相當於硬限制 ◎ PreferredDuringSchedulingIgnoredDuringExecution:強調優先滿足指定規則,調度器會嘗試調度Pod到Node上,但並不強求,相當於軟 限制。多個優先級規則還可以設置權重(weight)值,以定義執行的先 后順序。IgnoredDuringExecution的意思是:如果一個Pod所在的節點在Pod運 行期間標簽發生了變更,不再符合該Pod的節點親和性需求,則系統將 忽略Node上Label的變化,該Pod能繼續在該節點運行

8.  PodAffinity:Pod親和與互斥調度策略。Pod間的親和與互斥從Kubernetes 1.4版本開始引入。這一功能讓用 戶從另一個角度來限制Pod所能運行的節點:根據在節點上正在運行的 Pod的標簽而不是節點的標簽進行判斷和調度,要求對節點和Pod兩個條 件進行匹配。這種規則可以描述為:如果在具有標簽X的Node上運行了 一個或者多個符合條件Y的Pod,那么Pod應該(如果是互斥的情況,那么就變成拒絕)運行在這個Node上

9.  Pod的互斥性調度

10.  Taints和Tolerations(污點和容忍)Taint需要和Toleration配合使用,讓Pod避開那些不合適的Node。在 Node上設置一個或多個Taint之后,除非Pod明確聲明能夠容忍這些污 點,否則無法在這些Node上運行。Toleration是Pod的屬性,讓Pod能夠 (注意,只是能夠,而非必須)運行在標注了Taint的Node上 可以用kubectl taint命令為Node設置Taint信息:kubectl taint nodes node1 key=value:NoSchedule  這個設置為node1加上了一個Taint。該Taint的鍵為key,值為 value,Taint的效果是NoSchedule。這意味着除非Pod明確聲明可以容忍 這個Taint,否則就不會被調度到node1上.然后,需要在Pod上聲明Toleration。下面的兩個Toleration都被設置 為可以容忍(Tolerate)具有該Taint的Node,使得Pod能夠被調度到 node1上

tolerations:
- key: "key"
  operator: "Equal"
  value: value
  effect: "NoSchedule"

或者

tolerations:
- key: "key"
  operator: "Exists"
  effect: "NoSchedule"

Pod的Toleration聲明中的key和effect需要與Taint的設置保持一致, 並且滿足以下條件之一。◎ operator的值是Exists(無須指定value)。◎ operator的值是Equal並且value相等。如果不指定operator,則默認值為Equal。另外,有如下兩個特例。◎ 空的key配合Exists操作符能夠匹配所有的鍵和值。◎ 空的effect匹配所有的effect。在上面的例子中,effect的取值為NoSchedule,還可以取值為 PreferNoSchedule,這個值的意思是優先,也可以算作NoSchedule的軟 限制版本—一個Pod如果沒有聲明容忍這個Taint,則系統會盡量避免把 這個Pod調度到這一節點上,但不是強制的系統允許在同一個Node上設置多個Taint,也可以在Pod上設置多個 Toleration。Kubernetes調度器處理多個Taint和Toleration的邏輯順序為: 首先列出節點中所有的Taint,然后忽略Pod的Toleration能夠匹配的部 分,剩下的沒有忽略的Taint就是對Pod的效果了。下面是幾種特殊情◎ 如果在剩余的Taint中沒有NoSchedule效果,但是有 PreferNoSchedule效果,則調度器會嘗試不把這個Pod指派給這個節點。◎ 如果在剩余的Taint中存在effect=NoSchedule,則調度器不會把 該Pod調度到這一節點上。◎ 如果在剩余的Taint中有NoExecute效果,並且這個Pod已經在該節點上運行,則會被驅逐;如果沒有在該節點上運行,則也不會再被調 度到該節點上。

 

 

 

 

 

 

 

 

 

11.  Pod Priority Preemption:Pod優先級調度。在Kubernetes 1.8版本之前,當集群的可用資源不足時,在用戶提交 新的Pod創建請求后,該Pod會一直處於Pending狀態,即使這個Pod是一 個很重要(很有身份)的Pod,也只能被動等待其他Pod被刪除並釋放資 源,才能有機會被調度成功。Kubernetes 1.8版本引入了基於Pod優先級 搶占(Pod Priority Preemption)的調度策略,此時Kubernetes會嘗試釋 放目標節點上低優先級的Pod,以騰出空間(資源)安置高優先級的 Pod,這種調度方式被稱為“搶占式調度”。在Kubernetes 1.11版本中,該 特性升級為Beta版本,默認開啟,在后繼的Kubernetes 1.14版本中正式 Release。如何聲明一個負載相對其他負載“更重要”?我們可以通過以下 幾個維度來定義:◎ Priority,優先級;◎ QoS,服務質量等級;◎ 系統定義的其他度量指標。優先級搶占調度策略的核心行為分別是驅逐(Eviction)與搶占 (Preemption),這兩種行為的使用場景不同,效果相同。Eviction是 kubelet進程的行為,即當一個Node發生資源不足(under resource pressure)的情況時,該節點上的kubelet進程會執行驅逐動作,此時 Kubelet會綜合考慮Pod的優先級、資源申請量與實際使用量等信息來計 算哪些Pod需要被驅逐;當同樣優先級的Pod需要被驅逐時,實際使用的 資源量超過申請量最大倍數的高耗能Pod會被首先驅逐。對於QoS等級 為“Best Effort”的Pod來說,由於沒有定義資源申請(CPU/Memory Request),所以它們實際使用的資源可能非常大。Preemption則是 Scheduler執行的行為,當一個新的Pod因為資源無法滿足而不能被調度 時,Scheduler可能(有權決定)選擇驅逐部分低優先級的Pod實例來滿 足此Pod的調度目標,這就是Preemption機制。需要注意的是,Scheduler可能會驅逐Node A上的一個Pod以滿足 Node B上的一個新Pod的調度任務

12.   deployment更新。當更新Deployment時,系統創建了一個新的 ReplicaSet(nginx-deployment-3599678771),並將其副本數量擴展到 1,然后將舊的ReplicaSet縮減為2。之后,系統繼續按照相同的更新策 略對新舊兩個ReplicaSet進行逐個調整。最后,新的ReplicaSet運行了3個 新版本Pod副本,舊的ReplicaSet副本數量則縮減為0

 

 

 

13.  自動擴縮容機制。Kubernetes中的某個Metrics Server(Heapster或自定義Metrics Server)持續采集所有Pod副本的指標數據。HPA控制器通過Metrics Server的API(Heapster的API或聚合API)獲取這些數據,基於用戶定義 的擴縮容規則進行計算,得到目標Pod副本數量。當目標Pod副本數量與 當前副本數量不同時,HPA控制器就向Pod的副本控制器 (Deployment、RC或ReplicaSet)發起scale操作,調整Pod的副本數量, 完成擴縮容操作

 

 

 14.  指標的類型Master的kube-controller-manager服務持續監測目標Pod的某種性能 指標,以計算是否需要調整副本數量。目前Kubernetes支持的指標類型 如下:◎ Pod資源使用率:Pod級別的性能指標,通常是一個比率值,例 如CPU使用率。 ◎ Pod自定義指標:Pod級別的性能指標,通常是一個數值,例如接收的請數量。◎ Object自定義指標或外部自定義指標:通常是一個數值,需要 容器應用以某種方式提供,例如通過HTTP URL“/metrics”提供,或者使 用外部服務提供的指標采集URL。Kubernetes從1.11版本開始,棄用基於Heapster組件完成Pod的CPU 使用率采集的機制,全面轉向基於Metrics Server完成數據采集。Metrics Server將采集到的Pod性能指標數據通過聚合API(Aggregated API)如 metrics.k8s.io、custom.metrics.k8s.io和external.metrics.k8s.io提供給HPA 控制器進行查詢

15.  擴縮容算法詳解  Autoscaler控制器從聚合API獲取到Pod性能指標數據之后,基於下 面的算法計算出目標Pod副本數量,與當前運行的Pod副本數量進行對 比,決定是否需要進行擴縮容操作:

 即當前副本數×(當前指標值/期望的指標值),將結果向上取整。當計算結果與1非常接近時,可以設置一個容忍度讓系統不做擴縮 容操作。容忍度通過kube-controller-manager服務的啟動參數--horizontal- pod-autoscaler-tolerance進行設置,默認值為0.1(即10%),表示基於上 述算法得到的結果在[-10%-+10%]區間內,即[0.9-1.1],控制器都不會進行擴縮容操作。也可以將期望指標值(desiredMetricValue)設置為指標的平均值類 型,例如targetAverageValue或targetAverageUtilization,此時當前指標值 (currentMetricValue)的算法為所有Pod副本當前指標值的總和除以Pod 副本數量得到的平均值。此外,存在幾種Pod異常的情況,如下所述:◎ Pod正在被刪除(設置了刪除時間戳):將不會計入目標Pod副本數量。◎ Pod的當前指標值無法獲得:本次探測不會將這個Pod納入目標 Pod副本數量,后續的探測會被重新納入計算范圍。◎ 如果指標類型是CPU使用率,則對於正在啟動但是還未達到 Ready狀態的Pod,也暫時不會納入目標副本數量范圍。可以通過kube- controller-manager服務的啟動參數--horizontal-pod-autoscaler-initial- readiness-delay設置首次探測Pod是否Ready的延時時間,默認值為30s。 另一個啟動參數--horizontal-pod-autoscaler-cpuinitialization-period設置首 次采集Pod的CPU使用率的延時時間。如果在HorizontalPodAutoscaler中設置了多個指標,系統就會對每個 指標都執行上面的算法,在全部結果中以期望副本數的最大值為最終結 果。如果這些指標中的任意一個都無法轉換為期望的副本數(例如無法 獲取指標的值),系統就會跳過擴縮容操作。最后,在HPA控制器執行擴縮容操作之前,系統會記錄擴縮容建議 信息(Scale Recommendation)。控制器會在操作時間窗口(時間范圍 可以配置)中考慮所有的建議信息,並從中選擇得分最高的建議。這個 值可通過kube-controller-manager服務的啟動參數--horizontal-pod- autoscaler-downscale-stabilization-window進行配置,默認值為5min。這 個配置可以讓系統更為平滑地進行縮容操作,從而消除短時間內指標值 快速波動產生的影響

 16.  HorizontalPodAutoscaler配置詳解 。HorizontalPodAutoscaler資源對象處於Kubernetes的API 組“autoscaling”中,目前包括v1和v2兩個版本。其中autoscaling/v1僅支 持基於CPU使用率的自動擴縮容,autoscaling/v2則用於支持基於任意指 標的自動擴縮容配置,包括基於資源使用率、Pod指標、其他指標等類 型的指標數據,當前版本為autoscaling/v2beta2

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscalee
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

 ◎ scaleTargetRef:目標作用對象,可以是Deployment、 ReplicationController或ReplicaSet。◎ targetCPUUtilizationPercentage:期望每個Pod的CPU使用率都 為50%,該使用率基於Pod設置的CPU Request值進行計算,例如該值為 200m,那么系統將維持Pod的實際CPU使用值為100m。◎ minReplicas和maxReplicas:Pod副本數量的最小值和最大值, 系統將在這個范圍內進行自動擴縮容操作,並維持每個Pod的CPU使用 率為50%。

17.  基於autoscaling/v2beta2的HorizontalPodAutoscaler配置  

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

◎ scaleTargetRef:目標作用對象,可以是Deployment、 ReplicationController或ReplicaSet。◎ minReplicas和maxReplicas:Pod副本數量的最小值和最大值, 系統將在這個范圍內進行自動擴縮容操作,並維持每個Pod的CPU使用 率為50%。◎ metrics:目標指標值。在metrics中通過參數type定義指標的類 型;通過參數target定義相應的指標目標值,系統將在指標數據達到目標值時(考慮容忍度的區間,見前面算法部分的說明)觸發擴縮容操作。可以將metrics中的type(指標類型)設置為以下三種,可以設置一 個或多個組合,如下所述:(1)Resource:基於資源的指標值,可以設置的資源為CPU和內存。(2)Pods:基於Pod的指標,系統將對全部Pod副本的指標值進行平均值計算。(3)Object:基於某種資源對象(如Ingress)的指標或應用系統的任意自定義指標。

    metrics:
    - type: Pods
      pods:
        metric:
          name: packets-per-second
        target:
          type: AverageValue
          averageValue: 1k

其中,設置Pod的指標名為packets-per-second,在目標指標平均值為1000時觸發擴縮容操作。

18.  例子:設置指標的名稱為requests-per-second,其值來源於 Ingress“main-route”,將目標值(value)設置為2000,即在Ingress的每 秒請求數量達到2000個時觸發擴縮容操作:

    metrics:
    - type: Object
       object:
          metric:
            name: requests-per-second
          describedObject:
            apiVersion: extensions/v1beta1
            kind: Ingress
            name: main-route
          target:
            type: Value
            value: 2k

  例2: 設置指標的名稱為http_requests,並且該資源對象具有標簽“verb=GET”,在指標平均值達到500時觸發擴縮容操作

    metrics:
    - type: Object
       object:
          metric:
            name: 'http_requests'
            selector: 'verb=GET'
          target:
            type: AverageValue
            value: 500

  還可以在同一個HorizontalPodAutoscaler資源對象中定義多個類型的 指標,系統將針對每種類型的指標都計算Pod副本的目標數量,以最大值為准進行擴縮容操作

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu:
      target:
        type: AverageUtilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      targetAverageValue: 1k
  - type: Object
    object:
      metric:
        name: request-per-second
      describedObject:
        apiVersion: extensions/v1beta1
        kind: Ingress
        name: main-route
      target:
        kind: Value
        value: 10k

從1.10版本開始,Kubernetes引入了對外部系統指標的支持。例 如,用戶使用了公有雲服務商提供的消息服務或外部負載均衡器,希望基於這些外部服務的性能指標(如消息服務的隊列長度、負載均衡器的 QPS)對自己部署在Kubernetes中的服務進行自動擴縮容操作。這時, 就可以在metrics參數部分設置type為External來設置自定義指標,然后就 可以通過API“external.metrics.k8s.io”查詢指標數據了。當然,這同樣要 求自定義Metrics Server服務已正常工作

    - type: External
      external:
        metric:
          name: queue_message_ready
          selector: "queue=worker_tasks"
        target:
          type: AverageValue
          averageValue: 30

在使用外部服務的指標時,要安裝、部署能夠對接到Kubernetes HPA模型的監控系統,並且完全了解監控系統采集這些指標的機制,后 續的自動擴縮容操作才能完成

 

                                    第四章  Service

1.  外部服務Service  在某些環境中,應用系統需要將一個外部數據庫作為后端服務進行 連接,或將另一個集群或Namespace中的服務作為服務的后端,這時可 以通過創建一個無Label Selector的Service來實現

apiVersion: v1
kind: v1
metadata:
  name: mysql
  namespace: proxy
spec:
  type: NodePort
  ports:
  - protocol: TCP
    port: 3306
    targetPort: 3306
    nodePort: 32306
---
apiVersion: v1
kind: Endpoints
metadata:
  name: mysql
  namespace: proxy
subsets:
  - addresses:
      - ip: xxx.xxx.xxx.xxx
    ports:
      - port: 3306

 KubeDNS

2.  從Kubernetes 1.4版本開始,SkyDNS組件便被KubeDNS替換,主要 考慮是SkyDNS組件之間通信較多,整體性能不高。KubeDNS由3個容 器組成:kubedns、dnsmasq和sidecar,去掉了SkyDNS中的etcd存儲,將 DNS記錄直接保存在內存中,以提高查詢性能。kubedns容器監控 Kubernetes中Service資源的變化,根據Service的名稱和IP地址生成DNS 記錄,並將DNS記錄保存在內存中;dnsmasq容器從kubedns中獲取DNS 記錄,提供DNS緩存,為客戶端容器應用提供DNS查詢服務;sidecar提 供對kubedns和dnsmasq服務的健康檢查功能

 

 

 3.  從Kubernetes 1.11版本開始,Kubernetes集群的DNS服務由CoreDNS 提供。CoreDNS是CNCF基金會的一個項目,是用Go語言實現的高性 能、插件式、易擴展的DNS服務端。CoreDNS解決了KubeDNS的一些問 題,例如dnsmasq的安全漏洞、externalName不能使用stubDomains設 置,等等.CoreDNS支持自定義DNS記錄及配置upstream DNS Server,可以統 一管理Kubernetes基於服務的內部DNS和數據中心的物理DNS。CoreDNS沒有使用多個容器的架構,只用一個容器便實現了 KubeDNS內3個容器的全部功能

 

 

 

4.  CoreDNS的主要功能是通過插件系統實現的。CoreDNS實現了一種 鏈式插件結構,將DNS的邏輯抽象成了一個個插件,能夠靈活組合使 用.CoreDNS的主要功能是通過插件系統實現的。CoreDNS實現了一種 鏈式插件結構,將DNS的邏輯抽象成了一個個插件,能夠靈活組合使 用。

  常用的插件如下。◎ loadbalance:提供基於DNS的負載均衡功能。 ◎ loop:檢測在DNS解析過程中出現的簡單循環問題。 ◎ cache:提供前端緩存功能。 ◎ health:對Endpoint進行健康檢查。 ◎ kubernetes:從Kubernetes中讀取zone數據。 ◎ etcd:從etcd讀取zone數據,可以用於自定義域名記錄。 ◎ file:從RFC1035格式文件中讀取zone數據。 ◎ hosts:使用/etc/hosts文件或者其他文件讀取zone數據,可以用 於自定義域名記錄。 ◎ auto:從磁盤中自動加載區域文件。 ◎ reload:定時自動重新加載Corefile配置文件的內容。 ◎ forward:轉發域名查詢到上游DNS服務器。 ◎ proxy:轉發特定的域名查詢到多個其他DNS服務器,同時提 供到多個DNS服務器的負載均衡功能。 ◎ prometheus:為Prometheus系統提供采集性能指標數據的 URL。◎ pprof:在URL路徑/debug/pprof下提供運行時的性能數據。 ◎ log:對DNS查詢進行日志記錄。 ◎ errors:對錯誤信息進行日志記錄。 在下面的示例中為域名“cluster.local”設置了一系列插件,包括 errors、health、kubernetes、prometheus、forward、cache、loop、reload 和loadbalance,在進行域名解析時,這些插件將以從上到下的順序依次 執行

 

 

 另外,etcd和hosts插件都可以用於用戶自定義域名記錄。下面是使用etcd插件的配置示例,將以“.com”結尾的域名記錄配置 為從etcd中獲取,並將域名記錄保存在/skydns路徑下:

 

 

 

 

 

 如果用戶在etcd中插入一條“10.1.1.1 mycompany.com”DNS記錄:

 

 

 客戶端應用就能訪問域名“mycompany.com”了:

 

 

 forward和proxy插件都可以用於配置上游DNS服務器或其他DNS服 務器,當在CoreDNS中查詢不到域名時,會到其他DNS服務器上進行查 詢。在實際環境中,可以將Kubernetes集群外部的DNS納入CoreDNS, 進行統一的DNS管理

除了使用集群范圍的DNS服務(如CoreDNS),在Pod級別也能設 置DNS的相關策略和配置.在Pod的YAML配置文件中通過spec.dnsPolicy字段設置DNS策略, 例如:

 

 

   ◎ Default:繼承Pod所在宿主機的DNS設置。 ◎ ClusterFirst:優先使用Kubernetes環境的DNS服務(如 CoreDNS提供的域名解析服務),將無法解析的域名轉發到從宿主機繼 承的DNS服務器。 ◎ ClusterFirstWithHostNet:與ClusterFirst相同,對於以 hostNetwork模式運行的Pod,應明確指定使用該策略。 ◎ None:忽略Kubernetes環境的DNS配置,通過spec.dnsConfig自 定義DNS配置。這個選項從Kubernetes 1.9版本開始引入,到Kubernetes 1.10版本升級為Beta版,到Kubernetes 1.14版本升級為穩定版。 自定義DNS配置可以通過spec.dnsConfig字段進行設置,可以設置下 列信息。◎ nameservers:一組DNS服務器的列表,最多可以設置3個。 ◎ searches:一組用於域名搜索的DNS域名后綴,最多可以設置6 個。◎ options:配置其他可選DNS參數,例如ndots、timeout等,以 name或name/value對的形式表示。

  以下面的dnsConfig為例:

 

 

 該Pod被成功創建之后,容器內的DNS配置文件/etc/resolv.conf的內 容將被系統設置為:

 

 

 表示該Pod完全使用自定義的DNS配置,不再使用Kubernetes環境的 DNS服務

 

Ingress

使用Ingress進行負載分發時,Ingress Controller基於Ingress規則將客 戶端請求直接轉發到Service對應的后端Endpoint(Pod)上,這樣會跳 過kube-proxy的轉發功能,kube-proxy不再起作用。如果Ingress Controller提供的是對外服務,則實際上實現的是邊緣路由器的功能

 

 

   為了實現靈活的負載分發策略,Ingress策略可以按多種方式進行配 置,下面對幾種常見的Ingress轉發策略進行說明。1.轉發到單個后端服務上基於這種設置,客戶端到Ingress Controller的訪問請求都將被轉發 到后端的唯一Service上,在這種情況下Ingress無須定義任何rule。

  同一域名下,不同的URL路徑被轉發到不同的服務上 

 

 

 

   不同的域名(虛擬主機名)被轉發到不同的服務上

 

 不使用域名的轉發規則。這種配置用於一個網站不使用域名直接提供服務的場景,此時通過 任意一台運行ingress-controller的Node都能訪問到后端的服務。

 

 注意,使用無域名的Ingress轉發規則時,將默認禁用非安全 HTTP,強制啟用HTTPS。可以在Ingress的定義中設置一個annotation“ingress.kubernetes.io/ssl- redirect=false”來關閉強制啟用HTTPS的設置:

 

 這樣,到Ingress Controller的訪問就可以使用HTTP了

                                     第5章 核心組件運行機制

Kubernetes API Server原理解析

1.  總體來看,Kubernetes API Server的核心功能是提供Kubernetes各類 資源對象(如Pod、RC、Service等)的增、刪、改、查及Watch等HTTP Rest接口,成為集群內各個功能模塊之間數據交互和通信的中心樞紐, 是整個系統的數據總線和數據中心。除此之外,它還有以下一些功能特 性。(1)是集群管理的API入口。 (2)是資源配額控制的入口。 (3)提供了完備的集群安全機制。

2.  API Server的性能是決定Kubernetes集群整體性能的關鍵因素,因此 Kubernetes的設計者綜合運用以下方式來最大程度地保證API Server的性 能。(1)API Server擁有大量高性能的底層代碼。在API Server源碼中 使用協程(Coroutine)+隊列(Queue)這種輕量級的高性能並發代碼, 使得單進程的API Server具備了超強的多核處理能力,從而以很快的速 度並發處理大量的請求(2)普通List接口結合異步Watch接口,不但完美解決了 Kubernetes中各種資源對象的高性能同步問題,也極大提升了Kubernetes 集群實時響應各種事件的靈敏度。(3)采用了高性能的etcd數據庫而非傳統的關系數據庫,不僅解決 了數據的可靠性問題,也極大提升了API Server數據訪問層的性能。在 常見的公有雲環境中,一個3節點的etcd集群在輕負載環境中處理一個請 求的時間可以低於1ms,在重負載環境中可以每秒處理超過30000個請 求。

 

 圖5.3以一個完整的Pod調度過程為 例,對API Server的List-Watch機制進行說明

 

 首先,借助etcd提供的Watch API接口,API Server可以監聽 (Watch)在etcd上發生的數據操作事件,比如Pod創建事件、更新事 件、刪除事件等,在這些事件發生后,etcd會及時通知API Server。圖 5.3中API Server與etcd之間的交互箭頭表明了這個過程:當一個 ReplicaSet對象被創建並被保存到etcd中后(圖中的2.Create RepliatSet箭 頭),etcd會立即發送一個對應的Create事件給API Server(圖中的 3.Send RepliatSet Create Event箭頭),與其類似的6、7、10、11箭頭都 是針對Pod的創建、更新事件的。然后,為了讓Kubernetes中的其他組件在不訪問底層etcd數據庫的 情況下,也能及時獲取資源對象的變化事件,API Server模仿etcd的 Watch API接口提供了自己的Watch接口,這樣一來,這些組件就能近乎 實時地獲取它們感興趣的任意資源對象的相關事件通知了。圖5.3中 controller-manager、scheduler、kublet等組件與API Server之間的3個標記 有List-Watch的虛框表明了這個過程。同時,在監聽自己感興趣的資源 的時候,客戶端可以增加過濾條件,以List-Watch 3為例,node1節點上 的kubelet進程只對自己節點上的Pod事件感興趣。最后,Kubernetes List-Watch用於實現數據同步的代碼邏輯。客戶 端首先調用API Server的List接口獲取相關資源對象的全量數據並將其緩 存到內存中,然后啟動對應資源對象的Watch協程,在接收到Watch事 件后,再根據事件的類型(比如新增、修改或刪除)對內存中的全量資 源對象列表做出相應的同步修改,從實現上來看,這是一種全量結合增 量的、高性能的、近乎實時的數據同步方式。

3.  從圖5.6中可以看出,Kubernetes API Server作為集群的核心,負責 集群各功能模塊之間的通信。集群內的各個功能模塊通過API Server將 信息存入etcd,當需要獲取和操作這些數據時,則通過API Server提供的 REST接口(用GET、LIST或WATCH方法)來實現,從而實現各模塊之 間的信息交互。

 

   常見的一個交互場景是kubelet進程與API Server的交互。每個Node 上的kubelet每隔一個時間周期,就會調用一次API Server的REST接口報 告自身狀態,API Server在接收到這些信息后,會將節點狀態信息更新 到etcd中。此外,kubelet也通過API Server的Watch接口監聽Pod信息, 如果監聽到新的Pod副本被調度綁定到本節點,則執行Pod對應的容器創 建和啟動邏輯;如果監聽到Pod對象被刪除,則刪除本節點上相應的Pod 容器;如果監聽到修改Pod的信息,kubelet就會相應地修改本節點的Pod 容器。

  另一個交互場景是kube-controller-manager進程與API Server的交互。kube-controller-manager中的Node Controller模塊通過API Server提供 的Watch接口實時監控Node的信息,並做相應處理

  還有一個比較重要的交互場景是kube-scheduler與API Server的交 互。Scheduler通過API Server的Watch接口監聽到新建Pod副本的信息 后,會檢索所有符合該Pod要求的Node列表,開始執行Pod調度邏輯, 在調度成功后將Pod綁定到目標節點上。

  為了緩解集群各模塊對API Server的訪問壓力,各功能模塊都采用 緩存機制來緩存數據。各功能模塊定時從API Server獲取指定的資源對 象信息(通過List-Watch方法),然后將這些信息保存到本地緩存中, 功能模塊在某些情況下不直接訪問API Server,而是通過訪問緩存數據 來間接訪問API Server。

 

Controller Manager原理解析

1.  一般來說,智能系統和自動系統通常會通過一個“操作系統”來不斷修正系統的工作狀態。在Kubernetes集群中,每個Controller都是這樣的 一個“操作系統”,它們通過API Server提供的(List-Watch)接口實時監控集群中特定資源的狀態變化,當發生各種故障導致某資源對象的狀態發生變化時,Controller會嘗試將其狀態調整為期望的狀態。比如當某個 Node意外宕機時,Node Controller會及時發現此故障並執行自動化修復流程,確保集群始終處於預期的工作狀態。Controller Manager是 Kubernetes中各種操作系統的管理者,是集群內部的管理控制中心,也 是Kubernetes自動化功能的核心。

2.  Controller Manager內部包含Replication Controller、 Node Controller、ResourceQuota Controller、Namespace Controller、 ServiceAccount Controller、Token Controller、Service Controller及 Endpoint Controller這8種Controller,每種Controller都負責一種特定資源 的控制流程,而Controller Manager正是這些Controller的核心管理者

 

Replication Controller
  Replication Controller的核心作用是確保在任何時候集群中某個RC 關聯的Pod副本數量都保持預設值。如果發現Pod的副本數量超過預期 值,則Replication Controller會銷毀一些Pod副本;反之,Replication Controller會自動創建新的Pod副本,直到符合條件的Pod副本數量達到 預設值。需要注意:只有當Pod的重啟策略是Always時 (RestartPolicy=Always),Replication Controller才會管理該Pod的操作(例如創建、銷毀、重啟等)。在通常情況下,Pod對象被成功創建后 不會消失,唯一的例外是當Pod處於succeeded或failed狀態的時間過長 (超時參數由系統設定)時,該Pod會被系統自動回收,管理該Pod的副 本控制器將在其他工作節點上重新創建、運行該Pod副本。Replication Controller的職責,如下所述。(1)確保在當前集群中有且僅有N個Pod實例,N是在RC中定義的 Pod副本數量。 (2)通過調整RC的spec.replicas屬性值來實現系統擴容或者縮容。 (3)通過改變RC中的Pod模板(主要是鏡像版本)來實現系統的滾動升級。
Node Controller
  kubelet進程在啟動時通過API Server注冊自身的節點信息,並定時 向API Server匯報狀態信息,API Server在接收到這些信息后,會將這些 信息更新到etcd中。在etcd中存儲的節點信息包括節點健康狀況、節點 資源、節點名稱、節點地址信息、操作系統版本、Docker版本、kubelet 版本等。節點健康狀況包含“就緒”(True)“未就緒”(False)和“未 知”(Unknown)三種。Node Controller通過API Server實時獲取Node的相關信息,實現管理 和監控集群中的各個Node的相關控制功能,Node Controller的核心工作 流程如圖5.8所示

   (1)Controller Manager在啟動時如果設置了--cluster-cidr參數,那 么為每個沒有設置Spec.PodCIDR的Node都生成一個CIDR地址,並用該 CIDR地址設置節點的Spec.PodCIDR屬性,這樣做的目的是防止不同節 點的CIDR地址發生沖突。

  (2)逐個讀取Node信息,多次嘗試修改nodeStatusMap中的節點狀 態信息,將該節點信息和Node Controller的nodeStatusMap中保存的節點 信息做比較。如果判斷出沒有收到kubelet發送的節點信息、第1次收到 節點kubelet發送的節點信息,或在該處理過程中節點狀態變成非“健康”狀態,則在nodeStatusMap中保存該節點的狀態信息,並用Node Controller所在節點的系統時間作為探測時間和節點狀態變化時間。如果 判斷出在指定時間內收到新的節點信息,且節點狀態發生變化,則在 nodeStatusMap中保存該節點的狀態信息,並用Node Controller所在節點 的系統時間作為探測時間和節點狀態變化時間。如果判斷出在指定時間 內收到新的節點信息,但節點狀態沒發生變化,則在nodeStatusMap中 保存該節點的狀態信息,並用Node Controller所在節點的系統時間作為 探測時間,將上次節點信息中的節點狀態變化時間作為該節點的狀態變 化時間。如果判斷出在某段時間(gracePeriod)內沒有收到節點狀態信 息,則設置節點狀態為“未知”,並且通過API Server保存節點狀態。

  (3)逐個讀取節點信息,如果節點狀態變為非“就緒”狀態,則將 節點加入待刪除隊列,否則將節點從該隊列中刪除。如果節點狀態為 非“就緒”狀態,且系統指定了Cloud Provider,則Node Controller調用 Cloud Provider查看節點,若發現節點故障,則刪除etcd中的節點信息, 並刪除和該節點相關的Pod等資源的信息。

ResourceQuota Controller 

  目前Kubernetes支持如下三個層次的資源配額管理

    (1)容器級別,可以對CPU和Memory進行限制。(2)Pod級別,可以對一個Pod內所有容器的可用資源進行限制。(3)Namespace級別,為Namespace(多租戶)級別的資源限制, 包括:◎ Pod數量; ◎ Replication Controller數量; ◎ Service數量; ◎ ResourceQuota數量; ◎ Secret數量; ◎ 可持有的PV數量。

  Kubernetes的配額管理是通過Admission Control(准入控制)來控制 的,Admission Control當前提供了兩種方式的配額約束,分別是 LimitRanger與ResourceQuota。其中LimitRanger作用於Pod和Container, ResourceQuota則作用於Namespace,限定一個Namespace里的各類資源 的使用總額。

  如圖5.9所示,如果在Pod定義中同時聲明了LimitRanger,則用戶通 過API Server請求創建或修改資源時,Admission Control會計算當前配額 的使用情況,如果不符合配額約束,則創建對象失敗。對於定義了 ResourceQuota的Namespace,ResourceQuota Controller組件則負責定期 統計和生成該Namespace下的各類對象的資源使用總量,統計結果包括 Pod、Service、RC、Secret和Persistent Volume等對象實例個數,以及該 Namespace下所有Container實例所使用的資源量(目前包括CPU和內 存),然后將這些統計結果寫入etcd的resourceQuotaStatusStorage目錄 (resourceQuotas/status)下。寫入resourceQuotaStatusStorage的內容包含 Resource名稱、配額值(ResourceQuota對象中spec.hard域下包含的資源 的值)、當前使用值(ResourceQuota Controller統計出來的值)。隨后 這些統計信息被Admission Control使用,以確保相關Namespace下的資 源配額總量不會超過ResourceQuota中的限定值。

 

 Namespace Controller

  用戶通過API Server可以創建新的Namespace並將其保存在etcd中, Namespace Controller定時通過API Server讀取這些Namespace的信息。如 果Namespace被API標識為優雅刪除(通過設置刪除期限實現,即設置 DeletionTimestamp屬性),則將該NameSpace的狀態設置成Terminating 並保存到etcd中。同時Namespace Controller刪除該Namespace下的 ServiceAccount、RC、Pod、Secret、PersistentVolume、ListRange、 ResourceQuota和Event等資源對象。

  在Namespace的狀態被設置成Terminating后,由Admission Controller的NamespaceLifecycle插件來阻止為該Namespace創建新的資 源。同時,在Namespace Controller刪除該Namespace中的所有資源對象 后,Namespace Controller對該Namespace執行finalize操作,刪除Namespace的spec.finalizers域中的信

  如果Namespace Controller觀察到Namespace設置了刪除期限,同時 Namespace的spec.finalizers域值是空的,那么Namespace Controller將通 過API Server刪除該Namespace資源。

Service Controller與Endpoints Controller

  本節講解Endpoints Controller,在這之前,讓我們先看看Service、 Endpoints與Pod的關系。如圖5.10所示,Endpoints表示一個Service對應 的所有Pod副本的訪問地址,Endpoints Controller就是負責生成和維護所 有Endpoints對象的控制器。

 

   它負責監聽Service和對應的Pod副本的變化,如果監測到Service被 刪除,則刪除和該Service同名的Endpoints對象。如果監測到新的Service 被創建或者修改,則根據該Service信息獲得相關的Pod列表,然后創建 或者更新Service對應的Endpoints對象。如果監測到Pod的事件,則更新 它所對應的Service的Endpoints對象(增加、刪除或者修改對應的 Endpoint條目)。

  那么,Endpoints對象是在哪里被使用的呢?答案是每個Node上的 kube-proxy進程,kube-proxy進程獲取每個Service的Endpoints,實現了 Service的負載均衡功能。

 
Scheduler原理解析
  Kubernetes Scheduler在整個系統中承擔了“承上啟下”的重要功 能,“承上”是指它負責接收Controller Manager創建的新Pod,為其安排 一個落腳的“家”—目標Node;“啟下”是指安置工作完成后,目標Node上 的kubelet服務進程接管后繼工作,負責Pod生命周期中的“下半生”。
  具體來說,Kubernetes Scheduler的作用是將待調度的Pod(API新創 建的Pod、Controller Manager為補足副本而創建的Pod等)按照特定的調 度算法和調度策略綁定(Binding)到集群中某個合適的Node上,並將 綁定信息寫入etcd中。在整個調度過程中涉及三個對象,分別是待調度 Pod列表、可用Node列表,以及調度算法和策略。簡單地說, 就是通過 調度算法調度為待調度Pod列表中的每個Pod從Node列表中選擇一個最 適合的Node。
  
  隨后,目標節點上的kubelet通過API Server監聽到Kubernetes Scheduler產生的Pod綁定事件,然后獲取對應的Pod清單,下載Image鏡 像並啟動容器。完整的流程如圖5.11所示。

 

Kubernetes Scheduler當前提供的默認調度流程分為以下兩步。
(1)預選調度過程,即遍歷所有目標Node,篩選出符合要求的候 選節點。為此,Kubernetes內置了多種預選策略(xxx Predicates)供用 戶選擇
(2)確定最優節點,在第1步的基礎上,采用優選策略(xxx Priority)計算出每個候選節點的積分,積分最高者勝出。
Kubernetes Scheduler的調度流程是通過插件方式加載的“調度算法 提供者”(AlgorithmProvider)具體實現的。一個AlgorithmProvider其實 就是包括了一組預選策略與一組優先選擇策略的結構體

   Scheduler中可用的預選策略包含:NoDiskConflict、 PodFitsResources、PodSelectorMatches、PodFitsHost、 CheckNodeLabelPresence、CheckServiceAffinity和PodFitsPorts策略等。 其默認的AlgorithmProvider加載的預選策略Predicates包括: PodFitsPorts(PodFitsPorts)、PodFitsResources(PodFitsResources)、 NoDiskConflict(NoDiskConflict)、 MatchNodeSelector(PodSelectorMatches)和 HostName(PodFitsHost),即每個節點只有通過前面提及的5個默認預 選策略后,才能初步被選中,進入下一個流程

  

1)NoDiskConflict 判斷備選Pod的gcePersistentDisk或AWSElasticBlockStore和備選的節 點中已存在的Pod是否存在沖突。檢測過程如下。
(1)首先,讀取備選Pod的所有Volume的信息(即 pod.Spec.Volumes),對每個Volume執行以下步驟進行沖突檢測。
(2)如果該Volume是gcePersistentDisk,則將Volume和備選節點上 的所有Pod的每個Volume都進行比較,如果發現相同的 gcePersistentDisk,則返回false,表明存在磁盤沖突,檢查結束,反饋給 調度器該備選節點不適合作為備選Pod;如果該Volume是 AWSElasticBlockStore,則將Volume和備選節點上的所有Pod的每個 Volume都進行比較,如果發現相同的AWSElasticBlockStore,則返回 false,表明存在磁盤沖突,檢查結束,反饋給調度器該備選節點不適合 備選Pod。
(3)如果檢查完備選Pod的所有Volume均未發現沖突,則返回true,表明不存在磁盤沖突,反饋給調度器該備選節點適合備選Pod。

2)PodFitsResources 判斷備選節點的資源是否滿足備選Pod的需求,檢測過程如下。
(1)計算備選Pod和節點中已存在Pod的所有容器的需求資源(內 存和CPU)的總和。
(2)獲得備選節點的狀態信息,其中包含節點的資源信息。
(3)如果在備選Pod和節點中已存在Pod的所有容器的需求資源 (內存和CPU)的總和,超出了備選節點擁有的資源,則返回false,表 明備選節點不適合備選Pod,否則返回true,表明備選節點適合備選 Pod。

3)PodSelectorMatches 判斷備選節點是否包含備選Pod的標簽選擇器指定的標簽。
(1)如果Pod沒有指定spec.nodeSelector標簽選擇器,則返回true。
(2)否則,獲得備選節點的標簽信息,判斷節點是否包含備選Pod 的標簽選擇器(spec.nodeSelector)所指定的標簽,如果包含,則返回 true,否則返回false。

4)PodFitsHost 判斷備選Pod的spec.nodeName域所指定的節點名稱和備選節點的名 稱是否一致,如果一致,則返回true,否則返回false。

5)CheckNodeLabelPresence 如果用戶在配置文件中指定了該策略,則Scheduler會通過 RegisterCustomFitPredicate方法注冊該策略。該策略用於判斷策略列出 的標簽在備選節點中存在時,是否選擇該備選節點。
(1)讀取備選節點的標簽列表信息
(2)如果策略配置的標簽列表存在於備選節點的標簽列表中,且 策略配置的presence值為false,則返回false,否則返回true;如果策略配 置的標簽列表不存在於備選節點的標簽列表中,且策略配置的presence 值為true,則返回false,否則返回true。

6)CheckServiceAffinity 如果用戶在配置文件中指定了該策略,則Scheduler會通過 RegisterCustomFitPredicate方法注冊該策略。該策略用於判斷備選節點 是否包含策略指定的標簽,或包含和備選Pod在相同Service和Namespace 下的Pod所在節點的標簽列表。如果存在,則返回true,否則返回false。

7)PodFitsPorts 判斷備選Pod所用的端口列表中的端口是否在備選節點中已被占 用,如果被占用,則返回false,否則返回true。

Scheduler中的優選策略包含:LeastRequestedPriority、 CalculateNodeLabelPriority和BalancedResourceAllocation等。每個節點通 過優先選擇策略時都會算出一個得分,計算各項得分,最終選出得分值 最大的節點作為優選的結果(也是調度算法的結果)。

1)LeastRequestedPriority 該優選策略用於從備選節點列表中選出資源消耗最小的節點。
(1)計算出在所有備選節點上運行的Pod和備選Pod的CPU占用量 totalMilliCPU。
(2)計算出在所有備選節點上運行的Pod和備選Pod的內存占用量 totalMemory。
(3)計算每個節點的得分,計算規則大致如下,其中, NodeCpuCapacity為節點CPU計算能力,NodeMemoryCapacity為節點內存大小:

 

 2)CalculateNodeLabelPriority 如果用戶在配置文件中指定了該策略,則scheduler會通過 RegisterCustomPriorityFunction方法注冊該策略。該策略用於判斷策略列 出的標簽在備選節點中存在時,是否選擇該備選節點。如果備選節點的 標簽在優選策略的標簽列表中且優選策略的presence值為true,或者備選 節點的標簽不在優選策略的標簽列表中且優選策略的presence值為 false,則備選節點score=10,否則備選節點score=0。

3)BalancedResourceAllocation 該優選策略用於從備選節點列表中選出各項資源使用率最均衡的節 點。
(1)計算出在所有備選節點上運行的Pod和備選Pod的CPU占用量 totalMilliCPU。
(2)計算出在所有備選節點上運行的Pod和備選Pod的內存占用量 totalMemory。
(3)計算每個節點的得分,計算規則大致如下,其中, NodeCpuCapacity為節點的CPU計算能力,NodeMemoryCapacity為節點 的內存大小:

 

 

kubelet運行機制解析

  在Kubernetes集群中,在每個Node(又稱Minion)上都會啟動一個 kubelet服務進程。該進程用於處理Master下發到本節點的任務,管理 Pod及Pod中的容器。每個kubelet進程都會在API Server上注冊節點自身 的信息,定期向Master匯報節點資源的使用情況,並通過cAdvisor監控 容器和節點資源。

 節點管理
  當前每個kubelet都被授予創建和修改任何節點的權限。但是在實踐 中,它僅僅創建和修改自己。將來,我們計划限制kubelet的權限,僅允 許它修改和創建所在節點的權限。如果在集群運行過程中遇到集群資源 不足的情況,用戶就很容易通過添加機器及運用kubelet的自注冊模式來 實現擴容。
   kubelet在啟動時通過API Server注冊節點信息,並定時向API Server 發送節點的新消息,API Server在接收到這些信息后,將這些信息寫入 etcd。通過kubelet的啟動參數“--node-status- update-frequency”設置 kubelet每隔多長時間向API Server報告節點狀態,默認為10s。
 Pod管理

kubelet通過以下幾種方式獲取自身Node上要運行的Pod清單
(1)文件:kubelet啟動參數“--config”指定的配置文件目錄下的文 件(默認目錄為“/etc/ kubernetes/manifests/”)。通過--file-check- frequency設置檢查該文件目錄的時間間隔,默認為20s。
(2)HTTP端點(URL):通過“--manifest-url”參數設置。通過-- http-check-frequency設置檢查該HTTP端點數據的時間間隔,默認為 20s。
(3)API Server:kubelet通過API Server監聽etcd目錄,同步Pod列 表
  所有以非API Server方式創建的Pod都叫作Static Pod。kubelet將 Static Pod的狀態匯報給API Server,API Server為該Static Pod創建一個 Mirror Pod和其相匹配。Mirror Pod的狀態將真實反映Static Pod的狀態。 當Static Pod被刪除時,與之相對應的Mirror Pod也會被刪除。

 kubelet通過API Server Client 使用Watch加List的方式監聽“/registry/nodes/$”當前節點的名稱和“/registry/pods”目錄,將獲取的信息同步到本地緩存中。
   

kubelet監聽etcd,所有針對Pod的操作都會被kubelet監聽。如果發現 有新的綁定到本節點的Pod,則按照Pod清單的要求創建該Pod。
如果發現本地的Pod被修改,則kubelet會做出相應的修改,比如在 刪除Pod中的某個容器時,會通過Docker Client刪除該容器。
如果發現刪除本節點的Pod,則刪除相應的Pod,並通過Docker Client刪除Pod中的容器。
kubelet讀取監聽到的信息,如果是創建和修改Pod任務,則做如下 處理。
(1)為該Pod創建一個數據目錄。
(2)從API Server讀取該Pod清單。
(3)為該Pod掛載外部卷(External Volume)。
(4)下載Pod用到的Secret。
(5)檢查已經運行在節點上的Pod,如果該Pod沒有容器或Pause容 器(“kubernetes/pause”鏡像創建的容器)沒有啟動,則先停止Pod里所 有容器的進程。如果在Pod中有需要刪除的容器,則刪除這些容器。
(6)用“kubernetes/pause”鏡像為每個Pod都創建一個容器。該Pause 容器用於接管Pod中所有其他容器的網絡。每創建一個新的Pod,kubelet 都會先創建一個Pause容器,然后創建其他容器。“kubernetes/pause”鏡像 大概有200KB,是個非常小的容器鏡像。
(7)為Pod中的每個容器做如下處理。
◎ 為容器計算一個Hash值,然后用容器的名稱去查詢對應Docker 容器的Hash值。若查找到容器,且二者的Hash值不同,則停止Docker中 容器的進程,並停止與之關聯的Pause容器的進程;若二者相同,則不做任何處理。
◎ 如果容器被終止了,且容器沒有指定的restartPolicy(重啟策 略),則不做任何處理。
◎ 調用Docker Client下載容器鏡像,調用Docker Client運行容器

 

容器健康檢查
Pod通過兩類探針來檢查容器的健康狀態。一類是LivenessProbe探 針,用於判斷容器是否健康並反饋給kubelet。如果LivenessProbe探針探 測到容器不健康,則kubelet將刪除該容器,並根據容器的重啟策略做相 應的處理。如果一個容器不包含LivenessProbe探針,那么kubelet認為該 容器的LivenessProbe探針返回的值永遠是Success;另一類是 ReadinessProbe探針,用於判斷容器是否啟動完成,且准備接收請求。 如果ReadinessProbe探針檢測到容器啟動失敗,則Pod的狀態將被修改, Endpoint Controller將從Service的Endpoint中刪除包含該容器所在Pod的IP 地址的Endpoint條目

 
kube-proxy運行機制解析
   Kubernetes在創建服務時會為服務分配一個虛擬的IP地址,客戶端 通過訪問這個虛擬的IP地址來訪問服務,服務則負責將請求轉發到后端 的Pod上.在很多情況下,Service只是一個概念,而真正將Service的作用落實 的是它背后的kube-proxy服務進程。只有理解了kube-proxy的原理和機 制,我們才能真正理解Service背后的實現邏輯。
  在Kubernetes集群的每個Node上都會運行一個kube-proxy服務進 程,我們可以把這個進程看作Service的透明代理兼負載均衡器,其核心 功能是將到某個Service的訪問請求轉發到后端的多個Pod實例上。此 外,Service的Cluster IP與NodePort等概念是kube-proxy服務通過iptables 的NAT轉換實現的,kube-proxy在運行過程中動態創建與Service相關的 iptables規則,這些規則實現了將訪問服務(Cluster IP或NodePort)的請 求負載分發到后端Pod的功能。由於iptables機制針對的是本地的kube- proxy端口,所以在每個Node上都要運行kube-proxy組件,這樣一來,在 Kubernetes集群內部,我們可以在任意Node上發起對Service的訪問請 求。綜上所述,由於kube-proxy的作用,在Service的調用過程中客戶端 無須關心后端有幾個Pod,中間過程的通信、負載均衡及故障恢復都是透明的。
  起初,kube-proxy進程是一個真實的TCP/UDP代理,類似HA Proxy,負責從Service到Pod的訪問流量的轉發,這種模式被稱為 userspace(用戶空間代理)模式。如圖5.13所示,當某個Pod以Cluster IP方式訪問某個Service的時候,這個流量會被Pod所在本機的iptables轉 發到本機的kube-proxy進程,然后由kube-proxy建立起到后端Pod的 TCP/UDP連接,隨后將請求轉發到某個后端Pod上,並在這個過程中實 現負載均衡功能。

 

 Kubernetes從1.2版本開始,將iptables作為kube- proxy的默認模式。iptables模式下的kube-proxy不再起到Proxy的作用, 其核心功能:通過API Server的Watch接口實時跟蹤Service與Endpoint的 變更信息,並更新對應的iptables規則,Client的請求流量則通過iptables 的NAT機制“直接路由”到目標Pod。

 

 

   根據Kubernetes的網絡模型,一個Node上的Pod與其他Node上的Pod 應該能夠直接建立雙向的TCP/IP通信通道,所以如果直接修改iptables規 則,則也可以實現kube-proxy的功能,只不過后者更加高端,因為是全 自動模式的。與第1代的userspace模式相比,iptables模式完全工作在內 核態,不用再經過用戶態的kube-proxy中轉,因而性能更強

  iptables模式雖然實現起來簡單,但存在無法避免的缺陷:在集群中 的Service和Pod大量增加以后,iptables中的規則會急速膨脹,導致性能 顯著下降,在某些極端情況下甚至會出現規則丟失的情況,並且這種故 障難以重現與排查,於是Kubernetes從1.8版本開始引入第3代的 IPVS(IP Virtual Server)模式,如圖5.16所示。IPVS在Kubernetes 1.11 中升級為GA穩定版

 

   

iptables與IPVS雖然都是基於Netfilter實現的,但因為定位不同,二 者有着本質的差別:iptables是為防火牆而設計的;IPVS則專門用於高 性能負載均衡,並使用更高效的數據結構(Hash表),允許幾乎無限的 規模擴張,因此被kube-proxy采納為第三代模式
與iptables相比,IPVS擁有以下明顯優勢:
◎ 為大型集群提供了更好的可擴展性和性能
◎ 支持比iptables更復雜的復制均衡算法(最小負載、最少連接、 加權等);
◎ 支持服務器健康檢查和連接重試等功能;
◎ 可以動態修改ipset的集合,即使iptables的規則正在使用這個集 合。

由於IPVS無法提供包過濾、airpin-masquerade tricks(地址偽裝)、 SNAT等功能,因此在某些場景(如NodePort的實現)下還要與iptables 搭配使用。在IPVS模式下,kube-proxy又做了重要的升級,即使用 iptables的擴展ipset,而不是直接調用iptables來生成規則鏈

iptables規則鏈是一個線性的數據結構,ipset則引入了帶索引的數據 結構,因此當規則很多時,也可以很高效地查找和匹配我們可以將 ipset簡單理解為一個IP(段)的集合,這個集合的內容可以是IP地址、 IP網段、端口等,iptables可以直接添加規則對這個“可變的集合”進行操 作,這樣做的好處在於可以大大減少iptables規則的數量,從而減少性能 損耗
假設要禁止上萬個IP訪問我們的服務器,則用iptables的話,就需要 一條一條地添加規則,會在iptables中生成大量的規則;但是用ipset的 話,只需將相關的IP地址(網段)加入ipset集合中即可,這樣只需設置 少量的iptables規則即可實現目標。

kube-proxy針對Service和Pod創建的一些主要的iptables規則如下
◎ KUBE-CLUSTER-IP:在masquerade-all=true或clusterCIDR指定 的情況下對Service Cluster IP地址進行偽裝,以解決數據包欺騙問題。
◎ KUBE-EXTERNAL-IP:將數據包偽裝成Service的外部IP地 址
◎ KUBE-LOAD-BALANCER、KUBE-LOAD-BALANCER-LOCAL:偽裝Load Balancer 類型的Service流量。
◎ KUBE-NODE-PORT-TCP、KUBE-NODE-PORT-LOCAL- TCP、KUBE-NODE-PORTUDP、KUBE-NODE-PORT-LOCAL-UDP: 偽裝NodePort類型的Service流量。

               

                             第6章 深入分析集群安全機制

API Server認證管理
  我們知道,Kubernetes集群中所有資源的訪問和變更都是通過 Kubernetes API Server的REST API來實現的,所以集群安全的關鍵點就 在於如何識別並認證客戶端身份(Authentication),以及隨后訪問權限 的授權(Authorization)這兩個關鍵問題
Kubernetes集群提供了3種級別的客戶端身份認證方式
  ◎ 最嚴格的HTTPS證書認證:基於CA根證書簽名的雙向數字證 書認證方式。
  ◎ HTTP Token認證:通過一個Token來識別合法用戶
  ◎ HTTP Base認證:通過用戶名+密碼的方式認證。

 

CA認證大概包含下面幾個步驟
(1)HTTPS通信雙方的服務器端向CA機構申請證書,CA機構是 可信的第三方機構,它可以是一個公認的權威企業,也可以是企業自 身。企業內部系統一般都用企業自身的認證系統。CA機構下發根證 書、服務端證書及私鑰給申請者。
(2)HTTPS通信雙方的客戶端向CA機構申請證書,CA機構下發 根證書、客戶端證書及私鑰給申請者。
(3)客戶端向服務器端發起請求,服務端下發服務端證書給客戶 端。客戶端接收到證書后,通過私鑰解密證書,並利用服務器端證書中 的公鑰認證證書信息比較證書里的消息,例如,比較域名和公鑰與服務 器剛剛發送的相關消息是否一致,如果一致,則客戶端認可這個服務器 的合法身份。
(4)客戶端發送客戶端證書給服務器端,服務端在接收到證書 后,通過私鑰解密證書,獲得客戶端證書公鑰,並用該公鑰認證證書信 息,確認客戶端是否合法。
(5)客戶端通過隨機密鑰加密信息,並發送加密后的信息給服務 端。在服務器端和客戶端協商好加密方案后,客戶端會產生一個隨機的 密鑰,客戶端通過協商好的加密方案加密該隨機密鑰,並發送該隨機密 鑰到服務器端。服務器端接收這個密鑰后,雙方通信的所有內容都通過 該隨機密鑰加密。
上述是雙向認證SSL協議的具體通信過程,這種情況要求服務器和 用戶雙方都有證書。單向認證SSL協議則不需要客戶端擁有CA證書,對 於上面的步驟,只需將服務器端驗證客戶證書的過程去掉,之后協商對稱密碼方案和對稱通話密鑰時,服務器發送給客戶的密碼沒被加密即 可。

 

 

HTTP Token的認證是用一個很長的特殊編碼方式的並且難以被模 仿的字符串—Token來表明客戶身份的一種方式。在通常情況下,Token 是一個很復雜的字符串,比如我們用私鑰簽名一個字符串后的數據就可 以被當作一個Token。此外,每個Token對應一個用戶名,存儲在API Server能訪問的一個文件中。當客戶端發起API調用請求時,需要在 HTTP Header里放入Token,這樣一來,API Server就能識別合法用戶和 非法用戶了
HTTP Base認證:HTTP是無狀態的,瀏覽器和Web服務器之間可以通過 Cookie來進行身份識別。桌面應用程序(比如新浪桌面客戶端、 SkyDrive客戶端、命令行程序)一般不會使用Cookie,那么它們與Web 服務器之間是如何進行身份識別的呢?這就用到了HTTP Base認證,這種認證方式是把“用戶名+冒號+密碼”用BASE64算法進行編碼后的字符 串放在HTTP Request中的Header Authorization域里發送給服務端,服務 端在收到后進行解碼,獲取用戶名及密碼,然后進行用戶身份鑒權

API Server授權管理

當客戶端發起API Server調用時,API Server內部要先進行用戶認 證,然后執行用戶授權流程,即通過授權策略來決定一個API調用是否 合法。對合法用戶進行授權並且隨后在用戶訪問時進行鑒權,是權限與 安全系統的重要一環。簡單地說,授權就是授予不同的用戶不同的訪問 權限。API Server目前支持以下幾種授權策略(通過API Server的啟動參 數“--authorization-mode”設置)
◎ AlwaysDeny:表示拒絕所有請求,一般用於測試。
◎ AlwaysAllow:允許接收所有請求,如果集群不需要授權流 程,則可以采用該策略,這也是Kubernetes的默認配置。
◎ ABAC(Attribute-Based Access Control):基於屬性的訪問控 制,表示使用用戶配置的授權規則對用戶請求進行匹配和控制。
◎ Webhook:通過調用外部REST服務對用戶進行授權。
◎ RBAC:Role-Based Access Control,基於角色的訪問控制。
◎ Node:是一種專用模式,用於對kubelet發出的請求進行訪問控 制

 

RBAC授權模式詳解

   RBAC(Role-Based Access Control,基於角色的訪問控制)在 Kubernetes的1.5版本中引入,在1.6版本時升級為Beta版本,在1.8版本時 升級為GA  

    ◎ 對集群中的資源和非資源權限均有完整的覆蓋。
    ◎ 整個RBAC完全由幾個API對象完成,同其他API對象一樣,可 以用kubectl或API進行操作。
    ◎ 可以在運行時進行調整,無須重新啟動API Server。

1.RBAC的API資源對象說明
  RBAC引入了4個新的頂級資源對象:Role、ClusterRole、 RoleBinding和ClusterRoleBinding。同其他API資源對象一樣,用戶可以 使用kubectl或者API調用等方式操作這些資源對象
    1)角色(Role)一個角色就是一組權限的集合,這里的權限都是許可形式的,不存 在拒絕的規則。在一個命名空間中,可以用角色來定義一個角色,如果 是集群級別的,就需要使用ClusterRole了

 

 

rules中的參數說明如下。
  ◎ apiGroups:支持的API組列表,例如“apiVersion: batch/v1”“apiVersion: extensions:v1beta1”“apiVersion: apps/v1beta1”等
  ◎ resources:支持的資源對象列表,例如pods、deployments、 jobs等
  ◎ verbs:對資源對象的操作方法列表,例如get、watch、list、delete、replace、patch等
2)集群角色(ClusterRole)
  集群角色除了具有和角色一致的命名空間內資源的管理能力,因其 集群級別的范圍,還可以用於以下特殊元素的授權
    ◎ 集群范圍的資源,例如Node。
    ◎ 非資源型的路徑,例如“/healthz”。
    ◎ 包含全部命名空間的資源,例如pods(用於kubectl get pods -- all-namespaces這樣的操作授權)。

 

 

 

 

3)角色綁定(RoleBinding)和集群角色綁定 (ClusterRoleBinding)
  角色綁定或集群角色綁定用來把一個角色綁定到一個目標上,綁定 目標可以是User(用戶)、Group(組)或者Service Account。使用 RoleBinding為某個命名空間授權,使用ClusterRoleBinding為集群范圍內 授權
  RoleBinding可以引用Role進行授權。下面的例子中的RoleBinding將 在default命名空間中把pod-reader角色授予用戶jane,這一操作可以讓
jane讀取default命名空間中的Pod:

 

 

 例如,在下面的例子中,雖然secret-reader是一個集群角色,但是因 為使用了RoleBinding,所以dave只能讀取development命名空間中的 secret

 

 

 

 

 

   在這個例子中,Pod是一個命名空間內的資源,log就是一個下級資 源。要在一個RBAC角色中體現,就需要用斜線“/”來分隔資源和下級資 源。若想授權讓某個主體同時能夠讀取Pod和Pod log,則可以配置 resources為一個數組

 

 

 資源還可以通過名稱(ResourceName)進行引用。在指定 ResourceName后,使用get、delete、update和patch動詞的請求,就會被 限制在這個資源實例范圍內。例如,下面的聲明讓一個主體只能對一個 ConFigmap進行get和update操作

 

 

 

3.常用的角色示例.注意,下面的例子只展示了rules部分的內容
  (1)允許讀取核心API組中的Pod資源:

 

 

 

 

 

 (2)允許讀寫extensions和apps兩個API組中的deployment資源:

 

 

 (3)允許讀取pods及讀寫jobs

 

 

 (4)允許讀取一個名為my-config的ConfigMap(必須綁定到一個 RoleBinding來限制到一個Namespace下的ConfigMap)

 

 

 (5)讀取核心組的Node資源(Node屬於集群級的資源,所以必須 存在於ClusterRole中,並使用ClusterRoleBinding進行綁定):

 

 

 4.常見的角色綁定示例.注意,在下面的例子中只包含subjects部分的內容。

  (1)用戶名alice@example.com:

 

 

   (2)組名frontend-admins:

 

 

   (3)kube-system命名空間中的默認Service Account:

 

 

   (4)qa命名空間中的所有Service Account:

 

 

   (5)所有Service Account:

 

 

 

    (6)所有認證用戶(Kubernetes 1.5以上版本):

 

 

   (7)所有未認證用戶(Kubernetes 1.5以上版本):

 

 

   (8)全部用戶(Kubernetes 1.5以上版本):

 

 

 對系統角色的說明如表6.1所示

 

 

   有些默認角色不是以“system:”為前綴的,這部分角色是針對用戶的。其中包含超級用戶角色(cluster- admin),有的用於集群一級的角色(cluster-status),還有針對 Namespace的角色(admin、edit、view)

對核心Master組件角色的說明如表6.3所示

 

 

   RBAC API拒絕用戶通過編輯角色或者角色綁定進行提升權限。這 一限制是在API層面做出的,因此即使RBAC沒有啟用也仍然有效

  用戶要對角色進行創建或更新操作,需要滿足下列至少一個條件:

    (1)擁有一個角色的所有權限,且與該角色的生效范圍一致(如 果是集群角色,則是集群范圍;如果是普通角色,則可能是同一個命名 空間或者整個集群)
    (2)為用戶顯式授予針對該角色或集群角色的提權(escalate)操 作的權限(要求Kubernetes 1.12及以上版本)

對Service Account的授權管理

  默認的RBAC策略為控制平台組件、節點和控制器授予有限范圍的 權限,但是除kube-system外的Service Account是沒有任何權限的(除了 所有認證用戶都具有的discovery權限)。
  這就要求用戶為Service Account賦予所需的權限。細粒度的角色分 配能夠提高安全性,但也會提高管理成本。粗放的授權方式可能會給 Service Account多余的權限,但更易於管理

Admission Control

  突破了之前所說的認證和鑒權兩道關卡之后,客戶端的調用請求就 能夠得到API Server的真正響應了嗎?答案是:不能!這個請求還需要 通過Admission Control(准入控制)所控制的一個准入控制鏈的層層考 驗,才能獲得成功的響應。Kubernetes官方標准的“關卡”有30多個,還 允許用戶自定義擴展
  Admission Control配備了一個准入控制器的插件列表,發送給API Server的任何請求都需要通過列表中每個准入控制器的檢查,檢查不通 過,則API Server拒絕此調用請求。此外,准入控制器插件能夠修改請 求參數以完成一些自動化任務,比如ServiceAccount這個控制器插件。 當前可配置的准入控制器插件如下
    ◎ AlwaysAdmit:已棄用,允許所有請求
    ◎ AlwaysPullImages:在啟動容器之前總是嘗試重新下載鏡像。 這對於多租戶共享一個集群的場景非常有用,系統在啟動容器之前可以 保證總是使用租戶的密鑰去下載鏡像。如果不設置這個控制器,則在 Node上下載的鏡像的安全性將被削弱,只要知道該鏡像的名稱,任何人 便都可以使用它們了
    ◎ AlwaysDeny:已棄用,禁止所有請求,用於測試。
    ◎ DefaultStorageClass:會關注PersistentVolumeClaim資源對象的 創建,如果其中沒有包含任何針對特定Storage class的請求,則為其指 派指定的Storage class。在這種情況下,用戶無須在PVC中設置任何特定 的Storage class就能完成PVC的創建了。如果沒有設置默認的Storage class,該控制器就不會進行任何操作;如果設置了超過一個的默認 Storage class,該控制器就會拒絕所有PVC對象的創建申請,並返回錯誤信息。管理員必須檢查StorageClass對象的配置,確保只有一個默認 值。該控制器僅關注PVC的創建過程,對更新過程無效
    ◎ DefaultTolerationSeconds:針對沒有設置容忍 node.kubernetes.io/not-ready:NoExecute或者 node.alpha.kubernetes.io/unreachable:NoExecute的Pod,設置5min的默認 容忍時間。
    ◎ DenyEscalatingExec:攔截所有exec和attach到具有特權的Pod上 的請求。如果你的集群支持運行有escalated privilege權限的容器,又希 望限制用戶在這些容器內執行命令,那么強烈推薦使用它。
    ◎ EventReateLimit:Alpha版本,用於應對事件密集情況下對API Server造成的洪水攻擊。
    ◎ Initializers:Alpha。用於為動態准入控制提供支持,通過修改 待創建資源的元數據來完成對該資源的修改。
    ◎ LimitPodHardAntiAffinityTopology:該插件啟用了Pod的反親和性調度策略設置,在設置親和性策略參數requiredDuringSchedulingRequiredDuringExecution時要求將topologyKey 的值設置為“kubernetes.io/hostname”,否則Pod會被拒絕創建。
    ◎ LimitRanger:這個插件會監控進入的請求,確保請求的內容符 合在Namespace中定義的LimitRange對象里的資源限制。如果要在 Kubernetes集群中使用LimitRange對象,則必須啟用該插件才能實施這 一限制。LimitRanger還能用於為沒有設置資源請求的Pod自動設置默認 的資源請求,該插件會為default命名空間中的所有Pod設置0.1CPU的資 源請求
    ◎ MutatingAdmissionWebhook:Beta。這一插件會變更符合要求 的請求的內容,Webhook以串行的方式順序執行
    ◎ NamespaceAutoProvision:這一插件會檢測所有進入的具備命 名空間的資源請求,如果其中引用的命名空間不存在,就會自動創建命 名空間。
    ◎ NamespaceExists:這一插件會檢測所有進入的具備命名空間的 資源請求,如果其中引用的命名空間不存在,就會拒絕這一創建過程。
    ◎ NamespaceLifecycle:如果嘗試在一個不存在的Namespace中創 建資源對象,則該創建請求將被拒絕。當刪除一個Namespace時,系統 將會刪除該Namespace中的所有對象,包括Pod、Service等,並阻止刪除 default、kube-system和kube-public這三個命名空間。
    ◎ NodeRestriction:該插件會限制kubelet對Node和Pod的修改行 為。為了實現這一限制,kubelet必須使用system:nodes組中用戶名為 system:node:<nodeName>的Token來運行。符合條件的kubelet只能修改 自己的Node對象,也只能修改分配到各自Node上的Pod對象。在 Kubernetes 1.11以后的版本中,kubelet無法修改或者更新自身Node的 taint屬性。在Kubernetes 1.13以后,這一插件還會阻止kubelet刪除自己的Node資源,並限制對有kubernetes.io/或k8s.io/前綴的標簽的修改。
    ◎ ResourceQuota:用於資源配額管理目的,作用於Namespace。 該插件攔截所有請求,以確保在Namespace上的資源配額使用不會超 標。推薦在Admission Control參數列表中將這個插件排最后一個,以免 可能被其他插件拒絕的Pod被過早分配資源
    ◎ ServiceAccount:這個插件將ServiceAccount實現了自動化,如 果想使用ServiceAccount對象,那么強烈推薦使用它

  在API Server上設置參數即可定制我們需要的准入控制鏈,如果啟 用多種准入控制選項,則建議設置:在Kubernetes 1.9及之前的版本中使用的參數是--admission-control,並且其中的內容是順序相關的;在 Kubernetes 1.10及之后的版本中,該參數為--enable-admission-plugins, 並且與順序無關。

對Kubernetes 1.10及以上版本設置如下

 

 

對Kubernetes 1.9及以下版本設置如下:

 

 

Service Account
  Service Account也是一種賬號,但它並不是給Kubernetes集群的用戶 (系統管理員、運維人員、租戶用戶等)用的,而是給運行在Pod里的 進程用的,它為Pod里的進程提供了必要的身份證明
  在正常情況下,為了確保Kubernetes集群的安全,API Server都會對 客戶端進行身份認證,認證失敗的客戶端無法進行API調用。此外,在 Pod中訪問Kubernetes API Server服務時,是以Service方式訪問名為 Kubernetes的這個服務的,而Kubernetes服務又只在HTTPS安全端口443 上提供,那么如何進行身份認證呢.通過查看官方源碼,我們發現這是在用一種類似HTTP Token的新 認證方式—Service Account Auth,Pod中的客戶端調用Kubernetes API 時,在HTTP Header中傳遞了一個Token字符串,這類似於之前提到的 HTTP Token認證方式,但有以下幾個不同之處  

  ◎ 這個Token的內容來自Pod里指定路徑下的一個文件 (/run/secrets/kubernetes.io/serviceaccount/token),這種Token是動態生 成的,確切地說,是由Kubernetes Controller進程用API Server的私鑰(-- service-account-private-key-file指定的私鑰)簽名生成的一個JWT Secret。◎ 這個Token的內容來自Pod里指定路徑下的一個文件 (/run/secrets/kubernetes.io/serviceaccount/token),這種Token是動態生 成的,確切地說,是由Kubernetes Controller進程用API Server的私鑰(-- service-account-private-key-file指定的私鑰)簽名生成的一個JWT Secret。
  ◎ 在官方提供的客戶端REST框架代碼里,通過HTTPS方式與API Server建立連接后,會用Pod里指定路徑下的一個CA證書 (/run/secrets/kubernetes.io/serviceaccount/ca.crt)驗證API Server發來的 證書,驗證是否為CA證書簽名的合法證書。
  ◎ API Server在收到這個Token以后,采用自己的私鑰(實際上是 使用service-accountkey-file參數指定的私鑰,如果沒有設置此參數,則 默認采用tls-private-key-file指定的參數,即自己的私鑰)對Token進行合 法性驗證。

 

我們接下來繼續分析在上面的認證過程中所涉及 的Pod中的以下三個文件。
  ◎ /run/secrets/kubernetes.io/serviceaccount/token。
  ◎ /run/secrets/kubernetes.io/serviceaccount/ca.crt。
  ◎ /run/secrets/kubernetes.io/serviceaccount/namespace(客戶端采用 這里指定的namespace作為參數調用Kubernetes API)。

  這三個文件由於參與到Pod進程與API Server認證的過程中,起到了 類似secret(私密憑據)的作用,所以它們被稱為Kubernetes Secret對 象。Secret從屬於Service Account資源對象,屬於Service Account的一部 分,在一個Service Account對象里面可以包括多個不同的Secret對象,分 別用於不同目的的認證活動

  查看系統中的Service Account對象,看到有一個名為default 的Service Account對象,包含一個名為default-token-77oyg的Secret,這 個Secret同時是Mountable secrets,表明它是需要被掛載到Pod上的
default-token-77oyg包含三個數據 項,分別是token、ca.crt、namespace。聯想到Mountable secrets的標記, 以及之前看到的Pod中的三個文件的文件名,我們恍然大悟:在每個 Namespace下都有一個名為default的默認Service Account對象,在這個 Service Account里面有一個名為Tokens的可以當作Volume被掛載到Pod 里的Secret,當Pod啟動時,這個Secret會自動被掛載到Pod的指定目錄下,用來協助完成Pod中的進程訪問API Server時的身份鑒權

 

 

其中:
  (1)名為Tokens的Secret用於訪問API Server的Secret,也被稱為 Service Account Secret。
  (2)名為imagePullSecrets的Secret用於下載容器鏡像時的認證過 程,通常鏡像庫運行在Insecure模式下,所以這個Secret為空。
  (3)用戶自定義的其他Secret,用於用戶的進程。
  如果一個Pod在定義時沒有指定spec.serviceAccountName屬性,則系 統會自動為其賦值為default,即大家都使用同一個Namespace下的默認 Service Account。如果某個Pod需要使用非default的Service Account,則 需要在定義時指定:

 

 

 

Kubernetes之所以要創建兩套獨立的賬號系統,原因如下。
  ◎ User賬號是給人用的,Service Account是給Pod里的進程使用 的,面向的對象不同。
  ◎ User賬號是全局性的,Service Account則屬於某個具體的 Namespace。
  ◎ 通常來說,User賬號是與后端的用戶數據庫同步的,創建一個 新用戶通常要走一套復雜的業務流程才能實現,Service Account的創建 則需要極輕量級的實現方式,集群管理員可以很容易地為某些特定任務 創建一個Service Account。
  ◎ 對於這兩種不同的賬號,其審計要求通常不同。
  ◎ 對於一個復雜的系統來說,多個組件通常擁有各種賬號的配置 信息,Service Account是Namespace隔離的,可以針對組件進行一對一 的定義,同時具備很好的“便攜性”。  

接下來深入分析Service Account與Secret相關的一些運行機制
  我們知道Controller manager創建了 ServiceAccount Controller與Token Controller這兩個安全相關的控制器。其中ServiceAccount Controller一直監聽Service Account和Namespace的事 件,如果在一個  Namespace中沒有default Service Account,那么 ServiceAccount Controller會為該Namespace創建一個默認(default)的Service Account,這就是我們之前看到在每個Namespace下都有一個名 為default的Service Account的原因。

 
Secret私密憑據

  Secret的主要作用是保管私密數據,比如密 碼、OAuth Tokens、SSH Keys等信息。將這些私密信息放在Secret對象 中比直接放在Pod或Docker Image中更安全,也更便於使用和分發

 

 

   在上面的例子中,data域的各子域的值必須為BASE64編碼值,其 中password域和username域BASE64編碼前的值分別為value-1和value-2

  一旦Secret被創建,就可以通過下面三種方式使用它

    (1)在創建Pod時,通過為Pod指定Service Account來自動使用該 Secret
    (2)通過掛載該Secret到Pod來使用它。
    (3)在Docker鏡像下載時使用,通過指定Pod的 spc.ImagePullSecrets來引用它。
  第1種使用方式主要用在API Server鑒權方面.下面的例子展示了第2種使用方式:將一個Secret通過掛載的方式添加到Pod的 Volume中:

 

 

 

 

 

   每個單獨的Secret大小不能超過1MB,Kubernetes不鼓勵創建大的 Secret,因為如果使用大的Secret,則將大量占用API Server和kubelet的 內存。當然,創建許多小的Secret也能耗盡API Server和kubelet的內存

  我們可以通過Secret保管其他 系統的敏感信息(比如數據庫的用戶名和密碼),並以Mount的方式將 Secret掛載到Container中,然后通過訪問目錄中文件的方式獲取該敏感 信息。當Pod被API Server創建時,API Server不會校驗該Pod引用的 Secret是否存在。一旦這個Pod被調度,則kubelet將試着獲取Secret的 值。如果Secret不存在或暫時無法連接到API Server,則kubelet按一定的 時間間隔定期重試獲取該Secret,並發送一個Event來解釋Pod沒有啟動 的原因一旦Secret被Pod獲取,則kubelet將創建並掛載包含Secret的 Volume。只有所有Volume都掛載成功,Pod中的Container才會被啟動。 在kubelet啟動Pod中的Container后,Container中和Secret相關的Volume將 不會被改變,即使Secret本身被修改。為了使用更新后的Secret,必須刪 除舊Pod,並重新創建一個新Pod

Pod的安全策略配置
  為了更精細地控制Pod對資源的使用方式,Kubernetes從1.4版本開 始引入了PodSecurityPolicy資源對象對Pod的安全策略進行管理,並在 1.10版本中升級為Beta版,到1.14版本時趨於成熟。目前 PodSecurityPolicy資源對象的API版本為extensions/v1beta1,從1.10版本 開始更新為policy/v1beta1,並計划於1.16版本時棄用 extensions/v1beta1
PodSecurityPolicy的工作機制

  若想啟用PodSecurityPolicy機制,則需要在kube-apiserver服務的啟 動參數--enable-admission-plugins中進行設置

 

   在開啟PodSecurityPolicy准入控制器后,Kubernetes默認不允許創建 任何Pod,需要創建PodSecurityPolicy策略和相應的RBAC授權策略 (Authorizing Policies),Pod才能創建成功

創建一個PodSecurityPolicy,配置文件psp-non- privileged.yaml的內容如下

 

 

 

 在PodSecurityPolicy對象中可以設置下列字段來控制Pod運行時的各 種安全策略

1.特權模式相關配置.privileged:是否允許Pod以特權模式運行。
2.宿主機資源相關配置
(1)hostPID:是否允許Pod共享宿主機的進程空間。
(2)hostIPC:是否允許Pod共享宿主機的IPC命名空間。
(3)hostNetwork:是否允許Pod使用宿主機網絡的命名空間
(4)hostPorts:是否允許Pod使用宿主機的端口號,可以通過 hostPortRange字段設置允許使用的端口號范圍,以[min, max]設置最小 端口號和最大端口號。
(5)Volumes:允許Pod使用的存儲卷Volume類型,設置為“*”表 示允許使用任意Volume類型,建議至少允許Pod使用下列Volume類型。
◎ configMap ◎ downwardAPI ◎ emptyDir ◎ persistentVolumeClaim ◎ secret ◎ projected
(6)AllowedHostPaths:允許Pod使用宿主機的hostPath路徑名稱, 可以通過pathPrefix字段設置路徑的前綴,並可以設置是否為只讀屬性

 

 

 

結果為允許Pod訪問宿主機上以“/foo”為前綴的路徑,包 括“/foo”“/foo/”“/foo/bar”等,但不能訪問“/fool”“/etc/foo”等路徑,也不允 許通過“/foo/../”表達式訪問/foo的上層目錄
(7)FSGroup:設置允許訪問某些Volume的Group ID范圍,可以 將規則(rule字段)設置為MustRunAs、MayRunAs或RunAsAny。
   ◎ MustRunAs:需要設置Group ID的范圍,例如1~65535,要求 Pod的securityContext.fsGroup設置的值必須屬於該Group ID的范圍
   ◎ MayRunAs:需要設置Group ID的范圍,例如1~65535,不強 制要求Pod設置securityContext.fsGroup。
   ◎ RunAsAny:不限制Group ID的范圍,任何Group都可以訪問 Volume。
(8)ReadOnlyRootFilesystem:要求容器運行的根文件系統(root filesystem)必須是只讀的。
(9)allowedFlexVolumes:對於類型為flexVolume的存儲卷,設置 允許使用的驅動類型,例子如下。

 

 

 

3.用戶和組相關配置
(1)RunAsUser:設置運行容器的用戶ID(User ID)范圍,規則 字段(rule)的值可以被設置為MustRunAs、MustRunAsNonRoot或 RunAsAny。
  ◎ MustRunAs:需要設置User ID的范圍,要求Pod的 securityContext.runAsUser設置的值必須屬於該User ID的范圍。
  ◎ MustRunAsNonRoot:必須以非root用戶運行容器,要求Pod的 securityContext.runAsUser設置一個非0的用戶ID,或者鏡像中在USER字 段設置了用戶ID,建議同時設置allowPrivilegeEscalation=false以避免不 必要的提升權限操作。
  ◎ RunAsAny:不限制User ID的范圍,任何User都可以運行。
(2)RunAsGroup:設置運行容器的Group ID范圍,規則字段的值 可以被設置為MustRunAs、MustRunAsNonRoot或RunAsAny。
  ◎ MustRunAs:需要設置Group ID的范圍,要求Pod的 securityContext.runAsGroup設置的值必須屬於該Group ID的范圍。
  ◎ MustRunAsNonRoot:必須以非root組運行容器,要求Pod的 securityContext.runAsUser設置一個非0的用戶ID,或者鏡像中在USER字 段設置了用戶ID,建議同時設置allowPrivilegeEscalation=false以避免不必要的提升權限操作
  ◎ RunAsAny:不限制Group ID的范圍,任何Group的用戶都可以 運行
(3)SupplementalGroups:設置容器可以額外添加的Group ID范 圍,可以將規則(rule字段)設置為MustRunAs、MayRunAs或 RunAsAny。
  ◎ MustRunAs:需要設置Group ID的范圍,要求Pod的 securityContext.supplementalGroups設置的值必須屬於該Group ID范圍。
  ◎ MayRunAs:需要設置Group ID的范圍,不強制要求Pod設置 securityContext.supplementalGroups。
  ◎ RunAsAny:不限制Group ID的范圍,任何supplementalGroups 的用戶都可以運行。
4.提升權限相關配置
(1)AllowPrivilegeEscalation:設置容器內的子進程是否可以提升 權限,通常在設置非root用戶(MustRunAsNonRoot)時進行設置
(2)DefaultAllowPrivilegeEscalation:設置AllowPrivilegeEscalation 的默認值,設置為disallow時,管理員還可以顯式設置AllowPrivilegeEscalation來指定是否允許提升權限。
5.Linux能力相關配置
(1)AllowedCapabilities:設置容器可以使用的Linux能力列表,設 置為“*”表示允許使用Linux的所有能力(如NET_ADMIN、SYS_TIME 等)
(2)RequiredDropCapabilities:設置不允許容器使用的Linux能力 列表。
(3)DefaultAddCapabilities:設置默認為容器添加的Linux能力列 表,例如SYS_TIME等
6.SELinux相關配置
seLinux:設置SELinux參數,可以將規則字段(rule)的值設置為 MustRunAs或RunAsAny。
  ◎ MustRunAs:要求設置seLinuxOptions,系統將對Pod的 securityContext.seLinuxOptions設置的值進行校驗。
  ◎ RunAsAny:不限制seLinuxOptions的設置。
7.其他Linux相關配置
(1)AllowedProcMountTypes:設置允許的ProcMountTypes類型列 表,可以設置allowedProcMountTypes或DefaultProcMount。
(2)AppArmor:設置對容器可執行程序的訪問控制權限
(3)Seccomp:設置允許容器使用的系統調用(System Calls)的 profile。
(4)Sysctl:設置允許調整的內核參數

例1:基本沒有限制的安全策略,允許創建任意安全設置的Pod

 

 

 

 例2:要求Pod運行用戶為非特權用戶;禁止提升權限;不允許使用 宿主機網絡、端口號、IPC等資源;限制可以使用的Volume類型,等 等

 

 

  此外,Kubernetes建議使用RBAC授權機制來設置針對Pod安全策略 的授權,通常應該對Pod的ServiceAccount進行授權
    例如,可以創建如下ClusterRole(也可以創建Role)並將其設置為 允許使用PodSecurityPolicy

 

 然后創建一個ClusterRoleBinding與用戶和ServiceAccount進行綁 定

                          

                                 第7章 網絡原理

  Kubernetes網絡模型設計的一個基礎原則是:每個Pod都擁有一個獨 立的IP地址,並假定所有Pod都在一個可以直接連通的、扁平的網絡空 間中。所以不管它們是否運行在同一個Node(宿主機)中,都要求它們 可以直接通過對方的IP進行訪問。設計這個原則的原因是,用戶不需要 額外考慮如何建立Pod之間的連接,也不需要考慮如何將容器端口映射 到主機端口等問題
在Kubernetes的世界里,IP是以Pod為單位進行分配的。一 個Pod內部的所有容器共享一個網絡堆棧(相當於一個網絡命名空間, 它們的IP地址、網絡設備、配置等都是共享的)。按照這個網絡原則抽 象出來的為每個Pod都設置一個IP地址的模型也被稱作IP-per-Pod模型
  由於Kubernetes的網絡模型假設Pod之間訪問時使用的是對方Pod的 實際地址,所以一個Pod內部的應用程序看到的自己的IP地址和端口與 集群內其他Pod看到的一樣。它們都是Pod實際分配的IP地址。將IP地址 和端口在Pod內部和外部都保持一致,也就不需要使用NAT來進行地址 轉換了。Kubernetes的網絡之所以這么設計,主要原因就是可以兼容過 去的應用。當然,我們使用Linux命令“ip addr show”也能看到這些地 址,和程序看到的沒有什么區別。所以這種IP-per-Pod的方案很好地利 用了現有的各種域名解析和發現機制
  為每個Pod都設置一個IP地址的模型還有另外一層含義,那就是同 一個Pod內的不同容器會共享同一個網絡命名空間,也就是同一個Linux 網絡協議棧。這就意味着同一個Pod內的容器可以通過localhost來連接對 方的端口。這種關系和同一個VM內的進程之間的關系是一樣的,看起 來Pod內容器之間的隔離性減小了,而且Pod內不同容器之間的端口是共享的,就沒有所謂的私有端口的概念了

Kubernetes對集群網絡有如下要求:
  (1)所有容器都可以在不用NAT的方式下同別的容器通信
  (2)所有節點都可以在不用NAT的方式下同所有容器通信,反之 亦然。
  (3)容器的地址和別人看到的地址是同一個地址

網絡命名空間
  為了支持網絡協議棧的多個實例,Linux在網絡棧中引入了網絡命 名空間,這些獨立的協議棧被隔離到不同的命名空間中。處於不同命名 空間中的網絡棧是完全隔離的,彼此之間無法通信,就好像兩個“平行 宇宙”。通過對網絡資源的隔離,就能在一個宿主機上虛擬多個不同的 網絡環境。Docker正是利用了網絡的命名空間特性,實現了不同容器之 間的網絡隔離

  在Linux的網絡命名空間中可以有自己獨立的路由表及獨立的 iptables設置來提供包轉發、NAT及IP包過濾等功能
  為了隔離出獨立的協議棧,需要納入命名空間的元素有進程、套接 字、網絡設備等。進程創建的套接字必須屬於某個命名空間,套接字的 操作也必須在命名空間中進行。同樣,網絡設備也必須屬於某個命名空 間。因為網絡設備屬於公共資源,所以可以通過修改屬性實現在命名空 間之間移動。當然,是否允許移動與設備的特征有關
  1.網絡命名空間的實現
    Linux的網絡協議棧是十分復雜的,為了支持獨立的協議棧,相關 的這些全局變量都必須被修改為協議棧私有。最好的辦法就是讓這些全 局變量成為一個Net Namespace變量的成員,然后為協議棧的函數調用 加入一個Namespace參數。這就是Linux實現網絡命名空間的核心

  在建立了新的網絡命名空間,並將某個進程關聯到這個網絡命名空 間后,就出現了類似於如圖7.1所示的內核數據結構,所有網站棧變量 都被放入了網絡命名空間的數據結構中。這個網絡命名空間是其進程組 私有的,和其他進程組不沖突 
   

 

 

  在新生成的私有命名空間中只有回環設備(名為“lo”且是停止狀 態),其他設備默認都不存在,如果我們需要,則要一一手工建立。 Docker容器中的各類網絡棧設備都是Docker Daemon在啟動時自動創建 和配置的
  所有的網絡設備(物理的或虛擬接口、橋等在內核里都叫作Net Device)都只能屬於一個命名空間。當然,物理設備(連接實際硬件的 設備)通常只能關聯到root這個命名空間中。虛擬的網絡設備(虛擬的 以太網接口或者虛擬網口對)則可以被創建並關聯到一個給定的命名空 間中,而且可以在這些命名空間之間移動
  由於網絡命名空間代表的是一個獨立的協議棧,所以它 們之間是相互隔離的,彼此無法通信,在協議棧內部都看不到對方。那 么有沒有辦法打破這種限制,讓處於不同命名空間的網絡相互通信,甚 至和外部的網絡進行通信呢?答案就是“有,應用Veth設備對即可”。 Veth設備對的一個重要作用就是打通互相看不到的協議棧之間的壁壘, 它就像一條管子,一端連着這個網絡命名空間的協議棧,一端連着另一 個網絡命名空間的協議棧。所以如果想在兩個命名空間之間通信,就必 須有一個Veth設備對。后面會介紹如何操作Veth設備對來打通不同命名 空間之間的網絡

  2.網絡命名空間的操作

    我們可以使用Linux iproute2系列配置工具中的IP命令來操作網絡命 名空間。注意,這個命令需要由root用戶運行.創建一個命名空間:

 
    在命名空間中執行命令:
 

  也可以先通過bash命令進入內部的shell界面,然后執行各種命令

   

Veth設備對
  引入Veth設備對是為了在不同的網絡命名空間之間通信,利用它可 以直接將兩個網絡命名空間連接起來。由於要連接兩個網絡命名空間, 所以Veth設備都是成對出現的,很像一對以太網卡,並且中間有一根直 連的網線。既然是一對網卡,那么我們將其中一端稱為另一端的peer在Veth設備的一端發送數據時,它會將數據直接發送到另一端,並觸發 另一端的接收操作

 

 

 

1.Veth設備對的操作命令
  接下來看看如何創建Veth設備對,如何連接到不同的命名空間,並 設置它們的地址,讓它們通信。
  創建Veth設備對:

  創建后,可以查看Veth設備對的信息。使用ip link show命令查看所有網絡接口:

 
  有兩個設備生成了,一個是veth0,它的peer是veth1
   現在這兩個設備都在自己的命名空間中,那怎么能行呢?好了,如 果將Veth看作有兩個頭的網線,那么我們將另一個頭甩給另一個命名空 間

 

 這時可在外面這個命名空間中看兩個設備的情況

  只剩一個veth0設備了,已經看不到另一個設備了,另一個設備已經被轉移到另一個網絡命名空間中了

  在netns1網絡命名空間中可以看到veth1設備了,符合預期

 

   現在看到的結果是,兩個不同的命名空間各自有一個Veth的“網線 頭”,各顯示為一個Device(在Docker的實現里面,它除了將Veth放入容 器內,還將它的名字改成了eth0,簡直以假亂真,你以為它是一個本地 網卡嗎)

  現在可以通信了嗎?不行,因為它們還沒有任何地址,我們現在給 它們分配IP地址:

再啟動它們:

現在兩個網絡命名空間可以互相通信了

 網橋

  Linux可以支持多個不同的網絡,它們之間能夠相互通信,如何將 這些網絡連接起來並實現各網絡中主機的相互通信呢?可以用網橋。網 橋是一個二層的虛擬網絡設備,把若干個網絡接口“連接”起來,以使得 網絡接口之間的報文能夠互相轉發。網橋能夠解析收發的報文,讀取目 標MAC地址的信息,和自己記錄的MAC表結合,來決策報文的轉發目 標網絡接口。為了實現這些功能,網橋會學習源MAC地址(二層網橋 轉發的依據就是MAC地址)。在轉發報文時,網橋只需要向特定的網 口進行轉發,來避免不必要的網絡交互。如果它遇到一個自己從未學習 到的地址,就無法知道這個報文應該向哪個網絡接口轉發,就將報文廣 播給所有的網絡接口(報文來源的網絡接口除外)

  在實際的網絡中,網絡拓撲不可能永久不變。設備如果被移動到另 一個端口上,卻沒有發送任何數據,網橋設備就無法感知到這個變化, 網橋還是向原來的端口轉發數據包,在這種情況下數據就會丟失。所以 網橋還要對學習到的MAC地址表加上超時時間(默認為5min)。如果 網橋收到了對應端口MAC地址回發的包,則重置超時時間,否則過了 超時時間后,就認為設備已經不在那個端口上了,它就會重新廣播發送

   在實際的網絡中,網絡拓撲不可能永久不變。設備如果被移動到另 一個端口上,卻沒有發送任何數據,網橋設備就無法感知到這個變化, 網橋還是向原來的端口轉發數據包,在這種情況下數據就會丟失。所以 網橋還要對學習到的MAC地址表加上超時時間(默認為5min)。如果 網橋收到了對應端口MAC地址回發的包,則重置超時時間,否則過了 超時時間后,就認為設備已經不在那個端口上了,它就會重新廣播發 送

  Linux內核支持網口的橋接(目前只支持以太網接口)。但是與單 純的交換機不同,交換機只是一個二層設備,對於接收到的報文,要么 轉發,要么丟棄。運行着Linux內核的機器本身就是一台主機,有可能 是網絡報文的目的地,其收到的報文除了轉發和丟棄,還可能被送到網 絡協議棧的上層(網絡層),從而被自己(這台主機本身的協議棧)消 化,所以我們既可以把網橋看作一個二層設備,也可以把它看作一個三層設備

Linux網橋的實現

   Linux內核是通過一個虛擬的網橋設備(Net Device)來實現橋接 的。這個虛擬設備可以綁定若干個以太網接口設備,從而將它們橋接起 來。如圖7.3所示,這種Net Device網橋和普通的設備不同,最明顯的一 個特性是它還可以有一個IP地址

   網橋設備br0綁定了eth0和eth1。對於網絡協議棧的上 層來說,只看得到br0就行。因為橋接是在數據鏈路層實現的,上層不 需要關心橋接的細節,所以協議棧上層需要發送的報文被送到br0,網 橋設備的處理代碼判斷報文該被轉發到eth0還是eth1,或者兩者皆轉 發;反過來,從eth0或從eth1接收到的報文被提交給網橋的處理代碼, 在這里會判斷報文應該被轉發、丟棄還是被提交到協議棧上層

   而有時eth0、eth1也可能會作為報文的源地址或目的地址,直接參 與報文的發送與接收,從而繞過網橋

2.網橋的常用操作命令
  Docker自動完成了對網橋的創建和維護。為了進一步理解網橋,下面舉幾個常用的網橋操作例子,對網橋進行手工操作
  新增一個網橋設備:
    
brctl addbr xxxxx
  之后可以為網橋增加網口,在linux中一個網口其實就是一個物理網卡。將物理網卡和網橋連接起來
brctl addif xxxxx ethx

  網橋的物理網卡作為一個網口,由於在鏈路層工作,就不再需要IP 地址了,這樣上面的IP地址自然失效

ifconfig ethx 0.0.0.0

給網橋配置一個IP地址

ifconfig brxx xxx.xxx.xxx.xxx
 
iptables和Netfilter   

  在Linux網絡協議棧中有一組回調函數掛接點,通過這些掛接點掛 接的鈎子函數可以在Linux網絡棧處理數據包的過程中對數據包進行一 些操作,例如過濾、修改、丟棄等。整個掛接點技術叫作Netfilter和 iptables
  Netfilter負責在內核中執行各種掛接的規則,運行在內核模式中; 而iptables是在用戶模式下運行的進程,負責協助和維護內核中Netfilter 的各種規則表。二者互相配合來實現整個Linux網絡協議棧中靈活的數 據包處理機制

規則表Table 

  這些掛接點能掛接的規則也分不同的類型(也就是規則表 Table),我們可以在不同類型的Table中加入我們的規則。目前主要支 持的Table類型有:RAW、MANGLE、NAT和FILTER
  上述4個Table(規則鏈)的優先級是RAW最高,FILTER最低

路由

  Linux系統包含一個完整的路由功能。當IP層在處理數據發送或者 轉發時,會使用路由表來決定發往哪里

  如果主機與目的主機沒有直接相連,那么主機會將IP報文發送給默認的路由器,然后由路由 器來決定往哪里發送IP報文

Docker的網絡實現 

標准的Docker支持以下4類網絡模式。
  ◎ host模式:使用--net=host指定
  ◎ container模式:使用--net=container:NAME_or_ID指定
  ◎ none模式:使用--net=none指定
  ◎ bridge模式:使用--net=bridge指定,為默認設置
在Kubernetes管理模式下通常只會使用bridge模式
  在bridge模式下,Docker Daemon第1次啟動時會創建一個虛擬的網橋,默認的名稱是docker0,然后按照RPC1918的模型在私有網絡空間中 給這個網橋分配一個子網。針對由Docker創建的每一個容器,都會創建 一個虛擬的以太網設備(Veth設備對),其中一端關聯到網橋上,另一 端使用Linux的網絡命名空間技術,映射到容器內的eth0設備,然后從網橋的地址段內給eth0接口分配一個IP地址

  其中ip1是網橋的IP地址,Docker Daemon會在幾個備選地址段里給 它選一個地址,通常是以172開頭的一個地址。這個地址和主機的IP地 址是不重疊的。ip2是Docker在啟動容器時,在這個地址段選擇的一個 沒有使用的IP地址分配給容器。相應的MAC地址也根據這個IP地址,在 02:42:ac:11:00:00和02:42:ac:11:ff:ff的范圍內生成,這樣做可以確保不會 有ARP沖突
  啟動后,Docker還將Veth對的名稱映射到eth0網絡接口。ip3就是主 機的網卡地址。在一般情況下,ip1、ip2和ip3是不同的IP段,所以在默認不做任何 特殊配置的情況下,在外部是看不到ip1和ip2的
這樣做的結果就是,在同一台機器內的容器之間可以相互通信,不 同主機上的容器不能相互通信,實際上它們甚至有可能在相同的網絡地 址范圍內(不同主機上的docker0的地址段可能是一樣的)

  為了讓它們跨節點互相通信,就必須在主機的地址上分配端口,然后通過這個端口路由或代理到容器上。這種做法顯然意味着一定要在容 器之間小心謹慎地協調好端口的分配,或者使用動態端口的分配技術    

 

Kubernetes的網絡實現

   在我們 的例子中,容器就是圖7.8中的容器1和容器2。容器1和容器2共享一個 網絡的命名空間,共享一個命名空間的結果就是它們好像在一台機器上 運行,它們打開的端口不會有沖突,可以直接使用Linux的本地IPC進行 通信(例如消息隊列或者管道)。其實,這和傳統的一組普通程序運行 的環境是完全一樣的,傳統程序不需要針對網絡做特別的修改就可以移 植了,它們之間的互相訪問只需要使用localhost就可以

   

 Pod之間的通信 

  每一個Pod都有一個真實的全局IP地址,同一個Node內的不同Pod之間可以直接采用對方Pod的IP地址通信,而且不需要采用其他發現機制,例如DNS、Consul或者etcd

  Pod容器既有可能在同一個Node上運行,也有可能在不同的Node上運行,所以通信也分為兩類:同一個Node內Pod之間的通信和不同Node上Pod之間的通信

  1.同一個Node內Pod之間的通信
  我們看一下同一個Node內兩個Pod之間的關系

  可以看出,Pod1和Pod2都是通過Veth連接到同一個docker0網橋上 的,它們的IP地址IP1、IP2都是從docker0的網段上動態獲取的,它們和 網橋本身的IP3是同一個網段的
  另外,在Pod1、Pod2的Linux協議棧上,默認路由都是docker0的地址,也就是說所有非本地地址的網絡數據,都會被默認發送到docker0 網橋上,由docker0網橋直接中轉
  綜上所述,由於它們都關聯在同一個docker0網橋上,地址段相 同,所以它們之間是能直接通信的

 Pod的地址是與docker0在同一個網段的,我們知道docker0網段與宿 主機網卡是兩個完全不同的IP網段,並且不同Node之間的通信只能通過 宿主機的物理網卡進行,因此要想實現不同Node上Pod容器之間的通 信,就必須想辦法通過主機的這個IP地址進行尋址和通信

要想支持不同Node上Pod之間的通信,就要滿足兩個條 件:
(1)在整個Kubernetes集群中對Pod的IP分配進行規划,不能有沖 突;
(2)找到一種辦法,將Pod的IP和所在Node的IP關聯起來,通過這 個關聯讓Pod可以互相訪問
  根據條件1的要求,我們需要在部署Kubernetes時對docker0的IP地址 進行規划,保證每個Node上的docker0地址都沒有沖突。我們可以在規划后手工配置到每個Node上,或者做一個分配規則,由安裝的程序自己 去分配占用。例如,Kubernetes的網絡增強開源軟件Flannel就能夠管理 資源池的分配
根據條件2的要求,Pod中的數據在發出時,需要有一個機制能夠知 道對方Pod的IP地址掛在哪個具體的Node上。也就是說先要找到Node對 應宿主機的IP地址,將數據發送到這個宿主機的網卡,然后在宿主機上 將相應的數據轉發到具體的docker0上。一旦數據到達宿主機Node,則 那個Node內部的docker0便知道如何將數據發送到Pod

在圖7.10中,IP1對應的是Pod1,IP2對應的是Pod2。Pod1在訪問 Pod2時,首先要將數據從源Node的eth0發送出去,找到並到達Node2的 eth0。即先是從IP3到IP4的遞送,之后才是從IP4到IP2的遞送

Pod和Service網絡實戰

   這里使用虛擬機來完成實驗。如果要部署在物理機器上或者雲服務 商的環境中,則涉及的網絡模型很可能稍微有所不同。不過,從網絡角 度來看,Kubernetes的機制是類似且一致的

 

  Kubernetes的網絡模型要求每個Node上的容器都可以相互訪問
  默認的Docker網絡模型提供了一個IP地址段是172.17.0.0/16的docker0網橋。每個容器都會在這個子網內獲得IP地址,並且將docker0 網橋的IP地址(172.17.42.1)作為其默認網關。需要注意的是,Docker   宿主機外面的網絡不需要知道任何關於這個172.17.0.0/16的信息或者知 道如何連接到其內部,因為Docker的宿主機針對容器發出的數據,在物 理網卡地址后面都做了IP偽裝MASQUERADE(隱含NAT)。也就是 說,在網絡上看到的任何容器數據流都來源於那台Docker節點的物理IP 地址。這里所說的網絡都指連接這些主機的物理網絡
在Kubernetes的網絡模型中,每台主機上的docker0網橋都是可以被 路由到的。也就是說,在部署了一個Pod時,在同一個集群內,各主機 都可以訪問其他主機上的Pod IP,並不需要在主機上做端口映射。綜上 所述,我們可以在網絡層將Kubernetes的節點看作一個路由器。如果將 實驗環境改畫成一個網絡圖

  為了支持Kubernetes網絡模型,我們采取了直接路由的方式來實 現,在每個Node上都配置相應的靜態路由項,例如在192.168.1.129這個 Node上配置了兩個路由項

# route add -net 10.1.20.0 netmask 255.255.255.0 gw 192.168.1.130
# route add -net 10.1.30.0 netmask 255.255.255.0 gw 192.168.1.131

這意味着,每一個新部署的容器都將使用這個Node(docker0的網 橋IP)作為它的默認網關。而這些Node(類似路由器)都有其他 docker0的路由信息,這樣它們就能夠相互連通了

  我們檢查的第1個容器是運行 了“google_containers/pause:latest”鏡像的容器,它使用了Docker默認的網 絡模型 bridge;而我們檢查的第2個容器,也就是在RC/Pod中定義運行 的php-redis容器,使用了非默認的網絡配置和映射容器的模型,指定了 映射目標容器為“google_containers/ pause:latest”

  首先,一個Pod內的所有容器都需要共用同一個IP地址,這就意味 着一定要使用網絡的容器映射模式。然而,為什么不能只啟動第1個Pod 中的容器,而將第2個Pod中的容器關聯到第1個容器呢?我們認為 Kubernetes是從兩方面來考慮這個問題的:首先,如果在Pod內有多個容 器的話,則可能很難連接這些容器;其次,后面的容器還要依賴第1個 被關聯的容器,如果第2個容器關聯到第1個容器,且第1個容器死掉的 話,第2個容器也將死掉。啟動一個基礎容器,然后將Pod內的所有容器 都連接到它上面會更容易一些。因為我們只需要為基礎的這個 Google_containers/pause容器執行端口映射規則,這也簡化了端口映射的 過程。所以我們啟動Pod后的網絡模型類似於圖7.13

  在這種情況下,實際Pod的IP數據流的網絡目標都是這個 google_containers/pause容器。圖7.13有點兒取巧地顯示了是 google_containers/pause容器將端口80的流量轉發給了相關的容器。而 pause容器只是看起來轉發了網絡流量,但它並沒有真的這么做。實際 上,應用容器直接監聽了這些端口,和google_containers/pause容器共享 了同一個網絡堆棧。這就是為什么在Pod內部實際容器的端口映射都顯 示到google_containers/pause容器上了

綜上所述,google_containers/pause容器實際上只是負責接管這個 Pod的Endpoint,並沒有做更多的事情

生成service之后iptables變化

  第1行是掛在PREROUTING鏈上的端口重定向規則,所有進入的流 量如果滿足20.1.244.75: 80,則都會被重定向到端口33761。第2行是掛 在OUTPUT鏈上的目標地址NAT,做了和上述第1行規則類似的工作, 但針對的是當前主機生成的外出流量。所有主機生成的流量都需要使用 這個DNAT規則來處理。簡而言之,這兩個規則使用了不同的方式做了 類似的事情,就是將所有從節點生成的發送給20.1.244.75:80的流量重定 向到本地的33761端口

  至此,目標為Service IP地址和端口的任何流量都將被重定向到本地 的33761端口。這個端口連到哪里去了呢?這就到了kube-proxy發揮作用 的地方了。這個kube-proxy服務給每一個新創建的服務都關聯了一個隨 機的端口號,並且監聽那個特定的端口,為服務創建相關的負載均衡對象。在我們的實驗中,隨機生成的端口剛好是33761。通過監控Node1上 的Kubernetes-Service的日志,在創建服務時可以看到下面的記錄

  Kubernetes的kube-proxy看起來只是一個夾層,但實際上它只是在 Node上運行的一個服務。上述重定向規則的結果就是針對目標地址為服 務IP的流量,將Kubernetes的kube-proxy變成了一個中間的夾層

CNI模型

  CNI是由CoreOS公司提出的另一種容器網絡規范,現在已經被 Kubernetes、rkt、Apache Mesos、Cloud Foundry和Kurma等項目采納。 另外,Contiv Networking, Project Calico、Weave、SR-IOV、Cilium、 Infoblox、Multus、Romana、Plumgrid和Midokura等項目也為CNI提供網 絡插件的具體實現。圖7.18描述了容器運行環境與各種網絡插件通過 CNI進行連接的模型

1.CNI規范概述
  CNI提供了一種應用容器的插件化網絡解決方案,定義對容器網絡 進行操作和配置的規范,通過插件的形式對CNI接口進行實現。CNI是 由rkt Networking Proposal發展而來的,試圖提供一種普適的容器網絡解 決方案。CNI僅關注在創建容器時分配網絡資源,和在銷毀容器時刪除 網絡資源,這使得CNI規范非常輕巧、易於實現,得到了廣泛的支持
在CNI模型中只涉及兩個概念:容器和網絡
    ◎ 容器(Container):是擁有獨立Linux網絡命名空間的環境, 例如使用Docker或rkt創建的容器。關鍵之處是容器需要擁有自己的 Linux網絡命名空間,這是加入網絡的必要條件
    ◎ 網絡(Network):表示可以互連的一組實體,這些實體擁有 各自獨立、唯一的IP地址,可以是容器、物理機或者其他網絡設備(比 如路由器)等
  對容器網絡的設置和操作都通過插件(Plugin)進行具體實現, CNI插件包括兩種類型:CNI Plugin和IPAM(IP Address Management) Plugin。CNI Plugin負責為容器配置網絡資源,IPAM Plugin負責對容器 的IP地址進行分配和管理。IPAM Plugin作為CNI Plugin的一部分,與 CNI Plugin一起工作

2.CNI Plugin插件詳解
CNI Plugin包括3個基本接口的定義:添加(ADD)、刪除 (DELETE)、檢查(CHECK)和版本查詢(VERSION)。這些接口的具體實現要求插件提供一個可執行的程序,在容器網絡添加或刪除時 進行調用,以完成具體的操作
(1)添加:將容器添加到某個網絡。主要過程為在Container Runtime創建容器時,先創建好容器內的網絡命名空間(Network Namespace),然后調用CNI插件為該netns進行網絡配置,最后啟動容 器內的進程
......

3.IPAM Plugin插件詳解
  為了減輕CNI Plugin對IP地址管理的負擔,在CNI規范中設置了一個 新的插件專門用於管理容器的IP地址(還包括網關、路由等信息),被 稱為IPAM Plugin。通常由CNI Plugin在運行時自動調用IPAM Plugin完 成容器IP地址的分配
  IPAM Plugin負責為容器分配IP地址、網關、路由和DNS,典型的 實現包括host-local和dhcp。與CNI Plugin類似,IPAM插件也通過可執行 程序完成IP地址分配的具體操作。IPAM可執行程序也處理傳遞給CNI插 件的環境變量和通過標准輸入(stdin)傳入的網絡配置參數

在Kubernetes中使用網絡插件   

Kubernetes目前支持兩種網絡插件的實現
  ◎ CNI插件:根據CNI規范實現其接口,以與插件提供者進行對接
  ◎ kubenet插件:使用bridge和host-local CNI插件實現一個基本的 cbr0。
為了在Kubernetes集群中使用網絡插件,需要在kubelet服務的啟動 參數上設置下面兩個參數
  ◎ --network-plugin-dir:kubelet啟動時掃描網絡插件的目錄
  ◎ --network-plugin:網絡插件名稱,對於CNI插件,設置為cni即 可,無須關注--network-plugin-dir的路徑。對於kubenet插件,設置為 kubenet,目前僅實現了一個簡單的cbr0 Linux網橋
目前已有多個開源項目支持以CNI網絡插件的形式部署到 Kubernetes集群中,進行Pod的網絡設置和網絡策略的設置,包括 Calico、Canal、Cilium、Contiv、Flannel、Romana、Weave Net等

 
Kubernetes網絡策略

  為了實現細粒度的容器間網絡訪問隔離策略,Kubernetes從1.3版本 開始,由SIG-Network小組主導研發了Network Policy機制,目前已升級 為networking.k8s.io/v1穩定版本。Network Policy的主要功能是對Pod間 的網絡通信進行限制和准入控制,設置方式為將Pod的Label作為查詢條 件,設置允許訪問或禁止訪問的客戶端Pod列表。目前查詢條件可以作 用於Pod和Namespace級別
  為了使用Network Policy,Kubernetes引入了一個新的資源對象 NetworkPolicy,供用戶設置Pod間網絡訪問的策略。但僅定義一個網絡 策略是無法完成實際的網絡隔離的,還需要一個策略控制器(Policy Controller)進行策略的實現。策略控制器由第三方網絡組件提供,目前 Calico、Cilium、Kube-router、Romana、Weave Net等開源項目均支持網 絡策略的實現
  Network Policy的工作原理如圖7.19所示,policy controller需要實現 一個API Listener,監聽用戶設置的NetworkPolicy定義,並將網絡訪問規 則通過各Node的Agent進行實際設置(Agent則需要通過CNI網絡插件實 現)

 
 
網絡策略配置說明
   網絡策略的設置主要用於對目標Pod的網絡訪問進行限制,在默認 情況下對所有Pod都是允許訪問的,在設置了指向Pod的NetworkPolicy網 絡策略之后,到Pod的訪問才會被限制
 
apiversion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  PolicyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
    egress:
    - to:
      - ipBlock:
          cidr: 10.0.0.0/24
      ports:
      - protocol: TCP
        port: 5987

  主要的參數說明如下。 ◎ podSelector:用於定義該網絡策略作用的Pod范圍,本例的選 擇條件為包含“role=db”標簽的Pod。 ◎ policyTypes:網絡策略的類型,包括ingress和egress兩種,用 於設置目標Pod的入站和出站的網絡限制。 ◎ ingress:定義允許訪問目標Pod的入站白名單規則,滿足from條件的客戶端才能訪問ports定義的目標Pod端口號.- from:對符合條件的客戶端Pod進行網絡放行,規則包括基於客戶 端Pod的Label、基於客戶端Pod所在的Namespace的Label或者客戶端的IP 范圍。- ports:允許訪問的目標Pod監聽的端口號。 

◎ egress:定義目標Pod允許訪問的“出站”白名單規則,目標Pod 僅允許訪問滿足to條件的服務端IP范圍和ports定義的端口號
- to:允許訪問的服務端信息,可以基於服務端Pod的Label、基於服 務端Pod所在的Namespace的Label或者服務端IP范圍。
- ports:允許訪問的服務端的端口號。
通過本例的NetworkPolicy設置,對目標Pod的網絡訪問的效果如 下。
◎ 該網絡策略作用於Namespace“default”中含有“role=db”Label的 全部Pod。
◎ 允許與目標Pod在同一個Namespace中的包 含“role=frontend”Label的客戶端Pod訪問目標Pod。
◎ 允許屬於包含“project=myproject”Label的Namespace的客戶端 Pod訪問目標Pod。
◎ 允許從IP地址范圍“172.17.0.0/16”的客戶端Pod訪問目標Pod, 但是不包括IP地址范圍“172.17.1.0/24”的客戶端。
◎ 允許目標Pod訪問IP地址范圍“10.0.0.0/24”並監聽5978端口的服 務

 
 在Namespace級別設置默認的網絡策略
   默認禁止任何客戶端訪問該Namespace中的所有Pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyType:
  - Ingress
  默認允許任何客戶端訪問該Namespace中的所有Pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadate:
  name: allow-all
spec:
  podSelector: {}
  ingress:
  - {}
  policyType:
  - Ingress
  默認禁止該Namespace中的所有Pod訪問外部服務
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Egress
  默認允許該Namespace中的所有Pod訪問外部服務
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-allow
spec:
  podSelector: {}
  egress:
  - {}
  policyType:
  - Egress
  默認禁止任何客戶端訪問該Namespace中的所有Pod,同時禁止訪問 外部服務
  apiVersion: networking.k8s.io/ v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyType:
  - Ingress
  - Egress

 

Flannel

  Flannel之所以可以搭建Kubernetes依賴的底層網絡,是因為它能實 現以下兩點
  (1)它能協助Kubernetes,給每一個Node上的Docker容器都分配互 相不沖突的IP地址。
  (2)它能在這些IP地址之間建立一個覆蓋網絡(Overlay Network),通過這個覆蓋網絡,將數據包原封不動地傳遞到目標容器內

 
 

  flanneld進程並不簡單,它上連etcd,利用etcd來管理可分配的IP地 址段資源,同時監控etcd中每個Pod的實際地址,並在內存中建立了一 個Pod節點路由表;它下連docker0和物理網絡,使用內存中的Pod節點 路由表,將docker0發給它的數據包包裝起來,利用物理網絡的連接將 數據包投遞到目標flanneld上,從而完成Pod到Pod之間的直接地址通 信
  Flannel之間的底層通信協議的可選技術包括UDP、VxLan、AWS VPC等多種方式。通過源flanneld封包、目標flanneld解包,最終docker0 收到的就是原始的數據,對容器應用來說是透明的,感覺不到中間 Flannel的存在

   我們看一下Flannel是如何做到為不同Node上的Pod分配的IP不產生 沖突的。其實想到Flannel使用了集中的etcd存儲就很容易理解了。它每 次分配的地址段都在同一個公共區域獲取,這樣大家自然能夠互相協 調,不產生沖突了。而且在Flannel分配好地址段后,后面的事情是由 Docker完成的,Flannel通過修改Docker的啟動參數將分配給它的地址段 傳遞進去
--bip=10.244.1.5/24

  Flannel完美地實現了對Kubernetes網絡的支持,但是它引入了多個 網絡組件,在網絡通信時需要轉到flannel0網絡接口,再轉到用戶態的 flanneld程序,到對端后還需要走這個過程的反過程,所以也會引入一 些網絡的時延損耗
  另外,Flannel模型默認采用了UDP作為底層傳輸協議,UDP本身是 非可靠協議,雖然兩端的TCP實現了可靠傳輸,但在大流量、高並發的應用場景下還需要反復測試,確保沒有問題

 
Calico容器網絡和網絡策略實戰
  Calico是一個基於BGP的純三層的網絡方案,與OpenStack、 Kubernetes、AWS、GCE等雲平台都能夠良好地集成。Calico在每個計 算節點都利用Linux Kernel實現了一個高效的vRouter來負責數據轉發。 每個vRouter都通過BGP1協議把在本節點上運行的容器的路由信息向整 個Calico網絡廣播,並自動設置到達其他節點的路由轉發規則。Calico 保證所有容器之間的數據流量都是通過IP路由的方式完成互聯互通的。 Calico節點組網時可以直接利用數據中心的網絡結構(L2或者L3),不 需要額外的NAT、隧道或者Overlay Network,沒有額外的封包解包,能 夠節約CPU運算,提高網絡效率,如圖7.24所示
 
Calico在小規模集群中可以直接互聯,在大規模集群中可以通過額 外的BGP route reflector來完成,如圖7.25所示
此外,Calico基於iptables還提供了豐富的網絡策略,實現了 Kubernetes的Network Policy策略,提供容器間網絡可達性限制的功能

Calico的主要組件如下
  ◎ Felix:Calico Agent,運行在每個Node上,負責為容器設置網 絡資源(IP地址、路由規則、iptables規則等),保證跨主機容器網絡互 通。
  ◎ etcd:Calico使用的后端存儲。
  ◎ BGP Client:負責把Felix在各Node上設置的路由信息通過BGP 協議廣播到Calico網絡。
  ◎ Route Reflector:通過一個或者多個BGP Route Reflector來完成 大規模集群的分級路由分發
  ◎ CalicoCtl:Calico命令行管理工具。

                      
                               共享存儲原理
共享存儲機制概述

  PV是對底層網絡共享存儲的抽象,將共享存儲定義為一種“資源”, 比如Node也是一種容器應用可以“消費”的資源。PV由管理員創建和配 置,它與共享存儲的具體實現直接相關,例如GlusterFS、iSCSI、RBD 或GCE或AWS公有雲提供的共享存儲,通過插件式的機制完成與共享 存儲的對接,以供應用訪問和使用
  PVC則是用戶對存儲資源的一個“申請”。就像Pod“消費”Node的資 源一樣,PVC能夠“消費”PV資源。PVC可以申請特定的存儲空間和訪問 模式
,StorageClass和動態資源供應的機制得到了完 善,實現了存儲卷的按需創建,在共享存儲的自動化管理進程中實現了 重要的一步,通過StorageClass的定義,管理員可以將存儲資源定義為某種類別 (Class),正如存儲設備對於自身的配置描述(Profile),例如“快速 存儲”“慢速存儲”“有數據冗余”“無數據冗余”等。用戶根據StorageClass 的描述就能夠直觀地得知各種存儲資源的特性,就可以根據應用對存儲 資源的需求去申請存儲資源了

   Kubernetes從1.9版本開始引入容器存儲接口Container Storage Interface(CSI)機制,目標是在Kubernetes和外部存儲系統之間建立一 套標准的存儲管理接口,通過該接口為容器提供存儲服務,類似於 CRI(容器運行時接口)和CNI(容器網絡接口)
 
PV詳解
  PV作為存儲資源,主要包括存儲能力、訪問模式、存儲類型、回 收策略、后端存儲類型等關鍵信息的設置  
   下面的例子聲明的PV具有 如下屬性:5GiB存儲空間,訪問模式為ReadWriteOnce,存儲類型為 slow(要求在系統中已存在名為slow的StorageClass),回收策略為 Recycle,並且后端存儲類型為nfs(設置了NFS Server的IP地址和路 徑)
 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  nfs:
    path: /tmp
    server: 172.17.0.2
 
PV的關鍵配置參數
 

1.存儲能力(Capacity)
  描述存儲設備具備的能力,目前僅支持對存儲空間的設置 (storage=xx),未來可能加入IOPS、吞吐率等指標的設置
2.存儲卷模式(Volume Mode)
  Kubernetes從1.13版本開始引入存儲卷類型的設置 (volumeMode=xxx),可選項包括Filesystem(文件系統)和Block(塊 設備),默認值為Filesystem
目前有以下PV類型支持塊設備類型:◎ AWSElasticBlockStore ◎ AzureDisk ◎ FC ◎ GCEPersistentDisk ◎ iSCSI ◎ Local volume ◎ RBD(Ceph Block Device) ◎ VsphereVolume(alpha)

 
apiVersion: v1
kind: PersestentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  volumeMode: Block
  fc:
    targetWWNs: ["50060e801049cdf1"]
    lun: 0
    readOnly: false

3.訪問模式(Access Modes) 對PV進行訪問模式的設置,用於描述用戶的應用對存儲資源的訪 問權限。訪問模式如下
  ◎ ReadWriteOnce(RWO):讀寫權限,並且只能被單個Node掛 載。◎ ReadOnlyMany(ROX):只讀權限,允許被多個Node掛載。 ◎ ReadWriteMany(RWX):讀寫權限,允許被多個Node掛載。
某些PV可能支持多種訪問模式,但PV在掛載時只能使用一種訪問 模式,多種訪問模式不能同時生效

  表8.1 不同的存儲提供者支持的訪問模式

4.存儲類別(Class)

PV可以設定其存儲的類別,通過storageClassName參數指定一個 StorageClass資源對象的名稱。具有特定類別的PV只能與請求了該類別 的PVC進行綁定。未設定類別的PV則只能與不請求任何類別的PVC進行綁定
5.回收策略(Reclaim Policy)
  通過PV定義中的persistentVolumeReclaimPolicy字段進行設置,可 選項如下。◎ 保留:保留數據,需要手工處理。 ◎ 回收空間:簡單清除文件的操作(例如執行rm -rf /thevolume/* 命令)。◎ 刪除:與PV相連的后端存儲完成Volume的刪除操作(如AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等設備的內部Volume清 理)。目前,只有NFS和HostPath兩種類型的存儲支持Recycle策略;AWS EBS、GCE PD、Azure Disk和Cinder volumes支持Delete策略
6.掛載參數(Mount Options)
  在將PV掛載到一個Node上時,根據后端存儲的特點,可能需要設 置額外的掛載參數,可以根據PV定義中的mountOptions字段進行設置

apiVersion: v1
kind: PersistentVolume
metadata:
  name: gce-disk-l
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  mountOptions:
    - hard
    - nolock
    - nfservers=3
  gcePersistentDisk:
    fsType: ext4
    pdName: gce-disk-l

目前,以下PV類型支持設置掛載參數:
  ◎ AWSElasticBlockStore ◎ AzureDisk ◎ AzureFile ◎ CephFS ◎ Cinder (OpenStack block storage) ◎ GCEPersistentDisk ◎ Glusterfs ◎ NFS ◎ Quobyte Volumes ◎ RBD (Ceph Block Device) ◎ StorageOS◎ VsphereVolume ◎ iSCSI
7.節點親和性(Node Affinity)
  PV可以設置節點親和性來限制只能通過某些Node訪問Volume,可 以在PV定義中的nodeAffinity字段進行設置。使用這些Volume的Pod將 被調度到滿足條件的Node上

 

PV生命周期的各個階段

   某個PV在生命周期中可能處於以下4個階段(Phaes)之一。 ◎ Available:可用狀態,還未與某個PVC綁定。 ◎ Bound:已與某個PVC綁定。 ◎ Released:綁定的PVC已經刪除,資源已釋放,但沒有被集群 回收。◎ Failed:自動資源回收失敗。

PVC詳解

   PVC作為用戶對存儲資源的需求申請,主要包括存儲空間請求、訪 問模式、PV選擇條件和存儲類別等信息的設置。下例聲明的PVC具有 如下屬性:申請8GiB存儲空間,訪問模式為ReadWriteOnce,PV 選擇條 件為包含標簽“release=stable”並且包含條件為“environment In [dev]”的 標簽,存儲類別為“slow”(要求在系統中已存在名為slow的 StorageClass)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    request:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment,operator: In,values: [dev]}

PVC的關鍵配置參數說明如下。
  ◎ 資源請求(Resources):描述對存儲資源的請求,目前僅支持 request.storage的設置,即存儲空間大小。
  ◎ 訪問模式(Access Modes):PVC也可以設置訪問模式,用於描述用戶應用對存儲資源的訪問權限。其三種訪問模式的設置與PV的 設置相同
  ◎ 存儲卷模式(Volume Modes):PVC也可以設置存儲卷模式, 用於描述希望使用的PV存儲卷模式,包括文件系統和塊設備
  ◎ PV選擇條件(Selector):通過對Label Selector的設置,可使 PVC對於系統中已存在的各種PV進行篩選。系統將根據標簽選出合適 的PV與該PVC進行綁定。選擇條件可以使用matchLabels和              matchExpressions進行設置,如果兩個字段都設置了,則Selector的邏輯 將是兩組條件同時滿足才能完成匹配

 

◎ 存儲類別(Class):PVC 在定義時可以設定需要的后端存儲 的類別(通過storageClassName字段指定),以減少對后端存儲特性的 詳細信息的依賴。只有設置了該Class的PV才能被系統選出,並與該 PVC進行綁定
  PVC也可以不設置Class需求。如果storageClassName字段的值被設 置為空(storageClassName=""),則表示該PVC不要求特定的Class,系 統將只選擇未設定Class的PV與之匹配和綁定。PVC也可以完全不設置 storageClassName字段,此時將根據系統是否啟用了名為 DefaultStorageClass的admission controller進行相應的操作
  ◎ 未啟用DefaultStorageClass:等效於PVC設置storageClassName 的值為空(storageClassName=""),即只能選擇未設定Class的PV與之 匹配和綁定
  ◎ 啟用DefaultStorageClass:要求集群管理員已定義默認的 StorageClass。如果在系統中不存在默認的StorageClass,則等效於不啟 用DefaultStorageClass的情況。如果存在默認的StorageClass,則系統將 自動為PVC創建一個PV(使用默認StorageClass的后端存儲),並將它 們進行綁定。集群管理員設置默認StorageClass的方法為,在StorageClass的定義中加上一個annotation“storageclass.kubernetes.io/is- default-class= true”。如果管理員將多個StorageClass都定義為default,則 由於不唯一,系統將無法為PVC創建相應的PV

PV和PVC的生命周期
  我們可以將PV看作可用的存儲資源,PVC則是對存儲資源的需 求,PV和PVC的相互關系遵循如圖8.1所示的生命周期

 
資源供應
   Kubernetes支持兩種資源的供應模式:靜態模式(Static)和動態模 式(Dynamic)。資源供應的結果就是創建好的PV

    ◎ 靜態模式:集群管理員手工創建許多PV,在定義PV時需要將 后端存儲的特性進行設置
    ◎ 動態模式:集群管理員無須手工創建PV,而是通過 StorageClass的設置對后端存儲進行描述,標記為某種類型。此時要求 PVC對存儲的類型進行聲明,系統將自動完成PV的創建及與PVC的綁 定。PVC可以聲明Class為"",說明該PVC禁止使用動態模式

資源綁定
  在用戶定義好PVC之后,系統將根據PVC對存儲資源的請求(存儲 空間和訪問模式)在已存在的PV中選擇一個滿足PVC要求的PV,一旦 找到,就將該PV與用戶定義的PVC進行綁定,用戶的應用就可以使用 這個PVC了。如果在系統中沒有滿足PVC要求的PV,PVC則會無限期處 於Pending狀態,直到等到系統管理員創建了一個符合其要求的PV。PV 一旦綁定到某個PVC上,就會被這個PVC獨占,不能再與其他PVC進行 綁定了。在這種情況下,當PVC申請的存儲空間比PV的少時,整個PV 的空間就都能夠為PVC所用,可能會造成資源的浪費。如果資源供應使 用的是動態模式,則系統在為PVC找到合適的StorageClass后,將自動創 建一個PV並完成與PVC的綁定
資源使用
  Pod使用Volume的定義,將PVC掛載到容器內的某個路徑進行使 用。Volume的類型為persistentVolumeClaim,在后面的示例中再進行詳 細說明。在容器應用掛載了一個PVC后,就能被持續獨占使用。不過, 多個Pod可以掛載同一個PVC,應用程序需要考慮多個實例共同訪問一 塊存儲空間的問題
資源釋放
  當用戶對存儲資源使用完畢后,用戶可以刪除PVC,與該PVC綁定 的PV將會被標記為“已釋放”,但還不能立刻與其他PVC進行綁定。通過 之前PVC寫入的數據可能還被留在存儲設備上,只有在清除之后該PV 才能再次使用
資源回收
  對於PV,管理員可以設定回收策略,用於設置與之綁定的PVC釋 放資源之后如何處理遺留數據的問題。只有PV的存儲空間完成回收, 才能供新的PVC綁定和使用。回收策略詳見下節的說明
下面通過兩張圖分別對在靜態資源供應模式和動態資源供應模式 下,PV、PVC、StorageClass及Pod使用PVC的原理進行說明
圖8.2描述了在靜態資源供應模式下,通過PV和PVC完成綁定,並 供Pod使用的存儲管理機制

圖8.3描述了在動態資源供應模式下,通過StorageClass和PVC完成 資源動態綁定(系統自動生成PV),並供Pod使用的存儲管理機制

 

StorageClass詳解
  StorageClass作為對存儲資源的抽象定義,對用戶設置的PVC申請屏 蔽后端存儲的細節,一方面減少了用戶對於存儲資源細節的關注,另一 方面減輕了管理員手工管理PV的工作,由系統自動完成PV的創建和綁 定,實現了動態的資源供應。基於StorageClass的動態資源供應模式將 逐步成為雲平台的標准存儲配置模式

CSI存儲機制詳解
   Kubernetes從1.9版本開始引入容器存儲接口Container Storage Interface(CSI)機制,用於在Kubernetes和外部存儲系統之間建立一套 標准的存儲管理接口,通過該接口為容器提供存儲服務。CSI到 Kubernetes 1.10版本升級為Beta版,到Kubernetes 1.13版本升級為GA 版,已逐漸成熟
 
CSI的設計背景
                       
                             第10章  Kubernetes集群管理
 
Namespace:集群環境共享與隔離
   在一個組織內部,不同的工作組可以在同一個Kubernetes集群中工 作,Kubernetes通過命名空間和Context的設置對不同的工作組進行區 分,使得它們既可以共享同一個Kubernetes集群的服務,也能夠互不干 擾,如圖10.2所示
 
定義Context(運行環境)

  接下來,需要為這兩個工作組分別定義一個 Context,即運行環 境。這個運行環境將屬於某個特定的命名空間
  通過kubectl config set-context命令定義Context,並將Context置於之 前創建的命名空間中

  使用kubectl config view命令查看已定義的Context
  注意,通過kubectl config命令在${HOME}/.kube目錄下生成了一個 名為config的文件,文件的內容為以kubectl config view命令查看到的內 容。所以,也可以通過手工編輯該文件的方式來設置Context
 
設置工作組在特定Context環境下工作

使用kubectl config use-context <context_name>命令設置當前運行環 境。
下面的命令將把當前運行環境設置為ctx-dev

 
kubectl config use-context ctx-dev
Kubernetes資源管理
   CPU Request與Memory Request。 在大多數情況下,我們在定義Pod時並沒有定義這兩個參數,此時 Kubernetes會認為該Pod所需的資源很少,並可以將其調度到任何可用的 Node上。這樣一來,當集群中的計算資源不很充足時,如果集群中的 Pod負載突然加大,就會使某個Node的資源嚴重不足

為了避免系統掛掉,該Node會選擇“清理”某些Pod來釋放資源,此 時每個Pod都可能成為犧牲品。但有些Pod擔負着更重要的職責,比其他 Pod更重要,比如與數據存儲相關的、與登錄相關的、與查詢余額相關 的,即使系統資源嚴重不足,也需要保障這些Pod的存活,Kubernetes中 該保障機制的核心如下
  ◎ 通過資源限額來確保不同的Pod只能占用指定的資源
  ◎ 允許集群的資源被超額分配,以提高集群的資源利用率。
  ◎ 為Pod划分等級,確保不同等級的Pod有不同的服務質量 (QoS),資源不足時,低等級的Pod會被清理,以確保高等級的Pod穩 定運行。

計算資源管理

  1.詳解Requests和Limits參數.以CPU為例,圖10.3顯示了未設置Limits和設置了Requests、Limits 的CPU使用率的區別

 

   盡管Requests和Limits只能被設置到容器上,但是設置Pod級別的 Requests和Limits能大大提高管理Pod的便利性和靈活性,因此在 Kubernetes中提供了對Pod級別的Requests和Limits的配置對於CPU和 內存而言,Pod的Requests或Limits是指該Pod中所有容器的Requests或 Limits的總和(對於Pod中沒有設置Requests或Limits的容器,該項的值 被當作0或者按照集群配置的默認值來計算)。下面對CPU和內存這兩 種計算資源的特點進行說明

  

基於Requests和Limits的Pod調度機制
  當一個Pod創建成功時,Kubernetes調度器(Scheduler)會為該Pod 選擇一個節點來執行。對於每種計算資源(CPU和Memory)而言,每 個節點都有一個能用於運行Pod的最大容量值。調度器在調度時,首先 要確保調度后該節點上所有Pod的CPU和內存的Requests總和,不超過該 節點能提供給Pod使用的CPU和Memory的最大容量值
  這里需要注意:可能某節點上的實際資源使用量非常低,但是已運 行Pod配置的Requests值的總和非常高,再加上需要調度的Pod的 Requests值,會超過該節點提供給Pod的資源容量上限,這時Kubernetes 仍然不會將Pod調度到該節點上。如果Kubernetes將Pod調度到該節點 上,之后該節點上運行的Pod又面臨服務峰值等情況,就可能導致Pod資 源短缺。

Requests和Limits的背后機制

kubelet在啟動Pod的某個容器時,會將容器的Requests和Limits值轉化為相應的容器啟動參數傳遞給容器執行器(Docker或者rkt)
如果容器的執行環境是Docker,那么容器的如下4個參數是這樣傳 遞給Docker的
  1)spec.container[].resources.requests.cpu
這個參數會轉化為core數(比如配置的100m會轉化為0.1),然后 乘以1024,再將這個結果作為--cpu-shares參數的值傳遞給docker run命 令。在docker run命令中,--cpu-share參數是一個相對權重值(Relative Weight),這個相對權重值會決定Docker在資源競爭時分配給容器的資 源比例
  這里需要區分清楚的是:這個參數對於Kubernetes而言是絕對值, 主要用於Kubernetes調度和管理;同時Kubernetes會將這個參數的值傳遞 給docker run的--cpu-shares參數。--cpu-shares參數對於Docker而言是相對 值,主要用於資源分配比例 

2)spec.container[].resources.limits.cpu
  這個參數會轉化為millicore數(比如配置的1被轉化為1000,而配置 的100m被轉化為100),將此值乘以100000,再除以1000,然后將結果 值作為--cpu-quota參數的值傳遞給docker run命令。docker run命令中另 外一個參數--cpu-period默認被設置為100000,表示Docker重新計量和分 配CPU的使用時間間隔為100000μs(100ms)
Docker的--cpu-quota參數和--cpu-period參數一起配合完成對容器 CPU的使用限制:比如Kubernetes中配置容器的CPU Limits為0.1,那么 計算后--cpu-quota為10000,而--cpu-period為100000,這意味着Docker在100ms內最多給該容器分配10ms×core的計算資源用量,10/100=0.1 core 的結果與Kubernetes配置的意義是一致的

  注意:如果kubelet的啟動參數--cpu-cfs-quota被設置為true,那么 kubelet會強制要求所有Pod都必須配置CPU Limits(如果Pod沒有配置, 則集群提供了默認配置也可以)。從Kubernetes 1.2版本開始,這個-- cpu-cfs-quota啟動參數的默認值就是true

   

計算資源相關常見問題分析
(1)Pod狀態為Pending,錯誤信息為FailedScheduling。如果Kubernetes調度器在集群中找不到合適的節點來運行Pod,那么這個Pod 會一直處於未調度狀態,直到調度器找到合適的節點為止。每次調度器 嘗試調度失敗時,Kubernetes都會產生一個事件
對大內存頁(Huge Page)資源的支持
  在現代操作系統中,內存是以Page(頁,有時也可以稱之為 Block)為單位進行管理的,而不以字節為單位,包括內存的分配和回 收都基於Page。典型的Page大小為4KB,因此用戶進程申請1MB內存就需要操作系統分配256個Page,而1GB內存對應26萬多個Page!
  為了實現快速內存尋址,CPU內部以硬件方式實現了一個高性能的 內存地址映射的緩存表——TLB(Translation Lookaside Buffer),用來 保存邏輯內存地址與物理內存的對應關系。若目標地址的內存頁物理地 址不在TLB的緩存中或者TLB中的緩存記錄失效,CPU就需要切換到低 速的、以軟件方式實現的內存地址映射表進行內存尋址,這將大大降低 CPU的運算速度。針對緩存條目有限的TLB緩存表,提高TLB效率的最 佳辦法就是將內存頁增大,這樣一來,一個進程所需的內存頁數量會相 應減少很多。如果把內存頁從默認的4KB改為2MB,那么1GB內存就只 對應512個內存頁了,TLB的緩存命中率會大大增加。這是不是意味着 我們可以任意指定內存頁的大小,比如1314MB的內存頁?答案是否定 的,因為這是由CPU來決定的,比如常見的Intel X86處理器可以支持的 大內存頁通常是2MB,個別型號的高端處理器則支持1GB的大內存頁

  在Linux平台下,對於那些需要大量內存(1GB以上內存)的程序 來說,大內存頁的優勢是很明顯的,因為Huge Page大大提升了TLB的 緩存命中率,又因為Linux對Huge Page提供了更為簡單、便捷的操作接 口,所以可以把它當作文件來進行讀寫操作。Linux使用Huge Page文件 系統hugetlbfs支持巨頁,這種方式更為靈活,我們可以設置Huge Page的 大小,比如1GB、2GB甚至2.5GB,然后設置有多少物理內存用於分配 Huge Page,這樣就設置了一些預先分配好的Huge Page。可以將 hugetlbfs文件系統掛載在/mnt/huge目錄下,通過執行下面的指令完成設 置

mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge

  在設置完成后,用戶進程就可以使用mmap映射Huge Page目標文件 來使用大內存頁了,Intel DPDK便采用了這種做法,測試表明應用使用 大內存頁比使用4KB的內存頁性能提高了10%~15%

  Kubernetes 1.14版本對Linux Huge Page的支持正式更新為GA穩定 版。我們可以將Huge Page理解為一種特殊的計算資源:擁有大內存頁 的資源。而擁有Huge Page資源的Node也與擁有GPU資源的Node一樣, 屬於一種新的可調度資源節點(Schedulable Resource Node)
Huge Page也支持ResourceQuota來實現配額限制,類似CPU或者 Memory,但不同於CPU或者內存,Huge Page資源屬於不可超限使用的 資源,擁有Huge Page能力的Node會將自身支持的Huge Page的能力信息 自動上報給Kubernetes Master

  為此,Kubernetes引入了一個新的資源類型hugepages-<size>,來表 示大內存頁這種特殊的資源,比如hugepages-2Mi表示2MiB規格的大內 存頁資源。一個能提供2MiB規格Huge Page的Node,會上報自己擁有 Hugepages-2Mi的大內存頁資源屬性,供需要這種規格的大內存頁資源 的Pod使用,而需要Huge Page資源的Pod只要給出相關的Huge Page的聲 明,就可以被正確調度到匹配的目標Node上了。相關例子如下

 
apiVersion: v1
kind: Pod
metadata:
  generateName: hugepages-volume-
spec:
  containers:
  - image: fedora:latest
    command:
    - sleep:
    - inf
    name: example
    volumeMounts:
    - mountPath: /hugepages
      name: hugepage
    resource:
      limits:
        hugepages-2Mi: 100Mi
        memory: 100Mi
      request:
        memory:100Mi
volumes:
- name: hugepage
  emptyDir:
    medium: HugePages
    
資源配置范圍管理(LimitRange)
    默認情況下,Kubernetes不會對Pod加上CPU和內存限制,這意味 着Kubernetes系統中任何Pod都可以使用其所在節點的所有可用的CPU和 內存。通過配置Pod的計算資源Requests和Limits,我們可以限制Pod的 資源使用,但對於Kubernetes集群管理員而言,配置每一個Pod的 Requests和Limits是煩瑣的,而且很受限制。更多時候,我們需要對集 群內Requests和Limits的配置做一個全局限制

Kubernetes提供了LimitRange機制對Pod和容器的 Requests和Limits配置進一步做出限制。在下面的示例中,將說明如何 將LimitsRange應用到一個Kubernetes的命名空間中,然后說明 LimitRange的幾種限制方式,比如最大及最小范圍、Requests和Limits的 默認值、Limits與Requests的最大比例上限等

為Namespace設置LimitRange 
 
apiVersion: v1
kind: LimitRange
metadata:
  name: mylimits
spec:
  limits:
  - max:
      cpu: "4"
      memory: 2Gi
    min:
      cpu: 200m
      memory: 6Mi
    maxLimitRequestRatio:
      cpu: 3
      memory: 2
    type: Pod
  - default:
      cpu: 300m
      memory: 200Mi
    defaultRequest:
      cpu: 200m
      memory: 100Mi
    max:
      cpu: "2"
      memory: 1Gi
    min:
      cpu: 100m
      memory: 3Mi
    maxLimitRequestRatio:
      cpu: 5
      memory: 4
    type: Container

 

 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM