Kubernetes基础(二十二)-k8s持久化存储详解

news2024/11/24 9:38:08

1 volume

1.1 介绍

在容器中的磁盘文件是短暂的,当容器崩溃时,Kubelet会重新启动容器,但容器运行时产生的数据文件都将会丢失,之后容器会以最干净的状态启动。另外,当一个Pod运行多个容器时,各个容器可能需要共享一些文件,诸如此类的需求都可以使用Volume解决。Pod只需要通过.spec.volumes字段指定为Pod提供的卷,然后在容器中配置块,使用.spec.containers.volumeMounts字段指定卷挂载的目录即可。

在Kubernetes中,Volume也支持配置许多常用的存储,用于挂载到Pod中实现数据的持久化。

Kubernetes Volume支持的卷的类型有很多,以下为常用的卷:

  • CephFS
  • GlusterFS
  • ISCSI
  • Cinder
  • NFS
  • RBD
  • HostPath

当然,也支持一些Kubernetes独有的类型:

  • ConfigMap:用于存储配置文件
  • Secret:用于存储敏感数据
  • EmptyDir:用于一个Pod内多个容器的数据共享
  • PersistentVolumeClaim:对PersistentVolume的申请

以上列举的是一些比较常用的类型,其他支持的类型可以查看Volume的官方文档

https://kubernetes.io/docs/concepts/storage/volumes/

下面介绍集中常见的volume卷。

1.2 emptyDir

EmptyDir是一个特殊的Volume类型,与上述Volume不同的是,如果删除Pod,emptyDir卷中的数据也将被删除,所以一般emptyDir用于Pod中的不同Container共享数据,比如一个Pod存在两个容器A和B,容器A需要使用容器B产生的数据,此时可以采用emptyDir共享数据,类似的使用如Filebeat收集容器内程序产生的日志。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi

1.3 HostPath

HostPath卷可将节点上的文件或目录挂载到Pod上,用于实现Pod和宿主机之间的数据共享,常用的示例有挂载宿主机的时区至Pod,或者将Pod的日志文件挂载到宿主机等。

在配置HostPath时,有一个type的参数,用于表达不同的挂载类型,HostPath卷常用的type(类型)如下:

  • type为空字符串:默认选项,意味着挂载hostPath卷之前不会执行任何检查
  • DirectoryOrCreate:如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755的空目录,和Kubelet具有相同的组和权限
  • Directory:目录必须存在于给定的路径下
  • FileOrCreate:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为0644,和Kubelet具有相同的组和所有权
  • File:文件,必须存在于给定路径中
  • Socket:UNIX套接字,必须存在于给定路径中
  • CharDevice:字符设备,必须存在于给定路径中
  • BlockDevice:块设备,必须存在于给定路径中。
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-example-linux
spec:
  os: { name: linux }
  nodeSelector:
    kubernetes.io/os: linux
  containers:
  - name: example-container
    image: registry.k8s.io/test-webserver
    volumeMounts:
    - mountPath: /foo
      name: example-volume
      readOnly: true
  volumes:
  - name: example-volume
    # mount /data/foo, but only if that directory already exists
    hostPath:
      path: /data/foo # directory location on host
      type: Directory # this field is optional

1.4 nfs

和emptyDir、HostPath的配置方法类似,NFS的Volume配置也是在Volumes字段中配置的,和emptyDir不同的是,NFS属于持久化存储的一种,在Pod删除或者重启后,数据依旧会存储在NFS节点上。要使用nfs,k8s的node节点上需要安装好nfs客户端软件。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /my-nfs-data
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: my-nfs-server.example.com
      path: /my-nfs-volume
      readOnly: true

2 PersistentVolume

2.1 介绍

虽然volume实现了持久化存储,但是诸多高级特性还是无法实现。且没有生命周期的管理。在实际使用中volume面临的问题如下:

  • 当某个数据卷不再被挂载使用时,里面的数据如何处理?
  • 如果想要实现只读挂载如何处理?
  • 如果想要只能有一个Pod挂载如何处理?

为此k8s引入了两个新的API资源:PersistentVolume和PersistentVolumeClaim。

  • PersistentVolume(简称PV)是由Kubernetes管理员设置的存储
  • PersistentVolumeClaim(简称PVC)是对PV的请求,表示需要什么类型的PV。

和单独配置Volume类似,PV也可以使用NFS、GFS、CEPH等常用的存储后端,并且可以提供更加高级的配置,比如访问模式、空间大小以及回收策略等。

目前PV的提供方式有两种:静态或动态。

  • 静态PV由管理员提前创建
  • 动态PV无须提前创建,由storageclass创建

