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

CKA认证 | Day7 K8s存储

第七章 Kubernetes存储

1、数据卷与数据持久卷

为什么需要数据卷?

容器中的文件在磁盘上是临时存放的,这给容器中运行比较重要的应用程序带来一些问题。

  1. 问题1:当容器升级或者崩溃时,kubelet会重建容器,容器内文件会丢失;

  2. 问题2:一个Pod中运行多个容器并需要共享文件;

Kubernetes 卷(Volume) 这一抽象概念能够解决这两个问题。

1. 数据持久化
  • 容器的无状态性:Kubernetes 中的容器是无状态的,这意味着它们在重启或重新调度时,容器内的数据会丢失。
  • 持久化数据:通过使用数据卷,可以将数据持久化到外部存储系统中,确保数据在容器生命周期外仍然存在。
2. 数据共享
  • 多容器共享数据:在某些情况下,同一个 Pod 中的多个容器需要共享数据。数据卷允许这些容器共享同一个存储卷。
  • 跨容器协作:例如,一个容器生成日志文件,另一个容器需要读取这些日志文件。
3. 数据隔离
  • 独立存储:不同的 Pod 可以使用不同的数据卷,确保数据隔离,避免数据冲突。
  • 安全性:通过将敏感数据存储在独立的数据卷中,可以提高安全性。
4. 简化配置
  • 统一管理:使用数据卷可以简化应用程序的配置,因为存储配置可以在 Kubernetes 中集中管理。
  • 灵活性:可以根据需要选择不同的存储后端(如本地存储、云存储、网络文件系统等),而不需要修改应用程序代码。
5. 生命周期管理
  • 数据生命周期:数据卷可以独立于 Pod 的生命周期管理,确保数据在 Pod 重启或删除后仍然可用。
  • 自动挂载:Kubernetes 可以自动挂载和卸载数据卷,简化操作。

常用的数据卷:

官网:Volumes | Kubernetes

  • 节点本地存储(hostPath,emptyDir)
  • 网络存储(NFS,Ceph,GlusterFS)
  • 公有云存储(AWS EBS)
  • K8S资源存储(configmap,secret)

1.1 临时数据卷:emptyDir

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

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

选项:

  • sizeLimit:500Mi       //限制共享空间大小(比较少用)


示例:Pod内容器之间共享数据
apiVersion: v1
kind: Pod
metadata: name: emptydir-test
spec:containers:- name: write-container    //程序讲数据写入到文件image: centoscommand: ["bash","-c","for i in {1..100};do echo $i >> 
/data/hello;sleep 1;done"]volumeMounts:- name: data-volume    //通过volume卷名称引用mountPath: /data- name: read-container     //程序从文件中读取数据image: centoscommand: ["bash","-c","tail -f /data/hello"]volumeMounts:- name: data-volumemountPath: /datavolumes:      //定义卷的来源- name: data-volumeemptyDir: {}    //{}中为空值

验证1:验证容器之间是否能够共享数据

[root@k8s-master-1-71 ~]# kubectl apply -f emptydir-test.yaml
[root@k8s-master-1-71 ~]# kubectl get pods
NAME            READY   STATUS    RESTARTS      AGE
emptydir-test   2/2     Running   1 (21s ago)   3m14s
# 进入write-container查看
[root@k8s-master-1-71 ~]# kubectl exec -it emptydir-test -- bash[root@emptydir-test /]# tail -f /data/hello
...
99
100
command terminated with exit code 137
[root@emptydir-test /]# touch /data/aaa ; ls /data/     //往容器中写入临时文件
aaa  hello## 注:脚本循环结束后退出容器,按照默认的策略Always进行容器重启。
# 进入read-container查看
[root@k8s-master-1-71 ~]# kubectl exec -it emptydir-test -c read-container -- bash
[root@emptydir-test /]# ls /data/
aaa  hello

验证2:实际存储位置(基于节点的存储)

补充:Kubelet的工作目录为/var/lib/kubelet/,负载维护Pod数据的

[root@k8s-master-1-71 ~]# kubectl get pods -o wide
NAME            READY   STATUS    RESTARTS      AGE    IP              NODE             NOMINATED NODE   READINESS GATES
emptydir-test   2/2     Running   2 (45s ago)   5m3s   10.244.114.48   k8s-node2-1-73   <none>           <none># 根据查看到的Pod所在节点,找到相应的容器ID
[root@k8s-node2-1-73 ~]# docker ps | grep emptydir-test(例如:7c26307b-b290-4bac-9a3b-6f18ffc26776)
6deb6dc27ab2   centos                                              "bash -c 'for i in {…"   44 seconds ago   Up 43 seconds             k8s_write-container_emptydir-test_default_7c26307b-b290-4bac-9a3b-6f18ffc26776_3
7bb46657087b   centos                                              "bash -c 'tail -f /d…"   7 minutes ago    Up 7 minutes              k8s_read-container_emptydir-test_default_7c26307b-b290-4bac-9a3b-6f18ffc26776_0
fd4c6c671807   registry.aliyuncs.com/google_containers/pause:3.7   "/pause"                  7 minutes ago    Up 7 minutes              k8s_POD_emptydir-test_default_7c26307b-b290-4bac-9a3b-6f18ffc26776_0# 找到相关Pod关联的empty-dir目录,即可看到挂载的empty-dir目录内容
[root@k8s-node2-1-73 kubernetes.io~empty-dir]# pwd
/var/lib/kubelet/pods/7c26307b-b290-4bac-9a3b-6f18ffc26776/volumes/kubernetes.io~empty-dir[root@k8s-node2-1-73 kubernetes.io~empty-dir]# ls data-volume/
aaa  bbb  hello

Pod是节点级别的,Pod中的容器都是捆绑在一个节点上,所以 Pod删除了,卷也会被删除

[root@k8s-master-1-71 ~]# kubectl delete -f emptydir-test.yaml
[root@k8s-node2-1-73 kubernetes.io~empty-dir]# ls data-volume/
ls: 无法访问data-volume/: 没有那个文件或目录

1.2 节点数据卷:hostPath

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

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

选项:

  • path:    //将宿主机的目录或文件映射到容器中去

  • type:    //指定类型(目录或文件)


示例:将宿主机/tmp目录挂载到容器/data目录
apiVersion: v1
kind: Pod
metadata:name: hostpath-test
spec:containers:- name: busyboximage: busyboxargs:- /bin/sh- -c- sleep 36000volumeMounts:- name: data-volumemountPath: /datavolumes:- name: data-volumehostPath:path: /tmp        //Pod所在节点的/tmp目录type: Directory

验证:

[root@k8s-master-1-71 ~]# kubectl apply -f hostpath-test.yaml[root@k8s-master-1-71 ~]# kubectl get pods -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP              NODE             NOMINATED NODE   READINESS GATES
hostpath-test   1/1     Running   0          86s   10.244.114.49   k8s-node2-1-73   <none>           <none># 查看Pod所在节点的/tmp目录
[root@k8s-node2-1-73 ~]# ls /tmp/
systemd-private-85c79618aae44d7cbb01bccc046ecb10-chronyd.service-UMOhQf
systemd-private-fdf2b8d40ff841319feb738a463a8f6f-chronyd.service-GJT2Mn# 进入容器查看/data目录是否有相关映射文件
[root@k8s-master-1-71 ~]# kubectl exec -it hostpath-test -- sh
/ # ls /data/
systemd-private-85c79618aae44d7cbb01bccc046ecb10-chronyd.service-UMOhQf
systemd-private-fdf2b8d40ff841319feb738a463a8f6f-chronyd.service-GJT2Mn

1.3 网络数据卷:NFS

