(六)k8s存储

一、ConfigMap

ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制大对象

1.1、根据目录下所有文件创建

vim game.properties
vim ui.properties

# kubectl create configmap cm的名称 --from-file=文件目录(当前目录下有game.properties和ui.properties两个文件 )
kubectl create configmap game-config --from-file=usr/local/k8s/configmap/

--from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容

  • game.properties
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
  • ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

cm 是configMap的简称
在这里插入图片描述

查看配置文件内容
在这里插入图片描述

1.2、根据具体的单一的文件创建

# --from-file指定具体的文件即可
kubectl create configmap game-config-2 --from-file=/usr/local/k8s/configmap/game.properties
 
$ kubectl get configmaps game-config-2 -o yaml

在这里插入图片描述

1.3、使用字面值创建

# kubectl create configmap cm名称 --from-literal=key的名称=value的值 --from-literal=key的名称=value的值
kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
 
$ kubectl get configmaps special-config -o yaml

在这里插入图片描述

1.4、资源清单方式(推荐)

special-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm

env-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO

在这里插入图片描述

1.5、其它资源引入

下面的演示,configMap的话如果没有创建则默认使用前面(1.1、1-2、1-3、1-4)创建过的

1.5.1、使用 ConfigMap 来替代环境变量

在pod中引用

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: hub.atguigu.com/library/myapp:v1
      command: [ "/bin/sh", "-c", "env" ]  # 打印 当前容器的所有环境变量
      env:
        - name: SPECIAL_LEVEL_KEY  # 定义一个环境变量名称
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how    # 值是cm名称为special-config(前面有定义过这个cm)中的special.how的值(very) 
          name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type   # 值是cm名称为special-config(前面有定义过这个cm)中的special.type的值(charm)  
      envFrom:
        - configMapRef:
            name: env-config  # 引入整个cm名称为special-config的配置文件,且不加修改,里面的key在这里就是key,里面的value在这里就是value
  restartPolicy: Never

打印结果应该有三个是我们定义的,如下
在这里插入图片描述

1.5.2、用 ConfigMap 设置命令行参数

在pod中引用

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: hub.atguigu.com/library/myapp:v1
       #打印 变量,获取使用 ${key}
      command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(log_level)" ] 
      env:
        - name: SPECIAL_LEVEL_KEY  # 定义一个环境变量名称
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how    # 值是cm名称为special-config(前面有定义过这个cm)中的special.how的值(very) 
      envFrom:
        - configMapRef:
            name: env-config  # 引入整个cm名称为special-config的配置文件,且不加修改,里面的key在这里就是key,里面的value在这里就是value
  restartPolicy: Never

1.5.3、通过数据卷插件使用ConfigMap

在数据卷里面使用这个 ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: hub.atguigu.com/library/myapp:v1
      # 睡眠60秒,避免容器结束,因为我们需要进入到容器内部查看配置
      command: [ "/bin/sh", "-c", "sleep 60s" ]
      volumeMounts:
      - name: config-volume # 将名称为config-volume的挂载卷,挂载到容器的 /etc/config 目录下
        mountPath: /etc/config # 挂载到容器的 /etc/config 目录下,我们可以进入容器看下
  volumes:
    - name: config-volume  # 创建一个新的挂载卷,名称叫 config-volume
      configMap:
        name: special-config # 里面的配置来自于 cm(名称是special-config)
  restartPolicy: Neve

查看结果,在容器内部目录 /etc/config,会有两个文件

  • special.how
  • special.type

而这两个文件的内容就是value
在这里插入图片描述

1.5.4、热更新

这里把cm和deployment 一起创建在同一个yaml文件中了。

apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: hub.atguigu.com/library/myapp:v1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
        - name: config-volume
          configMap:
            name: log-config

查看配置内容
在这里插入图片描述

