一、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的简称

configmap 中的多个文件 configmap items_Pod

查看配置文件内容

configmap 中的多个文件 configmap items_configmap 中的多个文件_02

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

configmap 中的多个文件 configmap items_k8s_03

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

configmap 中的多个文件 configmap items_docker_04

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

configmap 中的多个文件 configmap items_linux_05

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

打印结果应该有三个是我们定义的,如下

configmap 中的多个文件 configmap items_Pod_06

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

configmap 中的多个文件 configmap items_linux_07

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

查看配置内容

configmap 中的多个文件 configmap items_configmap 中的多个文件_08

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

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

configmap 中的多个文件 configmap items_k8s_09


此时的变量值就会发生改变了

configmap 中的多个文件 configmap items_docker_10

二、Secret

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

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

Secret 有三种类型:

类型

说明

Service Account

用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中

Opaque

base64编码格式的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信息

configmap 中的多个文件 configmap items_configmap 中的多个文件_11

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=

configmap 中的多个文件 configmap items_linux_12

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,会发现已经自动解密了

configmap 中的多个文件 configmap items_k8s_13

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

我们可以直接使用 $变量名称 获取

configmap 中的多个文件 configmap items_docker_14

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 emptyDirfc 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目录下看到的数据是同步的。因为两者挂载的都是同一个卷。

第一个容器操作(名称可能和上面不太一样,意思以下就行)

configmap 中的多个文件 configmap items_k8s_15


第二个容器(名称可能和上面不太一样,意思以下就行)

configmap 中的多个文件 configmap items_configmap 中的多个文件_16

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 有权限写入欧

容器内部修改

configmap 中的多个文件 configmap items_k8s_17


宿主机node01,查看,(但是有一点问题就是为什么node02上面没有这个文件?意思是当前这个pod只是运行在node01,所以就挂载到node01,如果后续跑到了node02上面,数据也会迁移的node02上面?);

configmap 中的多个文件 configmap items_linux_18

四、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 插件

ReadWriteOnce

ReadOnlyMany

ReadWriteMany

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

configmap 中的多个文件 configmap items_configmap 中的多个文件_19


授权4个文件目录,让其他人方便挂载

configmap 中的多个文件 configmap items_linux_20

然后测试 nfs 挂载是否可以使用,即将本机的 /test 挂载到192.168.66.100的的/nfs 目录

configmap 中的多个文件 configmap items_k8s_21

以及测试 nfs1 挂载是否可以使用,即将本机的 /test 挂载到192.168.66.100的的/nfs1 目录

configmap 中的多个文件 configmap items_linux_22


而 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

configmap 中的多个文件 configmap items_configmap 中的多个文件_23

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(还没有创建成功)

configmap 中的多个文件 configmap items_Pod_24


修改我们的 PV 满足它的条件后,不需要重启这个 staetfuLSet 就可以看到已经启动了

configmap 中的多个文件 configmap items_linux_25

4.4.4、验证

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

4.5、关于 StatefulSet

  • 匹配 Pod name ( 网络标识 ) 的模式为:configmap 中的多个文件 configmap items_docker_26(序号),比如上面的示例: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),如下
  • configmap 中的多个文件 configmap items_docker_27

  • StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:configmap 中的多个文件 configmap items_Pod_28(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名(所以我们可以解析这个配置获取到这个3个副本的具体形象),如下
  • configmap 中的多个文件 configmap items_Pod_29

  • 再根据coredns来解析
  • configmap 中的多个文件 configmap items_docker_30

  • 结果
  • configmap 中的多个文件 configmap items_linux_31

  • 根据 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

configmap 中的多个文件 configmap items_k8s_32


2、再删除 和statefulSet一起创建的headless 的svc(理论上来说前面通过那个文件删除,这个svc也是会删除的

configmap 中的多个文件 configmap items_docker_33


3、再删除 PVC

configmap 中的多个文件 configmap items_k8s_34

4、删除对应的192.168.66.100nfs磁盘

删除/nfs /nfs1 /nfs2 /nfs3

configmap 中的多个文件 configmap items_docker_35


5、(方式一)解除pvc 和 pv的绑定 (或者你直接删除 这个pv 也行,因为你前面都删除 nfs 了,那现在这个pv名存实亡啊

我们可以看到 ,此时 PV 的状态是 released 状态

configmap 中的多个文件 configmap items_configmap 中的多个文件_36


通过修改命令,解除绑定

# kubectl edit 类型 名称
kubectl edit pv nfspv1

configmap 中的多个文件 configmap items_Pod_37


最后结果

configmap 中的多个文件 configmap items_linux_38


5、(方式二)或者你直接删除 这个pv 也行,因为你前面都删除 nfs 了,那现在这个pv名存实亡啊

kubectl delete pv --all