当前位置: 首页 > news >正文

K8S Storage

概述

一般情况下,K8S中的Pod都不应该将数据持久化到Pod中,因为Pod可能被随时创建和删除(扩容或缩容),即便是StatefulSet或Operator的Pod,也都不建议在Pod里存放数据,可以将数据持久化到Host上。K8S提供了非常丰富的存储相关的功能,使得我们可以方便的让Pod访问存储设备。K8S通过Volume挂载的方式让Pod来访问存储设备,Volume与Pod绑定并与Pod有相同的生命周期,Volume在Pod中定义,而Pod中的容器只需要使用volumeMounts就可以使用在Pod中定义的Volume。

Pod可以引用的Volume包含以下几类:

  • K8S内部的资源对象:ConfigMap,Secret,DownwardAPI,Projected Volume等。
  • Node上的资源:emptyDir,hostPath。
  • 持久化存储或网络存储:CephFS,FC,CSI,iSCSI,NFS,RBD等。
  • 存储厂商提供的存储卷:ScaleIO Volumes,StorageOS,VsphereVolume等。
  • 公有云提供的存储卷:AWS EBS,AzureDisk,AzureFile,GCE PersistentDisk等。

总之,在K8S Pod中能够使用几乎所有的存储类型和方式。特别的,通过K8S CSI(Container Storage Interface),我们还可以开发自己的存储访问插件,接入到特定的存储设备中。

Node本地存储

Node本地存储包含emptyDirhostPath两种类型。emptyDir用于存储临时数据,如缓存,删除Pod的时候会自动被清理,emptyDir可以指定成Memory类型,但会被统计成容器使用的内存。hostPath用于挂载Node的某个目录,对于大部分应用来说,都不应该直接使用hostPath,因为如果Pod被调度到了其它节点,其就无法访问到之前节点的hostPath中的数据。另外,hostPath上使用的数据不会被计算到存储资源使用统计,可能出现磁盘占满而没有提醒的情况。但如果Pod只会被调度到某个Node上,那么还是可以使用hostPath。

PV/PVC

PV是Persistent Volume,即持久化卷,是K8S最常用的存储访问方式,几乎所有的外部存储都可以通过PV来访问。

PVC是Persistent Volume Claim,即持久化卷声明,通过PVC来申请对PV的使用。PV和PVC是一一对应关系,PV只有通过PVC关联后,才能被使用。Pod通过volumeMounts来关联PVC。

PV通常由管理员来创建,管理员事先分配好一定数量的PV供Pod使用,不同的存储类别(NFS,Cloud,Ceph等)最后都对应成一系列的PV。

PVC通常由Pod来创建,在需要使用存储的时候通过PVC来申请PV。

以下是PV/PVC的关系图,

下面通过NFS来介绍PV/PVC的使用,

先搭建一个双节点的K8S集群(略),参考 K8S 概述。

接着在master节点上搭建NFS服务,主要命令如下,

# On Server:
sudo apt update
sudo apt install nfs-kernel-server
mkdir -p /home/sunny/nfs/root
echo "/home/sunny/nfs/root *(rw,sync,no_subtree_check)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo systemctl start nfs-kernel-server
sudo systemctl enable nfs-kernel-server# On Client:
sudo apt install nfs-common
sudo mount 192.168.126.16:/home/sunny/nfs/root /mnt

创建PV,pv.yaml,这里会指定NFS IP和路径,

apiVersion: v1
kind: PersistentVolume
metadata:name: nginx-pvlabels:type: nginx-pv
spec:capacity:storage: 1GiaccessModes:- ReadWriteManynfs:path: /home/sunny/nfs/rootserver: 192.168.126.16
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl apply -f pv.yaml
persistentvolume/nginx-pv created
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nginx-pv   1Gi        RWX            Retain           Available                                   3s

