Kubernetes(十)StatefulSet

一    有状态无状态理解

分布式系统的无状态和有状态详解

kubernetes 无状态服务和有状态服务

之前的博客我们学习了 'Deployment 和 ReplicaSet' 两种'资源对象'的使用

在实际使用的过程中,'Deployment 并不能编排'所有类型的应用,对'无状态服务编排'是非常容易的,但是对于'有状态服务'就无能为力了

那么问题来了:什么是'有状态'服务?什么是'无状态'服务

有状态案例讲解

我们常见的 'WEB 应用',是通过 'Session' 来保持用户的'登录状态'的

1)如果我们将 Session '持久化到节点上',那么该应用就是一个'有状态的服务了'

2)因为我现在登录进来你把我的 'Session 持久化到节点 A 上了',下次我'登录的时候'可能会将'请求路由到节点 B 上去了',但是'节点 B' 上根本就'没有我当前的 Session 数据',就会被认为是'未登录状态了'

3)这样就导致我'前后两次请求得到的结果不一致了'

4)所以一般为了'横向扩展',我们都会把这类 WEB 应用'改成无状态的服务',怎么改?

5)将 Session 数据'拆分'出来,存入一个'公共的地方',比如 Redis 里面;对于一些'客户端请求 API' 的情况,我们就'不使用 Session' 来保持用户状态,改成用 'JWT Token-->目前比较流行的' 也是可以的

备注:session和cookier理解,以及业界session共享方案

'容器化应用程序'的难点: 设计'有状态分布式组件'的部署'体系'结构

注意: 有状态服务和无状态服务'完全切分-->或者叫做解耦',不要'混合'使用

核心点: '稳定-->stable'、'有序-->sort'

1)稳定的、'唯一的'网络标识符  --> '这个是重点'

2)稳定的、'持久化'的存储

3)有序的、优雅的'部署和缩放' -->创建顺序列[1,n]

4)有序的、优雅的'删除和终止' -->删除'逆序'

5)有序的、自动'滚动更新'

二    StatefulSet

(1)Headless Service

因为StatefulSet是依赖于'Headless Service',所以先讲解'Headless Service'的特点,'不打算深入讲解',该篇服务于'StatefulSet'的,后续会'细讲'

(2)案例

说明: Headless Service本身也是'Service'的一种,'不指定'ClusterIP参数会'自动分配一个IP',所以Headless类型的Service'必须指定该参数是None'

备注: None和""的效果是一样的

注意:每次测试一定要保证'实验环境'的干净 -->字段不理解的话'--help'或者'explain'看帮助文档

apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  ports:
  - name: http
    port: 80
  clusterIP: None
  selector:
    app: nginx

 

1)headless service会为'关联的Pod分配一个域'

<service name>.$<namespace name>.svc.cluster.local

2)StatefulSet会为'关联的Pod保持一个不变的Pod Name'

statefulset中'Pod的hostname'格式为$(StatefulSet name)-$('pod序号')

3)StatefulSet会为'关联的Pod分配一个dnsName'

'<Pod Name>'.$<service name>.$<namespace name>.svc.cluster.local

(3)应用场景

三    StatefulSet

Pod名字称为'网络标识'-->'hostname'

'FQDN': $(podname).(headless service name).namespace.svc.cluster.local

'手动创建的PVC名称'必须符合之后创建的StatefulSet命名规则:'volumeClaimTemplates.name-pod_name'

(1)实验前准备

在开始之前,我们'先准备两个' 1G 的存储卷(PV),后面会详细'讲解 PV 和 PVC' 的使用方法的,这里我们'先不深究'

实现:'模拟nginx'作为'有状态'应用
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /tmp/pv001

---  '创建两个pv' --> 'hostPath方式提供本地存储'

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /tmp/pv002

 

备注: 这种'提供存储'方式是'宿主机挂载方式',对比理解docker 宿主机挂载的特点-->'覆盖'

