kubernetes之数据存储详解

news2025/1/11 1:34:39

目录

一、存储卷的作用

二、数据卷概述

三、数据卷emptyDir

四、数据卷hostPath

五、数据卷:NFS

六、持久卷概述

6.1PV静态供给

6.2PV 动态供给(StorageClass)

6.3 PV 生命周期

6.3.1 ACCESS MODES (访问模式)

6.3.2 RECLAIM POLICY (回收策略)

6.3.3 STATUS (状态)

6.3.4PV和PVC生命周期


一、存储卷的作用


容器部署过程中一般有以下三种数据:

• 启动时需要的初始数据,例如配置文件

• 启动过程中产生的临时数据,该临时数据需要多个容器间共享

• 启动过程中产生的持久化数据, 例如MySQL的data目录


二、数据卷概述


Kubernetes中的Volume提供了在容器中挂载外部存储的能力

Pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息后才可以使用相应的Volume

数据卷类型大致分类:

• 本地(hostPath , emptyDir等)

• 网络(NFS , Ceph , GlusterFS等)

• 公有云(AWS EBS等)

• K8S资源(configmap , secret等)


三、数据卷emptyDir


emptyDir卷: 是一个临时存储卷,与Pod生命周期绑定一起,如果 Pod删除了卷也会被删除。

应用场景: Pod中容器之间数据共享

官方示例: 卷 | Kubernetes

创建emptyDir.yaml

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-pod
spec:
  containers:
  - name: write   
    image: centos
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"]
    volumeMounts:
      - name: data          
        mountPath: /data
  - name: read    
    image: centos
    command: ["bash","-c","tail -f /data/hello"] 
    volumeMounts:
      - name: data          
        mountPath: /data    ## 挂载点
  volumes:
  - name: data  
    emptyDir: {}   ##  数据卷类型

emptyDir会在 pod 所在的本地创建一个empty-Dir文件夹存放挂载的文件

进入 write容器内, 我们可以看到 /data/hello 文件写入正常


四、数据卷hostPath


hostPath卷:挂载Node文件系统(Pod所在节点)上文件或者目 录到Pod中的容器。

应用场景: Pod中容器需要访问宿主机文件

官方示例:卷 | Kubernetes

创建hostPath.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
  - name: busybox 
    image: busybox 
    args:  
    - /bin/sh
    - -c
    - sleep 36000   
    volumeMounts:
    - name: data          
      mountPath: /data
  volumes:
  - name: data 
    hostPath:  
      path: /tmp
      type: Directory

查看 hostpath-pod所在的节点


五、数据卷:NFS


NFS数据卷: 提供对NFS挂载支持, 可以自动将NFS共享,路径挂载到Pod中

NFS:是一个主流的文件共享服务器。

# 安装包,所有访问共享文件的 服务器
yum install nfs-utils

## 编辑配置
vi /etc/exports
/ifs/kubernetes *(rw,no_root_squash)

## 创建共享文件夹
mkdir -p /ifs/kubernetes
systemctl start nfs
systemctl enable nfs
注:每个Node上都要安装nfs-utils包

把k8s-node1 作为 NFS Server 创建数据存储路径 

在 k8s-master1节点手动挂载/mnt 目录到 NFS Server

[root@k8s-master1 ~]# mount -t nfs  192.168.2.118:/ifs/kubernetes /mnt
[root@k8s-master1 ~]# umount /mnt 

 在 k8s-master1 节点的 /mnt 目录下创建文件 验证 nfs 挂载 是否成功

创建nginx-nfs.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: depployment-nfs
spec:
  selector:
    matchLabels:
      app: nginx-nfs
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx-nfs
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
      volumes:
      - name: wwwroot
        nfs:
          server: 192.168.2.118 
          path: /ifs/kubernetes

创建3个nfs pod 

我们进入容器 并创建 index.html , 在 k8s-node1 访问 成功


六、持久卷概述