创建PVC,pvc.yaml,

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nginx-pvcnamespace: default
spec:accessModes:- ReadWriteManyresources:requests:storage: 1Gi
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl apply -f pvc.yaml
persistentvolumeclaim/nginx-pvc created
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl get pvc
NAME        STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nginx-pvc   Bound    nginx-pv   1Gi        RWX                           2s
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
nginx-pv   1Gi        RWX            Retain           Bound    default/nginx-pvc                           2m59s

可以看出,此时PVC的状态是Bound,说明其已经找到了一个与此关联的PV,而PV的状态也由之前的Available变为Bound,且CLAIM是default/nginx-pvc,先就可以在Pod里使用这个PVC了。默认PV和PVC的回收策略都是Retain,需要手动删除数据,即便PV和PVC都被删除。

创建Nginx Pod,nginx.yaml,在配置文件中引用PVC,

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment
spec:selector:matchLabels:app: nginxreplicas: 3template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentvolumeMounts:- name: htmlmountPath: /usr/share/nginx/htmlports:- containerPort: 80volumes:- name: htmlpersistentVolumeClaim:claimName: nginx-pvc
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment created
sunny@xxx:~/k8s/storage/pvc_pv$ kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP              NODE                     NOMINATED NODE   READINESS GATES
nginx-deployment-574dc457cf-8ds25   1/1     Running   0          53s   10.244.96.163   r05u36-nex-wvie-spr-cd   <none>           <none>
nginx-deployment-574dc457cf-fnn9z   1/1     Running   0          53s   10.244.2.220    r05u30-nex-wvie-spr-cd   <none>           <none>
nginx-deployment-574dc457cf-h4kkk   1/1     Running   0          53s   10.244.96.162   r05u36-nex-wvie-spr-cd   <none>           <none>

在NFS服务器对应的路径上创建一个index.html文件,供Nginx使用,

sunny@r05u30-nex-wvie-spr-cd:~/nfs/root$ pwd
/home/sunny/nfs/root
sunny@r05u30-nex-wvie-spr-cd:~/nfs/root$ cat index.html
Hello Nginx with PV/PVC.
sunny@r05u30-nex-wvie-spr-cd:~/nfs/root$

由于3个Nginx Pod都关联到了同一个NFS路径,所以通过任何一个Pod都能访问到相同的index.html。如果Pod扩容,新的Pod使用的也是相同的NFS路径,所以这里就非常容易的实现了数据和代码的分离,无论代码部署到哪里,无论代码如何改变,数据都是统一存储的。

sunny@xxx:~/k8s/storage/pvc_pv$ wget 10.244.2.220
--2024-03-21 01:45:21--  http://10.244.2.220/
Connecting to 10.244.2.220:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 25 [text/html]
Saving to: ‘index.html’index.html                                        100%[===========================================================================================================>]      25  --.-KB/s    in 0s2024-03-21 01:45:21 (1.68 MB/s) - ‘index.html’ saved [25/25]sunny@xxx:~/k8s/storage/pvc_pv$ cat index.html
Hello Nginx with PV/PVC.
sunny@xxx:~/k8s/storage/pvc_pv$ wget 10.244.96.163
--2024-03-21 01:47:36--  http://10.244.96.163/
Connecting to 10.244.96.163:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 25 [text/html]
Saving to: ‘index.html.1’index.html.1                                      100%[===========================================================================================================>]      25  --.-KB/s    in 0s2024-03-21 01:47:36 (79.9 KB/s) - ‘index.html.1’ saved [25/25]sunny@xxx:~/k8s/storage/pvc_pv$ cat index.html
Hello Nginx with PV/PVC.

StorageClass/PVC

PV/PVC确实能实现几乎所有存储的统一访问,但有一个问题是PV需要由管理员事先创建好,如果创建PVC的时候没有可用的PV,则PVC的状态会一直是Pending。不同的Pod可能需要不同规格和类型的PV,管理员需要创建和维护数量巨大的PV,这无疑是增加了K8S集群管理员的负担。

StorageClass可以解决上面的问题。当我们在创建PVC的时候可以指定一个StorageClass,PVC在创建过程中会根据StorageClass的描述自动创建需要的PV,不用管理员手动创建。

