第二十二章、有状态应用编排-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.应用发布时,按照固定顺序升级Pod

StatefulSet:主要面向有状态应用管理的控制器(当然也可以使用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-2

2、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   9m53s

2、示例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          2m1s

3、添加状态和自动伸缩

[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工作模式

  1. 用户创建一个CRD
  2. apiserver转发请求给webhook
  3. webhook负责CRD的缺省值配置和配置项校验
  4. controller获取到新建的CRD,处理“创建副本”等关联逻辑
  5. controller实时监测CRD相关集群信息并反馈到CRD状态

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、工作流程

  1. 用户创建sidecarset
  2. webhook负责siecars的缺省值配置和配置项校验
  3. 用户创建pod
  4. webhook负责向pod注入sidecar容器
  5. controller实时监测pod信息,并更新到sidecarset状态

第二十五章、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:da

25.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

[root@master1 ~]# kubectl get svc nginx
NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   192.168.0.82   <none>        22/TCP    2d5h
[root@master1 ~]# ipvsadm -Ln -t 192.168.0.82:22
Prot LocalAddress:Port Scheduler Flags-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.0.82:22 nq-> 172.7.34.2:22                Masq    1      0          0-> 172.7.61.3:22                Masq    1      0          0
[root@master3 ~]# ipvsadm -Ln -t 192.168.0.82:22
Prot LocalAddress:Port Scheduler Flags-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.0.82:22 nq-> 172.7.34.2:22                Masq    1      0          0-> 172.7.61.3:22                Masq    1      0          0

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/kubectl

25.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-有状态应用编排相关推荐

  1. 从零开始入门 K8s | 有状态应用编排 - StatefulSet

    作者 | 酒祝  阿里巴巴技术专家 本文整理自<CNCF x Alibaba 云原生技术公开课>第 22 讲. 关注"阿里巴巴云原生"公众号,回复关键词**" ...

  2. ASP.NET Core 借助 K8S 玩转容器编排

    Production-Grade Container Orchestration - Automated container deployment, scaling, and management. ...

  3. 课时 22:有状态应用编排 StatefulSet(酒祝)

    本文将主要分享以下四方面的内容: "有状态"需求 用例解读 操作演示 架构设计 "有状态"需求 课程回顾 我们之前讲到过 Deployment 作为一个应用编排 ...

  4. 云计算:OpenStack、Docker、K8S(Kubernetes容器编排工具)的演进史 | 附推荐阅读

    目录 引子 OpenStack 的诞生 OpenStack 是什么 Docker 的出现 K8S(Kubernetes) - 为 Docker 而生 推荐阅读 引子 作为一名程序员,设计程序架构.优化 ...

  5. K8S有状态静态Pod经典示例

    研究K8S有一阵子了,这个东西很好用,但是也有很多坑,个人觉得很多地方还有待改进,K8S的静态Pod是一个利器,但也必须对它有全面的了解才能运作. 今天给大家示例一个从头到尾自己研究出来的项目,需要一 ...

  6. k8s节点状态异常思路

    要解决和了解节点状态为何会发生异常需要先了解k8s体系组件的基本知识与原理 在k8s容器集群运行过程,时长遇到节点运行状态异常的问题和因为组件异常.系统内核死锁.资源不足等原因引起节点状态不可知. 该 ...

  7. k8s有状态与无状态的区别

    无状态服务:deployment Deployment被设计用来管理无状态服务的pod,每个pod完全一致.什么意思呢? 无状态服务内的多个Pod创建的顺序是没有顺序的. 无状态服务内的多个Pod的名 ...

  8. k8s 有状态服务部署

    可以使用 kubesphere,快速搭建 MySQL 环境. 有状态服务抽取配置为 ConfigMap 对应界面中的配置中心->配置 有状态服务必须使用 pvc 持久化数据 对应界面中的存储卷 ...

  9. k8s如何实现容器编排?一次性说清!

    身为让容器应用实现大规模工业生产的一大功臣,过去几年,Kubernetes  势头迅猛,BAT.京东.美团.字节都走上了全域容器化部署以及云原生架构的康庄大道. 而作为支撑阿里万亿级应用背后的核心,阿 ...

  10. K8s 资源全汇总 | K8s 大咖带你 31 堂课从零入门 K8s

    关注"阿里巴巴云原生"公众号,后台回复"入门",即可下载课程全部 PPT 合集! 从 2019 年,云原生技术开始大规模普及,以"云"为核心 ...

最新文章

  1. NLP(3)| seq to seq 模型
  2. BZOJ3799 : 字符串重组
  3. 指定域的名称或安全标识(SID)与该域的信任信息不一致.
  4. python编程入门教学电子书-Python编程入门电子书教程,看这几个就够了
  5. 用户空间使用内核头文件
  6. 抽象线程之Parallel类
  7. TypeError: Layout of the output array image is incompatible with cv::Mat
  8. 【往届已EI稳定检索】第二届计算机科学与管理科技国际学术会议,多主题征稿!...
  9. Mysql update 语句(chm文档)
  10. 上位机和下位机的区别是什么
  11. PS学习笔记之色彩模式
  12. MATAL角度弧度互换
  13. 应用内更新,直接跳转到对应手机应用市场
  14. 国中假期 part 1
  15. Pytorch使用GPU加速
  16. 营销型网站文案写作的8个技巧
  17. SAP—IDoc操作步骤
  18. 你还在为“收钱”发愁吗?BeeCloud打造支付平台
  19. EventBus3 简单使用及注意点
  20. qt程序异常结束crashed

热门文章

  1. 李宏毅机器学习Lesson2——Logistic Regression实现收入预测
  2. 64位plsql连接oracle,PLSQL Developer连接Oracle11g 64位数据库配置详解(转)
  3. ARCGIS空间自相关技术的实现
  4. ICESat2学习笔记4 :Windows下使用HDFView查看ICESat-2 hdf5文件
  5. vue 针试打印机实现
  6. installshield使用教程
  7. Qcom WAPI方案
  8. 回忆杀:那些年,山寨机,暗号*#220807#,和玩过的mrp游戏
  9. vs2005下载|中文版|官方
  10. 七:如何实现代理与日志清除技术