k8s(kubernetes)的PV / PVC / StorageClass(理论+实践)

NFS总是不支持PVC扩容

先来个一句话总结:PV、PVC是K8S用来做存储管理的资源对象,它们让存储资源的使用变得可控,从而保障系统的稳定性、可靠性。StorageClass则是为了减少人工的工作量而去自动化创建PV的组件。所有Pod使用存储只有一个原则:先规划后申请再使用

一、理论

1、PV概念

PV是对K8S存储资源的抽象,PV一般由运维人员创建和配置,供容器申请使用。

没有PV之前,服务器的磁盘没有分区的概念,有了PV之后,相当于通过PV对服务器的磁盘进行分区。

2、PVC概念

PVC 是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。类似于某个应用程序向操作系统的D盘申请1G的使用空间。

PVC 创建成功之后,Pod 就可以以存储卷(Volume)的方式使用 PVC 的存储资源了。Pod 在使用 PVC 时必须与PVC在同一个Namespace下。

3、PV / PVC的关系

PV相当于对磁盘的分区,PVC相当于APP(应用程序)向某个分区申请多少空间。比如说安装WPS程序时,一般会告知我们安装它需要多少存储空间,让你选择在某个磁盘下安装。如果将来某个分区磁盘满了,也不会影响别的分区磁盘的使用。

一旦 PV 与PVC绑定,Pod就可以使用这个 PVC 了。如果在系统中没有满足 PVC 要求的 PV,PVC则一直处于 Pending 状态,直到系统里产生了一个合适的 PV。

4、StorageClass概念

K8S有两种存储资源的供应模式:静态模式和动态模式,资源供应的最终目的就是将适合的PV与PVC绑定:

  • 静态模式:管理员预先创建许多各种各样的PV,等待PVC申请使用。

  • 动态模式:管理员无须预先创建PV,而是通过StorageClass自动完成PV的创建以及与PVC的绑定。

StorageClass就是动态模式,根据PVC的需求动态创建合适的PV资源,从而实现存储卷的按需创建。

一般某个商业性的应用程序,会用到大量的Pod,如果每个Pod都需要使用存储资源,那么就需要人工时不时的去创建PV,这也是个麻烦事儿。解决方法就是使用动态模式:当Pod通过PVC申请存储资源时,直接通过StorageClass去动态的创建对应大小的PV,然后与PVC绑定,所以基本上PV → PVC是一对一的关系。

5、Provisioner概念

在创建 PVC 时需要指定 StorageClass,PVC 选择到对应的StorageClass后,与其关联的 Provisioner 组件来动态创建 PV 资源。

那Provisioner是个啥呢?其实就一个存储驱动,类似操作系统里的磁盘驱动。

StorageClass 资源对象的定义主要包括:名称、Provisioner、存储的相关参数配置、回收策略。StorageClass一旦被创建,则无法修改,只能删除重新创建。

PV和PVC的生命周期,包括4个阶段:资源供应(Provisioning)、资源绑定(Binding)、资源使用(Using)、资源回收(Reclaiming)。首先旧的有资源供应,说白了就是得有存储驱动,然后才能创建、绑定和使用、回收。

6、使用PV / PVC前后对比

6.1、通过描述对比

在没有使用PV、PVC之前,各个Pod都可以任意的向存储资源里(比如NFS)写数据,随便一个Pod都可以往磁盘上插一杠子,长期下去磁盘的管理会越来越混乱,然后导致数据使用超限,磁盘爆掉,最后导致磁盘上的所有应用全部挂掉。

为了解决这个问题,引入了PV、PVC的概念,达到限制Pod写入存储数据大小的目的,从而更好地保障了系统的可用性、稳定性。

有了PVC、PV之后,所有Pod使用存储资源,保持一个原则:先规划 → 后申请 → 再使用。

那你肯定有一个疑问,“StorageClass是自动化创建PV,跟原本的无序不可控是一样的效果啊,都可以随便占用存储资源啊”。

