k8s-有状态应用编排
第二十二章、有狀態應用編排-StatefulSet
22.1、需求背景
Deployment:管理所有同版本的Pod都是一摸一樣的副本1.定義一組Pod的期望數量,controller維持pod數量與期望數量一致2.配置Pod發布方式,cotroller會按照給定策略更新pod,保證更新過程中不可用的pod數量在限定范圍內3.如果發布有問題,支持“一鍵”回滾需求分析:Deployment可以滿足嗎?1.Pod之間并非相同的副本,每個Pod有一個獨立標識2.Pod獨立標識要能夠對應到一個固定的網絡標識(ip or hostname),并在發布后能持續保持3.每個Pod有一塊獨立的存儲盤,并在發布升級后還能繼續掛載原有的盤(保留數據)4.應用發布時,按照固定順序升級PodStatefulSet:主要面向有狀態應用管理的控制器(當然也可以使用statefulset管理有狀態應用)
1.每個Pod有Order序號,會按照序號創建、刪除、更新Pod
2.通過配置headless service,使每個Pod有一個唯一的網絡標識(hostname)
3.通過配置pvc template,每個pod有一塊獨享的pv存儲盤
4.支持一定數量的灰度發布
22.2、用例解讀
# 1、準備存儲 [root@master1 nfs-share]# showmount -e master1 Export list for master1: /nfs-share * [root@master1 nfs-share]# showmount -e master2 Export list for master2: /nfs-share * [root@master1 nfs-share]# showmount -e master3 Export list for master3: /nfs-share * [root@master1 sts]# cat pv.yaml apiVersion: v1 kind: PersistentVolume metadata:name: nfs-pv1 spec:capacity:storage: 5GivolumeMode: FilesystemaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: Recyclenfs:path: /nfs-shareserver: 192.168.153.132 --- apiVersion: v1 kind: PersistentVolume metadata:name: nfs-pv2 spec:capacity:storage: 5GivolumeMode: FilesystemaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: Recyclenfs:path: /nfs-shareserver: 192.168.153.133 --- apiVersion: v1 kind: PersistentVolume metadata:name: nfs-pv3 spec:capacity:storage: 5GivolumeMode: FilesystemaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: Recyclenfs:path: /nfs-shareserver: 192.168.153.134 --- [root@master1 sts]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-pv1 5Gi RWO Recycle Available 6s nfs-pv2 5Gi RWO Recycle Available 6s nfs-pv3 5Gi RWO Recycle Available 6s [root@master1 sts]##2、創建StatefulSet [root@master1 sts]# cat sts.yaml apiVersion: v1 kind: Service metadata:name: nginxlabels:app: nginx spec:ports:- port: 80clusterIP: Noneselector:app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata:name: nginx-web spec:selector:matchLabels:app: nginxserviceName: "nginx" #sts必須要有對應的servicereplicas: 3template:metadata:labels:app: nginxspec:containers:- name: nginximage: reg.mt.com:5000/nginx:v1ports:- containerPort: 80name: webvolumeMounts:- name: www-storagemountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: www-storagespec:accessModes: [ "ReadWriteOnce" ]resources:requests:storage: 2Gi [root@master1 sts]# kubectl apply -f sts.yaml [root@master1 sts]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-storage-nginx-web-0 Bound nfs-pv1 5Gi RWO 2m50s www-storage-nginx-web-1 Bound nfs-pv2 5Gi RWO 2m38s www-storage-nginx-web-2 Bound nfs-pv3 5Gi RWO 2m2s [root@master1 sts]# kubectl get svc/nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx ClusterIP None <none> 80/TCP 3m3s [root@master1 sts]# kubectl get endpoints nginx NAME ENDPOINTS AGE nginx 172.7.26.2:80,172.7.26.3:80,172.7.67.2:80 3m20s [root@master1 sts]# kubectl get pods NAME READY STATUS RESTARTS AGE nginx-web-0 1/1 Running 0 3m35s #創建順序倉0->1->2 nginx-web-1 1/1 Running 0 3m23s nginx-web-2 1/1 Running 0 2m47s[root@master1 sts]# kubectl get sts/nginx-web -o yaml ... status:collisionCount: 0currentReplicas: 3currentRevision: nginx-web-749477f8fdobservedGeneration: 1readyReplicas: 3replicas: 3updateRevision: nginx-web-749477f8fdupdatedReplicas: 3 [root@master1 sts]# dig -t A nginx.default.svc.cluster.local ... ;; ANSWER SECTION: nginx.default.svc.cluster.local. 5 IN A 172.7.26.3 nginx.default.svc.cluster.local. 5 IN A 172.7.26.2 nginx.default.svc.cluster.local. 5 IN A 172.7.67.2# 3、更新鏡像 [root@master1 sts]# kubectl set image sts nginx-web nginx=reg.mt.com:5000/nginx:v2 statefulset.apps/nginx-web image updated [root@master1 sts]# kubectl get pods //從2->1->0 進行更新 NAME READY STATUS RESTARTS AGE nginx-web-0 1/1 Running 0 22s nginx-web-1 1/1 Running 0 26s nginx-web-2 1/1 Running 0 30s[root@master1 sts]# kubectl get sts/nginx-web -o yaml status:collisionCount: 0currentReplicas: 3currentRevision: nginx-web-85846c6f77 #已經變化observedGeneration: 2readyReplicas: 3replicas: 3updateRevision: nginx-web-85846c6f77updatedReplicas: 3 [root@master1 sts]# kubectl get pvc #沒有變 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-storage-nginx-web-0 Bound nfs-pv1 5Gi RWO 13m www-storage-nginx-web-1 Bound nfs-pv2 5Gi RWO 12m www-storage-nginx-web-2 Bound nfs-pv3 5Gi RWO 12m[root@master1 sts]# kubectl get pods -o wide #ip有一個變化了 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-web-0 1/1 Running 0 5m30s 172.7.67.2 master3 <none> <none> nginx-web-1 1/1 Running 0 5m34s 172.7.26.3 master1 <none> <none> nginx-web-2 1/1 Running 0 5m38s 172.7.67.3 master3 <none> <none>不同于Deployment使用ReplicaSet來管理版本和維持副本數,StatefulSet controller直接管理下屬的Pod。而Pod中用一個Label來標識版本:controller-revision-hash
22.3、架構設計
1、管理模式
當前版本的StatefulSet指揮在ControllerRevision和pod中添加OwnnerReference,并不會在PVC中添加。擁有OwnerReference的資源,在刪除的時候會級聯刪除資源。即默認情況下刪除statefulset則ControllerRevision和Pod都會被刪除,但是PVC不會被刪除
[root@master1 sts]# kubectl get controllerrevisions #statefuleset使用controllerrevisions 管理pod NAME CONTROLLER REVISION AGE nginx-web-749477f8fd statefulset.apps/nginx-web 1 17m nginx-web-85846c6f77 statefulset.apps/nginx-web 2 8m11s [root@master1 sts]# kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS nginx-web-0 1/1 Running 0 131m app=nginx,controller-revision-hash=nginx-web-85846c6f77,statefulset.kubernetes.io/pod-name=nginx-web-0 nginx-web-1 1/1 Running 0 131m app=nginx,controller-revision-hash=nginx-web-85846c6f77,statefulset.kubernetes.io/pod-name=nginx-web-1 nginx-web-2 1/1 Running 0 131m app=nginx,controller-revision-hash=nginx-web-85846c6f77,statefulset.kubernetes.io/pod-name=nginx-web-22、StatefuleSet控制器
- 首先通過注冊 Informer 的 Event Handler(事件處理),來處理 StatefulSet 和 Pod 的變化。在 Controller 邏輯中,每一次收到 StatefulSet 或者是 Pod 的變化,都會找到對應的 StatefulSet 放到隊列。緊接著從隊列取出來處理后,先做的操作是 Update Revision,也就是先查看當前拿到的 StatefulSet 中的 template,有沒有對應的 ControllerRevision。如果沒有,說明 template 已經更新過,Controller 就會創建一個新版本的 Revision,也就有了一個新的 ControllerRevision hash 版本號。
- 然后 Controller 會把所有版本號拿出來,并且按照序號整理一遍。這個整理的過程中,如果發現有缺少的 Pod,它就會按照序號去創建,如果發現有多余的 Pod,就會按照序號去刪除。當保證了 Pod 數量和 Pod 序號滿足 Replica 數量之后,Controller 會去查看是否需要更新 Pod。也就是說這兩步的區別在于,Manger pods in order 去查看所有的 Pod 是否滿足序號;而后者 Update in order 查看 Pod 期望的版本是否符合要求,并且通過序號來更新。
- Update in order 其更新過程如上圖所示,其實這個過程比較簡單,就是刪除 Pod。刪除 Pod 之后,其實是在下一次觸發事件,Controller 拿到這個 success 之后會發現缺少 Pod,然后再從前一個步驟 Manger pod in order 中把新的 Pod 創建出來。在這之后 Controller 會做一次 Update status,也就是之前通過命令行看到的 status 信息。
3、擴容模擬
StatefulSet下的pod,從序號0開始創建(從一個Pod擴容到3個Pod,默認是先創建Pod1,Pod1 Ready后,才會創建Pod2)。因此replicas=N的StatefulSet,創建出的Pod序號為[0,N)
4、擴縮容管理策略
5、發布模擬
6、spec字段解析
- Replicas: 期望數量
- Selector:選擇器,必須匹配.spec.template.metadata.labels
- Template: pod模板
- VolumeClaimTemplate: PVC模板列表
- ServiceName: 對應的Headless Service的名字,用于給Pod生成唯一的網絡標識
- PodManagementPolicy: Pod管理策略
- OrderedReady:順序執行
- Parallel:并行
- UpdateStrategy: Pod升級策略
- RollingUpdate: 滾動升級
- OnDelete: 禁止主動升級
- RevisionHisotryLimit: 保留歷史ControllerRevision的數量限制(默認為10)
- Partition: 滾動升級時,保留舊版本Pod的數量,假設replicas=N,partition=M(M<=N),則最終舊版本Pod為[0,M) 新版本Pod為[M,N)
第二十三章、Kubernetes API編程范式
23.1、需求背景
Kubernetes Custom resources definition(CRD) 背景問題:
- 用戶自定義資源需求比較多
- 希望kubernetes提供聚合各個子資源的功能
- Kubernetes原生資源無法滿足需求
- Kubernetes APIServer擴展比較復雜
CRD 介紹:
- 在Kubernetes 1.7 版本被引入
- 可以根據自己的需求添加自定義Kubernetes對象資源
- 自定義資源與Kubernetes原生內置資源一樣共用kubectl CLI,安全,RBAC等功能
- 用戶同時可以開發自定義控制器感知或者操作自定義資源的變化
23.2、用例解讀
1、基礎用法
apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata:# 名稱必須與下面的spec字段匹配,格式為: <plural>.<group>name: crontabs.stable.example.com spec:# 用于REST API的組名稱: /apis/<group>/<version>group: stable.example.com# 此CustomResourceDefinition支持的版本列表versions:- name: v1# 每個版本都可以通過服務標志啟用/禁用。served: true# 必須將一個且只有一個版本標記為存儲版本。storage: true# 指定crd資源作用范圍在命名空間或集群scope: Namespacednames:# URL中使用的復數名稱: /apis/<group>/<version>/<plural>plural: crontabs# 在CLI(shell界面輸入的參數)上用作別名并用于顯示的單數名稱singular: crontab# kind字段使用駝峰命名規則. 資源清單使用如此kind: CronTab# 短名稱允許短字符串匹配CLI上的資源,意識就是能通過kubectl 在查看資源的時候使用該資源的簡名稱來獲取。shortNames:- ct --- apiVersion: "stable.example.com/v1" kind: CronTab metadata:name: my-new-cron-object spec: #因為crd定義的spec沒有定義,這里可以隨便定義,但是格式為key:valuecronSpec: "* * * * */5"image: my-awesome-cron-image[root@master1 crd]# kubectl get crontab/my-new-cron-object -o yaml apiVersion: stable.example.com/v1 kind: CronTab metadata:annotations:kubectl.kubernetes.io/last-applied-configuration: |{"apiVersion":"stable.example.com/v1","kind":"CronTab","metadata":{"annotations":{},"name":"my-new-cron-object","namespace":"default"},"spec":{"cronSpec":"* * * * */5","image":"my-awesome-cron-image"}}creationTimestamp: "2021-03-15T12:58:47Z"generation: 1name: my-new-cron-objectnamespace: defaultresourceVersion: "843195"selfLink: /apis/stable.example.com/v1/namespaces/default/crontabs/my-new-cron-objectuid: 1b839c01-4c34-4ed7-8b31-81879a192b47 spec:cronSpec: '* * * * */5'image: my-awesome-cron-image [root@master1 crd]# kubectl get ct NAME AGE my-new-cron-object 9m34s [root@master1 crd]# kubectl get crontab NAME AGE my-new-cron-object 9m53s2、示例2說明-CRD字段校驗&增加狀態列
對CRD的內容部分限制。使用OpenAPI v3 模式來驗證我們自定義的資源對象: [root@master1 crd]# cat crd.yaml apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata:name: crontabs.stable.example.com spec:group: stable.example.comversions:- name: v1served: truestorage: truescope: Namespacednames:plural: crontabssingular: crontabkind: CronTabshortNames:- ctvalidation:openAPIV3Schema:properties:spec:properties:cronSpec: #--必須是字符串,并且必須是正則表達式所描述的形式type: stringpattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'replicas: #----必須是整數,最小值必須為1,最大值必須為10type: integerminimum: 1maximum: 10additionalPrinterColumns: #限制狀態列- name: Spectype: stringdescription: The cron spec defining the interval a CronJob is runJSONPath: .spec.cronSpec- name: Replicastype: integerdescription: The number of jobs launched by the CronJobJSONPath: .spec.replicas- name: Agetype: dateJSONPath: .metadata.creationTimestamp [root@master1 crd]# cat my-crontabl.yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata:name: my-new-cron-object spec:cronSpec: "* * * * */5"image: my-awesome-cron-imagereplicas: 5 [root@master1 crd]# kubectl get crontab NAME SPEC REPLICAS AGE my-new-cron-object * * * * */5 5 2m1s3、添加狀態和自動伸縮
[root@master1 crd]# cat crd.yaml apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata:name: crontabs.stable.example.com spec:group: stable.example.comversions:- name: v1served: truestorage: truescope: Namespacednames:plural: crontabssingular: crontabkind: CronTabshortNames:- ctvalidation:openAPIV3Schema:properties:spec:properties:cronSpec: #--必須是字符串,并且必須是正則表達式所描述的形式type: stringpattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'replicas: #----必須是整數,最小值必須為1,最大值必須為10type: integerminimum: 1maximum: 10additionalPrinterColumns: #限制狀態列- name: Spectype: stringdescription: The cron spec defining the interval a CronJob is runJSONPath: .spec.cronSpec- name: Replicastype: integerdescription: The number of jobs launched by the CronJobJSONPath: .spec.replicas- name: Agetype: dateJSONPath: .metadata.creationTimestamp# 自定義資源的子資源描述subresources:# 啟用狀態子資源。status: {}# 啟用scale子資源scale:specReplicasPath: .spec.replicasstatusReplicasPath: .status.replicaslabelSelectorPath: .status.labelSelector [root@master1 crd]# cat my-crontabl.yaml apiVersion: "stable.example.com/v1" kind: CronTab metadata:name: my-new-cron-object spec:cronSpec: "* * * * */5"image: my-awesome-cron-imagereplicas: 2 [root@master1 crd]# kubectl apply -f my-crontabl.yaml crontab.stable.example.com/my-new-cron-object created [root@master1 crd]# kubectl get ct NAME SPEC REPLICAS AGE my-new-cron-object * * * * */5 2 3s [root@master1 crd]# kubectl scale --replicas=5 crontabs/my-new-cron-object #scale報錯失敗 The crontabs "my-new-cron-object" is invalid: metadata.resourceVersion: Invalid value: 0x0: must be specified for an update [root@master1 crd]# kubectl edit ct/my-new-cron-object #修改replicas為5成功,待后續詳細了解原因 crontab.stable.example.com/my-new-cron-object edited [root@master1 crd]# kubectl get ct NAME SPEC REPLICAS AGE my-new-cron-object * * * * */5 5 28s更多用法,參考:https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
23.3、架構設計
Controller 控制器概覽:
- Kubernetes提供一種可插拔式的方法來擴展或者控制聲明式Kubernetes資源
- 控制器是Kubernetes的大腦,負責控制大部分資源的操作
- Deployment等就是通過Kube-controller-manager來部署對應的Pod并維護Pod指定的數量和狀態
- 用戶聲明完成CRD后,需要創建一個控制器來操作自定義資源完成目標
Controller工作概覽:
- Informer 負責watch和list apiserver
- Informer收到信息后,調用對應的函數把對應的事件放到Queue中。
- Controller 從Queue中獲取到對象后,會去做相應的操作
工作流程:
首先,通過 kube-apiserver 來推送事件,比如 Added, Updated, Deleted;然后進入到 Controller 的 ListAndWatch() 循環中;ListAndWatch 中有一個先入先出的隊列,在操作的時候就將其 Pop() 出來;然后去找對應的 Handler。Handler 會將其交給對應的函數(比如 Add(), Update(), Delete())。
一個函數一般會有多個 Worker。多個 Worker 的意思是說比如同時有好幾個對象進來,那么這個 Controller 可能會同時啟動五個、十個這樣的 Worker 來并行地執行,每個 Worker 可以處理不同的對象實例。
工作完成之后,即把對應的對象創建出來之后,就把這個 key 丟掉,代表已經處理完成。如果處理過程中有什么問題,就直接報錯,打出一個事件來,再把這個 key 重新放回到隊列中,下一個 Worker 就可以接收過來繼續進行相同的處理。
第二十四章、Kubernetes API編程利器(待補充)
備注:本章節,待后續補充和完善
24.1、Operator概述
An Operator represents human operational knowledge in software, to reliably manage an application. They are methods of packaging, deploying, and managing a Kubernetes application.The goal of an Operator is to put operational knowledge into software. Previously this knowledge only resided in the minds of administrators, various combinations of shell scripts or automation software like Ansible. It was outside of your Kubernetes cluster and hard to integrate. With Operators, CoreOS changed that.通俗的講:就是運維和部署的操作交給Operator實現,比如備份、擴容、故障恢復、聲明式部署、版本升級等1、基本概念:
- CRD:Custom Resource Definition,允許用戶自定義kubernetes資源
- CR:Custom Resource,CRD的具體實例
- webhook: webhook關聯在apiserver上,是一種HTTP回調,一個基于web應用實現的webhook會在特定事件發生時把消息發送給特定的URL。用戶一般可以定義兩類webhook:
- mutating webhook(變更傳入對象)
- validating webhook(傳入對象校驗)
- 工作隊列:controller核心組件,controller會監控集群內關注資源對象的變化,并把相關對象的事件(動作和key),存儲入工作隊列中
- controller: controller是檢測集群狀態變化,并據此做出相應處理的控制循環。它關聯一個工作隊列,循環處理隊列內容。每一個controller都試圖把集群狀態向預期狀態推動,只是關注的對象不同,如replicaset controller和endpoint controller
- operator: operator是描述、部署和管理kubernetes應用的一套機制,從實現上來說,operator = CRD + webhook + controller
- Operator和K8s controller的關系:
- 所有的Operator都是用了Controller模式,但并不是所有Controller都是Operator,只有當它滿足: controller模式 + API擴展 + 專注于某個App/中間件時,才是一個Operator。
- Operator就是使用CRD實現的定制化的Controller. 它與內置K8S Controller遵循同樣的運行模式(比如 watch, diff, action)
- Operator是特定領域的Controller實現
示例:mysql-operator是如何進行定時備份的:
2、常見operator工作模式
24.2、Operator Framework
CoreOS提供了Operator Framework加速Operator的開發。主要包含組件:Operator SDK,Operator Lifecycle Manager,Operator registry
1、Operator Framework概述:
- Operator framework給用戶提供了webhook和controller框架,包括消息通知、失敗重新入隊列等等,開發人員僅需關系被管理應用的運維邏輯實現
- 主流Operator framework項目:
- kubebuilder:?https://github.com/kubernetes-sigs/kubebuilder
- operator-sdk:?https://github.com/operator-framework/operator-sdk
- 兩者沒有本質上的區別,都是使用controller-tools和controller-runtime。細節上kubebuilder相應的策略、部署、代碼生成腳手架更完善,如Makefile和Kustomsize等工具的集成;operator sdk則支持與ansible operator、Operator Lifecycle Manager的集成
2、Operator capability levels
Operators come in different maturity levels in regards to their lifecycle management capabilities for the application or workload they deliver. The capability models aims to provide guidance in terminology to express what features users can expect from an Operator.
- Level I: 常見的Deployment,ConfigMap,PV,PVC創建等
- Level II: 版本或配置更新
- Level III: 整個生命周期管理,備份,故障恢復
- Level IV: 監控,告警,計量
- Level V: 自動擴容、自愈、預警、調優、異常檢測
3、最佳實踐
https://sdk.operatorframework.io/docs/
24.3、工作流程
第二十五章、Kubernetes網絡模型進階
25.1、Kubernetes網絡模型來龍去脈
前人挖坑:早期Docker網絡的由來與弊端
凡是用過Docker的,都見過Docker0 Bridge 和172.XX:
- 最好的便利設計是與外部世界解耦,使用私網地址+ 內部Bridge
- 出宿主機,采用SNAT借IP,進宿主機用DNAT借端口
- 問題就是一堆NAT包在跑,難以區分宿主機和容器的流量。假如相同容器不同node組成group對外訪問服務的時候。難以實現
后人填坑:Kubernetes新人新氣象
一句話,讓一個功能聚集小團隊(Pod)正大光明的擁有自己的身份證 --- IP:
- Pod的IP是真身份證,通信全球就這一個號,拒絕任何變造(NAT)
- Pod內的容器共享這個身份
- 實現手段不限,可以讓外部路由器幫你加條目,也可以自己創建Overlay網絡穿越
官方說明:https://github.com/containernetworking/cni
25.2、Pod如何上網
網絡包不會自己非:只能一步一步爬
我們從兩個維度來看:
-
1)協議層次,需要從L2層(MAC)To L3層(IP尋址) To L4(4層協議+端口)
-
2)網絡拓撲,需要從容器空間To宿主機空間To遠端
接入:容器和宿主機空間之間鏈接方式;流控:網絡策略;通道:兩個host之間的通信方式
一個簡單的路由方案:Flannel-host-gw
IPAM方案:
- 每個Node獨占網段,Gateway放在本地,好處是管理簡單,壞處pod ip是無法跨Node飄逸
網絡包勇往直前的一生:
- 1、Pod-netns 內出生:容器應用產生高層數據,比如從左邊10.244.0.2發送到10.244.1.3,根據路由決定目的Mac,如屬于同一subnet內直接發送給本機另一個容器,如另一網段,則填寫Gw-mac(cni0橋),通過pod-netns內veth pair發送到cni0橋;
- 2、mac-橋轉發:Bridge的默認行為是按照mac轉發數據,如目的mac為本橋上某port(另一容器)就直接轉發;如目的為cni0 mac,則數據上送到Host協議棧處理;
- 3、ip-主機路由轉發:此時包剝離mac層,進入IP尋址層,如目的IP為本機Gw(cni0)則上送到本地用戶進程,如目的為其他網段,則查詢本地路由表(見圖ip route塊),找到該網段對應的遠端Gw-ip(其實是遠端主機IP),通過neigh系統查詢出遠端Gw-ip的mac地址,作為數據的目的mac,通過主機eth0送食醋胡;
- 4、IP-遠端路由轉發:由于mac地址的指引,數據包準確送到遠端節點,通過eth0進入主機協議棧,再查詢一次路由表,正常的話目的網段的對應設備為自己的cni0 iP,數據發送給了cni0;
- 5、mac-遠程橋轉發:cni0作為host inernal port on Bridge,收到數據包后,繼續推送,IP層的處理末端填寫目的ip(10.244.1.3)的mac,此時通過bridge fdb表查出mac(否則發arp廣播),經過bridge mac轉發,包終于到了目的容器netns。
發送方向主要有三個方向:1.同pod內同container,2.同node上不同pod,3.不同node上不同pod;
#創建兩個pod,nginx-web-0和nginx-web-1互訪。#1、信息查看 [root@master1 ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-web-0 1/1 Running 1 2d4h 172.7.61.3 master1 <none> <none> nginx-web-1 1/1 Running 1 2d4h 172.7.34.2 master3 <none> <none>nginx-web-0(172.7.61.3)訪問 nginx-web-1(172.7.34.2)步驟1:查看nginx-web-0的路由信息 [root@master1 sts]# kubectl exec -it nginx-web-0 -- ip route list #nginx-web-0的路由 default via 172.7.61.1 dev eth0 #默認路由,61.1為nginx-web-0所在node(master1)pod從etcd獲取到的網 172.7.61.0/24 dev eth0 proto kernel scope link src 172.7.61.3步驟2:查看master1 docker0配置信息 [root@master1 sts]# ifconfig docker0 docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.7.61.1 netmask 255.255.255.0 broadcast 172.7.61.255inet6 fe80::42:3eff:fe01:437d prefixlen 64 scopeid 0x20<link>ether 02:42:3e:01:43:7d txqueuelen 0 (Ethernet)RX packets 5156 bytes 424166 (414.2 KiB)RX errors 0 dropped 0 overruns 0 frame 0TX packets 5246 bytes 464075 (453.1 KiB)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0步驟3:查看master1的路由信息 [root@master1 ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.116.2 0.0.0.0 UG 100 0 0 ens33 172.7.34.0 192.168.153.134 255.255.255.0 UG 0 0 0 ens34 #訪問34.0網段的GW為153.134 172.7.61.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0 192.168.116.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33 192.168.153.0 0.0.0.0 255.255.255.0 U 101 0 0 ens34步驟4:查看192.168.153.134(master3)的路由信息 [root@master3 ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.116.2 0.0.0.0 UG 100 0 0 ens33 172.7.34.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0 #本機pod網段 172.7.61.0 192.168.153.132 255.255.255.0 UG 0 0 0 ens34 #master1的pod網段 192.168.116.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33 192.168.153.0 0.0.0.0 255.255.255.0 U 101 0 0 ens34[root@master1 sts]# kubectl exec -it nginx-web-1 -- ip route list #nginx-web-1的路由 default via 172.7.34.1 dev eth0 172.7.34.0/24 dev eth0 proto kernel scope link src 172.7.34.2步驟5:抓包分析 #在nginx-web-0中ping 172.7.34.2 -c 1,在master3上抓包 [root@master3 ~]# tcpdump -i veth95c84ba 19:37:41.998341 ARP, Request who-has 172-7-34-2.lightspeed.livnmi.sbcglobal.net tell 172-7-34-1.lightspeed.livnmi.sbcglobal.net, length 28 //誰擁有34.2的ip,請告訴34.1(master3所在pod網段的GW)19:37:41.998388 ARP, Reply 172-7-34-2.lightspeed.livnmi.sbcglobal.net is-at 02:42:ac:07:22:02 (oui Unknown), length 28 //34.2的mac地址為 02:42:ac:07:22:0219:37:41.998395 IP 172-7-61-3.lightspeed.rcsntx.sbcglobal.net > 172-7-34-2.lightspeed.livnmi.sbcglobal.net: ICMP echo request, id 65, seq 1, length 64 //61.3 發送ICMP 請求 到34.219:37:41.998421 IP 172-7-34-2.lightspeed.livnmi.sbcglobal.net > 172-7-61-3.lightspeed.rcsntx.sbcglobal.net: ICMP echo reply, id 65, seq 1, length 64 //34.2回復 61.3 ICMP請求19:37:47.014048 ARP, Request who-has 172-7-34-1.lightspeed.livnmi.sbcglobal.net tell 172-7-34-2.lightspeed.livnmi.sbcglobal.net, length 28 //誰擁有34.1的ip,請告訴34.219:37:47.014177 ARP, Reply 172-7-34-1.lightspeed.livnmi.sbcglobal.net is-at 02:42:62:e5:d2:da (oui Unknown), length 28 //34.1的mac為 02:42:62:e5:d2:da25.3、Service究竟如何工作
Service = Internal Load Balance @Client side //service為客戶端的負載均衡
1)一群Pod組成一組功能后端
2)定義一個穩定的虛IP作為訪問前端,一般還附贈一個DNS域名,Client無需感知Pod的細節;
3)kube-proxy是實現核心,隱藏了大量復雜性,通過apisrever監控Pod/Service的變化,反饋到LB配置中。
4)LB的實現機制與目標解耦,可以是用戶態進程,也可以是一堆精心設計的Rules(iptables/ipvs)
三步 ! 寫一個高端大氣的LVS 版Service
背景知識:一定要讓kernel認為vip是本地地址,這樣4層LVS才能開始干活
- 第一步:綁定VIP到本地(欺騙內核)
- ip route add to local 192.168.60.200/32 dev eth0 proto kernel
- 第二步:為這個虛IP創建一個IPVS的Virtual server
- ipvsadm -A -t 192.168.60.200:9376 -s rr -p 600
- 第三步:為這個IPVS service創建相應的real server
- ipvsadm -a -t 192.168.60.200:9376 -r 10.1.2.3:80 -m
- ipvsadm -a -t 192.168.60.200:9376 -r 10.1.4.5:80 -m
- ipvsadm -a -t 192.168.60.200:9376 -r 10.1.3.8:80 -m
25.4、負載均衡
Service類型:
- ClusterIP: Node內部使用,將Service 承載在一個內部ClusterIP上,注意該服務只能保證集群內可達,這也是默認的服務類型
- NodePort:供集群外部調用,將Service承載在Node的靜態端口,其實服務創建的時候也會自動創建一個CLusterIP,這樣依賴,服務就暴露在Node的端口上,集群外的用戶通過?$NODE_IP:$NODE_PORT?的方式訪問Service。
- LoadBalancer: 給云廠商預留的擴展接口,將Service通過外部云廠商的負載均衡端口承載,其實為方便云廠商的插件編寫,NodePor和ClusterIP兩種機制也會自動創建,云廠商可以有選擇將外部LB掛載到這兩種機制上去。
- ExternalName:去外面自由的飛翔,將Servie的服務完全映射到外部的名稱(如域名),這種機制完全依賴外部實現,Service與CNAME掛鉤,內部不會自動創建任何機制
一個真正能工作云上的、從0搭建的負載均衡系統
[root@master1 ~]# kubectl port-forward --address 192.168.153.132 pod/nginx-web-1 8888:2222 Forwarding from 192.168.153.132:8888 -> 2222 [root@master1 ~]# netstat -tunlp |grep 88 tcp 0 0 192.168.153.132:8888 0.0.0.0:* LISTEN 40196/kubectl25.5、思考一下
- 容器層的網絡就就那個如何與宿主機網絡共存,overlay or underlay?
- Service還可以有怎么樣的實現?
- 為什么一個容器編排系統要大力搞服務發現和負載均衡?
第二十六章、理解CNI和CNI插件
26.1、CNI概述
CNI是啥?
- Container Network Interface,容器網絡的API接口
- Kubelet通過這個標準的API調用不同的網絡插件實現配置網絡
- CNI插件:一系列實現了CNI API接口的網絡插件
Kubernetes中如何使用?
- 1、配置CNI配置文件(/etc/cni/net.d/xxnet.conf)
- 2、安裝CNI二進制插件(/opt/cni/bin/xxnet)
- 3、在這個節點上創建Pod
- 4、Kubelet會根據CNI配置文件執行CNI插件
- 5、Pod的網絡就配置完成了
如果只是使用CNI插件的話,大部分CNI插件的提供者都可以一鍵安裝:kubectl apply -f XXXX/flannel.yml,Flannel會通過Daemonset自動把配置和二進制拷貝到Node的配置文件夾中
26.2、CNI挑選
路由:要求底層網絡二層可達的能力,Underlay 容器和宿主機在同一個網絡
挑選建議:
26.3、如何開發自己的CNI插件
CNI插件實現通常需要兩個階段:
- 一個二進制的CNI插件去配置Pod的網卡和IP等 ==> 給Pod插上網線
- 一個Daemon 進程去管理Pod之間的網絡打通 ==> 給Pod連上網絡
階段1、給Pod插上網線
- 1、給Pod準備虛擬網卡
- 創建“veth“虛擬網卡對
- 將一端的網卡挪到Pod中,另外一段放在主機的網絡空間中
- 2、給Pod分配IP地址
- 給Pod分配集群中唯一的IP地址
- 一般會把Pod網段按Node分段
- 每個Pod再從Node段中分配IP
- 3、配置Pod的IP和路由
- 給Pod的虛擬網卡(pod內)配置分配到的IP
- 給Pod的網卡(pod內)上配置集群網段的路由
- 在宿主機上配置到Pod的IP地址的路由到對端虛擬網卡上(pod外)
階段2、給Pod連上網絡:讓每一個Pod的IP在集群中都能被訪問到
- 1、CNI Daemon進程學習到集群所有Pod的IP和其所在節點
- 通常通過請求K8s APIServer拿到現有Pod的IP地址和節點
- 監聽K8s APIServer新的Node和Pod的創建自動配置
- 2、CNI Daemon配置網絡來打通Pod的IP的訪問
- 創建到所有Node 的通道:Overlay隧道,VPC路由表,BGP路由等
- 將所有Pod的IP地址跟其所在Node的通道關聯起來:Linux路由,Fdb路由表,OVS流表等
26.4、課后思考實踐
- 在自己公司的網絡環境中,選擇哪一種網絡插件最合適?
- 嘗試自己實現一個CNI插件
總結
以上是生活随笔為你收集整理的k8s-有状态应用编排的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的魔术方法
- 下一篇: 酷派s6电信版开机显示无服务器,电信版酷