以下是StorageClass/PVC的关系图,

下面通过NFS来介绍StorageClass/PVC的使用,

搭建K8S和NFS参考上面内容。

StorageClass通过Provisioner来创建PV,Provisioner有很多,不同的存储类别有不同的实现,是第三方的组件。Provisioner要创建PV,意味着其能访问K8S集群,需要为其分配权限。

创建RBAC(Role Based Access Control),rbac.yaml,

apiVersion: v1
kind: Namespace
metadata:name: nginxns
---
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisionernamespace: nginxns
---
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: managed-run-nfs-client-provisioner
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: nginxns
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: nginxns
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: nginxns
subjects:- kind: ServiceAccountname: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: nginxns
roleRef:kind: Rolename: leader-locking-nfs-client-provisionerapiGroup: rbac.authorization.k8s.io
sunny@xxxx:~/k8s/storage/storageclass$ kubectl apply -f rbac.yaml
namespace/nginxns created
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/managed-run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created

创建Storage Class, storage_class.yaml,这里会定义storage class名称,在创建PVC的时候会使用,以及与之关联的provisioner,

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: managed-nfs-storagenamespace: nginxns
provisioner: provisioner-nfs-storage
parameters:archiveOnDelete: "false"
sunny@xxx:~/k8s/storage/storageclass$ kubectl apply -f storage_class.yaml
storageclass.storage.k8s.io/managed-nfs-storage created
sunny@r05u30-nex-wvie-spr-cd:~/k8s/storage/storageclass$

创建 NFS provisioner,nfs-provisioner.yaml,这里面会指定provisioner名称,NFS地址,路径,serviceAccountName等,

apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisionernamespace: nginxns
spec:replicas: 1selector:matchLabels:app: nfs-client-provisionerstrategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisioner#image: quay.io/external_storage/nfs-client-provisioner:latestimage: gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0volumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: provisioner-nfs-storage- name: NFS_SERVERvalue: 192.168.126.16- name: NFS_PATHvalue: /home/sunny/nfs/rootvolumes:- name: nfs-client-rootnfs:server: 192.168.126.16path: /home/sunny/nfs/root
sunny@xxx:~/k8s/storage/storageclass$ kubectl apply -f nfs-provisioner.yaml
deployment.apps/nfs-client-provisioner created

创建PVC,pvc.yaml,在这里会指定storageClassName,

kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: test-claimnamespace: nginxns
spec:accessModes:- ReadWriteManystorageClassName: managed-nfs-storageresources:requests:storage: 5Mi
sunny@xxx:~/k8s/storage/storageclass$ kubectl apply -f pvc.yaml
persistentvolumeclaim/test-claim created
sunny@xxx:~/k8s/storage/storageclass$ kubectl get pvc -n nginxns
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim   Bound    pvc-03905a65-efe4-4a5d-a10c-f5b50f026c4c   5Mi        RWX            managed-nfs-storage   12s
sunny@xxx:~/k8s/storage/storageclass$ kubectl get pv -n nginxns
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS          REASON   AGE
pvc-03905a65-efe4-4a5d-a10c-f5b50f026c4c   5Mi        RWX            Delete           Bound    nginxns/test-claim   managed-nfs-storage            41s

创建完PVC以后,可以看到pv也自动创建好了,且NFS根目录下也创建了相关的供PV使用的目录,在该目录中增加index.html,

sunny@xxx:~/nfs/root$ pwd
/home/sunny/nfs/root
sunny@xxx:~/nfs/root$ ll
total 0
drwxrwxrwx 3 sunny  sunny   73 Mar 21 02:30 ./
drwxrwxrwx 3 sunny  sunny   18 Mar 20 02:06 ../
drwxrwxrwx 2 nobody nogroup  6 Mar 21 02:30 nginxns-test-claim-pvc-03905a65-efe4-4a5d-a10c-f5b50f026c4c/
sunny@xxx:~/nfs/root$ cd nginxns-test-claim-pvc-03905a65-efe4-4a5d-a10c-f5b50f026c4c/
sunny@xxx:~/nfs/root/nginxns-test-claim-pvc-03905a65-efe4-4a5d-a10c-f5b50f026c4c$ cat index.html
Hello Storage Class.