NFS:是一个主流的文件共享服务器(注:每个Node上都要安装nfs-utils包)

# 服务端部署
[root@k8s-node1-1-72 ~]# yum install -y nfs-utils
[root@k8s-node1-1-72 ~]# vi /etc/exports     //NFS共享配置目录
/ifs/kubernetes *(rw,no_root_squash)
# 解释:
共享目录 访问来源限制(权限)
[root@k8s-node1-1-72 ~]# mkdir -p /ifs/kubernetes[root@k8s-node1-1-72 ~]# systemctl enable nfs --now# 客户端测试
[root@k8s-node2-1-73 ~]# yum install -y nfs-utils
[root@k8s-node2-1-73 ~]# mount -t nfs 192.168.1.72:/ifs/kubernetes /mnt
[root@k8s-node2-1-73 ~]# df -Th | grep /ifs/kubernetes
192.168.1.72:/ifs/kubernetes nfs4       37G  4.3G   33G   12% /mnt

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

选项:

  • server: //指定NFS地址

  • path: /指定NFS共享目录


示例:将Nginx网站程序根目录持久化到 NFS存储,为多个Pod提供网站程序文件
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nfs-podname: nfs-pod
spec:selector:matchLabels:app: nginxreplicas: 3template:metadata:labels:app: nginxspec:containers:- name: webimage: nginxvolumeMounts:- name: data-volumemountPath: /usr/share/nginx/html    //挂载的目录volumes:- name: data-volume      //volume卷名称nfs:server: 192.168.1.72     //指定NFS地址path: /ifs/kubernetes    //NFS共享目录

验证1:容器之间的数据是否共享

[root@k8s-node2-1-73 ~]# kubectl apply -f nfs-pod.yaml
[root@k8s-master-1-71 ~]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGEnfs-pod-9b57f886-d8rzx   1/1     Running   0          10m
nfs-pod-9b57f886-tk99z   1/1     Running   0          55s
nfs-pod-9b57f886-wpr9q   1/1     Running   0          10m# 进入容器1,创建文件测试
[root@k8s-node2-1-73 ~]# kubectl exec -it nfs-pod-9b57f886-d8rzx -- bash
root@nfs-pod-9b57f886-d8rzx:/# df -Th |grep /ifs/kubernetes
192.168.1.72:/ifs/kubernetes nfs4      37G  4.3G   33G  12% /usr/share/nginx/html
root@nfs-pod-9b57f886-d8rzx:~# touch /usr/share/nginx/html/aaaaa
root@nfs-pod-9b57f886-d8rzx:~# ls /usr/share/nginx/html/
aaaaa# 进入容器2,查看文件
[root@k8s-node2-1-73 ~]# kubectl exec -it nfs-pod-9b57f886-tk99z -- bash
root@nfs-pod-9b57f886-tk99z:/# ls /usr/share/nginx/html/
aaaaa

验证2:增加/删除Pod是否能继续使用共享存储数据

# 删除Pod,查看是否能继续使用共享存储数据
[root@k8s-master-1-71 ~]# kubectl delete pod nfs-pod-9b57f886-tk99z[root@k8s-master-1-71 ~]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
nfs-pod-9b57f886-d8rzx   1/1     Running   0          10m
nfs-pod-9b57f886-fg7n7   1/1     Running   0          55s    //新增Pod
nfs-pod-9b57f886-wpr9q   1/1     Running   0          10m[root@k8s-node2-1-73 ~]# kubectl exec -it nfs-pod-9b57f886-fg7n7 -- bash
root@nfs-pod-9b57f886-fg7n7:/# ls /usr/share/nginx/html/
aaaaa# 增加Pod,查看是否能继续使用共享存储数据
[root@k8s-master-1-71 ~]# kubectl scale deployment nfs-pod --replicas=5
[root@k8s-node2-1-73 ~]# kubectl exec -it nfs-pod-9b57f886-sj7m6 -- bash
root@nfs-pod-9b57f886-sj7m6:/# df -Th | grep /ifs/kubernetes
192.168.1.72:/ifs/kubernetes nfs4      37G  4.3G   33G  12% /usr/share/nginx/html
root@nfs-pod-9b57f886-sj7m6:/# ls /usr/share/nginx/html/
aaaaa

1.4 持久数据卷概述

持久卷(Persistent Volumes, PV) 是用于管理存储的重要概念。

安全性:如果要设置安全方面的认证,都需要提前将安全配置写入YAML,因此将会暴露在YAML文件中,导致安全性减低;

专业性:在应用的部署上,使用者对K8S、存储的不了解,对于建设者来说,倡导职责上的分离。

1.4.1 PV、PVC

持久卷(Persistent Volumes, PV)

  • 定义:持久卷是集群中的一块存储,由管理员配置和管理。它们独立于 Pod 的生命周期,可以被多个 Pod 使用。
  • 生命周期:持久卷的生命周期独立于 Pod,即使 Pod 被删除,数据仍然保留。

对存储资源创建和使用的抽象,使得存储作为集群中的资源管理(定义后端存储)

持久卷声明(Persistent Volume Claims, PVC)

  • 定义:持久卷声明是用户对持久卷的请求。用户不需要了解底层存储的细节,只需要声明所需的存储大小和访问模式。
  • 生命周期:持久卷声明的生命周期与 Pod 的生命周期无关,可以独立存在。
  • 绑定:持久卷声明会被绑定到一个满足其请求的持久卷上。一旦绑定,PVC 和 PV 之间是一对一的关系。

让用户不需要关心具体的Volume实现细节(访问模式、存储容量大小)

Pod申请PVC作为卷来使用,Kubernetes通过PVC查找绑定的PV,并Mount给Pod。

支持持久卷的存储插件:Persistent Volumes | Kubernetes

1.4.2 PV与PVC使用流程

官网:配置 Pod 以使用 PersistentVolume 作为存储 | Kubernetes

PVC配置示例:

---   //容器应用
apiVersion: apps/v1
kind: Deployment
metadata: labels:app: my-podname: my-pod
spec:selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: webimage: nginxvolumeMounts:- name: data-volumemountPath: /usr/share/nginx/html    //挂载的目录volumes:- name: data-volumepersistentVolumeClaim:claimName: my-pvc---   //PVC 卷需求模板
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: my-pvc      //与claimName对应进行关联
spec:accessModes:- ReadWriteManyresources:requests:storage: 5Gi
  • 查看PVC命令:kubectl get pvc
[root@k8s-master-1-71 ~]# kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
my-pod-6fbc98b678-42m29   0/1     Pending   0          10s
[root@k8s-master-1-71 ~]# kubectl get pvc
NAME     STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
my-pvc   Pending                                                     18s

注意:如果没有可用的PV,PVC无法进行资源分配会处于在Pending状态

PV配置示例:

apiVersion: v1
kind: PersistentVolume
metadata:name: my-pv      //随便定义
spec:capacity:storage: 5Gi     //后端存储定义资源accessModes:- ReadWriteManynfs:path: /ifs/kubernetesserver: 192.168.1.72
  • 查看PV命令:kubectl get pv
