Kubernetes入门 十四、存储管理

news/2024/10/17 12:35:18/

目录

  • 临时存储
    • hostPath
    • EmptyDir
  • NFS 挂载
    • 安装NFS
    • 配置文件
  • PV和PVC
    • 概述
    • PV 的回收策略
    • PV 的访问模式
    • PV 的状态
    • 使用PV和PVC
    • 生命周期
  • 动态供应
    • StorageClass
    • 动态供应流程
    • NFS 动态供应案例

临时存储

Kubernetes 支持很多类型的卷。 Pod可以同时使用任意数目的卷类型。

临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。

但是Kubernetes 不会销毁 持久卷

hostPath

hostPath 允许挂载 Node 上的文件系统到 Pod 里面去。如果 Pod 需要使用 Node 上的文件,可以使用 hostPath。

即使 Pod 被删除后重启,也可以重新加载到保存在 Node 主机上的该目录,该目录下的文件不会丢失。

注意:hostPath 之所以被归为临时存储,是因为实际开发中,我们一般都是通过 Deployment 部署 Pod 的,一旦 Pod 被 Kubernetes 杀死或重启拉起等,并不一定会部署到原来的 Node 节点中。

除了必需的 path 属性之外,用户可以选择性地为 hostPath 卷指定 type

取值行为
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
Directory在给定路径上必须存在的目录。
FileOrCreate如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
File在给定路径上必须存在的文件。
Socket在给定路径上必须存在的 UNIX 套接字。
CharDevice在给定路径上必须存在的字符设备。
BlockDevice在给定路径上必须存在的块设备。

hostPath 的典型应用就是时间同步:通常而言,Node 节点的时间是同步的(云厂商提供的云服务器的时间都是同步的),但是,Pod 中的容器的时间就不一定了,有 UTC 、CST 等;同一个 Pod ,如果部署到中国,就必须设置为 CST 了。

创建文件hostPath-pod.yaml,内容如下:

apiVersion: v1
kind: Pod
metadata:name: nginx-host-pathnamespace: defaultlabels:app: nginx
spec:containers:- name: nginx    image: nginx:1.20.2resources:limits:cpu: 200mmemory: 500Mirequests:cpu: 100mmemory: 200Miports:- containerPort:  80name:  httpvolumeMounts:- name: localtimemountPath: /etc/localtimevolumes:- name: localtimehostPath: # hostPathpath: /usr/share/zoneinfo/Asia/ShanghairestartPolicy: Always

上面示例就是把node上的目录的时区文件挂载到容器中。

EmptyDir

emptyDir 主要用于一个 Pod 中不同的 Container 共享数据使用的,由于只是在 Pod 内部使用,因此与其他 volume 比较大的区别是,当 Pod 如果被删除了,那么 emptyDir 也会被删除。

存储介质可以是任意类型,如 SSD、磁盘或网络存储。可以将 emptyDir.medium 设置为 Memory 让 k8s 使用 tmpfs(内存支持文件系统),速度比较快,但是重启 tmpfs 节点时,数据会被清除,且设置的大小会计入到 Container 的内存限制中。

emptyDir 的一些用途:

    • 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
    • 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。

emptyDir 卷存储在该节点的磁盘或内存中,如果设置 emptyDir.medium = Memory ,那么就告诉 Kubernetes 将数据保存在内存中,并且在 Pod 被重启或删除前,所写入的所有文件都会计入容器的内存消耗,受到容器内存限制约束。

在这里插入图片描述

示例:

创建两个pod,挂载同一个emptyDir。

第一个:

apiVersion: v1
kind: Pod
metadata:name: test-pd1
spec:containers:- image: nginxname: nginx-emptydirvolumeMounts:- mountPath: /cachename: cache-volumevolumes:- name: cache-volumeemptyDir: {}

第二个:

apiVersion: v1
kind: Pod
metadata:name: test-pd2
spec:containers:- image: nginxname: nginx-emptydirvolumeMounts:- mountPath: /cachename: cache-volumevolumes:- name: cache-volumeemptyDir: {}

这样如果在一个容器中修改了文件夹,另一个容器也会跟着修改。具体效果不再演示。

NFS 挂载

NFS是持久化数据卷的方式之一。

NFS 的简介:网络文件系统,英文Network File System(NFS),是由 SUN公司研制的UNIX表示层协议(presentation layer protocol),能使使用者访问网络上别处的文件就像在使用自己的计算机一样。