创建Nginx,nginx.yaml,

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment-storageclassnamespace: nginxns
spec:selector:matchLabels:app: nginx-storageclassreplicas: 3template:metadata:labels:app: nginx-storageclassspec:containers:- name: nginximage: nginx:1.14.2imagePullPolicy: IfNotPresentvolumeMounts:- name: htmlmountPath: /usr/share/nginx/htmlports:- containerPort: 80volumes:- name: htmlpersistentVolumeClaim:claimName: test-claim
sunny@xxx:~/k8s/storage/storageclass$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment-storageclass created
sunny@xxx:~/k8s/storage/storageclass$ kubectl get pod -n nginxns -o wide
NAME                                             READY   STATUS    RESTARTS   AGE   IP              NODE                     NOMINATED NODE   READINESS GATES
nfs-client-provisioner-7c5d5f57b-shrd8           1/1     Running   0          37m   10.244.96.164   r05u36-nex-wvie-spr-cd   <none>           <none>
nginx-deployment-storageclass-86bb9496f8-5mpvg   1/1     Running   0          18s   10.244.2.221    r05u30-nex-wvie-spr-cd   <none>           <none>
nginx-deployment-storageclass-86bb9496f8-c8zbp   1/1     Running   0          18s   10.244.96.166   r05u36-nex-wvie-spr-cd   <none>           <none>
nginx-deployment-storageclass-86bb9496f8-z4sxm   1/1     Running   0          18s   10.244.96.165   r05u36-nex-wvie-spr-cd   <none>           <none>

访问Pod,

sunny@xxx:~/k8s/storage/storageclass$ wget 10.244.2.221
--2024-03-21 03:06:12--  http://10.244.2.221/
Connecting to 10.244.2.221:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 21 [text/html]
Saving to: ‘index.html’index.html                                        100%[===========================================================================================================>]      21  --.-KB/s    in 0s2024-03-21 03:06:12 (53.6 KB/s) - ‘index.html’ saved [21/21]sunny@xxx:~/k8s/storage/storageclass$ cat index.html
Hello Storage Class.

StorageClass确实能自动创建PV,减少管理员准备PV的工作,但是我们也发现StorageClass的配置多了不少,需要定义RBAC,StorageClass等资源对象,需要创建provisioner这个额外的Pod等。所以,如果在一些复杂场景下需要频繁创建和维护PV,我们可以使用StorageClass + PVC的模来使用存储,如果在一些简单的场景下只需要一些固定的PV,我们可以使用PV + PVC的模式来使用存储。

CSI

在 Kubernetes 中,存储插件的开发主要有以下几种方式:

  1. CSI插件:Container Storage Interface (CSI) 是 Kubernetes 的标准插件接口,是全新的插件方案,插件和驱动调用通过grpc协议,功能丰富,支持存储卷动态提供、快速、动态扩容等等。
  2. FlexVolume插件:FlexVolume 是 Kubernetes 的早期存储插件接口之一,它提供了一个简单的接口,但局限性却很大,用于将存储驱动程序接入到 Kubernetes 中。通过实现 FlexVolume 接口,可以将各种存储系统接入到 Kubernetes 集群中,包括 NFS、GlusterFS、Ceph 等等。
  3. in-tree插件:in-tree 存储插件是 Kubernetes 的早期存储插件接口之一,它将存储驱动程序嵌入到 Kubernetes 主体代码库中。in-tree 插件可以实现对本地存储、NFS、iSCSI 等存储系统的支持。不过,由于 in-tree 插件需要嵌入到 Kubernetes 主体代码库中,因此对于插件开发者而言,维护成本较高,并且需要适应 Kubernetes 主体代码库的版本变化。