kubernetes支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes引入PV和PVC两种资源对象。 

  • PV全称叫做 Persistent Volume,持久化存储卷。它是用来描述或者说用来定义一个存储卷的,这个通常都是由运维工程师来定义。
  • PVC的全称是Persistent Volume claim,是持久化存储的请求。它是用来描述希望使用什么样的或者说是满足什么条件的PⅣ存储。

PVC的使用逻辑在Pod 中定义一个存储卷(该存储卷类型为 PVC),定义的时候直接指定大小,PVC必须与对应的PV建立关系,PVC会根据配置的定义去PV申请,而PV是由存储空间创建出来的。PV和 PVC是Kubernetes 抽象出来的一种存储资源。

PV是存储资源的抽象,PV资源是属于集群资源,跨namespace(不受命名空间隔离)

在这里插入图片描述

 PV资源清单文件:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存储类型,与底层真正存储对应
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 2Gi
  accessModes: #访问模式
  storageClassName: #存储类别
  persistentVolumeReclaimPolicy: #回收策略

PV的关键配置参数说明:
存储类型
    底层实际存储的类型,kubernetes支持多种存储类型(nfs、cifs、glusterfs等),每种存储类型的配置都有所差异
存储能力(capacity)
    目前只支持存储空间的设置( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置
访问模式(accessModes)
    用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
        ReadWriteOnce (RWO) :读写权限,但是只能被单个节点挂载
        ReadOnlyMany (ROX) :只读权限,可以被多个节点挂载
        ReadWriteMany (RWX) :读写权限,可以被多个节点挂载
        需要注意的是,底层不同的存储类型可能支持的访问模式不同
回收策略(persistentVolumeReclaimPolicy)
    当PV不再被使用了之后,对其的处理方式。目前支持三种策略:
        Retain (保留) 保留数据,需要管理员手工清理数据
        Recycle (回收) 清除PV中的数据,效果相当于执行rm -rf /thevolume/*
        Delete (删除) 与PV相连的后端存储完成volume的删除操作,当然这常见于云服务商的存储服务
        需要注意的是,底层不同的存储类型可能支持的回收策略不同
存储类别
    PV可以通过storageClassName参数指定一个存储类别
        具有特定类别的PV只能与请求了该类别的PVC进行绑定
        未设定类别的PV则只能与不请求任何类别的PVC进行绑定
状态(status)
    一个PV的生命周期中,可能会处于4种不同的阶段:
        Available(可用):表示可用状态,还未被任何PVC绑定
        Bound(已绑定):表示PV已经被PVC绑定
        Released(已释放):表示PVC被删除,但是资源还未被集群重新声明
        Failed(失败):表示该PV的自动回收失败

6.1PV静态供给

创建pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    path: /ifs/kubernetes
    server: 192.168.2.118

创建deployment-pvc.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: depployment-pvc
spec:
  selector:
    matchLabels:
      app: nginx-pvc
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx-pvc
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
      volumes:
      - name: wwwroot
        persistentVolumeClaim:  
          claimName: my-pvc   ## 引用PVC

---
apiVersion: v1    
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:      ## 读写权限
  - ReadWriteMany
  resources:
    requests:       ## 请求的资源大小
      storage: 1Gi

创建my-pv ,资源1G

创建deployment-pvc.yaml 使用的 my-pv ,使用存储1G ,权限为 读写执行

我们 修改nginx 容器 内容的修改,访问成功。说明创建 PV 是 OK的。

6.2PV 动态供给(StorageClass)

现在PV使用方式称为静态供给,需要K8s运维工程师提前创建一堆PV,供开发者使用。

       上面介绍的PV和PVC模式是需要运维人员先创建好PV,然后开发人员定义好PVC进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,对于运维人员来说维护成本很高,kubernetes提供一种自动创建PV的机制,叫storageclass,它的作用就是创建PV的模板。

创建StorageClass需要定义PV的属性,比如存储类型、大小等:另外创建这种PV需要用到的存储插件,比如ceph等。

有了这两部分信息,Kubernetes就能够根据用户批交的 PVC,找到对应的 storageClass,然后 Kubernetes就会调用storageClass声明的存储插件,自动创建需要的PV并进行绑定。

因此, K8s开始支持PV动态供给, 使用StorageClass对象实现。存储类 | Kubernetes

 基于NFS实现PV动态供给

部署NFS实现自动创建PV插件

# 授权访问apiserver
kubectl apply -f rbac.yaml
    
# 部署插件,需修改里面NFS服务器地址与共享目录
kubectl apply -f deployment.yaml 
    
# 创建存储类    
kubectl apply -f class.yaml  

创建 rbac.yaml

---
kind: ServiceAccount
apiVersion: v1
metadata:
  name: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

 创建deployment.yaml

​
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
---
kind: Deployment
apiVersion: apps/v1 
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.2.118  ## NFS服务器 k8s-node1
            - name: NFS_PATH
              value: /ifs/kubernetes
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.2.118 
            path: /ifs/kubernetes    ## 数据共享目录

 创建 class.yaml 存储类

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "true"

创建存储类 并查看

kubectl apply  -f    rbac.yaml deployment.yaml class.yaml 
kubectl get sc  # 查看存储类

创建deployment-sc.yaml web示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: depployment-sc
spec:
  selector:
    matchLabels:
      app: nginx-sc
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx-sc
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
        ports:
        - containerPort: 80
      volumes:
      - name: wwwroot
        persistentVolumeClaim:  
          claimName: my-pvc3   ## 引用PVC

---
apiVersion: v1    
kind: PersistentVolumeClaim
metadata:
  name: my-pvc3
spec:
  storageClassName: "managed-nfs-storage" ## 通知  storageclass 自动创建 PV
  accessModes:      ## 读写权限
  - ReadWriteMany
  resources:
    requests:         ## 请求的资源大小
      storage: 2Gi    ## 资源大小 2G

 动态创建 pv 绑定请求 my-pvc3 , nginx-sc pod 成功申请到了 2G 的资源

NFS server服务器的路径 /ifs/kubernetes 下会自动创建 my-pvc3 的存储路径

我们可以 成功访问 index.html

删除deployment-sc pod

kubectl delete  -f  deployment-sc.yaml

使用 Delete (删除) 回收策略, PVC删除后,动态创建的PV 也会删除

小结

动态创建了PV资源,通过PVC申请使用PV资源,将PVC与PV进行绑定,最后Pod通过引用PVC来使用PV资源(也就是pod最后间接的使用到底层的存储资源)

即在pod中声明要使用的PVC,然后将容器的目录挂载到PVC所指定的PV上,最后挂载到PV上的容器内的目录下的所有文件会同时持久化到nfs对应的共享目录中。因为容器内的被挂载目录是通过PVC引用挂载到PV上,而PV又是建立在nfs存储之上,也就是最后的数据会持久化到PV所声明的nfs服务器的对应共享目录下。

6.3 PV 生命周期

6.3.1 ACCESS MODES (访问模式)

AccessModes 是用来对PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

• ReadWriteOnce (RWO):读写权限,但是只能被单个节点挂载

• ReadOnlyMany (ROX):只读权限,可以被多个节点挂载

• ReadWriteMany (RWX):读写权限,可以被多个节点挂载

6.3.2 RECLAIM POLICY (回收策略)

目前PV 支持的策略有三种:

• Retain (保留): 保留数据,需要管理员手工清理数据

• Recycle (回收):清除 PV 中的数据,效果相当于执行rm -rf /ifs/kuberneres/*

• Delete (删除):与 PV 相连的后端存储同时删除

6.3.3 STATUS (状态)

一个PV 的生命周期中,可能会处于4中不同的阶段:

• Available (可用):表示可用状态,还未被任何 PVC 绑定

• Bound (已绑定):表示 PV 已经被PVC 绑定

• Released (已释放): PVC 被删除,但是资源还未被集群重新声明

• Failed (失败): 表示该 PV 的自动回收失败

6.3.4PV和PVC生命周期

PV和PVC的生命周期见下图:

PVC和PV是一 一对应的, PV和PVC之间的相互作用遵循以下生命周期:

  • 资源供应:管理员手动创建底层存储和PV,状态处于Available
  • 资源绑定:用户创建PVC, kubernetes负责根据PVC的声明去寻找PV,并绑定,状态为Bound

在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的

       a. 一旦找到,就将该PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了

       b. 如果找不到,PVC则会无限期处于Pending状态, 直到等到系统管理员创建了一个符 合其要求的PV

       PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了

  • 资源使用:用户可在pod中像volume一样使用pvc

        Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。

  • 资源释放:用户删除pvc来释放pv

        当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为”已释放 (Released)“,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还被                         留在存储设备上,只有在清除之后该PV才能再次使用。

  • 资源回收:kubernetes根据pv设置的回收策略进行资源的回收

对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收(处于Available状态),才能供新的PVC绑定和使用

 参考: K8S数据存储(高级存储之 PV、PVC、PV和PVC生命周期K8S数据存储(高级存储之 PV、PVC、

K8S数据存储(高级存储之 PV、PVC、PV和PVC生命周期)_途径日暮不赏丶的博客-CSDN博客_k8s 查看pv

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis场景2】缓存更新策略(双写一致)

在业务初始阶段,流量很少的情况下,通过直接操作数据是可行的操作,但是随着业务量的增长,用户的访问量也随之增加,在该阶段自然需要使用一些手段(缓存)来减轻数据库的压力;所谓遇事不决,那就加一…

vue 基础入门:vue 的调试工具

1. 安装 vue-devtools 调试工具 vue 官方提供的 vue-devtools 调试工具,能够方便开发者对 vue 项目进行调试与开发。 Chrome 浏览器在线安装 vue-devtools :https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajb…

六、应用层(五)万维网(www)

目录 5.1 WWW的概念与组成结构 5.2 超文本传输协议(HTTP) 5.2.1 HTTP的操作过程 5.2.2 HTTP的特点 5.2.3 HTTP的报文结构 5.1 WWW的概念与组成结构 万维WWW(World Wide Web)简称web并非某种特殊的计算机网络。它…

商城管理系统

商城管理系统 文章目录商城管理系统要求:项目结构图AddProductServlet添加商品:AddToCart将商品添加至购物车ClearCart清空购物车DeleteProductServlet删除商品EditProductServlet修改商品FindProductServlet查找商品LoginServlet登录ProductControl商品…

初级C语言之【数组】

🦖作者:学写代码的恐龙 🦖博客主页:学写代码的恐龙博客主页 🦖专栏:【初级c语言】 🦖语录:❀未来的你,一定会感谢现在努力奋斗的自己❀ 初级C语言之【数组】一&#xff…

Swagger在线API文档

Swagger 解决的问题 随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。前端和后端的唯一联系,变成了 API 接口,所以 API 文档变成…

C++进阶---C++11

本篇主要是介绍C11中新添加的一些特性。 文章目录 1.C11简介2.列表初始化3.变量类型推导4.新增容器---静态数组array5.右值引用6.lambda表达式7.包装器8.新的类功能9.可变参数模板一、C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1),使得C03这个名字…

Python高阶函数装饰器

“ 从CANoe vTESTstudio版本7开始,支持使用python编辑器编写python脚本。其中CANoe提供了许多API接口给python使用,大大扩展了python的可用性。在python中使用装饰器定义capl中的事件处理程序(on key/on timer等)。对此我们有必要…

C语言—宏定义

宏定义的作用是替换&#xff0c;再复杂也只能替换&#xff0c;不能用做计算&#xff1b; 一个源文件将另一个源文件的全部内容包含进来&#xff1b; 条件编译&#xff1b; 不带参数的宏定义&#xff1a; #include <stdio.h> #define PI 3.14int main() {printf("…

聊聊Redis消息队列-实现异步秒杀

一、前言 消息队列&#xff08;Message Queue&#xff09;, 字面意思就是存放消息的队列&#xff0c;最简单的消息队列模型包括3个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理&#xff08;Message Broker&#xff09;;生产者&#xff…

Shell编程补充

Shell编程补充shell的变量定义变量的单双引号的不同输出变量父子shellshell子串BASHshell子串的用法shell统计变量长度输出程序运行时间结论:shell扩展变量用于处理变量值的创建子shell(进程列表)查看是否开启子shell在运行内置命令,外置命令shell编程总结shell的变量 定义变量…

小黑实习debug中遇到了函数式编程的混乱,特此进行的日常积累:python函数积累1

函数参数中有默认值&#xff0c;在函数内部会创建一块区域并维护这个默认值 # 在函数内存中会维护一块区域存储 [1,2,666,666,666] 100010001 def func(a1,a2[]):a2.append(666)print(a1,a2)func(100) func(1000)100 [666] 1000 [666, 666] def func(a1,a2[]):a2.append(666…

【nowcoder】笔试强训Day7

目录 一、选择题 二、编程题 2.1Fibonacci数列 2.2合法括号序列判断 一、选择题 1.JAVA属于&#xff08; &#xff09;。 A 操作系统 B 办公软件 C 数据库系统 D 计算机语言 计算机软件主要分为系统软件与应用软件两大类。系统软件主要包括操作系统、语言处理系统、数…

three.js之形状缓冲几何体

文章目录简介例子解释其他圆弧矩形专栏目录请点击 简介 Shape用来定义一个二维形状平面 官网常常与ShapeGeometry(形状缓冲几何体)搭配使用 官网&#xff0c;我们可以下运行下面的例子 例子 <!DOCTYPE html> <html lang"en"><head><meta cha…

玩转GPT--在线文本生成项目[可入坑~科普系列]

文章目录前言效果页面说明文字个数top_KTop_Ptemperature聊天上下文关联记忆项目部署获取项目获取模型运行彩蛋总结前言 没办法&#xff0c;最近ChatGPT杀疯了&#xff0c;没忍住&#xff0c;还是想look&#xff0c;look。没办法&#xff0c;哪个帅小伙能够忍受的了一个可以和…

数学知识---数论(质数和约数)

文章目录 1.质数1.1质数的判定---试除法1.2分解质因数---试除法1.3筛质数2.约数2.1试除法求约数2.2约数个数2.3约数之和2.4最大公约数---欧几里得算法(辗转相除法)1.质数 质数是针对所有大于1的自然数定义的,在大于1的整数中,如果只包含1和本身这两个约数,就被定义成为质…

【SpringCloud Alibaba】 初始化Sentinel

Sentinel 概念 特征 主要特征 开源生态​ 开启控制台 初始化工程 Sentinel 概念 分布式系统的流量防卫兵。随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、流量路由、熔断降级、系统自适应过载保护…

Biotin-PEG-NH2,Biotin-PEG-amine,生物素-PEG-氨基材料改性用化学试剂

英文名称&#xff1a;Biotin-PEG-NH2&#xff0c;Biotin-PEG-amine 中文名称&#xff1a;生物素-聚乙二醇-氨基 Biotin-PEG-NH2是氨基化PEG中的一种&#xff0c;他可以用于材料改性&#xff1b;氨基和很多基团可以反应&#xff0c;如&#xff1a;羧基&#xff0c;活性酯&…

JavaScript:封装单向链表10种常见的操作方法

链表的优势 1.要存储多个元素的时候&#xff0c;除了数组还可以选择链表。 2.与之数组不同的是&#xff0c;链表中的元素在内存中不必是连续的空间。 3.链表中的每个元素由一个存储元素本身的节点和一个指向下一个节点的引用(指针或者连接)组成。 相比于数组&#xff0c;链表有…

QGIS基础:根据字段属性值或基于规则进行分类符号化显示

以下操作是对数据进行分类符号化&#xff0c;下面是原始操作数据&#xff1a; 基于分类符号化的字段是如下所示&#xff08;ZDTZM&#xff09;: A B C D 找到数据图层&#xff0c;右键属性&#xff0c;找到【符号化】&#xff0c;点击如下所示的分类&#xff1a; 在【valu…