其实不然,使用StorageClass只是自动化了创建PV的流程,但依旧执行的是一个存储可控的流程。每个Pod使用多少存储空间是固定的,Pod没有办法超额使用存储空间,更不会影响到别的应用,要出故障也只是某个Pod自己出故障。

6.2、通过图片对比

没有使用PV、PVC之前的情况,如下面2张图:

有了PV、PVC之后的情况,如下图

二、实践

在实践PV、PVC、StorageClass之前,需要读者朋友自行安装NFS服务器。文中演示的内容是通过yaml编排自动到NFS服务器起上创建PV。

1、Pod使用PV、PVC挂载存储卷

1.1、编排PV、PVC、Pod挂载PVC

文中演示的是:Pod的某个目录挂载到NFS的某个目录下。使用了nginx镜像,将html文件写在PV所在的NFS服务器上,最终可以看到利用PV / PVC 成功挂载上去了。

yaml文件如下:

# PV编排
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv1
  namespace: dev1
  labels:
    pv: nfs-pv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  # Recycle 删除PVC会同步删除PV | Retain 删除PVC不会同步删除PV
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /data/nfstest/share/pv1
    server: 10.20.1.20
    readOnly: false
---
# PVC 编排,通过selector查找PV,K8S里的资源查找都是通过selector查找label标签
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc1
  namespace: dev1
  labels:
    pv: nfs-pvc1
spec:
  resources:
    requests:
      storage: 100Mi
  accessModes:
    - ReadWriteOnce
  selector:
    matchLabels:
      pv: nfs-pv1
---
# Pod挂载PVC,这里为了测试,直接通过node节点的hostPort暴露服务
apiVersion: v1
kind: Pod
metadata:
  name: webapp
  namespace: dev1
  labels:
    app: webapp
spec:
  containers:
    - name: webapp
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
          hostPort: 8081
      volumeMounts:
        - name: workdir
          mountPath: /usr/share/nginx/html
  volumes:
    - name: workdir
      persistentVolumeClaim:
        claimName: nfs-pvc1

执行kubectl命令,查看实践效果如下:

2、Pod使用StorageClass自动挂载存储卷

2.1、安装 Provisioner

文中选择通过helm的方式安装nfs-subdir-external-provisioner,这种方式相对简单。安装文档、安装过程见下文:

  • 安装文档

https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#nfs

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

  • 安装过程

通过以下3个步骤完成nfs-subdir-external-provisioner的安装。

  1. 安装helm,本文以mac为例

brew install heml

     2.安装nfs-subdir-external-provisioner,执行以下2个命令:

$ helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
$ helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner -n kube-system \
    --set image.repository=dyrnq/nfs-subdir-external-provisioner \
    --set nfs.server=10.20.1.20 \
    --set nfs.path=/data/nfstest/nfs-storage

这里注意几个参数:

image.repository:修改了镜像的地址,默认用的国外镜像很有可能拉不下来

nfs.server:你的NFS服务器地址

nfs.path:存储目录

  1. 查看helm安装的结果:

执行命令:helm list -A,查看helm安装结果:

查看是否创建了对应的pod,如果没有修改镜像地址会一直拉取失败,如下图:

修改镜像地址后成功启动Pod,如下图:

2.2、使用StorageClass

文中演示的是:Pod利用StorageClass自动创建PV,同时在对应的存储目录上创建了文件,写入了数据。

yaml文件如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage-1
provisioner: cluster.local/nfs-subdir-external-provisioner
parameters:
  # 设置为"false"时删除PVC不会保留数据,"true"则保留数据
  archiveOnDelete: "false"
mountOptions:
  # 指定NFS版本,这个需要根据NFS Server版本号设置
  - nfsvers=4
---
# 创建PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-storage-pvc-1
  namespace: dev1