NFS 卷能将 NFS挂载到 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

在这里插入图片描述

注意:实际开发中,不建议使用 NFS 作为 Kubernetes 集群持久化的驱动。

安装NFS

  • 本次以 Master (192.168.65.100)节点作为 NFS 服务端:
yum install -y nfs-utils
  • 在 Master(192.168.65.100)节点创建 /etc/exports 文件:
# * 表示暴露权限给所有主机;* 也可以使用 192.168.0.0/16 代替,表示暴露给所有主机
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
  • 在 Master(192.168.65.100)节点创建 /nfs/data/ (共享目录)目录,并设置权限:
mkdir -pv /nfs/data/
chmod 777 -R /nfs/data/
  • 在 Master(192.168.65.100)节点启动 NFS :
systemctl enable rpcbind
systemctl enable nfs-server
systemctl start rpcbind
systemctl start nfs-server
  • 在 Master(192.168.65.100)节点加载配置:
exportfs -r
  • 在 Master(192.168.65.100)节点检查配置是否生效:
exportfs
# /nfs/data/
  • 在 Node(192.168.65.101、192.168.65.102)节点安装 nfs-utils :
# 服务器端防火墙开放111、662、875、892、2049的 tcp / udp 允许,否则远端客户无法连接。
yum install -y nfs-utils
  • 在 Node(192.168.65.101、192.168.65.102)节点,执行以下命令检查 nfs 服务器端是否有设置共享目录:
# showmount -e $(nfs服务器的IP)
showmount -e 192.168.65.100
  • 在 Node(192.168.65.101、192.168.65.102)节点,执行以下命令挂载 nfs 服务器上的共享目录到本机路径 /root/nd
mkdir /nd
# mount -t nfs $(nfs服务器的IP):/root/nfs_root /root/nfsmount
mount -t nfs 192.168.65.100:/nfs/data /nd
  • 在 Node (192.168.65.101)节点写入一个测试文件:
echo "hello nfs server" > /nd/test.txt
  • 在 Master(192.168.65.100)节点验证文件是否写入成功:
cat /nfs/data/test.txt

配置文件

使用时,配置如下:

apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: defaultlabels:app: nginx
spec:containers:- name: nginximage: nginx:1.20.2resources:limits:cpu: 200mmemory: 500Mirequests:cpu: 100mmemory: 200Miports:- containerPort: 80name:  httpvolumeMounts:- name: localtimemountPath: /etc/localtime- name: html  mountPath: /usr/share/nginx/html/ # / 一定是文件夹volumes:- name: localtimehostPath:path: /usr/share/zoneinfo/Asia/Shanghai- name: html nfs: # 使用 nfs 存储驱动path: /nfs/data  # nfs 共享的目录server:  192.168.65.100  # nfs 服务端的 IP 地址或 hostname restartPolicy: Always

PV和PVC

概述

前面我们已经学习了使用 NFS 提供存储,此时就要求用户会搭建 NFS 系统,并且会在 yaml 配置 NFS,这就带来的一些问题:

  • ① 开发人员对 Pod 很熟悉,非常清楚 Pod 中的容器那些位置适合挂载出去。但是,由于 Kubernetes 支持的存储系统非常之多,开发人员并不清楚底层的存储系统,而且要求开发人员全部熟悉,不太可能(术业有专攻,运维人员比较熟悉存储系统)。

  • ② 在 yaml 中配置存储系统,就意味着将存储系统的配置信息暴露,非常不安全(容易造成泄露)。

为了能够屏蔽底层存储实现的细节,方便用户使用,Kubernetes 引入了 PV 和 PVC 两种资源对象。

持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。

