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内容器之间共享数据
javascript 复制代码
apiVersion: v1
kind: Pod
metadata: 
  name: emptydir-test
spec:
  containers:
  - name: write-container    //程序讲数据写入到文件
    image: centos
    command: ["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: centos
    command: ["bash","-c","tail -f /data/hello"]
    volumeMounts:
      - name: data-volume
        mountPath: /data
  volumes:      //定义卷的来源
  - name: data-volume
    emptyDir: {}    //{}中为空值

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

javascript 复制代码
[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数据的

javascript 复制代码
[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删除了,卷也会被删除

javascript 复制代码
[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目录
javascript 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 36000
    volumeMounts:
      - name: data-volume
        mountPath: /data
  volumes:
  - name: data-volume
    hostPath:
      path: /tmp        //Pod所在节点的/tmp目录
      type: Directory

验证:

javascript 复制代码
[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包)

javascript 复制代码
# 服务端部署
[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提供网站程序文件
javascript 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nfs-pod

  name: nfs-pod
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: web
        image: nginx
        volumeMounts:
        - name: data-volume
          mountPath: /usr/share/nginx/html    //挂载的目录
      volumes:
      - name: data-volume      //volume卷名称
        nfs:
          server: 192.168.1.72     //指定NFS地址
          path: /ifs/kubernetes    //NFS共享目录

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

javascript 复制代码
[root@k8s-node2-1-73 ~]# kubectl apply -f nfs-pod.yaml
[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-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是否能继续使用共享存储数据

javascript 复制代码
# 删除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配置示例:

javascript 复制代码
---   //容器应用
apiVersion: apps/v1
kind: Deployment
metadata: 
  labels:
    app: my-pod

  name: my-pod
spec:
  selector:
    matchLabels:
      app: nginx

  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: web
        image: nginx
        volumeMounts:
        - name: data-volume
          mountPath: /usr/share/nginx/html    //挂载的目录
      volumes:
      - name: data-volume
        persistentVolumeClaim:
          claimName: my-pvc

---   //PVC 卷需求模板
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc      //与claimName对应进行关联
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  • 查看PVC命令: kubectl get pvc
javascript 复制代码
[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配置示例:

javascript 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv      //随便定义
spec:
  capacity:
    storage: 5Gi     //后端存储定义资源
  accessModes:
    - ReadWriteMany
  nfs:
    path: /ifs/kubernetes
    server: 192.168.1.72
  • 查看PV命令: kubectl get pv
javascript 复制代码
[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

测试:

javascript 复制代码
[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配置示例:

javascript 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001      //随便定义
spec:
  capacity:
    storage: 5Gi     //后端存储定义资源
  accessModes:
    - ReadWriteMany
  nfs:
    path: /ifs/kubernetes/pv001
    server: 192.168.1.72
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002      //随便定义
spec:
  capacity:
    storage: 15Gi     //后端存储定义资源
  accessModes:
    - ReadWriteMany
  nfs:
    path: /ifs/kubernetes/pv002
    server: 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回收策略
javascript 复制代码
[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个文件:

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

kubectl get sc                       # 查看存储类

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

流程图:


  • rbac.yaml 示例:
javascript 复制代码
[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 deployed
  namespace: 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: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: 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 deployed
  namespace: 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 deployed
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
  • class.yaml 配置示例:
javascript 复制代码
[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 配置示例:
javascript 复制代码
[root@k8s-master-1-71 nfs-external-provisioner]# cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner    //部署了NFS供给程序的容器
          image: lizhenliang/nfs-subdir-external-provisioner:v4.0.1    //镜像地址
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME     //class.yaml中的PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.1.72     //需要指定后端NFS存储
            - name: NFS_PATH
              value: /ifs/kubernetes
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.1.72     //需要指定后端NFS存储
            path: /ifs/kubernetes
  • PVC指定存储类配置示例:
javascript 复制代码
---
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 存储类插件

javascript 复制代码
[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   AGE

nfs-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中指定存储类名称

javascript 复制代码
[root@k8s-master-1-71 ~]# kubectl apply -f my-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-pod
  name: my-pod
spec:
  selector:
    matchLabels:
      app: my-pod
  template:
    metadata:
      labels:
        app: my-pod
    spec:
      containers:
      - name: my-pod
        image: nginx
        volumeMounts:
        - name: data-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: data-volume
        persistentVolumeClaim:
          claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc

spec:
  storageClassName: "managed-nfs-storage"
  accessModes:
    - ReadWriteMany
  resources:
    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:

javascript 复制代码
[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时保留数据备份

javascript 复制代码
[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目录已重新命名(备份)

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

javascript 复制代码
[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-svc
  name: headless-svc
spec:
  clusterIP: None     //指定SVC的clusterIP为None
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    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+卷申请模)

javascript 复制代码
[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: 80
    name: web
  clusterIP: None     //设置cluster-IP为None
  selector:
    app: nginx     //指定StatefulSet的Pod

# StatefulSet 配置示例
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx-headless"  # 指定 headless Service 服务名称
  replicas: 3          # by default is 1
  minReadySeconds: 10  # by default is 0
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www-data
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:     //卷申请模板(为StatefulSet专门设置的类型)
  - metadata:
      name: www-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"    # 指定 storageClass(kubectl get sc)
      resources:
        requests:
          storage: 1Gi

查看信息:

javascript 复制代码
[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的域名 网络连通性
javascript 复制代码
# 创建普通的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

测试存储:

javascript 复制代码
[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. 数据卷挂载

两种数据类型:

  • 键值
  • 多行数据

相关命令:

创建ConfigMap**:** kubectl create configmap --from-file=path/to/bar

bash 复制代码
kubectl create configmap my-configmap --from-literal=abc=123 --from-literal=cde=456

查看ConfigMap:kubectl get configmap
官方:ConfigMap | Kubernetes


ConfigMap 配置示例:

1)部署ConfigMap的YAML

javascript 复制代码
[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: 6379
    host: 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

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

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

javascript 复制代码
[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


命令示例:

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

YAML示例:

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

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

2)部署Secret的YAML

javascript 复制代码
[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

javascript 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: secret-demo-pod
spec:
  containers:
    - name: nginx
      image: nginx 
      env:
        - name: USERNAME    # 容器中的变量
          valueFrom:
            secretKeyRef:
               name: secret-demo
               key: username
        - name: PASSWORD    # 容器中的变量
          valueFrom:
            secretKeyRef:
               name: secret-demo
               key: password
       volumeMounts:
       - name: config
         mountPath: "/config"
         readOnly: true
  volumes:
    - name: config
      secret:
        secretName: secret-demo
        items:
        - key: username 
          path: username.txt
        - key: password
          path: password.txt

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

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

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

相关推荐
yulingfeng595 分钟前
Centos7 yum 报错“Could not resolve host: mirrorlist.centos.org; Unknown error“
linux·运维·centos
海域云赵从友25 分钟前
香港 GPU 服务器托管引领 AI 创新,助力 AI 发展
运维·服务器·人工智能
有Li1 小时前
AutoFOX:一种冠状动脉X线造影与OCT的自动化跨模态3D融合框架|文献速递-视觉大模型医疗图像应用
运维·3d·自动化
BuluAI2 小时前
Lazydocker:高效便捷的Docker管理工具
运维·docker·容器
奔波儿灞爱霸波尔奔3 小时前
人工智能之基于阿里云进行人脸特征检测部署
人工智能·阿里云·云计算
百家方案8 小时前
「下载」京东数科-数字孪生智慧园区解决方案:打通园区数据、融合园区业务、集成园区服务、共建园区生态,实现真实与数字孪生园区
人工智能·云计算·智慧园区·数智化园区
空白诗9 小时前
玩转OCR | 腾讯云智能结构化OCR推动跨行业高效精准的文档处理与数据提取新时代
云计算·ocr·腾讯云·玩转腾讯云 ocr
huhy~9 小时前
基于Ubuntu2404桌面版制作qcow2镜像
centos·云计算
飘飘燃雪10 小时前
Linux Modbus协议详解,代码示例
linux·运维·服务器·modbus
蜗牛hb10 小时前
Kali基础知识
linux·运维·服务器