spec:
  storageClassName: nfs-storage-1    #需要与上面创建的storageclass的名称一致
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Mi
---
kind: Pod
apiVersion: v1
metadata:
  name: nfs-storage-pod-1
  namespace: dev1
spec:
  containers:
    - name: nfs-storage-pod-1
      image: busybox
      command:
        - "/bin/sh"
      args:
        - "-c"
        - "touch /mnt/teststorage && echo 111 > /mnt/teststorage && exit 0 || exit 1"  ## 创建一个名称为"SUCCESS"的文件
      volumeMounts:
        - name: nfs-pvc
          mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: nfs-storage-pvc-1

执行kubectl命令后,可以看到如下效果:

可以看到如我们预料的那样,通过storageClass自动创建了PV,同时在NFS对应的存储目录上创建了文件,写入了数据。

至此,我们实践过程全部结束。

三、总结

本文主要讲解了PV、PVC、StorageClass的理论和实战。

一句话总结:PV、PVC是K8S用来做存储管理的资源对象,它们让存储资源的使用变得可控,从而保障系统的稳定性、可靠性。StorageClass则是为了减少人工的工作量而去自动化创建PV的组件。所有Pod使用存储只有一个原则:先规划后申请再使用

四、Volume之Persistent Volume持久卷

基本概念

「1. PV 持久卷」:PersistentVolume,其是K8s中对实际物理存储系统的抽象。作为K8s中集群层面的资源,与节点资源类似。PV不属于任何命名空间「2. PVC 持久卷申领」:PersistentVolumeClaim,其是K8s中用户对存储请求的抽象。类似地,Pod会消耗节点资源,而PVC则会消耗PV资源

具体地,通过PV实现了对底层真实存储系统的抽象,使得开发者只需通过创建PVC资源即可。而开发者可以直接将PVC的名称作为卷在工作负载资源中进行引用。当集群中存在满足PVC要求的PV时,则会将该PV与PVC进行双向绑定

静态制备

此场景下K8s集群的管理员需要先创建相应的PV资源,然后才能供开发者使用该资源。下面即是通过hostPath卷创建PV资源的示例。此时不难理解,PV实际上是对物理存储资源的抽象

# 1. 创建持久卷PV

apiVersion: v1
kind: PersistentVolume
metadata:
  # PV卷名称
  name: mongodb-pv
spec:
  # 容量
  capacity: 
    # 存储大小: 100MB
    storage: 100Mi
  # 该卷支持的访问模式
  accessModes:
    - ReadWriteOnce # RWO, 该卷可以被一个节点以读写方式挂载
    - ReadOnlyMany  # ROX, 该卷可以被多个节点以只读方式挂载
  # 回收策略: 保留
  persistentVolumeReclaimPolicy: Retain
  # 该持久卷的实际存储类型: 此处使用HostPath类型卷
  hostPath:
    path: /tmp/MongodbData

这里对卷的访问模式进行说明,其支持下述选项。值得一提的是,卷虽然可以能够支持多种访问模式,但其在同一时刻只能以一种访问模式进行挂载。例如,某卷可以被节点A以RWO模式挂载,也可以被B节点以ROX模式挂载。但不能同时使用两种模式挂载

**1. ReadWriteOnce (RWO)**:卷可以被一个节点以读写方式挂载。ReadWriteOnce访问模式允许运行在同一节点上的多个Pod访问卷 **2. ReadOnlyMany (ROX)**:卷可以被多个节点以只读方式挂载 **3. ReadWriteMany (RWX)**:卷可以被多个节点以读写方式挂载 **4. ReadWriteOncePod (RWOP)**:在Kubernetes 1.22以上版本时,卷可以被单个Pod以读写方式挂载

这里对卷的回收策略进行补充说明

「1. Retain 保留」

当PVC资源被删除时,其所绑定的PV不会被删除,但该PV的状态会变为「Released已释放」。由于此时PV所关联的物理存储系统上仍然存在历史数据,故该PV此时不能直接被申领。需要集群管理员手动进行处置。一方面对于PV资源而言,其需要删除并重新创建;另一方面,对于PV所关联的物理存储系统上的数据,需要根据实际情况判断是直接删除还是保留以便能够复用数据