pv属于集群资源,没有namespace隔离性。同一个pv可以被不同namespace中的资源访问。

pvc有namespace隔离性,只能被同一namespace中的资源使用,创建pvc时如果不指定namespace,则默认创建在default命名空间中

2.2 pv回收策略

当用户使用完volume(pv本质上也是volume)时,可以删除PVC对象,从而通过回收策略回收pv资源。目前回收策略有以下三种。

  • Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,Volume被视为已释放,管理员可以手动回收卷。不指定回收策略默认为retain。
  • Recycle(k8s1.14版本开始已废弃):回收,如果Volume插件支持,Recycle策略会对卷执行rm -rf清理该PV,并使其可用于下一个新的PVC,目前只有NFS和HostPath支持该策略。
  • Delete:删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete,目前支持Delete的存储后端包括AWS EBS、GCE PD、Azure Disk、OpenStack Cinder等。

对于 Kubernetes 1.29,仅nfs和hostPath卷类型支持回收。

2.3 pv访问策略

在实际使用PV时,可能针对不同的应用会有不同的访问策略,比如某类Pod可以读写,某类Pod只能读,或者需要配置是否可以被多个不同的Pod同时读写等,此时可以使用PV的访问策略进行简单控制,目前支持的访问策略如下:

  • ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为RWO。
  • ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为ROX。
  • ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为RWX。
  • ReadWriteOncePod:只能被一个Pod以读写的模式挂载,命令中可以被缩写为RWOP(1.22以上版本)。

虽然PV在创建时可以指定不同的访问策略,但是也要后端的存储支持才行。比如一般情况下大部分块存储是不支持ReadWriteMany的,具体后端存储支持的访问模式可以参考

https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes。

2.4 基于nfs或nas创建pv

[root@k8s-master01 ~]# cat pv-nfs.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs ##pv名
spec:
  capacity:
    storage: 5Gi ##pv的容量
  volumeMode: Filesystem ##卷的模式,目前支持Filesystem(文件系统) 和 Block(块),其中Block类型需要后端存储支持,默认为文件系统
  accessModes:
    - ReadWriteOnce  ##pv的访问模式
  persistentVolumeReclaimPolicy: Recycle ##pv的回收策略
  storageClassName: nfs-slow ##存储类型的名称,pvc通过该名字访问到pv
  nfs: ##pv的类型
    path: /data/nfs ##nfs服务器共享的目录
    server: 172.18.102.233 ##nfs服务器ip地址
[root@k8s-master01 ~]# kubectl create -f pv-nfs.yaml 
persistentvolume/pv-nfs created
[root@k8s-master01 ~]# kubectl get persistentvolume
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv-nfs   5Gi        RWO            Recycle          Available           nfs-slow                14s

2.5 创建hostpath类型的pv

一般不推荐使用该类型的pv,因为这种情况下使用的是宿主机的一个目录,pod和宿主机强绑定,不再具备高可用性。这种情况下需要利用污点,让pod和宿主机强绑定。

kind: PersistentVolume
apiVersion: v1
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: hostpath
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

2.6 创建cephrbd类型的pv

需要提前在ceph侧创建好密钥,具体配置可参考我的另一篇文章

K8S使用开源CEPH作为后端StorageClass

apiVersion: v1
kind: PersistentVolume
metadata:
  name: ceph-rbd-pv
spec:
  capacity:
    storage: 1Gi
  storageClassName: ceph-fast
  accessModes:
    - ReadWriteOnce
  rbd:
    monitors: ##cephmon节点的ip,端口通常为6789
      - 192.168.1.123:6789
      - 192.168.1.124:6789
      - 192.168.1.125:6789
    pool: rbd ##rbd存储池的名称,可以使用ceph osd pool ls查看
    image: ceph-rbd-pv-test  ##rbd块名称,可以使用rbd create POOL_NAME/IMAGE_NAME --size 1024创建,使用rbd list POOL_NAME查看
    user: admin #Rados的用户名,默认是admin
    secretRef: #用于验证Ceph身份的密钥
      name: ceph-secret
    fsType: ext4 #文件类型,可以是ext4、XFS等
    readOnly: false #是否是只读挂载

2.7 pv的状态

  • Available:可用,没有被PVC绑定的空闲资源。
  • Bound:已绑定,已经被PVC绑定。
  • Released:已释放,PVC被删除,但是资源还未被重新使用。
  • Failed:失败,自动回收失败。

3 PersistentVolumeClaim

pv创建好了,如何使用呢,这时就需要用到pvc,pvc可以去申请pv资源。