注意: 此时node1和node2对应'路径并没有创建','之后'使用的时候'才会创建' --> 两个节点都'没有对应的路径'

(2)StatefulSet讲解

​容器通过'resources'来申请'计算资源'-->'cpu、memory',通过' volumeClaimTemplates'来申请'存储资源'
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: default
spec:
  serviceName: "nginx"  --> 'Headless Service的名称'
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - name: web
          containerPort: 80
        volumeMounts:
        - name: www                        --> '一个pod肯嗯有多个pv,具体容器使用哪个pv'
          mountPath: /usr/share/nginx/html --> '容器中的挂载点'

  volumeClaimTemplates:                    --> 'pod申请使用的存储的申请' --> '针对该StatefulSet下所有的pod'
  - metadata:
      name: www               
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
注意: 如果系统没有复合要求的PV,pod会一直处于Pending,是因为没有可用的pv,查看对应的pvc的状态'也是pending'

 

一旦'pv被使用',hostPath这种方式,才会在node创建对应的'存储路径'

查看'pod所在的节点'hostpath形式,访问节点403原因 -->是'mount'的形式,所以会'覆盖'容器目录数据(index.html)

继续实验:访问另外一个pod(在另外一个pod上),发现访问'也是403' -->二者的'数据来源'不一样 --> '有状态'

测试

我们'仔细观察整个过程'出现了两个 Pod:web-0 和 web-1,而且这两个 Pod 是'按照顺序进行创建的',web-0 '启动起来后' web-1 '才开始'创建

如同上面 StatefulSet 概念中所提到的,StatefulSet 中的 '每个Pod' 拥有一个具有'稳定的、独一无二'的身份标志。这个标志基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。

Pod 的名称的形式为'<statefulset name>-<ordinal index>',我们这里的对象拥有两个副本,所以它创建了两个 'Pod 名称'分别为:'web-0 和 web-1'

继续探究: 我们可以使用 kubectl exec 命令'进入到容器中'查看它们的 'hostname'

现象: StatefulSet '对应的这两个 Pod 的' hostname 与 Pod '名字是一致的',都被'分配了'对应的编号

继续探究

'pv'和对应的'pod'进行绑定 --> '一旦绑定'-->pvc名称 = 'pod中声明的名称 + pod name'

我们'可以看到'Controlled By: StatefulSet/web,证明我们的 Pod 是'直接受到 StatefulSet 控制器管理的',没有'中间商赚差价'

busybox测试

现在我们创建一个 busybox'该镜像中有一系列的工具'的容器,在容器中用 DNS 的方式来访问一下这个 Headless Service

这里写成yaml文件的形式,如果只是'单纯的为了测试',所以'没必要写资源清单文'件来声明,用kubectl run命令'启动一个测试的容器'即可

kubectl run -it --image busybox:1.28.3 test --restart=Never --rm /bin/sh

备注: busybox '最新版本的镜像有 BUG',会出现nslookup'提示无法解析'的问题,我们这里使用'老一点的镜像版本'1.28.3
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: busybox
  name: busybox-deployment
  namespace: default
spec:
# dp的副本数
  replicas: 1
# 工作负载选择那些标签的pod,组成组
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
# PID为1的常驻进程
      - command:
        - sleep
        - "36000"
        image: busybox:1.28.3
        # 思考:镜像的标签为none出现的原因
        imagePullPolicy: IfNotPresent
        name: busybox
      dnsPolicy: ClusterFirst
      # dnsPolicy: Default
      # 特点:只继承,不使用内部的dns
      restartPolicy: Always

1)我们直接解析 'Headless Service' 的名称,可以看到得到的是'两个 Pod 的解析记录' --> 而不是服务的ip,'本身也就没有ip'

2)但实际上如果我们通过'nginx这个 DNS' 去访问我们的服务的话,并'不会随机或者轮询'背后的两个 Pod,而是访问到'一个固定的 Pod',所以'不能代替'普通的 Service