持久卷声明(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PV 的缺点:

① 需要运维事先准备好 PV 池。

② 资源浪费:没有办法预估合适的 PV,假设运维向 k8s 申请了 20m 、50m、10G 的 PV,而开发人员申请 2G 的 PVC ,那么就会匹配到 10G 的PV ,这样会造成 8G 的空间浪费。

也有人称 PV 为静态供应。

PV 的回收策略

  1. 保留(Retain)

回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然存在这前一声明人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷:

  1. 删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在。
  2. 根据情况,手动清除所关联的存储资产上的数据。
  3. 手动删除所关联的存储资产。

如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。

  1. 删除(Delete)

对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。管理员需要根据用户的期望来配置 StorageClass; 否则 PV 卷被创建之后必须要被编辑或者修补。

  1. 回收(Recycle)

警告: 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。

如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。

PV 的访问模式

访问模式(accessModes):用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

    • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载。
    • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。
    • ReadWriteMany(RWX):读写权限,可以被多个节点挂载。

PV 的状态

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

    • Available(可用):表示可用状态,还未被任何 PVC 绑定。
    • Bound(已绑定):表示 PV 已经被 PVC 绑定。
    • Released(已释放):表示 PVC 被删除,但是资源还没有被集群重新释放。
    • Failed(失败):表示该 PV 的自动回收失败。

使用PV和PVC

  1. 创建 PV (一般是运维人员操作)

先创建文件夹:

mkdir -pv /nfs/data/10m
mkdir -pv /nfs/data/20m
mkdir -pv /nfs/data/500m
mkdir -pv /nfs/data/1Gi

创建PV的资源文件 k8s-pv.yaml,内容如下:

apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-pv-10m
spec:storageClassName: nfs-storage # 创建pv的存储类名,用于分组,要与pvc的相同capacity:  # 容量配置storage: 10m  # 容量accessModes:  #访问模式- ReadWriteOncepersistentVolumeReclaimPolicy: Retain  #回收策略nfs: # 使用 nfs 存储驱动path: /nfs/data/10m # nfs 共享的目录,mkdir -pv /nfs/data/10mserver: 192.168.65.100 # nfs 服务端的 IP 地址或 hostname
---
apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-pv-20m
spec:storageClassName: nfs-storage # 用于分组capacity:storage: 20maccessModes:- ReadWriteOncenfs: # 使用 nfs 存储驱动path: /nfs/data/20m # nfs 共享的目录,mkdir -pv /nfs/data/20mserver: 192.168.65.100 # nfs 服务端的 IP 地址或 hostname
--- 
apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-pv-500m
spec:storageClassName: nfs-storage # 用于分组capacity:storage: 500maccessModes:- ReadWriteOncenfs: # 使用 nfs 存储驱动path: /nfs/data/500m # nfs 共享的目录,mkdir -pv /nfs/data/500mserver: 192.168.65.100 # nfs 服务端的 IP 地址或 hostname
---
apiVersion: v1
kind: PersistentVolume
metadata:name: nfs-pv-1g
spec:storageClassName: nfs-storage # 用于分组capacity:storage: 1GiaccessModes:- ReadWriteOncenfs: # 使用 nfs 存储驱动path: /nfs/data/1Gi # nfs 共享的目录,mkdir -pv /nfs/data/1Giserver: 192.168.65.100 # nfs 服务端的 IP 地址或 hostname

执行文件:

kubectl apply -f k8s-pv.yaml
  1. 创建PVC (一般是开发人员)

一般PVC是随着Pod一起创建,编辑资源文件k8s-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nginx-pvc-500m  # pvc的名字namespace: defaultlabels:app: nginx-pvc-500m  # pvc的label
spec:storageClassName: nfs-storage  # 与pv的一致accessModes:  # 访问模式,与pv的一致- ReadWriteOnceresources:  # 资源定义requests:storage: 500m  # 最大存储500m,资源可以小于pv,但是不能大于pv,否则匹配不上
---
apiVersion: v1
kind: Pod  # 创建pod
metadata:name: nginxnamespace: defaultlabels:app: nginx
spec:containers:- name: nginximage: nginx:1.20.2resources:limits:cpu: 200mmemory: 500Mirequests:cpu: 100mmemory: 200Miports:- containerPort:  80name:  httpvolumeMounts:  # 定义挂载数据卷- name: html  # 要挂载的数据卷的名字mountPath: /usr/share/nginx/html/   # 挂载大屏容器的目录volumes:  # 定义数据卷- name: html  # 数据卷的名字persistentVolumeClaim:  # 这个使用pvc作为存储卷claimName:  nginx-pvc-500m  # 要关联的pvc的名字,与定义pvc的一 致readOnly: false  restartPolicy: Always

执行文件:

kubectl apply -f k8s-pvc.yaml

成功部署之后,这样就完成了

生命周期

在这里插入图片描述

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

  • ① 资源供应:管理员手动创建底层存储和 PV。

  • ② 资源绑定:

    • 用户创建 PVC ,Kubernetes 负责根据 PVC 声明去寻找 PV ,并绑定在用户定义好 PVC 之后,系统将根据 PVC 对存储资源的请求在以存在的 PV 中选择一个满足条件的。
      • 一旦找到,就将该 PV 和用户定义的 PVC 进行绑定,用户的应用就可以使用这个 PVC 了。
      • 如果找不到,PVC 就会无限期的处于 Pending 状态,直到系统管理员创建一个符合其要求的 PV 。
    • PV 一旦绑定到某个 PVC 上,就会被这个 PVC 独占,不能再和其他的 PVC 进行绑定了。
  • ③ 资源使用:用户可以在 Pod 中像 Volume 一样使用 PVC ,Pod 使用 Volume 的定义,将 PVC 挂载到容器内的某个路径进行使用。

  • ④ 资源释放:

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

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

动态供应

StorageClass

上面通过手动的方式创建了一个 NFS Volume,这在管理很多 Volume 的时候不太方便。Kubernetes 还提供了 StorageClass 来动态创建 PV,不仅节省了管理员的时间,还可以封装不同类型的存储供 PVC 选用。

StorageClass 包括四个部分

  • provisioner:指定 Volume 插件的类型,包括内置插件(如 kubernetes.io/glusterfs)和外部插件(如 external-storage 提供的 ceph.com/cephfs)。
  • mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hardnfsvers=4.1 等选项。
  • parameters:指定 provisioner 的选项,比如 kubernetes.io/aws-ebs 支持 typezoneiopsPerGB 等参数。
  • reclaimPolicy:指定回收策略,同 PV 的回收策略。

在使用 PVC 时,可以通过 DefaultStorageClass 准入控制设置默认 StorageClass, 即给未设置 storageClassName 的 PVC 自动添加默认的 StorageClass。而默认的 StorageClass 带有 annotation storageclass.kubernetes.io/is-default-class=true

在这里插入图片描述

动态供应流程

在这里插入图片描述

  • ① 集群管理员预先创建存储类(StorageClass)。

  • ② 用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim)。

  • ③ 存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume)。

  • ④ 系统读取存储类的信息。

  • ⑤ 系统基于存储类的信息,在后台自动创建 PVC 需要的 PV 。

  • ⑥ 用户创建一个使用 PVC 的 Pod 。

  • ⑦ Pod 中的应用通过 PVC 进行数据的持久化。

  • ⑧ PVC 使用 PV 进行数据的最终持久化处理。