CSI 全称 Container Storage Interface,是容器编排系统(CO)如k8s等扩展容器存储的一种实现方式,基于gRPC实现,是当前主流的存储扩展方式。为什么会使用CSI?首先,CSI 可以满足不同编排系统的需求,除了k8s,还可以比如 Mesos,Swarm。其次,CSI 是容器化部署,可以减少环境依赖,增强安全性,丰富插件的功能。CSI 的设计思想,把插件的职责从之前讲的 “两阶段处理”,扩展成了 Provision、Attach 和 Mount 三个阶段。

CSI 主要包含两个部分:CSI Controller Server 与 CSI Node Server。

  • Controller Server 是控制端的功能,负责将卷与具体节点进行配置,每个集群中只需要有一个Controller提供服务。
  • Node Server 负责k8s负载节点上的卷配置,每个节点都有一个Node提供服务。

CSI部署架构,

参考:

CSI规范

k8s-编写CSI插件

CSI 插件开发简介

相关文章:

K8S Storage

概述 一般情况下&#xff0c;K8S中的Pod都不应该将数据持久化到Pod中&#xff0c;因为Pod可能被随时创建和删除&#xff08;扩容或缩容&#xff09;&#xff0c;即便是StatefulSet或Operator的Pod&#xff0c;也都不建议在Pod里存放数据&#xff0c;可以将数据持久化到Host上。…...

Day54-nginx限速-访问日志-错误日志精讲

Day54-nginx限速-访问日志-错误日志精讲 测试请求限制连接限制&#xff08;limit_conn&#xff09;下载速度限制(limit_rate) ngx_http_core_module综合配置1.Nginx状态监控1.1 Nginx status介绍1.2 Nginx status配置1.3 基本状态数据如下所示&#xff1a;&#xff08;注意本地…...

SQL经典面试题

这里写目录标题 1 背概念2 学例子 1 背概念 1 事务 事务是最小的不可在分的工作单元&#xff0c;事务的操作要么同时成功,要么同时失败。 ACID: 原子性、一致性、隔离性、持久性 2 约束 主键约束&#xff1b;外键约束&#xff08;少用&#xff0c;会增加程序的耦合性&#xff…...

Java基础知识总结(14)