pvc中定义了要使用的存储类型,存储空间大小以及存储的访问模式,例如申请一个大小为5Gi且只能被一个Pod只读访问的nfs存储。

下图是一个典型的pod使用pv作为volume的流程。

管理员创建pv,用户创建pvc和pv进行绑定,pod使用pvc申请到的pv资源作为volume来持久化存储数据。

那么pvc和pv是如何进行绑定的呢,主要依赖以下参数

参数描述
StorageclassPV 与 PVC 的 storageclass 类名必须相同(或同时为空)。
AccessMode主要定义 volume 的访问模式,PV 与 PVC 的 AccessMode 必须相同。
Size主要定义 volume 的存储容量,PVC 中声明的容量必须小于等于 PV,如果存在多个满足条件的 PV,则选择最小的 PV 与 PVC 绑定。

pvc和pv绑定后,我们就可以通过在pod中使用pvc来申请pv资源了。只需要在pod的yaml文件中配置一个persistentVolumeClaim类型的volumes,claimName配置为PVC的名称即可

下面详细介绍下如何创建和使用pvc

3.1 创建pvc与pv进行绑定

3.1.1 创建pv
[root@k8s-master01 ~]# cat pv-nfs.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs  
spec:
  capacity:
    storage: 5Gi    #pv的容量
  volumeMode: Filesystem    
  accessModes:
    - ReadWriteOnce     ##pv的访问模式
  persistentVolumeReclaimPolicy: Recycle    
  storageClassName: nfs-slow    ##存储类型的名称,pvc通过该名字访问到pv
  nfs:  
    path: /data/nfs 
    server: 172.18.102.233  
3.1.2 创建pvc
[root@k8s-master01 ~]# cat nfs-pvc.yaml 
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-pvc-claim   ##pvc名
spec:
  storageClassName: nfs-slow    ##pv的类名
  accessModes:
    - ReadWriteOnce ##访问模式,和pv要一致
  resources:
    requests:
      storage: 3Gi  ##请求的容量大小,不能超过pv的大小

检查是否成功绑定

[root@k8s-master01 ~]# kubectl create -f pv-nfs.yaml
[root@k8s-master01 ~]# kubectl create -f nfs-pvc.yaml 
persistentvolumeclaim/nfs-pvc-claim created
[root@k8s-master01 ~]# 
[root@k8s-master01 ~]# kubectl get pvc
NAME            STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc-claim   Bound    pv-nfs   5Gi        RWO            nfs-slow       6s

3.2 使用pvc

pod绑定pvc,创建pod时pvc会自动去pv处申请资源作为pod的volume

[root@k8s-master01 ~]# cat pod-nfs-pvc.yaml 
kind: Pod
apiVersion: v1
metadata:
  name: nfs-pv-pod
spec:
  volumes:
    - name: nfs-pv-storage
      persistentVolumeClaim:
       claimName: nfs-pvc-claim  ##和pvc的名称一致
  containers:
    - name: nfs-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: nfs-pv-storage

检查pod是否成功挂载volume资源

[root@k8s-master01 ~]# kubectl create -f pod-nfs-pvc.yaml 
pod/nfs-pv-pod created
[root@k8s-master01 ~]# kubectl exec -it nfs-pv-pod -- bash
root@nfs-pv-pod:/# ls /usr/share/nginx/html/
root@nfs-pv-pod:/# df -h
Filesystem                Size  Used Avail Use% Mounted on
overlay                    39G   22G   18G  57% /
tmpfs                      64M     0   64M   0% /dev
tmpfs                     2.0G     0  2.0G   0% /sys/fs/cgroup
shm                        64M     0   64M   0% /dev/shm
/dev/vda2                  39G   22G   18G  57% /etc/hosts
172.18.102.233:/data/nfs   39G   22G   18G  57% /usr/share/nginx/html
tmpfs                     3.8G   12K  3.8G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                     2.0G     0  2.0G   0% /proc/acpi
tmpfs                     2.0G     0  2.0G   0% /proc/scsi
tmpfs                     2.0G     0  2.0G   0% /sys/firmware

4 动态存储storageclass

在介绍pv那一节我们就提到,创建pv有静态和动态两种方式。前面提到的一直是静态创建pv的方式,即管理员手动创建pv。这一节介绍动态创建的方式,即通过storageclass动态创建pv,有了storageclass,我们不需要在手动创建pv,只需要创建好storageclass,再将pvs和storageclass绑定,即可通过storageclass动态的创建pv。