「2. Delete 删除」

其不需要管理员人工进行介入操作,系统不仅会将PV资源删除,同时还会将该PV资源所关联的物理存储系统上的数据一并删除掉

K8s集群的管理员创建PV资源,效果如下所示

当K8s集群中的PV资源准备好后,开发者就可以通过创建PVC资源来获取PV了。这里我们将storageClassName字段设置为空字符串,意为不使用存储类进行动态制备,而是使用集群管理员提前创建好的PV。即所谓的静态制备;然后在部署应用的配置文件中,直接引用PVC的名称进行卷的定义即可。需要注意 PVC所属的命名空间 与 使用该PVC的Pod 需在同一个命名空间下

# 1. 创建持久卷申领PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc
spec:
  resources:
    # 申请30MB的存储空间
    requests:
      storage: 30Mi
  # 访问模式
  accessModes:
  - ReadWriteOnce
  # 禁止使用动态制备的卷, 即使用静态制备
  storageClassName: ""

---

# 2. 创建数据库应用

apiVersion: apps/v1
# 资源类型
kind: ReplicaSet
metadata:
  # RS名称
  name: my-mongodb-app
spec:
  # 副本数量
  replicas: 1
  # 标签选择器
  selector:
    matchLabels:
      app: db
  # Pod 模板
  template:
    metadata:
      # 标签信息
      labels:
        app: db
    spec:
      # 容器信息
      containers:
      - name: my-mongodb-app
        image: mongo
        volumeMounts:
        # 将名为mongodb-data的卷挂载到容器内的指定路径
        - name: mongodb-data
          # MongoDB默认的数据存储路径
          mountPath: /data/db
      # 卷信息
      volumes:       
      - name: mongodb-data
        # 引用PVC的名称即可
        persistentVolumeClaim:
          claimName: mongodb-pvc

当PVC创建完成后,一旦集群中存在满足PVC指定的容量要求、访问模式的PV资源后。该PVC即会与相应的PV进行双向绑定。如下所示,同时会发现应用所在Pod也部署成功

动态制备

对于静态制备而言,无论采用何种回收策略。事实上都需要系统管理员再次创建PV资源。为此动态制备应运而生,K8s集群在收到用户创建的PVC后,会通过PVC中所指定的StorageClass存储类自动地制备出相应的PV资源以供使用。在动态制备场景下,系统管理员只需提前创建相应的若干个StorageClass存储类资源即可。具体地,在StorageClass存储类中会关联真实的物理存储系统和相应的provisioner制备器。其中动态制备的PV会继承相应的StorageClass中定义的回收策略。由于这里我们是通过Kind搭建K8s集群环境的,其已经内置了一个StorageClass存储类。如下所示

此时,开发者只需在创建PVC的过程通过storageClassName字段指定相应的存储类名称即可。类似地在部署应用时直接引用PVC名称定义卷

# 1. 创建持久卷申领PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongodb-pvc-2
spec:
  resources:
    # 申请20MB的存储空间
    requests:
      storage: 30Mi
  # 访问模式
  accessModes:
  - ReadWriteOnce
  # 使用名为standard的存储类进行动态制备
  storageClassName: "standard"

---

# 2. 创建数据库应用

apiVersion: apps/v1
# 资源类型
kind: ReplicaSet
metadata:
  # RS名称
  name: my-mongodb-app-2
spec:
  # 副本数量
  replicas: 1
  # 标签选择器
  selector:
    matchLabels:
      app: db2
  # Pod 模板
  template:
    metadata:
      # 标签信息
      labels:
        app: db2
    spec:
      # 容器信息
      containers:
      - name: my-mongodb-app-2
        image: mongo
        volumeMounts:
        # 将名为mongodb-data-2的卷挂载到容器内的指定路径
        - name: mongodb-data-2
          # MongoDB默认的数据存储路径
          mountPath: /data/db
      # 卷信息
      volumes:       
      - name: mongodb-data-2
        # 引用PVC的名称即可
        persistentVolumeClaim:
          claimName: mongodb-pvc-2