[root@k8s-master-1-71 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
my-pv   5Gi        RWX            Retain           Bound    default/my-pvc                           11s
[root@k8s-master-1-71 ~]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
my-pvc   Bound    my-pv    5Gi        RWX                           2m30s
[root@k8s-master-1-71 ~]# kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
my-pod-6fbc98b678-42m29   1/1     Running   0          3m5s

测试:

[root@k8s-master-1-71 ~]# kubectl exec -it my-pod-6fbc98b678-42m29 -- bash
root@my-pod-6fbc98b678-42m29:/# df -Th | grep /ifs/kubernetes
192.168.1.72:/ifs/kubernetes nfs4      37G  4.3G   33G  12% /usr/share/nginx/html
root@my-pod-6fbc98b678-42m29:/# ls /usr/share/nginx/html/
aaaaa

思考:多PV配置

挂载PV的时候,不能将挂载点挂到同一目录,为保证应用的唯一性,需要在挂载点的节点上创建各自的目录,避免冲突

多PV配置示例:

apiVersion: v1
kind: PersistentVolume
metadata:name: pv001      //随便定义
spec:capacity:storage: 5Gi     //后端存储定义资源accessModes:- ReadWriteManynfs:path: /ifs/kubernetes/pv001server: 192.168.1.72
---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv002      //随便定义
spec:capacity:storage: 15Gi     //后端存储定义资源accessModes:- ReadWriteManynfs:path: /ifs/kubernetes/pv002server: 192.168.1.72

总结:

1、PV与PVC怎么匹配?

主要根据PVC的存储容量访问模式进行匹配

2、存储容量怎么匹配?

容量只会向上匹配,如已有未使用PV有10G、20G,申请5G,向上取最近的PV容量10G

3、PV与PVC的关系?

一对一,存在绑定关系

4、容量请求是否有实际的限制?

目前容量请求主要用作于PVC与PV进行匹配的,只是抽象的存在,而具体的限制取决于后端存储,即请求容量不能超过共享存储的实际容量

1.4.3 PV 生命周期

1)AccessModes(访问模式):

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

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

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

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

备注:块存储(单节点)、文件系统、对象存储(多节点)