每个storageclass都包含下面几个参数

  • provisioner:提供pv卷的存储类型
  • parameters:与后端存储对接时使用的参数,取决于provisioner中指定的存储。如ceph存储可以指定cluster id和pool id等。
  • reclaimPolicy:指定通过storageclass创建出来的pv的回收策略。可以是 Delete 或者 Retain。如果 StorageClass 对象被创建时没有指定 reclaimPolicy,它将默认为 Delete。
  • mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hard 和 nfsvers=4.1 等选项。

4.1 创建storageclass

本例使用ceph类型的storageclass

[root@k8s-master02 kubernetes]# cat storageclass.yaml 
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: 395b7a30-eb33-460d-8e38-524fc48c58cb  ##ceph集群ID
   pool: k8s   ##ceph集群的pool名
   imageFeatures: layering  ##定义创建的rbd features
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: ceph-csi
   csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
   csi.storage.k8s.io/controller-expand-secret-namespace: ceph-csi
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: ceph-csi
   csi.storage.k8s/fstype: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
   - discard

4.2 创建pvc绑定storageclass

在pvc声明中指定storageclass的名称即可绑定

[root@k8s-master01 ~]# cat ceph-csi-release-v3.9/examples/rbd/pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc  ##要与storageclass的名称一致

创建完成之后,StorageClass会自动创建PV,然后与PVC进行绑定。此时pod和pvc绑定即可使用pv资源作为volume来持久化存储数据。

最后来一张图解释storageclass、pvc、pv之间的关系

前面我们了解到pv的创建有静态和动态两种方式,静态即管理员手动创建。动态即通过storageclass创建。在动态创建的场景下。我们不需要手动创建pv,只需要创建storageclass,storageclass会和后端存储绑定。同时创建pvc和storageclass绑定。在pod的yaml文件中,我们声明好要使用哪个pvc。声明好后,在创建pod时,storageclass会自动根据pvc里的定义自动创建pv,同时pv和pvc会自动绑定,pv作为存储资源提供给pod使用。

5 pod删除后pv中的数据会怎么样

pod删除后pv中的数据不会受到影响。只有在pv绑定的pvc被删除,即pv和pvc的绑定被解除时。pv中的数据可能会根据其根据设置的回收策略被删除。

当pv的回收策略为retain:pvc删除后,pv不会被删除。如果手动将pv删除,pv对应的后端存储中的空间也不会被删除。如果使用相同的参数再次创建pv,依然会使用这段存储空间。

当pv的回收策略为delete时:pvc删除后,pv会被删除。pv中的数据是否会被删除取决于后端存储类型,比如测试发现hostpath类型的pv使用delete回收策略。pv被删除后,pv中的数据不会被删除。

5.1 修改pv的回收策略

[root@k8s-master01 ~]# kubectl patch pv task-pv-volume -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'   ##task-pv-volume为pv名

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

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

相关文章

科技守护大唐遗宝,预防保护传承千年

​ 一、“大唐遗宝——何家村窖藏出土文物展” 陕西历史博物馆的“唐朝遗宝——何家村窖藏出土文物展”算得上是博物馆展览的典范。展览不仅在于展现了数量之多、等级之高、种类之全,更在于对唐朝历史文化的深入揭露。 走入大唐财产展厅,好像穿越千年前…

【Azure 架构师学习笔记】- Azure Databricks (7) --Unity Catalog(UC) 基本概念和组件

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (6) - 配置Unity Catalog 前言 在以前的Databricks中,主要由Workspace和集群、SQL Warehouse组成, 这两年Databricks公…

Bert基础(一)--transformer概览

1、简介 当下最先进的深度学习架构之一,Transformer被广泛应用于自然语言处理领域。它不单替代了以前流行的循环神经网络(recurrent neural network, RNN)和长短期记忆(long short-term memory, LSTM)网络,并且以它为基础衍生出了诸如BERT、GPT-3、T5等…

安全架构设计理论与实践

一、考点分布 安全架构概述(※※)安全模型(※※※)信息安全整体架构设计网络安全体系架构设计区块链技术(※※) 二、安全架构概述 被动攻击:收集信息为主,破坏保密性 主动攻击&#…

深度学习发展的艺术

将人类直觉和相关数学见解结合后,经过大量研究试错后的结晶,产生了一些成功的深度学习模型。 深度学习模型的进展是理论研究与实践经验相结合的产物。科学家和工程师们借鉴了人类大脑神经元工作原理的基本直觉,并将这种生物学灵感转化为数学模…

Mac环境Obsidian的ExcaliDraw添加中文字体

Mac环境Obsidian的ExcaliDraw添加中文字体 ExcaliDraw画图工具直接看图 ExcaliDraw画图工具 顾名思义,这是画图用的,但是系统不支持中文字体,所以需要下载中文字体自己放进去。 直接看图

HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-SPI

目录 一、 SPI 概述二、SPI 模块相关API三、接口调用实例四、SPI HDF驱动开发4.1、开发步骤(待续...) 坚持就有收获 一、 SPI 概述 SPI 是串行外设接口(Serial Peripheral Interface)是一种高速的全双工同步的通信总线。 SPI 是由 Motorola 公司开发&a…

VUE3 中导入Visio 图形

微软的Visio是一个功能强大的图形设计工具,它能够绘制流程图,P&ID,UML 类图等工程设计中常用的图形。它要比其它图形设计软件要简单许多。以后我的博文中将更多地使用VISO 来绘制图形。之前我一直使用的是corelDraw。 Visio 已经在工程设…

新增长100人研讨会:快消零售专场探讨招商加盟数字化转型实战

2024年2月2日下午,一场由纷享销客与杨国福集团联合主办的招商加盟数字化转型研讨会在上海成功举办。本次研讨会汇聚了众多快消零售业界的领军人物,共同探讨行业未来的新增长点。 会议伊始,杨国福集团数字化中心负责人王林林发表了主题演讲&a…

php伪协议之phar

一.phar协议 用于将多个 PHP 文件、类、库、资源(如图像、样式表)等打包成一个单独的文件。这个归档文件可以像其他 PHP 文件一样被包含(include)或执行。PHAR 归档提供了一种方便的方式来分发和安装 PHP 应用程序和库&#xff0c…

【unity实战】使用unity制作一个类似Rust的3D生存建造建筑系统(附项目源码)

配置连接点 材质 连接器控制 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Connector : MonoBehaviour {[Header("连接器位置")]public ConnectorPosition connectorPosition;[Header("连接器所属建筑类型&qu…

以太坊 Dencun 升级与潜在机会

撰文:Biteye 核心贡献者 Fishery Isla 文章来源Techub News专栏作者,搜Tehub News下载查看更多Web3资讯。 以太坊网络升级 Dencun 测试网版本在 2024 年 1 月 17 日上线了 Goerli 测试网,1 月 30 日成功上线了 Sepolia 测试网,D…

RocketMQ—RocketMQ消息重复消费问题

RocketMQ—RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题: 生产者多次投递消息:如果生产者发送消息时,连接有延迟,MQ还没收到消息,生产者又发送了一次消息; 消费者方扩容…

两步为软件设置开机自启动(Windows)

两步为软件设置开机自启动(Windows) 有些软件本身没有开机自启动的设置,以下是两种解决的方式,在这里主要介绍第二种(更简单更推荐) 第一种方法: 使用任务计划程序 第二种方法: …

gRPC 备查

简介 HTTP/2 HTTP/2 的三个概念 架构 使用流程 gRPC 的接口类型 1.单一RPC 2.服务器流式RPC 3.客户端式流式RPC 4.双向流式RPC

vue3-动画技巧

Vue 提供了 <Transition> 和 <TransitionGroup> 组件来处理元素进入、离开和列表顺序变化的过渡效果。但除此之外&#xff0c;还有许多其他制作网页动画的方式在 Vue 应用中也适用。这里我们会探讨一些额外的技巧。 基于 CSS class 的动画 对于那些不是正在进入或…

kali虚拟机桥接模式快速设置

第一步&#xff1a;配置 IP、掩码、网关 vim /etc/network/interfaces第二步&#xff1a;配置 DNS&#xff1a; vi /etc/resolv.conf第三步&#xff1a;重启网卡 service networking restart如果还不行建议重启一下虚拟机

c# #if 与 Conditional属性宏的区别

测试代码 using System; using System.Diagnostics;namespace ConsoleApp1 {public class TestClass{[Conditional("Debug1")]public static void Func1(){Console.WriteLine("Conditional 宏");}public static void Func2(){ #if Debug2Console.WriteLin…

Rabbitmq入门与应用(六)-rabbitmq的消息确认机制

rabbitmq的消息确认机制 确认消息是否发送给交换机 配置 server:port: 11111 spring:rabbitmq:port: 5672host: 192.168.201.81username: adminpassword: 123publisher-confirm-type: correlated编码RabbitTemplate.ConfirmCallback ConfirmCallback 是一个回调接口&#xf…

Leetcode3011. 判断一个数组是否可以变为有序

Every day a Leetcode 题目来源&#xff1a;3011. 判断一个数组是否可以变为有序 解法1&#xff1a;分组循环 排序 适用场景&#xff1a;按照题目要求&#xff0c;数组会被分割成若干组&#xff0c;每一组的判断/处理逻辑是相同的。 核心思想&#xff1a; 外层循环负责遍…