效果如下所示,动态制备的PV成功与PVC进行了绑定,应用也处于运行状态

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/876016.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp child.onFieldChange is not a function

uni-forms // 所有子组件参与校验,使用 for 可以使用 awiatfor (let i in childrens) {const child childrens[i];let name realName(child.name);if (typeof child.onFieldChange function) {const result await child.onFieldChange(tempFormData[name]);if (result) {…

SpringBoot 处理 @KafkaListener 消息

消息监听容器 1、KafkaMessageListenerContainer 由spring提供用于监听以及拉取消息,并将这些消息按指定格式转换后交给由KafkaListener注解的方法处理,相当于一个消费者; 看看其整体代码结构: 可以发现其入口方法为doStart(),…

前端用html写excel文件直接打开

源码 <html xmlns:o"urn:schemas-microsoft-com:office:office" xmlns:x"urn:schemas-microsoft-com:office:excel" xmlns"http://www.w3.org/TR/REC-html40"> <head><meta charset"UTF-8"><!--[if gte mso 9]&…

C++学习笔记之引用(基础)

C学习笔记之引用 https://www.runoob.com/cplusplus/cpp-references.html 引用变量是一个别名&#xff0c;它是已存在变量的另一个名字 一旦把引用初始化为某个变量&#xff0c;可以使用该引用名称或变量名称来指向变量 1、引用vs指针 引用和指针之间有一些相似&#xff0c;也…

计算机的错误计算(九十三)

摘要 探讨 log(y,x) 即以 x 为底 y 的对数的计算精度问题。 Log(y,x)运算是指 x 为底 y 的对数。 例1. 计算 log(123667.888, 0.999999999999999) . 不妨在Python中计算&#xff0c;则有&#xff1a; 若在 Excel 单元格中计算&#xff0c;则有几乎同样的输出&#xff1a; 然…

C++多态 学习

目录 一、多态的概念 二、多态的实现 三、纯虚函数和多态类 四、多态的原理 一、多态的概念 多态&#xff1a;多态分为编译时多态(静态多态)和运行时多态(动态多态)。编译时多态主要是我们之前学过的函数重载和函数模板&#xff0c;他们在传不同类型的参数就可以调用不同的函…

OpenCV-Python笔记(上)

安装 全局安装 pip install opencv-python项目虚拟环境安装 # 进入项目根路径执行 .venv/bin/pip install opencv-python计算机眼中的图像 一张图片由大小比如&#xff08;100*100&#xff09;决定&#xff0c;说明存在100*100的像素点&#xff0c;每个像素点存在颜色通道&…

ppt文件怎么压缩变小一些?8种压缩PPT文件的方法推荐

ppt文件怎么压缩变小一些&#xff1f;在现代工作环境中&#xff0c;PPT文件常常是我们展示信息和分享想法的主要工具。然而&#xff0c;当这些文件变得庞大时&#xff0c;它们不仅会占用大量的存储空间&#xff0c;还可能导致处理速度变慢&#xff0c;影响整体工作效率。这种情…

4+1视图模型

逻辑视图&#xff08;Logical View&#xff09; 逻辑视图主要关注系统的功能分解&#xff0c;即系统如何被划分为不同的逻辑组件&#xff08;如类、接口、包等&#xff09;&#xff0c;以及这些组件之间的交互关系。它帮助开发者理解系统的业务逻辑和功能结构。 开发视图&…

【人工智能】OpenAI发布GPT-o1模型:推理能力的革命性突破,这将再次刷新编程领域的格局!