NFS 动态供应案例

注意:不一定需要设置 NFS 动态供应,可以直接使用云厂商提供的 StorageClass 。

部署 NFS 动态供应:

创建文件k8s-nfs-provisioner.yaml:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 指定一个供应商的名字 
# or choose another name, 必须匹配 deployment 的 env PROVISIONER_NAME'
parameters:archiveOnDelete: "false" # 删除 PV 的时候,PV 中的内容是否备份
---
apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisionernamespace: default
spec:replicas: 1strategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisioner  # 账号的名字,用来管理权限containers:- name: nfs-client-provisionerimage: ccr.ccs.tencentyun.com/gcr-containers/nfs-subdir-external-provisioner:v4.0.2volumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: k8s-sigs.io/nfs-subdir-external-provisioner- name: NFS_SERVERvalue: 192.168.65.100 # NFS 服务器的地址- name: NFS_PATHvalue: /nfs/data # NFS 服务器的共享目录volumes:- name: nfs-client-rootnfs:server: 192.168.65.100path: /nfs/data
# 下面都是权限相关的配置了
---
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisionernamespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: nfs-client-provisioner-runner
rules:- apiGroups: [""]resources: ["nodes"]verbs: ["get", "list", "watch"]- 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: ServiceAccountname: nfs-client-provisionernamespace: default
roleRef:kind: ClusterRolename: nfs-client-provisioner-runnerapiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: leader-locking-nfs-client-provisionernamespace: default
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-provisionernamespace: default
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: default
roleRef:kind: Rolename: leader-locking-nfs-client-provisionerapiGroup: rbac.authorization.k8s.io

上面涉及到Role和RoleBinding等权限相关的资源,这里可以不用管,后面还会介绍,只需要知道他是负责创建和管理权限的即可。因为我们要创建StorageClass必须要制定一个provisioner(制备器,可存储供应商),要让provisioner帮我们管理pv,那就要给他一定的权限。