map集合 /* java.util.Map接口中常用的方法 1、Map和Collection 没有继承关系 2、Map集合以key和value的方式存储数据&#xff1a;键值对key和valuea都是引用数据类型key和value都是存储对象的内存地址key起到主导地位&#xff0c;value是key的一个附属品 3、Map接口中常用的方…...

MacOS - GCC 版本升级解决方案

Mac 中自带的 GCC 版本是 4.2.1&#xff0c;由于版本太低&#xff0c;在很多操作的时候会报错。因此需要对其进行升级&#xff0c;这里使用 Homebrew 来下载最新的 GCC。 安装 Homebrew MacOS 的终端中输入如下的命令来安装 Homebrew $ /usr/bin/ruby -e "$(curl -fsSL …...

小程序绕过 sign 签名

之前看到了一篇文章 小程序绕过sign签名思路 之前在做小程序渗透时也遇到了这种情况&#xff0c;但是直接放弃测试了&#xff0c;发现这种思路后&#xff0c;又遇到了这种情况&#xff0c;记录下过程 并没有漏洞分享&#xff0c;仅仅是把小程序也分享出来&#xff0c;方便大家…...

【Canvas与艺术】绘制动态太极图

【图例】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>绘制旋转太极图</title><style type"text/css"&g…...

Llama 2 模型

非常清楚&#xff01;&#xff01;&#xff01;Llama 2详解 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/649756898?utm_campaignshareopn&utm_mediumsocial&utm_psn1754103877518098432&utm_sourcewechat_session一些补充理解&#xff1a; 序列化&#xff…...

SQLiteC/C++接口详细介绍sqlite3_stmt类(十一)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十二&#xff09; 43、sqlite3_reset sqlite3_reset 函数用于重置已经编…...

【理解机器学习算法】之Clustering算法(Agglomerative Clustering)

聚合聚类(Agglomerative Clustering)是一种层次聚类算法&#xff0c;通过逐步合并或“聚集”它们来构建嵌套聚类。这种方法采用自底向上的方式构建聚类层次&#xff1a;它从将每个数据点作为单个聚类开始&#xff0c;然后迭代合并最接近的聚类对&#xff0c;直到所有数据点合并…...

千帆AppBuilder开发参考-应用API调用说明

介绍 百度智能云千帆AppBuilder平台提供了AppBuilder-SDK&#xff0c;开发者可使用SDK&#xff0c;快捷的开发功能&#xff0c;提升开发效率。 AppBuilder-SDK提供了完整的AI原生应用开发套件&#xff0c;包括丰富的开发组件和应用示例代码。开发组件包括大模型组件、AI能力组…...

python自定义日历库,与对应calendar库函数功能基本一致

目录 自定义日历库 常用列表 日期列表 常用函数 闰年判断 月份天数 元旦序号 日历表头 星期序号 序号及天数 月历字串 打印月历 年历字串 打印年历 对比测试 测试结果 完整代码 运行结果 自定义日历库 自定义日历库函数&#xff0c;并使得其与python calend…...

css3鼠标悬停图片特效,图片悬停效果源码

特效介绍 css3鼠标悬停图片特效,图片悬停效果源码&#xff0c;可以在网页上面作为自己的动态加载名片&#xff0c;放到侧边栏或者网站合适的位置即可 动态效果 代码下载 css3鼠标悬停图片特效,图片悬停效果源码...

使用CSS3画出一个叮当猫HTML源码

我们经常使用PS或者Flash制作动画&#xff0c;本文则介绍了如何用CSS3画出个叮当猫&#xff0c;实现过程很有趣&#xff0c;感兴趣的朋友可以参考一下 首先&#xff0c;先把HTML结构搭建好&#xff1a; <div class"wrapper"> <!--叮当猫整体--> <di…...

Spring Boot 自动化单元测试类的编写过程

前言 Web环境模拟测试 企业开发不仅要保障业务层与数据层的功能安全有效&#xff0c;也要保障表现层的功能正常。但是我们一般对表现层的测试都是通过postman手工测试的&#xff0c;并没有在打包过程中代码体现表现层功能被测试通过。那么能否在测试用例中对表现层进行功能测…...

复试专业前沿问题问答合集8-3——RNN、Hadoop、GPT大语言模型

复试专业前沿问题问答合集8-3——RNN、Hadoop、GPT大语言模型 深度学习中的的RNN、Hadoop、GPT大语言模型的原理关系问答: GPT(Generative Pre-trained Transformer)和RNN(Recurrent Neural Network)是两种在自然语言处理(NLP)领域广泛使用的深度学习模型。它们在处理…...

序列的使用

目录 序列的创建 序列的使 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 在许多数据库之中都会存在有一种数据类型 — 自动增长列&#xff0c;它能够创建流水号。如果想在 Oracle 中实现这样的自动增长列&#xff0c;可…...

golang kafka sarama 源码解析

消费者组重平衡 github.com/!shopify/saramav1.27.2/consumer_group.go func (c *consumerGroup) newSession(ctx context.Context, topics []string, handler ConsumerGroupHandler, retries int) (*consumerGroupSession, error) {// 获取broker组协调器coordinator, err :…...

Flutter知识点整理

JVM 1.Flutter Hot reload 实现原理 一、原理概述 Hot Reload 只能在 Debug 模式下使用&#xff0c;因为 Debug 模式下&#xff0c;Flutter 采用的是 JIT&#xff08; 动态编译&#xff09;&#xff0c;代码是运行在 Dart VM 上&#xff0c;JIT 将 Dart 编译成可以运行在 Dart…...

现代游戏引擎架构

一、并行编程 1.1 为什么需要并行编程 游戏的渲染计算对算力要求很高&#xff0c;所以我们需要把操作系统的资源利用到极致。 但是摩尔定律已经不在适用了&#xff0c;硬件的发展目前已经达到瓶颈。所以我们需要通过数量来提高计算效率。 1.2 并行编程基础 进程与线程&#…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...