在人工智能领域&#xff0c;推理能力的提升一直是研究者们追求的目标。就在两天前&#xff0c;OpenAI正式发布了其首款具有推理能力的大语言模型——o1。这款模型的推出&#xff0c;不仅标志着AI技术的又一次飞跃&#xff0c;也为开发者和用户提供了全新的工具来解决复杂问题。…

【实践】应用访问Redis突然超时怎么处理?

目录标题 问题描述分析过程查看监控数据系统监控指标JVM监控指标Redis监控指标分析应用异常单机异常规律集群异常规律统计超时的key 初步结论验证结论访问Redis链路slowlogRedis单节点info all定位redis节点定位异常keybigkeystcpdump定位大key影响 经验总结 问题描述 某产品线…

【验收交付资料】系统培训方案(doc原件)

1. 培训目的 2. 培训方式 3. 培训内容 4. 培训讲师 5. 培训教材 6. 培训质量保证 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查…

Pikachu靶场之XSS

先来点鸡汤&#xff0c;少就是多&#xff0c;慢就是快。 环境搭建 攻击机kali 192.168.146.140 靶机win7 192.168.146.161 下载zip&#xff0c;pikachu - GitCode 把下载好的pikachu-master&#xff0c;拖进win7&#xff0c;用phpstudy打开网站根目录&#xff0c;.....再用…

豆包MarsCode编程助手:产品功能解析与应用场景探索!

随着现代技术的不断进化升级&#xff0c;人工智能正在逐步改变着我们的日常工作方式。特别是对于复杂的项目&#xff0c;代码编写、优化、调试、测试等环节充满挑战。为了简化这些环节、提高开发效率&#xff0c;许多智能编程工具应运而生&#xff0c;豆包MarsCode 编程助手就是…

nodejs基础教程之-异步编程promise/async/generator

1. 异步 所谓"异步"&#xff0c;简单说就是一个任务分成两段&#xff0c;先执行第一段&#xff0c;然后转而执行其他任务&#xff0c;等做好了准备&#xff0c;再回过头执行第二段,比如&#xff0c;有一个任务是读取文件进行处理&#xff0c;异步的执行过程就是下面…

二、Kubernetes中pod的管理及优化

一 kubernetes 中的资源 1.1 资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 kubernetes的本质上就是一个集群系统&#xff0c;用户可以在集群中部署各种服务 所谓的部署服务&#xff0c;其实就是在kub…

【运维监控】Prometheus+grafana监控zookeeper运行情况

运维监控系列文章入口&#xff1a;【运维监控】系列文章汇总索引 文章目录 一、prometheus二、grafana三、prometheus集成grafana监控zookeeper1、修改zookeeper配置2、修改prometheus配置3、导入grafana模板4、验证 本示例通过zookeeper自带的监控信息暴露出来&#xff0c;然后…

Ceisum(SuperMap iClient3D for Cesium)实现平面裁剪

1&#xff1a;参考API文档&#xff1a;SuperMap iClient3D for Cesium 开发指南 2&#xff1a;官网示例&#xff1a;support.supermap.com.cn:8090/webgl/Cesium/examples/webgl/examples.html#layer 3&#xff1a;SuperMap iServer&#xff1a;欢迎使用 SuperMap iServer 11…

C语言---函数指针基础总结万字(4)

一、 函数 1.函数是一段可以重复执行的代码。 它可以接受不同的参数&#xff0c; 完成对应的操作。 下面的例子就是一个函数 int plus(int n) {return n; }上面的代码声明了一个函数plus()。 2.函数声明的语法有以下几点&#xff0c;需要注意。 返回值类型。 函数声明时&a…

每日奇难怪题(持续更新)

1.以下程序输出结果是() int main() {int a 1, b 2, c 2, t;while (a < b < c) {t a;a b;b t;c--;}printf("%d %d %d", a, b, c); } 解析:a1 b2 c2 a<b 成立 ,等于一个真值1 1<2 执行循环体 t被赋值为1 a被赋值2 b赋值1 c-- c变成1 a<b 不成立…