问题: 如果分别解析对应的 Pod 呢?

可以看到'解析 web-0.nginx' 的时候解析到了 'web-0 这个 Pod 的 IP'web-1.nginx 解析到了 web-1 这个 Pod 的 IP,而且这个 'DNS 地址还是稳定的',因为 'Pod 名称就是固定的'

备注: 其它Pod就可以通过'pod的域名'来访问'StatefulSet创建'的pod

继续

可以看到 StatefulSet 控制器仍然会'按照顺序'创建出两个 Pod 副本出来,而且 Pod 的'唯一标识'依然没变,所以这两个 Pod 的'网络标识还是固定的',我们依然可以通过web-0.nginx去访问到web-0这个 Pod

备注: 虽然 'Pod 已经重建了',对应 'Pod IP 已经变化了',但是'访问这个 Pod 的地址-->域名'依然没变,并且他们'依然还是关联的之前的 PVC',数据并不会丢失

强调: 我们关系的是'数据是否持久话',而'不是pod变化与否',所以'pod重建导致ip变化不影响'

 (3)通过控制器删除pod

'逆序'删除,删除后'数据也没有丢失'

(4)结构图

StatefulSet

(5)自己的一些理解

(1)提供稳定的'网络标识'

StatefulSet 创建的pod的名称,按照'从零开始'的顺序索引,这个会体现在'pod的名称和主机名称上',同样还会体现在'pod对应的固定存储上'

  1)'StatefulSet名称': web

  2)'Headless Service'名称: nginx

  3)'pod名称': web-0 和 web-1 -->格式: 'web-${index}'

  4)'volumeClaimTemplates': 'www'

  5)创建出来的'pvc名称':www-web-0  www-web-0   --> 格式: 'sts卷申请模板名称-${pod_name}'

  6)pod的在coredns中'fqdn名称': web-0.nginx.default.svc.cluster.local -->格式: '${pod_name}'.'${Headless Service name}'.'${namespace}'.svc.vluster.local

+++++++++++++++  '分割线'

服务暴露的方式: 直接指定某个'具体的pod'

一个StatefulSet要求你'创建一个用来记录'每个pod'网络标记'的headless Service

通过这个Service,每个pod将拥有'独立的DNS记录',这样'集群里它的伙伴'或者客户端就可以通过'主机名'找到它。

+++++++++++++++  '分割线'

(2)StatefulSet的保障机制

一个有状态的pod总会被一个'完全一致的'pod-->'替换两者相同的名称,主机名和存储等',这个替换发生在kubernetes发现'旧pod不存在时'-->例如'手动删除'这个pod

那么当Kubernetes'不能确定'一个pod的状态呢?如果它创建一个完全一致的pod,那系统中就会有两个完全一致的pod在同时运行,这两个pod会'绑定到相同的存储',所以这两个相同标记的进程会'同时写相同'的文件。

为了保证两个拥有'相同标记和绑定相同'持久卷声明的有状态的pod实例'不会同时运行',statefulset遵循at-most-one语义,也就是说一个StatefulSet必须在'准确确认一个pod不再运行后',才会去创建它的'替换pod'

+++++++++++++++  '分割线'

(3)pod重启后是'如何找到'对应存储的

首先重启之后-->'重点:sts配置文件没有变化',网络标识别'hostname=pod_name'不变,'Headless Service'不变,该pod申请的'pvc名称不变-->能够找到对应的存储',同时该pod在dns中的'fqdn-->注意fqdn的组成'没有发生变化

可以这样理解: pod的'fqdn'和申请的'pvc'具有'强一致性的关联'

client  --> 通过该pod的'fqdn'找到'对应的存储'

(5)相关参数解读

①  管理策略

②   更新策略

达到的效果: '灰度发布'

(6)有状态应用的高级用法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值