部署上面文件即可:

kubectl apply -f k8s-nfs-provisioner.yaml

这样就让provisioner通过StorageClass帮我们管理PV了。

下面使用时,指定StorageClass的名字:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nginx-pvcnamespace: defaultlabels:app: nginx-pvc
spec:storageClassName: nfs-client # 注意此处accessModes:- ReadWriteOnceresources:requests:storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: defaultlabels:app: nginx
spec:containers:- name: nginximage: nginx:1.20.2resources:limits:cpu: 200mmemory: 500Mirequests:cpu: 100mmemory: 200Miports:- containerPort:  80name:  httpvolumeMounts:- name: localtimemountPath: /etc/localtime- name: htmlmountPath: /usr/share/nginx/html/ volumes:- name: localtimehostPath:path: /usr/share/zoneinfo/Asia/Shanghai- name: html    persistentVolumeClaim:claimName:  nginx-pvcreadOnly: false  restartPolicy: Always

目前,只需要运维人员部署好各种 storageclass,开发人员在使用的时候,创建 PVC 即可;但是,存储系统太多太多,运维人员也未必会一一掌握,可以 Rook 来统一管理。


http://www.ppmy.cn/news/1095895.html

相关文章

【LeetCode-中等题】17. 电话号码的字母组合

文章目录 题目方法一:递归回溯 题目 方法一:递归回溯 参考讲解:还得用回溯算法!| LeetCode:17.电话号码的字母组合 首先可以画出树图: 先将数字对应的字符集合 加入到一个map集合 这里需要一个index来控…

Chrome 108版(64-bit 108.0.5359.125)网盘下载

还在用Selenium的朋友们注意了,目前Chrome的最新版是116,而官方的Chromedriver只支持到115版。 可惜Google不提供旧版Chrome的下载方式,需要旧版的很难回去了。如果真的想要旧版的Chrome,只能民间自救。 我在2022年12月备份了C盘…

ClickHouse 存算分离改造:小红书自研云原生数据仓库实践

ClickHouse 作为业界性能最强大的 OLAP 系统,在小红书内部被广泛应用于广告、社区、直播和电商等多个业务领域。然而,原生 ClickHouse 的 MPP 架构在运维成本、弹性扩展和故障恢复方面存在较大局限性。为应对挑战,小红书数据流团队基于开源 C…

matlab求解方程组-求解过程中限制解的取值范围

文章目录 问题背景代码my_fun.mmain.m 结果展示:不加入F(4)加入F(4) 问题背景 求解方程组的时候,对某些未知数的求解结果的取值范围有要求。例如在某些物理问题求解中,要求待求解量大于0。 代码 一共两个文件: my_fun.m main.mmy_fun.m function Fm…

concrt140.dll怎么下载,concrt140.dll修复工具(修复精灵下载)一键修复问题

今天,我将为大家介绍一个非常常见的问题:由于找不到concrt140.dll,无法继续执行代码怎么办。这个问题可能会让很多网友感到头疼,但是别担心,我会为大家提供5种最全详细的恢复方法。在接下来我将详细介绍这些问题及其解决方法。希望…

未来之路:数字孪生在车联网中的关键作用

随着车联网技术的快速发展,共享车辆出现在城市各个角落,数字孪生作为一种虚拟仿真技术,正在物联网行业内迅速崭露头角。数字孪生不仅提供了对车辆的实时监测,还为汽车制造商和车主带来了多方面的优势和应用,下面简单从…

高通DSP架构和HVX指令介绍

1. Qualcomm Snapdragon™处理器 Qualcomm Snapdragon™是高通的移动平台处理器,是一种系统级芯片(SoC),包含了CPU、GPU、DSP、调制解调器、无线电、摄像头处理器、安全处理器等多种功能。Snapdragon处理器广泛应用于智能手机、平板电脑、智能手表、智能音箱等移动设备中。…

【2023年11月第四版教材】第11章《成本管理》(第1部分)

第11章《成本管理》(第1部分) 1 章节说明2 管理基础3 管理过程3.1 管理ITTO汇总★★★ 1 章节说明 【本章分值预测】大部分内容不变,细节有一些变化,预计选择题考3-4分,案例和论文都有可能考;是需要重点学…