一、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 目录中 |
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信息
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 插件 | 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
授权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 ( 网络标识 ) 的模式为:(序号),比如上面的示例: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 为:(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.100
nfs磁盘
删除/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