我们直接修改镜像内部配置文件(不建议,应该修改对应的yaml文件后,用apply来更新,使镜像重启重新加载最新的配置文件,因为在实际情况中你并不知道那些地方用到了这个,你不可能打开实现的镜像内部配置文件,一处一处的找

kubectl edit configmap log-config #在文件里面定义的 cm

在这里插入图片描述
此时的变量值就会发生改变了
在这里插入图片描述

二、Secret

Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。Secret 可以以 Volume 或者环境变量的方式使用。

引用的方式和 configMap 大多一致,就是类型需要改一下就好了

Secret 有三种类型:

类型说明
Service Account用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中
Opaquebase64编码格式的Secret,用来存储密码、密钥等
kubernetes.io/dockerconfigjson用来存储私有 docker registry 的认证信息

2.1、Service Account(sa)

Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建(我们创建不了),并且会自动挂载到 Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中,这个目录就会保存当前命名空间下的访问apiServer的权限证书了。每一个名称空间下都会有不同的正式

查看访问apiServer的证书凭证

$ kubectl run nginx --image nginx
	deployment "nginx" created
$ kubectl get pods
	NAME                     READY     STATUS    RESTARTS   AGE
	nginx-3137573019-md1u2   1/1       Running   0          13s
# 你也可以直接进入这个容器,到对应目录下看	
$ kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount
	ca.crt  # 具体的证书了
	namespace # 那个命名空间下的
	token   # token信息

在这里插入图片描述

2.2、Opaque Secret(md5)

和上面的configMap的其它资源的引用是一样的,要注意的是引用的时候一些类型的定义,一些是 configMapKeyRef,在这里就是secretKeyRef了

2.2.1、前置条件-创建secret.yaml

Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式

$ echo -n "admin" | base64  # 对admin 进行md5加密
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64  # 对 1f2d1e2e67df 进行md5加密
MWYyZDFlMmU2N2Rm

创建sec.yaml

apiVersion: v1
kind: Secret # 这里的类型是Secret了,如果是cm就是configMap
metadata:
  name: mysecret  # 名称
type: Opaque # 3中类型中的哪一种
data:   # k-v 和configMap 一样的
  password: MWYyZDFlMmU2N2Rm  # 必须是md5加密后的数据
  username: YWRtaW4=

在这里插入图片描述

2.2.2、其它资源引用

和上面的configMap的其它资源的引用是一样的,要注意的是引用的时候一些类型的定义,一些是 configMapKeyRef,在这里就是secretKeyRef了

2.2.2.1、将 Secret 挂载到 Volume 中
apiVersion: v1
kind: Pod
metadata:
  labels:
    name: seret-test
  name: seret-test
spec:
  volumes:
  - name: secrets  # 定义各异名称叫 secrets 的挂载卷
    secret:   # 类型是secret,注意之前的是 configMap
      secretName: mysecret     # 引用具体的 sc配置文件
  containers:
  - image: hub.atguigu.com/library/myapp:v1
    name: db
    volumeMounts:
    - name: secrets  # 使用上面定义的 密码挂载卷
      mountPath: "/etc/secrets/"
      readOnly: true  

进入容器查看,在对应目录下有具体的文件(key),内容是value,会发现已经自动解密了
在这里插入图片描述

2.2.2.2、将 Secret 导出到环境变量中
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pod-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: pod-deployment
    spec:
      containers:
      - name: pod-1
        image: hub.atguigu.com/library/myapp:v1
        ports:
        - containerPort: 80
        env:
        - name: TEST_USER  # 重新定义变量名称,用的是mysecret里面username的值
          valueFrom:
            secretKeyRef: # 这里是 secretKeyRef 而不是configMapKeyRef
              name: mysecret
              key: username  
        - name: TEST_PASSWORD  # 重新定义变量名称,用的是mysecret里面password 的值
          valueFrom:
            secretKeyRef:  # 这里是 secretKeyRef 而不是configMapKeyRef
              name: mysecret
              key: password

我们可以直接使用 $变量名称 获取
在这里插入图片描述

2.2.2.3、Secret设置命令行参数

和上面的configMap的其它资源的引用是一样的,要注意的是引用的时候一些类型的定义,一些是 configMapKeyRef,在这里就是secretKeyRef了

2.3、kubernetes.io/dockerconfigjson (docker-registry)

我们知道k8s底层还是使用docker的,所以我们在拉取和下载镜像的时候,都是使用的docker,那么docker在拉取一些私有的仓库的时候,是需要认证的

docker login hub.docker.com guokangjie
docker logout hub.docker.com

如果docker认证失败,那么k8s也是拉取不了的,所以我们可以直接使用上面的名称在每台服务器上登录好(会在一个目录保存登录凭证),即可。但是如果有时候需要很多个仓库,而且只是用一下,拉取一次镜像而已,我们可以使用k8s定义一个登录凭证,在拉取那个镜像的时候,使用那个厂库凭证即可。

使用 Kuberctl 创建 docker registry 认证的 secret

# kubectl create secret(kind) docker-registry(3中类型的哪一种)  myregistrykey(名称,后面引用) --docker-server=仓库地址 --docker-username=用户名 --docker-password=密码  --docker-email=邮箱
kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL

在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey

apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
    - name: foo
      image: roc/awangyang:v1
  imagePullSecrets:
    - name: myregistrykey # 上面定义的凭证

三、Volume

  • 容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题

  • Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所f以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod 可以同时使用任意数量的卷

Kubernetes 支持以下类型的卷:
awsElasticBlockStore azureDisk azureFile cephfs csi downwardAPI emptyDir
fc flocker gcePersistentDisk gitRepo glusterfs hostPath iscsi local nfs 等等

3.1、emptyDir (不会挂载到宿主机上)

当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中所有的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除

emptyDir 的用法有:

  • 暂存空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • Web服务器容器提供数据时,保存内容管理器容器提取的文件
apiVersion: v1
kind: Pod
metadata:
  name: test-pd1
spec:
  containers:
  - image: k8s.gcr.io/test-webserver  # 定义其中一个镜像
    name: test1
    volumeMounts:
    - mountPath: /cache   # 将 cache-volume 挂载到这个镜像的 /cache 目录
      name: cache-volume
  - image: buxybox  # 定义其中第二个镜像
    name: test2
    volumeMounts:
    - mountPath: /test   # 将 cache-volume 挂载到这个镜像的 /test 目录
      name: cache-volume
  volumes:   # 定义挂载卷
  - name: cache-volume  # 名称是 cache-volume
    emptyDir: {}  # 类型是 空卷(前面的有secret和configMap,因为这两者都是可以存在文件的)

我们可以进入2个容器,分别在test1容器的 /cache目录下操作文件,比如新建立,追加文件内容等等,那么在 test2容器中的 /test目录下看到的数据是同步的。因为两者挂载的都是同一个卷。
第一个容器操作(名称可能和上面不太一样,意思以下就行)
在这里插入图片描述
第二个容器(名称可能和上面不太一样,意思以下就行)
在这里插入图片描述

3.1、hostPath

hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中。
hostPath 的用途如下:

  • 运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath
  • 在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath
  • 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在

除了所需的 path 属性之外,用户还可以为 hostPath 卷指定 type

行为
空字符串(默认)用于向后兼容,这意味着在挂载 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为 0755,与 Kubelet 具有相同的组和所有权。
Directory给定的路径下必须存在目录
FileOrCreate如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为 0644,与 Kubelet 具有相同的组和所有权。
File给定的路径下必须存在文件
Socket给定的路径下必须存在 UNIX 套接字
CharDevice给定的路径下必须存在字符设备
BlockDevice给定的路径下必须存在块设备

使用这种卷类型是请注意,因为:

  • 由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点的行为可能会有所不同,(即集群中每个节点的目录都需要创建,比如说 容器test1挂载宿主机的 /data目录,那么集群中 master01,node01,node02都需要创建目录 /data
  • 当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
  • 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath 卷 (每个节点的挂载目录必须让docker有写入和读取的权限

 
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd  # 挂载到容器内部的 /test-pd 目录
      name: test-volume  # 选择下面那个定义的挂载卷
  volumes:
  - name: test-volume
    hostPath:  # 方式是 hostPath(不是之前的 emptyDir,configMap,secret等等了)
      # directory location on host   宿主机目录
      path: /data
      # this field is optional  宿主机挂载的类型,这里是 给定的路径下必须存在目录,可以根据上面的表格修改
      type: Directory

在2台node节点(集群节点机器上,我这里不在mater上建立是,我现在确认master上面有污点,不会运行pod,而且也保证以后不会改,所以master上不需要建立,实际情况需要实际修改)上创建
/data目录(记得确保这个目录docker 有权限写入欧
容器内部修改
在这里插入图片描述
宿主机node01,查看,(但是有一点问题就是为什么node02上面没有这个文件?意思是当前这个pod只是运行在node01,所以就挂载到node01,如果后续跑到了node02上面,数据也会迁移的node02上面?);在这里插入图片描述

四、PV 以及 PCV

(疑问?这个stateSet 必须和 pv 和pvc 一起使用? hotsPath不行??????????????)

与 hostPath 区别有 ,不一定是主机的磁盘,可以是其它主机类型的磁盘

类型说明
PersistentVolume (PV)是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统
静态 pv (了解)集群管理员创建一些 PV。它们带有可供群集用户使用的实际存储的细节。它们存在于 Kubernetes API 中,可用于消费
动态 (了解)当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses :PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 “” 可以有效地禁用其动态配置要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器]。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作
PersistentVolumeClaim (PVC)是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)

即我们将 多种类型的磁盘统一化处理,设置为PV,这样我们在k8s内部使用的时候,不用考虑磁盘的差异性,要如何处理等等问题。而PVC 是为了解决不通磁盘挂载能力的选择器,比如固态硬盘就会快很多,普通的磁盘肯定要慢一点,又或者一个磁盘可以挂载10G,一些磁盘只能挂载1G等等这些条件来选择。因此我们可以直接跟PVC打交道即可,PVC帮忙选择。

  • 绑定 - 说明
    master 中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定在一起。如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦 PV 和 PVC 绑定后, PersistentVolumeClaim 绑定是排他性的,不管它们是如何绑定的,即 PVC 跟PV 绑定是一对一的映射

  • 持久化卷声明的保护 - 说明
    PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用

  • 持久化卷类型
    PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:

    • GCEPersistentDisk AWSElasticBlockStore AzureFile AzureDisk FC (Fibre Channel)
    • FlexVolume Flocker NFS iSCSI RBD (Ceph Block Device) CephFS
    • Cinder (OpenStack block storage) Glusterfs VsphereVolume Quobyte Volumes
    • HostPath VMware Photon Portworx Volumes ScaleIO Volumes StorageOS

4.1、PV 访问模式

PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式

  • ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany——该卷可以被多个节点以只读模式挂载
  • ReadWriteMany——该卷可以被多个节点以读/写模式挂载

在命令行中,访问模式缩写为:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany
Volume 插件ReadWriteOnceReadOnlyManyReadWriteMany
AWSElasticBlockStoreAWSElasticBlockStore--
AzureFile
AzureDisk--
CephFS
Cinder--
FC-
FlexVolume-
Flocker--
GCEPersistentDisk-
Glusterfs
HostPath--
iSCSI-
PhotonPersistentDisk--
Quobyte
NFS
RBD-
VsphereVolume-- (当 pod 并列时有效)
PortworxVolume-
ScaleIO-
StorageOS--

4.2、回收策略

  • Retain(保留)——手动回收
  • Recycle(回收)——基本擦除( rm -rf /thevolume/* )
  • Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除

当前,只有 HostPath 支持回收(基本擦除)策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略

4.3、状态

卷可以处于以下的某种状态:

  • Available(可用)——一块空闲资源还没有被任何声明绑定
  • Bound(已绑定)——卷已经被声明绑定
  • Released(已释放)——声明被删除,但是资源还未被集群重新声明
  • Failed(失败)——该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称

4.4、实验

4.4.1、前置准备(安装 NFS 服务器 )

# 每个机器都需要安装 包括k8s集群,不然不能使用挂载命令
yum install -y nfs-common nfs-utils  rpcbind
mkdir /nfs /nfs1 /nfs2 /nfs3
chmod 666 /nfs /nfs1 /nfs2 /nfs3
chown nfsnobody /nfs /nfs1 /nfs2 /nfs3
cat /etc/exports
    /nfs *(rw,no_root_squash,no_all_squash,sync)
    /nfs1 *(rw,no_root_squash,no_all_squash,sync)
    /nfs2 *(rw,no_root_squash,no_all_squash,sync)
    /nfs3 *(rw,no_root_squash,no_all_squash,sync)
    
systemctl start rpcbind
systemctl start nfs

我们需要准备4个 nfs的磁盘用来制作 pv ,所以我们 在192.168.66.100的机器上创建4个nfs类型的磁盘,分别对应主机192.168.66.100的下面4个目录/nfs/nfs1/nfs2/nfs3
在这里插入图片描述
授权4个文件目录,让其他人方便挂载

在这里插入图片描述

然后测试 nfs 挂载是否可以使用,即将本机的 /test 挂载到192.168.66.100的的/nfs 目录
在这里插入图片描述

以及测试 nfs1 挂载是否可以使用,即将本机的 /test 挂载到192.168.66.100的的/nfs1 目录
在这里插入图片描述
而 nfs2 和 nfs3 就不测试了,应该是没有问题的

4.4.2、部署PV

apiVersion: v1
kind: PersistentVolume  # 类型 pv
metadata:
  name: nfspv1  # 当前 pv 的名称
spec:
  capacity:
    storage: 10Gi  # 当前 PV 容量为1G,这会是一个选择的指标
  accessModes:
    - ReadWriteOnce  # 当前 PV 访问模式 RWO,这会是一个选择的指标  (上面有说明其它类型)
  persistentVolumeReclaimPolicy: Retain # 当前 PV 的回收策略, (上面有说明其它类型)
  storageClassName: nfs  # 当前 PV 的一个标签,区分磁盘的速度
  nfs:
    path: /nfs   # 当前 PV 的真实主机目录是 192.168.66.100 的/nfs 
    server: 10.66.66.10 # 当前 PV 的真实主机目录是 192.168.66.100
---
apiVersion: v1
kind: PersistentVolume  # 类型 pv
metadata:
  name: nfspv2  # 当前 pv 的名称
spec:
  capacity:
    storage: 5Gi  # 当前 PV 容量为1G,这会是一个选择的指标
  accessModes:
    - ReadOnlyMany  # 当前 PV 访问模式 ROX,这会是一个选择的指标  (上面有说明其它类型)
  persistentVolumeReclaimPolicy: Retain # 当前 PV 的回收策略, (上面有说明其它类型)
  storageClassName: nfs  # 当前 PV 的一个标签,区分磁盘的速度
  nfs:
    path: /nfs1   # 当前 PV 的真实主机目录是 192.168.66.100 的/nfs1 
    server: 10.66.66.10 # 当前 PV 的真实主机目录是 192.168.66.100
---
apiVersion: v1
kind: PersistentVolume  # 类型 pv
metadata:
  name: nfspv3  # 当前 pv 的名称
spec:
  capacity:
    storage: 5Gi  # 当前 PV 容量为1G,这会是一个选择的指标
  accessModes:
    - ReadWriteMany  # 当前 PV 访问模式 RWX,这会是一个选择的指标  (上面有说明其它类型)
  persistentVolumeReclaimPolicy: Retain # 当前 PV 的回收策略, (上面有说明其它类型)
  storageClassName: slow  # 当前 PV 的一个标签,区分磁盘的速度,随意起名
  nfs:
    path: /nfs2   # 当前 PV 的真实主机目录是 192.168.66.100 的/nfs2 
    server: 10.66.66.10 # 当前 PV 的真实主机目录是 192.168.66.100
---
apiVersion: v1
kind: PersistentVolume  # 类型 pv
metadata:
  name: nfspv4  # 当前 pv 的名称
spec:
  capacity:
    storage: 1Gi  # 当前 PV 容量为1G,这会是一个选择的指标
  accessModes:
    - ReadOnlyMany  # 当前 PV 访问模式 ROX,这会是一个选择的指标  (上面有说明其它类型)
  persistentVolumeReclaimPolicy: Retain # 当前 PV 的回收策略, (上面有说明其它类型)
  storageClassName: nfs  # 当前 PV 的一个标签,区分磁盘的速度
  nfs:
    path: /nfs3   # 当前 PV 的真实主机目录是 192.168.66.100 的/nfs3 
    server: 10.66.66.10 # 当前 PV 的真实主机目录是 192.168.66.100

将上面的192.168.66.100的 4个目录 创建成 pv
在这里插入图片描述

4.4.3、创建服务并使用 PVC

StatefulSet 必须要创建一个 clusterIp 的headless的svc,来一起使用

apiVersion: v1
kind: Service # 创建一个svc
metadata:
  name: nginx
  labels:
    app: nginx  # 这个svc 的标签是 app=nginx
spec:
  ports:
  - port: 80
    name: web  # 给80端口起一个名称
  clusterIP: None  # 表明为 headless 的 clusterIp 服务
  selector:
    app: nginx  # 管理 含有 app=ningx  标签的pod
---
apiVersion: apps/v1
kind: StatefulSet  # 创建  statefulSet 服务
metadata:
  name: web # 名称
spec:
  selector:
    matchLabels:
      app: nginx  # 只管理  含有 app=ningx  标签的pod
  serviceName: "nginx"  # 指定 svc 的名称是上面定义的那个 headless 的svc
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx  # 当前pod 的标签
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www  # 使用下面定义的那个 pvc
          mountPath: /usr/share/nginx/html  # 挂载容器内部的 /usr/share/nginx/html 目录
  volumeClaimTemplates:
  - metadata:
      name: www  # 这个pvc 的名称
    spec:
      accessModes: [ "ReadWriteOnce" ]  # 这个pvc 选择 pv的模式(pv的accessModes必须得等于ReadWriteOnce才能被使用)
      storageClassName: "nfs" # 这个pvc 选择 storageClassName 的模式(pv的storageClassName 必须得等于 nfs 才能被使用)
      resources:
        requests:
          storage: 1Gi   # 这个pvc 选择storage 的大小(pv的storage 大于等于 1G,,才能被使用)

创建后,查看只有一个 pod被创建成功,因为按照我们的条件只有一个pv 满足条件,而且这个PV 又是 RWO 模式(ReadWriteOnce——该卷可以被单个节点以读/写模式挂载),所以只有能有一个,并且我们之前说过statefulSet 是有序创建的,所以第二个都没有创建成功,那么别说第三个了,所以这里显示了 web-0,web-1(还没有创建成功)
在这里插入图片描述
修改我们的 PV 满足它的条件后,不需要重启这个 staetfuLSet 就可以看到已经启动了
在这里插入图片描述

4.4.4、验证

  • 我们在192.168.66.100的对应的nfs挂载目录创建数据,会同步到对应的pod的/usr/share/nginx/html目录下。
  • 并且我们手动删除pod后,在创建新的pod的名称是不会变的,只是ip会发生改变

4.5、关于 StatefulSet

  • 匹配 Pod name ( 网络标识 ) 的模式为: ( s t a t e f u l s e t 名 称 ) − (statefulset名称)- (statefulset)(序号),比如上面的示例:web-0,web-1,web-2

  • StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化,我们随便进入一个pod,访问我们的web-0(通过这里的规则),是可以访问的(所以集群内部可以通过这个域名直接访问单个pod),如下
    在这里插入图片描述

  • StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为: ( s e r v i c e n a m e ) . (service name). (servicename).(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名(所以我们可以解析这个配置获取到这个3个副本的具体形象),如下
    在这里插入图片描述
    再根据coredns来解析
    在这里插入图片描述
    结果
    在这里插入图片描述

  • 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Pod
    name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

Statefulset的启停顺序:

  • 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。(这个在们前面实验中已经证明了,当pv只有一个满足条件的时候,后面的pod都没有创建)
  • 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
  • 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。

StatefulSet使用场景:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
  • 稳定的网络标识符,即 Pod 重新调度后其PodName 和 HostName 不变,并不是IP 地址,前面图示说明了
  • 有序部署,有序扩展,基于 init containers 来实现。
  • 有序收缩。

4.6、StatefulSet 删除

1、先根据文件删除 statefulSet
在这里插入图片描述
2、再删除 和statefulSet一起创建的headless 的svc(理论上来说前面通过那个文件删除,这个svc也是会删除的
在这里插入图片描述
3、再删除 PVC
在这里插入图片描述

4、删除对应的192.168.66.100nfs磁盘
删除/nfs /nfs1 /nfs2 /nfs3
在这里插入图片描述
5、(方式一)解除pvc 和 pv的绑定 (或者你直接删除 这个pv 也行,因为你前面都删除 nfs 了,那现在这个pv名存实亡啊
我们可以看到 ,此时 PV 的状态是 released 状态
在这里插入图片描述
通过修改命令,解除绑定

# kubectl edit 类型 名称
kubectl edit pv nfspv1

在这里插入图片描述
最后结果
在这里插入图片描述
5、(方式二)或者你直接删除 这个pv 也行,因为你前面都删除 nfs 了,那现在这个pv名存实亡啊

kubectl delete pv --all
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值