2)RECLAIM POLICY(回收策略):

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

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

  • Recycle(回收):清除 PV 中的数据,效果相当于执行 rm -rf /ifs/kuberneres/*(一般结合StorageClass使用,NFS暂时无法看出效果)

  • Delete(删除):与 PV 相连的后端存储同时删除(一般结合StorageClass使用,NFS暂时无法看出效果)

persistentVolumeReclaimPolicy: 回收策略

3)STATUS(状态):

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

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

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

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

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


示例:观察回收状态和默认的Retain回收策略
[root@k8s-master-1-71 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
my-pv   5Gi        RWX            Retain           Bound    default/my-pvc                           7h7m## 解释:目前回收策略为 Retain ,状态为 Bound(表示 PV 已经被 PVC 绑定)
[root@k8s-master-1-71 ~]# kubectl delete -f my-pvc.yaml
[root@k8s-master-1-71 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM            STORAGECLASS   REASON   AGE
my-pv   5Gi        RWX            Retain           Released   default/my-pvc                           7h18m## 解释:连着删除Pod和PCV后,回收策略为 Retain ,状态为 Released(PVC 被删除,但是资源还未被集群重新声明)
[root@k8s-node1-1-72 ~]# ls /ifs/kubernetes/    //PV中的资源依旧保留,需要管理员手工清理数据
aaaaa

注意:删除PVC后,原来Bound的PV就无法继续使用,即使重新apply pvc.yaml,也是Pending状态。

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

1.5 PV 动态供给(StorageClass)

PV静态供给明显的缺点是维护成本太高了,需要提前创建PV且不灵活! 因此,K8s开始支持PV动态供给,使用StorageClass对象实现。StorageClass可以根据客户的PVC需求,通过PVC需求自动创建后端存储PV,且自动去绑定,无需像NFS还要创建目录隔离应用。

优点:

  • PV无需额外的提前独立创建;

  • PVC直接获取,也不用等待合适的PV;

支持动态供给的存储插件:Storage Classes | Kubernetes

相关GitHub部署:https://github.com/kubernetes-sigs/sig-storage-lib-external-provisioner

了解:Volume Plugin是支持存储的类型,Internal Provisioner内部是否支持

例如NFS内部是不支持的(不能直接PVC动态供给),且K8s默认不支持NFS动态供给,需要单独部署社区开发的插件

项目地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

部署StorageClass插件需要的3个文件:

cd deploy
kubectl apply -f rbac.yaml           # 授权访问apiserver
kubectl apply -f deployment.yaml     # 部署插件,需修改里面NFS服务器地址与共享目录
kubectl apply -f class.yaml          # 创建存储类(标识使用哪个存储)kubectl get sc                       # 查看存储类

补充:一个集群中可以有多个存储类,而一个存储类一般对应一个存储

流程图:


  • rbac.yaml 示例:

[root@k8s-master-1-71 nfs-external-provisioner]# cat rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: default
---
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: ServiceAccountname: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: 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-provisioner# replace with namespace where provisioner is deployednamespace: 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-provisioner# replace with namespace where provisioner is deployednamespace: default
subjects:- kind: ServiceAccountname: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: default
roleRef:kind: Rolename: leader-locking-nfs-client-provisionerapiGroup: rbac.authorization.k8s.io
  • class.yaml 配置示例:
[root@k8s-master-1-71 nfs-external-provisioner]# cat class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: managed-nfs-storage     //StroagaClass存储类,在PVC中需要指定的标识(类似ingressclass选择Nginx)
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'  //与Deployment的变量PROVISIONER_NAME保持一致 
parameters:archiveOnDelete: "false"
  • deployment.yaml 配置示例:
[root@k8s-master-1-71 nfs-external-provisioner]# cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: default
spec:replicas: 1strategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisioner    //部署了NFS供给程序的容器image: lizhenliang/nfs-subdir-external-provisioner:v4.0.1    //镜像地址volumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAME     //class.yaml中的PROVISIONER_NAMEvalue: k8s-sigs.io/nfs-subdir-external-provisioner- name: NFS_SERVERvalue: 192.168.1.72     //需要指定后端NFS存储- name: NFS_PATHvalue: /ifs/kubernetesvolumes:- name: nfs-client-rootnfs:server: 192.168.1.72     //需要指定后端NFS存储path: /ifs/kubernetes
  • PVC指定存储类配置示例:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: my-pvc
spec:storageClassName: "managed-nfs-storage"    //指定StorageClass存储类(class.yaml)accessModes:- ReadWriteMany  resources:requests:storage: 5Gi

测试1:基于NFS提供PVC动态供给

步骤1:部署NFS-StorageClass 存储类插件

[root@k8s-master-1-71 nfs-external-provisioner]# kubectl apply -f .
storageclass.storage.k8s.io/managed-nfs-storage created
deployment.apps/nfs-client-provisioner created
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/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# 创建的Pod为deployment.yaml中的NFS供给程序容器
[root@k8s-master-1-71 nfs-external-provisioner]# kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGEnfs-client-provisioner-5848c9cddc-zkts2   1/1     Running   0          2m27s# 创建的storageclass为class.yaml中指定的存储类
[root@k8s-master-1-71 nfs-external-provisioner]# kubectl get storageclass
NAME                  PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           false                  2m54s

步骤2:在PVC中指定存储类名称

[root@k8s-master-1-71 ~]# kubectl apply -f my-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: my-podname: my-pod
spec:selector:matchLabels:app: my-podtemplate:metadata:labels:app: my-podspec:containers:- name: my-podimage: nginxvolumeMounts:- name: data-volumemountPath: /usr/share/nginx/htmlvolumes:- name: data-volumepersistentVolumeClaim:claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: my-pvcspec:storageClassName: "managed-nfs-storage"accessModes:- ReadWriteManyresources:requests:storage: 5Gi[root@k8s-master-1-71 ~]# kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
my-pod-6fbc98b678-t2gvg                   1/1     Running   0          4m11s
nfs-client-provisioner-5848c9cddc-zkts2   1/1     Running   0          24m[root@k8s-master-1-71 ~]# kubectl get pvc     //查看PVC
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
my-pvc   Bound    pvc-51d00de4-52ea-4f1a-988a-bc3e5b7e961e   5Gi        RWX            managed-nfs-storage   3s[root@k8s-master-1-71 ~]# kubectl get pv      //查看PV
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM            STORAGECLASS          REASON   AGE
pvc-51d00de4-52ea-4f1a-988a-bc3e5b7e961e   5Gi        RWX            Delete           Bound      default/my-pvc   managed-nfs-storage            6s# 进入容器查看挂载
[root@k8s-master-1-71 ~]# kubectl exec -it my-pod-6fbc98b678-t2gvg -- bash
root@my-pod-6fbc98b678-t2gvg:/# df -Th | grep pvc-51d00de4-52ea-4f1a-988a-bc3e5b7e961e
192.168.1.72:/ifs/kubernetes/default-my-pvc-pvc-51d00de4-52ea-4f1a-988a-bc3e5b7e961e nfs4      37G  4.4G   33G  12% /usr/share/nginx/html

# 查看NFS服务器的挂载目录,可看到随机生成的PV目录

结论:通过基于NFS提供PVC动态供给,无需再定义单独的PV,也无需为了指定到某个NFS目录手动创建子目录,动态供给会自动帮忙实现PV及随机生成NFS子目录

注意:由于NFS提供PVC动态供给的PV的默认回收策略是 Delete ,所以在删除PVC的同时,也会将PV一起删除;在NFS目录上的文件也一并删除。


测试2:

[root@k8s-master-1-71 ~]# kubectl apply -f my-pvc2.yaml
[root@k8s-node1-1-72 ~]# ls /ifs/kubernetes/
aaaaa  default-my-pvc2-pvc-b7769112-aea6-4533-9e5c-5d14cd67fc65  default-my-pvc-pvc-51d00de4-52ea-4f1a-988a-bc3e5b7e961e[root@k8s-master-1-71 ~]# kubectl delete -f my-pvc2.yaml
[root@k8s-node1-1-72 ~]# ls /ifs/kubernetes/
aaaaa  default-my-pvc-pvc-51d00de4-52ea-4f1a-988a-bc3e5b7e961e

由于考虑数据的重要性,希望删除PVC时保留数据备份

[root@k8s-master-1-71 nfs-external-provisioner]# vi class.yaml
...archiveOnDelete: "True"     //将archiveOnDelete修改为 True,即可在删除PVC时保留备份# 需要删除class.yaml并重新apply应用
[root@k8s-master-1-71 nfs-external-provisioner]# kubectl delete -f class.yaml[root@k8s-master-1-71 nfs-external-provisioner]# kubectl apply -f class.yaml
# 测试删除PVC
[root@k8s-master-1-71 ~]# kubectl delete -f my-pvc.yaml

# 查看NFS服务器的挂载目录,原来的PV目录已重新命名(备份)

0

2、有状态应用部署初探

无状态与有状态:

Deployment控制器设计原则:管理的所有Pod一模一样提供同一个服务(replicas),使用共享存储,之间没有连接关系,也不考虑在哪台Node运 行,可随意扩容和缩容。这种应用称为“无状态”,例如Web服务

在实际的场景中,这并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往有 依赖关系,部署的角色也不一样,例如主从关系、主备关系,这种应用称为“有状态”,例如MySQL主从、Etcd集群

无状态特点:

  1. 每个Pod都一样,且提供同一种服务

  2. Pod之间没有连接关系

  3. 使用共享存储

有状态特点:

  1. 每个Pod不对等,角色属性也不同

  2. Pod之间有连接关系(类似数据库主从)

  3. 每个Pod的数据都是有差异化的,需要独立的存储进行持久化,否则会产生冲突

2.1 StatefulSet 控制器介绍

StatefulSet控制器用于部署有状态应用,满足一些有状态应用的需求:

  • Pod有序的部署、扩容、删除和停止

  • Pod分配一个稳定的且唯一的网络标识

  • Pod分配一个独享的存储

2.2 StatefulSet 部署应用实践

1)稳定的网络ID(域名)

使用 Headless Service(相比普通Service只是将spec.clusterIP定义为None)来维护Pod网络身份。 并且添加 serviceName: “headless-svc” 字段指定 StatefulSet控制器 要使用这个Headless Service。让StatefulSet控制器为其创建每个Pod固定的域名解析地址。

DNS解析名称:<statefulsetName-index>.<service-name> .<namespace-name>.svc.cluster.local

[root@k8s-master-1-71 ~]# kubectl create deployment stateful-pod --image=nginx
[root@k8s-master-1-71 ~]# kubectl expose deployment stateful-pod --port=80 --target-port=80 --dry-run=client -o yaml > headless-svc.yaml
[root@k8s-master-1-71 ~]# kubectl apply -f headless-svc.yaml
apiVersion: v1
kind: Service
metadata:labels:app: headless-svcname: headless-svc
spec:clusterIP: None     //指定SVC的clusterIP为Noneports:- port: 80protocol: TCPtargetPort: 80selector:app: stateful-pod[root@k8s-master-1-71 ~]# kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
headless-svc   ClusterIP   None             <none>        80/TCP    8s

补充:Cluster-IP为None,标识该Service为headlress无头服务,这种Service将没有负载均衡转发的功能;

2)稳定的存储

StatefulSet的存储卷 使用VolumeClaimTemplate创建,称为卷申请模板(针对StatefulSet专门设置的类型),当StatefulSet使用VolumeClaimTemplate创建 一个PersistentVolume时,同样也会为每个Pod分配并创建一个编号的PVC(一个PV对应一个PVC)

参考文档:StatefulSets | Kubernetes

参考:https://github.com/lizhenliang/k8s-statefulset


示例:StatefulSet部署(包括无头服务、StatefulSet+卷申请模)

[root@k8s-master-1-71 ~]# kubectl apply -f statefulset-test.yaml
# headless Service 服务配置示例
apiVersion: v1
kind: Service
metadata:name: nginx-headless     //headlessService服务名称,需要和StatefulSet的ServiceName保持一致labels:app: nginx
spec:ports:- port: 80name: webclusterIP: None     //设置cluster-IP为Noneselector:app: nginx     //指定StatefulSet的Pod# StatefulSet 配置示例
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: web
spec:selector:matchLabels:app: nginx # has to match .spec.template.metadata.labelsserviceName: "nginx-headless"  # 指定 headless Service 服务名称replicas: 3          # by default is 1minReadySeconds: 10  # by default is 0template:metadata:labels:app: nginx # has to match .spec.selector.matchLabelsspec:terminationGracePeriodSeconds: 10containers:- name: nginximage: nginxports:- containerPort: 80name: webvolumeMounts:- name: www-datamountPath: /usr/share/nginx/htmlvolumeClaimTemplates:     //卷申请模板(为StatefulSet专门设置的类型)- metadata:name: www-dataspec:accessModes: [ "ReadWriteOnce" ]storageClassName: "managed-nfs-storage"    # 指定 storageClass(kubectl get sc)resources:requests:storage: 1Gi

查看信息:

[root@k8s-master-1-71 ~]# kubectl get pods
NAME                                      READY   STATUS    RESTARTS      AGE
web-0                                     1/1     Running   0             4m32s
web-1                                     1/1     Running   0             3m31s
web-2                                     1/1     Running   0             2m51s# 查看 headless Service
[root@k8s-master-1-71 ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx-headless   ClusterIP   None         <none>        80/TCP    9m52s# 查看 headless Service 对应后端的Pod
[root@k8s-master-1-71 ~]# kubectl get ep
NAME                                          ENDPOINTS                                            AGE
nginx-headless                                10.244.114.10:80,10.244.117.50:80,10.244.117.51:80   13m# 查看 PVC
[root@k8s-master-1-71 ~]# kubectl get pvc
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
www-data-web-0   Bound    pvc-c8f06f51-5048-4a43-b822-9f6bcd1bd20c   1Gi        RWO            managed-nfs-storage   10m
www-data-web-1   Bound    pvc-ae9c2a66-0bcb-489e-98d0-0350d480fb68   1Gi        RWO            managed-nfs-storage   9m37s
www-data-web-2   Bound    pvc-50d69a6e-d3ef-42e2-bd13-e6c954d78677   1Gi        RWO            managed-nfs-storage   8m57s# 查看 PV
[root@k8s-master-1-71 ~]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM                    STORAG                                                 ECLASS          REASON   AGE
pvc-50d69a6e-d3ef-42e2-bd13-e6c954d78677   1Gi        RWO            Delete           Bound      default/www-data-web-2   manage                                                 d-nfs-storage            9m1s
pvc-ae9c2a66-0bcb-489e-98d0-0350d480fb68   1Gi        RWO            Delete           Bound      default/www-data-web-1   manage                                                 d-nfs-storage            9m41s
pvc-c8f06f51-5048-4a43-b822-9f6bcd1bd20c   1Gi        RWO            Delete           Bound      default/www-data-web-0   manage                                                 d-nfs-storage            10m

测试网络:

  • ① 一个普通的headless-service对应Deplyment的域名解析,以及headless-service对应statefulset的域名解析
  • ② 测试 statefulset的域名 网络连通性
# 创建普通的headless-service对应Deplyment的域名解析
[root@k8s-master-1-71 ~]# kubectl create deployment web --image=nginx[root@k8s-master-1-71 ~]# kubectl expose deployment web --port=80 --target-port=80 --dry-run=client -o yaml > test-svc.yaml
[root@k8s-master-1-71 ~]# kubectl apply -f test-svc.yaml(   //修改 clusterIP: None
[root@k8s-master-1-71 ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx-headless   ClusterIP   None         <none>        80/TCP    30m
web              ClusterIP   None         <none>        80/TCP    6m45s
[root@k8s-master-1-71 ~]# kubectl get ep
NAME                                          ENDPOINTS                                            AGE
nginx-headless                                10.244.114.10:80,10.244.117.50:80,10.244.117.51:80   31m
web                                           10.244.117.52:80                                     7m34s# 创建测试bs镜像pod
[root@k8s-master-1-71 ~]# kubectl run bs --image=busybox:1.28.4 -- sleep 24h
[root@k8s-master-1-71 ~]# kubectl exec -it bs -- sh
/ # nslookup web
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name:      web
Address 1: 10.244.117.52 10-244-117-52.web.default.svc.cluster.local
## DNS解析名称:<PodIP>.<service-name> .<namespace-name>.svc.cluster.local/ # nslookup nginx-headless
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name:      nginx-headless
Address 1: 10.244.114.10 web-1.nginx-headless.default.svc.cluster.local
Address 2: 10.244.117.50 web-0.nginx-headless.default.svc.cluster.local
Address 3: 10.244.117.51 web-2.nginx-headless.default.svc.cluster.local
## DNS解析名称:<statefulsetName-index>.<service-name> .<namespace-name>.svc.cluster.local[root@k8s-master-1-71 ~]# kubectl exec -it bs -- sh
/ # ping web-1.nginx-headless.default.svc.cluster.local
PING web-1.nginx-headless.default.svc.cluster.local (10.244.114.10): 56 data bytes
64 bytes from 10.244.114.10: seq=0 ttl=62 time=0.539 ms
64 bytes from 10.244.114.10: seq=1 ttl=62 time=0.474 ms

测试存储:

[root@k8s-node1-1-72 ~]# ls /ifs/kubernetes/     //自动生成独立的PV,
default-www-data-web-1-pvc-ae9c2a66-0bcb-489e-98d0-0350d480fb68
default-www-data-web-2-pvc-50d69a6e-d3ef-42e2-bd13-e6c954d78677
default-www-data-web-0-pvc-c8f06f51-5048-4a43-b822-9f6bcd1bd20c[root@k8s-master-1-71 ~]# kubectl exec -it web-0 -- bash
root@web-0:/# cd /usr/share/nginx/html/ ; echo 111 > index.html[root@k8s-node1-1-72 ~]# cat /ifs/kubernetes/default-www-data-web-0-pvc-c8f06f51-5048-4a43-b822-9f6bcd1bd20c/index.html
111
[root@k8s-node1-1-72 ~]# ls /ifs/kubernetes/default-www-data-web-2-pvc-50d69a6e-d3ef-42e2-bd13-e6c954d78677///目录为空
## 因此Statefulset控制器创建的每个Pod的存储为独立的

思考:在有状态环境部署下,分布式应用组件的角色不同,每个Pod也不相同,而在上述 StatefulSet部署示例中,假设运行一个etcd数据集群,有3个副本且肯定只有一个指定的镜像(镜像相同),如何区分这3个Pod的角色

解答:通过配置文件区分(每个节点的名称、IP、存储目录区分),需要保证启动的3个副本容器,每一个都能按照自己的角色(配置文件)进行启动。而配置文件则需要根据statefulset控制器部署的Pod容器编号进行区分,判断当前启动的是第几个容器,就使用哪个配置文件进行启动,从而实现Pod的角色。例如:etcd-0.conf、etcd-1.conf、etcd-2.conf

参考同类型的案例:运行 ZooKeeper,一个分布式协调系统 | Kubernetes


— StatefulSet 与 Deployment区别:有身份的!

身份三要素(唯一性):

  • 域名

  • 主机名

  • 存储(PVC)

3、应用程序数据存储

  • ConfigMap:存储配置文件
  • Secret:存储敏感数据

3.1 ConfigMap 存储应用配置

创建ConfigMap后,数据实际会持久化存储在K8s中Etcd,然后通过创建Pod时引用该数据。

应用场景:应用程序配置(类似配置管理中心如apollo、nacos)

Pod使用configmap数据有两种方式:

  1. 变量注入到容器里

  2. 数据卷挂载

两种数据类型:

  • 键值
  • 多行数据

相关命令:

创建ConfigMapkubectl create configmap --from-file=path/to/bar

kubectl create configmap my-configmap --from-literal=abc=123 --from-literal=cde=456

查看ConfigMap:kubectl get configmap

官方:ConfigMap | Kubernetes


ConfigMap 配置示例:

1)部署ConfigMap的YAML

[root@k8s-master-1-71 ~]# kubectl apply -f configmap-demo.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: configmap-demo
data:abc: "123"     //key/value键值方式(字符串需要“”)cde: "456"redis.properties: |     //多行数据方式port: 6379host: 192.168.31.10# 查看创建的ConfigMap
[root@k8s-master-1-71 ~]# kubectl get configmap
NAME               DATA   AGE
configmap-demo     3      7s
## 备注:DATA显示为3,表示存储了3个数据,包括abc、cde、redis.properties

2)部署Pod的YAML

[root@k8s-master-1-71 ~]# kubectl apply -f configmap-pod.yaml
apiVersion: v1
kind: Pod
metadata:name: configmap-demo-pod
spec:containers:- name: nginximage: nginx# 定义环境变量env:- name: ABCD # 容器中的变量,请注意这里和 ConfigMap 中的键名是不一样的valueFrom:configMapKeyRef:name: configmap-demo      # 这个值来自 ConfigMapkey: abc     # 需要取值的键- name: CDEFvalueFrom:configMapKeyRef:name: configmap-demokey: cdevolumeMounts:- name: configmountPath: "/config"   # 挂载到哪个目录readOnly: true         # 挂载文件为只读volumes:# 在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中- name: configconfigMap:name: configmap-demo    # 提供想要挂载的 ConfigMap 的名字# 来自 ConfigMap 的一组键,将被创建为文件items:- key: "redis.properties"   # configmap-demo的Keypath: "redis.conf"     # 挂载后的名字

测试:进入pod中验证是否注入变量和挂载

[root@k8s-master-1-71 ~]# kubectl exec -it configmap-test-pod-5dff5f64c6-ncmnd -- bash
root@configmap-test-pod-5dff5f64c6-ncmnd:/# echo $ABCD
123
root@configmap-test-pod-5dff5f64c6-ncmnd:/# echo $CDEF
456
root@configmap-test-pod-5dff5f64c6-ncmnd:/# cat /config/redis.conf
port: 6379
host: 192.168.31.10

3.2 Secret 存储敏感信息

与ConfigMap类似,区别在于Secret主要存储敏感数据,所有的数据要经过base64编码。

应用场景:凭据

kubectl create secret 支持三种数据类型:

  • docker-registry:存储镜像仓库认证信息(镜像仓库需要账户密码认证)
  • generic:存储用户名、密码:
    • 例如:kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
    • 例如:kubectl create secret generic my-secret --from-literal=username=produser --from-literal=password=123456
  • tls:存储证书:kubectl create secret tls --cert=path/to/tls.cert --key=path/to/tls.key
    • 例如 :05 K8s网络 使用ingress - tls 部署https

相关命令:

  • 查看Secret:kubectl get secret

相关base64命令:

  • # echo -n 'admin' | base64          //加密(YWRtaW4=)
  • # echo YWRtaW4= | base64 -d  //解密(admin)

补充: 在Secret 配置文件中未作显式设定时,默认的 Secret 类型是 Opaque


命令示例:

[root@k8s-master ~]# kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=123456

YAML示例:

1)将用户名密码进行编码

[root@k8s-master-1-71 ~]# echo -n 'admin' | base64   //用户名加密
YWRtaW4=
[root@k8s-master-1-71 ~]# echo -n '123456' | base64  //密码加密
MTIzNDU2

2)部署Secret的YAML

[root@k8s-master-1-71 ~]# vi secret-demo.yaml
apiVersion: v1
kind: Secret
metadata:name: my-secret
type: Opaque    //默认的 Secret 类型是 Opaque
data:username: YWRtaW4=password: MTIzNDU2# 查看创建的ConfigMap
[root@k8s-master-1-71 ~]# kubectl get secret
NAME              TYPE                DATA   AGE
secret-demo       Opaque              2      5s

TYPE类型参考:Secrets | Kubernetes

3)部署Pod的YAML

apiVersion: v1
kind: Pod
metadata:name: secret-demo-pod
spec:containers:- name: nginximage: nginx env:- name: USERNAME    # 容器中的变量valueFrom:secretKeyRef:name: secret-demokey: username- name: PASSWORD    # 容器中的变量valueFrom:secretKeyRef:name: secret-demokey: passwordvolumeMounts:- name: configmountPath: "/config"readOnly: truevolumes:- name: configsecret:secretName: secret-demoitems:- key: username path: username.txt- key: passwordpath: password.txt

备注:Pod使用Secret数据与ConfigMap方式一样

测试:进入pod中验证是否注入变量和挂载

[root@k8s-master-1-71 ~]# kubectl exec -it secret-test-pod-6554b98c8-8hrbb -- bash
root@secret-test-pod-6554b98c8-8hrbb:/# echo $USERNAME
admin
root@secret-test-pod-6554b98c8-8hrbb:/# echo $PASSWORD
123456
root@secret-test-pod-6554b98c8-8hrbb:/# ls /config/
password.txt  username.txt

课后作业

1、创建一个secret,并创建2个pod,pod1挂载该secret,路径为/secret,pod2使用环境变量引用该 secret,该变量的环境变量名为ABC

  • secret名称:my-secret
  • pod1名称:pod-volume-secret
  • pod2名称:pod-env-secret

2、 创建一个pv,再创建一个pod使用该pv

  • 容量:5Gi
  • 访问模式:ReadWriteOnce

3、创建一个pod并挂载数据卷,不可以用持久卷

  • 卷来源:emptyDir、hostPath任意
  • 挂载路径:/data

4、将pv按照名称、容量排序,并保存到/opt/pv文件

小结

本篇为 【Kubernetes CKA认证 Day7】的学习笔记,希望这篇笔记可以让您初步了解到 数据卷与数据持久卷、有状态应用部署、应用程序数据存储案例 ;课后还有扩展实践,不妨跟着我的笔记步伐亲自实践一下吧!


Tip:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解。

相关文章:

CKA认证 | Day7 K8s存储

第七章 Kubernetes存储 1、数据卷与数据持久卷 为什么需要数据卷&#xff1f; 容器中的文件在磁盘上是临时存放的&#xff0c;这给容器中运行比较重要的应用程序带来一些问题。 问题1&#xff1a;当容器升级或者崩溃时&#xff0c;kubelet会重建容器&#xff0c;容器内文件会…...

.net core 的数据库编程

Python基础 Python是一种高级编程语言&#xff0c;由Guido van Rossum于1980年代后期发明&#xff0c;并于1991年首次发布。它以简洁的语法和易于阅读的代码风格著称&#xff0c;因而成为程序员和数据科学家等领域的热门选择。在这篇文章中&#xff0c;我们将深入探讨Python的…...

再生核希尔伯特空间(RKHS)上的分位回归

1. 基本定义和理论基础 1.1 再生核希尔伯特空间(RKHS) 给定一个非空集合 X \mathcal{X} X&#xff0c;一个希尔伯特空间 H \mathcal{H} H 称为再生核希尔伯特空间&#xff0c;如果存在一个函数 K : X X → R K: \mathcal{X} \times \mathcal{X} \rightarrow \mathbb{R} K…...

结构方程模型【SEM】:非线性、非正态、交互作用及分类变量分析

利用结构方程模型建模往往遇到很多‘特殊’情况&#xff1a;1&#xff09;变量间为非直线关系&#xff1b;2&#xff09;变量间存在交互作用&#xff1b;3&#xff09;数据不满足正态分布&#xff1b;4&#xff09;变量为非正态类型的数值变量&#xff0c;如0&#xff0c;1数据…...

不安全物联网的轻量级加密:综述

Abstract 本文综述了针对物联网&#xff08;IoT&#xff09;的轻量级加密解决方案。这项综述全面覆盖了从轻量级加密方案到不同类型分组密码的比较等多个方面。同时&#xff0c;还对硬件与软件解决方案之间的比较进行了讨论&#xff0c;并分析了当前最受信赖且研究最深入的分组…...

DeepSpeed 使用 LoRA 训练后文件结构详解

DeepSpeed 使用 LoRA 训练后文件结构详解 在大语言模型&#xff08;LLM&#xff09;的训练过程中&#xff0c;DeepSpeed 提供了强大的分布式训练能力&#xff0c;而 LoRA&#xff08;Low-Rank Adaptation&#xff09;通过参数高效微调技术显著减少了资源占用。完成训练后&…...

Mysql数据 新增、修改和删除操作时,这些变化如何被转换为Kafka消息?

Mysql数据 新增、修改和删除操作时,这些变化如何被转换为Kafka消息? 为了在FlinkCDC中配置MySQL同步到Kafka,并采用debezium-json数据格式,我们需要了解当执行新增、修改和删除操作时,这些变化如何被转换为Kafka消息。下面我们将详细介绍这些变化情况,并提供具体的数据样…...

高等数学 8.1向量及其线性运算

8.1 向量及其线性运算 文章目录 8.1 向量及其线性运算一、向量的概念向量的线性运算1.向量的加减法2.向量与数的乘法 三、空间直角坐标系四、利用坐标作向量的线性运算五、向量的模、方向角、投影1.向量的模与两点间的距离公式2.方向角与方向余弦3.向量在轴上的投影 一、向量的…...

向bash shell脚本传参

例子&#xff1a; ~ script % touch parameter.sh ~ script % chmod 755 parameter.sh ~ % vim parameter.shparameter.sh: #!/usr/bin/env bashecho the name of current script is $0echo the first parameter is $1echo the second parameter is $2echo all parameters: $…...

高精度算法:加减乘除 (学习笔记)

加法&#xff1a; 现有vector<int>a,b;并且已经输入了内容且倒置 vector<int> plus(vector<int>a,vector<int> b){ int as a.size(); int bs b.size(); vector<int>total; int carry 0; int ar 0, br 0; //读取位数 while (ar < as &am…...

JVM 主要组成部分与内存区域

一、JVM 主要组成部分&#xff1a; JVM的主要包含两个组件和两个子系统&#xff0c;分别为&#xff1a; &#xff08;1&#xff09;本地库接口(Native Interface)&#xff1a;与native lib(本地方法库)交互&#xff0c;融合其他编程语言为Java所用&#xff0c;是与其它编程语言…...

10分钟掌握项目管理核心工具:WBS、甘特图、关键路径法全解析

一、引言 在项目管理的广阔天地里&#xff0c;犹如一场精心编排的交响乐演奏&#xff0c;每个乐器、每个音符都需精准配合才能奏响美妙乐章。而 WBS&#xff08;工作分解结构&#xff09;、甘特图、关键路径法无疑是这场交响乐中的关键乐章&#xff0c;它们从不同维度为项目管…...

python语音机器人(青云客免费api)

强调&#xff1a;不用登录注册&#xff0c;直接使用就好 青云客智能聊天机器人API python代码&#xff0c;直接可以运行&#xff1a; 1、安装库&#xff1a; pip install requests pyttsx3 SpeechRecognition sounddevice numpy scipy2、完整代码&#xff1a; import request…...

策略模式以及优化

使用场景 在一个条件语句中又包含了多个条件语句 具体策略类会过多 把抽象策略和具体策略放在一个枚举类里。 方法 exe() 相当于抽象策略&#xff0c;而A和B就相当于实现了抽象策略的具体策略 这样就只需要一个枚举类就可以解决具体策略类过多的问题 public enum Strategy {A{O…...

解决tomcat双击startup.bat乱码的几种方法

新环境&#xff0c;win10&#xff0c;今天下载了tomcat9.0.98&#xff0c;是压缩绿色版的&#xff0c;解压缩安装到了&#xff1a; D:\java\apache-tomcat-9.0.98 可以通过‪D:\java\apache-tomcat-9.0.98\bin\startup.bat双击来启动tomcat。 但是日志显示乱码。 后来找到了几种…...

计算机网络 (12)物理层下面的传输媒体

前言 计算机网络物理层下面的传输媒体是计算机网络设备之间的物理通路&#xff0c;也称为传输介质或传输媒介&#xff0c;并不包含在计算机网络体系结构中&#xff0c;而是处于物理层之下。 一、传输媒体的分类 导向型媒体&#xff1a;电磁波被导引沿着固体媒体传播。常见的导向…...

Spark生态圈

Spark 主要用于替代Hadoop中的 MapReduce 计算模型。存储依然可以使用 HDFS&#xff0c;但是中间结果可以存放在内存中&#xff1b;调度可以使用 Spark 内置的&#xff0c;也可以使用更成熟的调度系统 YARN 等。 Spark有完善的生态圈&#xff1a; Spark Core&#xff1a;实现了…...

如何计算相位差

如何计算相位差 假设我们有两个同频率的正弦信号&#xff1a; 这里两个信号的角频率w2πf是相同的&#xff0c;根据同频正弦信号相位差的计算方法&#xff0c;直接用两个信号的相位相减。 再来看利用波形图计算相位差的例子&#xff1a; 另一种计算方式&#xff1a;...

Bash Shell知识合集

1. chmod命令 创建一个bash shell脚本 hello.sh ~script $ touch hello.sh脚本创建完成后并不能直接执行&#xff0c;我们要用chmod命令授予它可执行的权限&#xff1a; ~script $ chmod 755 hello.sh授权后的脚本可以直接执行&#xff1a; ~script $ ./hello.sh2.指定运行…...

《信管通低代码信息管理系统开发平台》Windows环境安装说明

1 简介 《信管通低代码信息管理系统应用平台》提供多环境软件产品开发服务&#xff0c;包括单机、局域网和互联网。我们专注于适用国产硬件和操作系统应用软件开发应用。为事业单位和企业提供行业软件定制开发&#xff0c;满足其独特需求。无论是简单的应用还是复杂的系统&…...

如何查看服务器内存占用情况?

如何查看服务器的内存占用情况&#xff1f;你知道内存使用情况对服务器性能的重要性吗&#xff1f;内存是服务器运行的核心资源之一&#xff0c;了解内存的占用情况可以帮助你优化系统性能。 要查看服务器的内存占用情况&#xff0c;首先需要确定你使用的是哪种操作系统。不同…...

【源码】Sharding-JDBC源码分析之SQL中影子库ShadowSQLRouter路由的原理

Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 4、SpringBoot集成Sharding-JDBC-5.3.0分库分表 5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表 6、【…...

OCR实践-Table-Transformer

前言 书接上文 OCR实践—PaddleOCR Table-Transformer 与 PubTables-1M table-transformer&#xff0c;来自微软&#xff0c;基于Detr&#xff0c;在PubTables1M 数据集上进行训练&#xff0c;模型是在提出数据集同时的工作&#xff0c; paper PubTables-1M: Towards comp…...

代码随想录五刷day6

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣144. 二叉树的前序遍历(递归)二、力扣144. 二叉树的前序遍历(迭代)三、力扣145. 二叉树的后序遍历(递归)四、力扣145. 二叉树的后序遍历(迭代)五、力扣…...

【自信息、信息熵、联合熵、条件熵、互信息】

文章目录 一、自信息 I(X)二、信息熵&#xff1a;衡量系统的混乱程度信息熵 H(X)联合熵 H(X,Y) 三、条件熵H(Y|X) 联合熵H(X,Y) - 信息熵H(X)四、互信息 I(X,Y)五、总结References 一、自信息 I(X) 自信息(Self-information) 是由香农提出的&#xff0c;用来衡量单一事件发生…...

我的秋招总结

我的秋招总结 个人背景 双非本&#xff0c;985硕&#xff0c;科班 准备情况 以求职为目的学习Java的时间大概一年。 八股&#xff0c;一开始主要是看B站黑马的八股文课程&#xff0c;背JavaGuide和小林coding还有面试鸭。 算法&#xff0c;250&#xff0c;刷了3遍左右 项目&…...

page_ref_freeze浅析

最近在研究struct page的引用计数refcount&#xff0c;看到有个page_ref_freeze()特性很有意思。用这篇博客记录一下。 本文分析基于linux4.19.195 static inline int page_ref_freeze(struct page *page, int count) {int ret likely(atomic_cmpxchg(&page->_refcoun…...

Python毕业设计选题:基于python的酒店推荐系统_django+hadoop

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 酒店客房管理 客房类型管理 客房预定管理 用户…...

选择 SquashFS 作为启动分区的文件系统格式:详细教程

SquashFS 是一种高压缩率的只读文件系统,广泛用于嵌入式系统、Linux 发行版以及其他需要节省存储空间的场景。它特别适合用于启动分区、只读根文件系统(rootfs)等应用,因为它通过压缩技术极大地节省了存储空间。在本博客中,我们将详细介绍如何在 RK3568 等嵌入式设备上使用…...

Unity 读Excel,读取xlsx文件解决方案

Unity读取表格数据 效果&#xff1a; 思路&#xff1a; Unity可以解析Json&#xff0c;但是读取Excel需要插件的帮助&#xff0c;那就把这个功能分离开&#xff0c;读表插件就只管读表转Json&#xff0c;Unity就只管Json解析&#xff0c;中间需要一个存储空间&#xff0c;使用…...

【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数

【C语言】函数指针与指针函数 文章目录 [TOC](文章目录) 前言一、指针数组二、数组指针三、函数指针四、指针函数五、函数指针数组六、回调函数七、参考资料总结 前言 使用工具&#xff1a; 1.DEVC 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、…...

EasyExcel 模板+公式填充

使用 CellWriteHandler 的实现类来实现公式写入 Data NoArgsConstructor public class CustomCellWriteHandler implements CellWriteHandler {private int maxRowNum 2000;// 动态传入列表数量public CustomCellWriteHandler(int maxRowNum) {this.maxRowNum maxRowNum;}Ov…...

vue最新源码探索分析

我在github上fork了最新版本vue3.5版本的源码并做了大幅删除&#xff0c;保留最核心的代码&#xff0c;有兴趣的可以看看&#xff0c;欢迎大家提出PR 仓库地址 https://github.com/greatanimalion/core 本项目vue版本3.5.13 为了方便查看与分析&#xff0c;减少心智负担 已…...

产品初探Devops!以及AI如何赋能Devops?

DevOps源自Development&#xff08;开发&#xff09;和Operations&#xff08;运维&#xff09;的组合&#xff0c;是一种新的软件工程理念&#xff0c;旨在打破传统软件工程方法中“开发->测试->运维”的割裂模式&#xff0c;强调端到端高效一致的交付流程&#xff0c;实…...

深入解析MVCC中Undo Log版本底层存储读取逻辑

一、引言 多版本并发控制&#xff08;MVCC&#xff0c;Multi-Version Concurrency Control&#xff09;是一种广泛应用于关系数据库管理系统中的并发控制技术。它通过保存数据的历史版本&#xff0c;使得在事务并发执行时&#xff0c;每个事务都能看到数据的一致性视图。在MVC…...

【生产问题记录-Mysql分区】

描述 月报是根据日报的数据统计出来的&#xff0c;但是今天早上发现月报没有数据&#xff0c;日报是有数据的&#xff0c;那么为什么会导致这个结果呢&#xff1f; 问题解决 设计 因为日报table_day每天的数据量都在60w&#xff0c;所以我们采用了分区的形式&#xff0c;进…...

大型语言模型(LLMs)演化树 Large Language Models

大型语言模型&#xff08;LLMs&#xff09;演化树 Large Language Models flyfish 下面的图来自论文地址 Transformer 模型&#xff08;如 BERT 和 GPT-3&#xff09;已经给自然语言处理&#xff08;NLP&#xff09;领域带来了革命性的变化。这得益于它们具备并行化能力&…...

【LeetCode: 3159. 查询数组中元素的出现位置 + 统计下标】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

git自动压缩提交的脚本

可以将当前未提交的代码自动执行 git addgit commitgit squash Git 命令安装指南 1. 创建脚本目录 如果目录不存在&#xff0c;创建它&#xff1a; mkdir -p ~/.local/bin2. 创建脚本文件 vim ~/.local/bin/git-squash将完整的脚本代码复制到此文件中。 3. 设置脚本权限…...

uniapp中Nvue白屏问题 ReferenceError: require is not defined

uniapp控制台输出如下 exception function:createInstanceContext, exception:white screen cause create instanceContext failed,check js stack ->Uncaught ReferenceError: require is not defined 或者 exception function:createInstanceContext, exception:white s…...

Centos8安装图形化界面

由于Centos8已经停止维护&#xff0c;所以在使用的时候会遇到yum元数据找不到的情况 1、更新yum数据源 进入目录&#xff1a; cd /etc/yum.repos.d/ 修改文件&#xff1a; sed -i s/mirrorlist/#mirrorlist/g /etc/yum.repos.d/CentOS-* sed -i s|#baseurlhttp://mirror.cent…...

2023年厦门市第30届小学生C++信息学竞赛复赛上机操作题(三、2023C. 太空旅行(travel))

#include <bits/stdc.h>using namespace std;struct Ship {int u; // 从地球到火星的时间int v; // 从火星到天王星的时间 };// 自定义比较函数 bool cmp(const Ship &a, const Ship &b) {return a.u max(a.v, b.u) b.v < b.u max(b.v, a.u) a.v; }int ma…...

Doris的SQL原理解析

今天来介绍下Doris的SQL原理解析&#xff0c;主要从语法、解析、分析、执行等几个方面来介绍&#xff0c;可以帮助大家对Doris底层有个清晰的理解~ 一、Doris简介 Apache Doris是一个基于MPP架构的高性能、实时的分析型数据库&#xff0c;能够较好的满足报表分析、即席查询、…...

【RAG实战】语言模型基础

语言模型赋予了计算机理解和生成人类语言的能力。它结合了统计学原理和深度神经网络技术&#xff0c;通过对大量的样本数据进行复杂的概率分布分析来学习语言结构的内在模式和相关性。具体地&#xff0c;语言模型可根据上下文中已出现的词序列&#xff0c;使用概率推断来预测接…...

探索 .idea 文件夹:Java Maven 工程的隐形守护者

一、.idea文件夹深度解析&#xff1a;IntelliJ IDEA项目配置的核心 在Java Maven工程的开发环境中&#xff0c;.idea文件夹扮演着举足轻重的角色。这是IntelliJ IDEA项目特有的一个配置文件夹&#xff0c;它包含了项目所需的各种配置信息&#xff0c;以确保项目能够在不同的开…...

JAVA代理模式和适配器模式

文章目录 Java 代理模式和适配器模式代理模式&#xff08;Proxy Pattern&#xff09;适配器模式&#xff08;Adapter Pattern&#xff09;代理模式和适配器模式的区别 代理模式的使用举例静态代理实现:用代理模式记录方法调用日志动态代理实现:使用 Java 动态代理记录方法调用日…...

Python大数据可视化:基于python大数据的电脑硬件推荐系统_flask+Hadoop+spider

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 价格区间界面 用户信息界面 品牌管理 笔记本管理 电脑主机…...

【YOLOv3】源码(train.py)

概述 主要模块分析 参数解析与初始化 功能&#xff1a;解析命令行参数&#xff0c;设置训练配置项目经理制定详细的施工计划和资源分配日志记录与监控 功能&#xff1a;初始化日志记录器&#xff0c;配置监控系统项目经理使用监控和记录工具&#xff0c;实时跟踪施工进度和质量…...

一维、线性卡尔曼滤波的例程(MATLAB)

这段 MATLAB 代码实现了一维线性卡尔曼滤波器的基本功能&#xff0c;用于估计在存在噪声的情况下目标状态的真实值 文章目录 一维线性卡尔曼滤波代码运行代码介绍1. **初始化部分**2. **数据生成**3. **卡尔曼滤波器实现**4. **结果可视化**5. **统计输出** 源代码 总结 一维线…...

【Rust自学】6.2. Option枚举

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 6.2.1. 什么是Option枚举 它定义于标准库中&#xff0c;在Prelude&#xff08;预导入模块&#xff09;中&#xff0c;负责描述这样的场景…...