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

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

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

相关文章

Python调用R语言中的程序包来执行回归树、随机森林、条件推断树和条件推断森林算法

要使用Python调用R语言中的程序包来执行回归树、随机森林、条件推断树和条件推断森林算法&#xff0c;重新计算中国居民收入不平等&#xff0c;并进行分类汇总&#xff0c;我们可以使用rpy2库。rpy2允许在Python中嵌入R代码并调用R函数。以下是一个详细的步骤和示例代码&#x…

关于JAVA方法值传递问题

1.1 前言 之前在学习C语言的时候&#xff0c;将实参传递给方法&#xff08;或函数&#xff09;的方式分为两种&#xff1a;值传递和引用传递&#xff0c;但在JAVA中只有值传递&#xff08;颠覆认知&#xff0c;基础没学踏实&#xff09; 参考文章&#xff1a;https://blog.csd…

Excel基础知识

一&#xff1a;数组 一行或者一列数据称为一维数组&#xff0c;多行多列称为二维数组&#xff0c;数组支持算术运算&#xff08;如加减乘除等&#xff09;。 行&#xff1a;{1,2,3,4} 数组中的每个值用逗号分隔列&#xff1a;{1;2;3;4} 数组中的每个值用分号分隔行列&#xf…

基于DIODES AP43781+PI3USB31531+PI3DPX1207C的USB-C PD Video 之全功能显示器连接端口方案

随着USB-C连接器和PD功能的出现&#xff0c;新一代USB-C PD PC显示器可以用作个人和专业PC工作环境的电源和数据集线器。 虽然USB-C PD显示器是唯一插入墙壁插座的交流电源输入设备&#xff0c;但它可以作为数据UFP&#xff08;上游接口&#xff09;连接到连接到TCD&#xff0…

gazebo_world 基本围墙。

如何使用&#xff1f; 参考gazebo harmonic的官方教程。 本人使用harmonic的template&#xff0c;在里面进行修改就可以分流畅地使用下去。 以下是world 文件. <?xml version"1.0" ?> <!--Try sending commands:gz topic -t "/model/diff_drive/…

解决无法在 Ubuntu 24.04 上运行 AppImage 应用

在 Ubuntu 24.04 中运行 AppImage 应用的完整指南 在 Ubuntu 24.04 中&#xff0c;许多用户可能会遇到 AppImage 应用无法启动的问题。即使你已经设置了正确的文件权限&#xff0c;AppImage 仍然拒绝运行。这通常是由于缺少必要的库文件所致。 问题根源&#xff1a;缺少 FUSE…

Pytorch使用手册-DCGAN 指南(专题十四)

1. Introduction 本教程将通过一个示例介绍 DCGANs(深度卷积生成对抗网络)。我们将训练一个生成对抗网络(GAN),在给它展示大量真实名人照片后,它能够生成新的“名人”图片。这里的大部分代码来源于 PyTorch 官方示例中的 DCGAN 实现,而本文档将对该实现进行详细解释,并…

springboot配置oracle+达梦数据库多数据源配置并动态切换

项目场景&#xff1a; 在工作中很多情况需要跨数据库进行数据操作,自己总结的经验希望对各位有所帮助 问题描述 总结了几个问题 1.识别不到mapper 2.识别不到xml 3.找不到数据源 原因分析&#xff1a; 1.配置文件编写导致识别mapper 2.配置类编写建的格式有问题 3.命名…

html+css+js网页设计 美食 家美食1个页面

htmlcssjs网页设计 美食 家美食1个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#xf…

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用

&#x1f31f; 关于我 &#x1f31f; 大家好呀&#xff01;&#x1f44b; 我是一名大三在读学生&#xff0c;目前对人工智能领域充满了浓厚的兴趣&#xff0c;尤其是机器学习、深度学习和自然语言处理这些酷炫的技术&#xff01;&#x1f916;&#x1f4bb; 平时我喜欢动手做实…

Vue使用Tinymce 编辑器

目录 一、下载并重新组织tinymce结构二、使用三、遇到的坑 一、下载并重新组织tinymce结构 下载 npm install tinymce^7 or yarn add tinymce^7重构目录 在node_moudles里找到tinymce文件夹&#xff0c;把里面文件拷贝一份放到public下&#xff0c;如下&#xff1a; -- pub…

odoo中@api.model, @api.depends和@api.onchange 装饰器的区别

文章目录 1. api.model用途特点示例 2. api.depends用途特点示例 3. api.onchange用途特点示例 总结 在 Odoo 中&#xff0c;装饰器&#xff08;decorators&#xff09;用于修饰方法&#xff0c;以指定它们的行为和触发条件。api.model、api.depends 和 api.onchange 是三个常用…

EMNLP'24 最佳论文解读 | 大语言模型的预训练数据检测:基于散度的校准方法

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 张伟超&#xff0c;中国科学院计算所网络数据科学与技术重点实验室三年级直博生 内容简介 近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;的…

大数据技术-Hadoop(一)Hadoop集群的安装与配置

目录 一、准备工作 1、安装jdk&#xff08;每个节点都执行&#xff09; 2、修改主机配置 &#xff08;每个节点都执行&#xff09; 3、配置ssh无密登录 &#xff08;每个节点都执行&#xff09; 二、安装Hadoop&#xff08;每个节点都执行&#xff09; 三、集群启动配置&a…

PilotGo

title: 解锁 PilotGo&#xff1a;智能化运维的得力助手 date: ‘2024-12-29’ category: blog tags: PilotGo运维管理智能化工具技术应用 sig: ops archives: ‘2024-12’ author:way_back summary: PilotGo 作为一款创新的运维管理工具&#xff0c;凭借其智能化的特性和丰富的…

折腾日记:如何让吃灰笔记本发挥余热——搭建一个相册服务

背景 之前写过&#xff0c;我在家里用了一台旧的工作站笔记本做了服务器&#xff0c;连上一个绿联的5位硬盘盒实现简单的网盘功能&#xff0c;然而&#xff0c;还是觉的不太理想&#xff0c;比如使用filebrowser虽然可以备份文件和图片&#xff0c;当使用手机使用网页&#xf…

使用seata实现分布式事务管理

配置 版本说明&#xff1a;springCloud Alibaba组件版本关系 我用的是spring cloud Alibaba 2.2.1.RELEASE 、springboot 2.2.1.RELEASE、nacos 2.0.1、seata1.2.0,jdk1.8 seata 主要用于在分布式系统中对数据库进行事务回滚&#xff0c;保证全局事务的一致性。 seata的使用…

【总结】动态规划

线性dp LeetCode题单&#xff0c; 从记忆化搜索到递推 Pre&#xff1a; 从最初状态到最终状态等价&#xff0c;那么从最终状态开始和最初状态开始结果一样。 递归时不会产生其他负面结果&#xff0c;即无论何时进入递归&#xff0c;只要递归参数相同&#xff0c;结果就相同。 …

RabbitMQ中的异步Confirm模式:提升消息可靠性的利器

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff09;扮演着至关重要的角色&#xff0c;它能够解耦系统组件、提高系统的可扩展性和可靠性。RabbitMQ作为一款广泛使用的消息队列中间件&#xff0c;提供了多种机制来确保消息的可靠传递。其中&#xff…

买卖预测工具

设计一个用于在交易市场中寻找确定性或大概率盈利的买卖预测工具是一个具有挑战性但非常有潜力的项目。你可以通过以下几个步骤进行思路规划&#xff1a; 1. 明确目标 大概率盈利&#xff1a;工具的目的是找出大概率盈利的交易机会。不能完全依赖于100%确定性&#xff0c;因为…