Kubernetes中的配置与储存
-
简单储存:Volume(EmptyDir、HostPath、NFS)
-
高级储存:PV、PVC
-
配置储存:ConfigMqp、Secret
配置储存
ConfigMap
一般用于去存储 Pod 中应用所需的一些配置信息,或者环境变量,将配置于 Pod 分开,避免应为修改配置导致还需要重新构建 镜像与容器。
新建一个配置文件redis.properties
redis.host=127.0.0.1 redis.port=6379 redis.password=123456 # 创建一个名为 redis-config 的 ConfigMap [root@master /]# kubectl create configmap redis-config --from-file=redis.properties
查看该Config的配置信息
[root@master k8s]# kubectl describe cm redis-config Name: redis-config Namespace: default Labels: <none> Annotations: <none> Data ==== info: ---- redis.host=127.0.0.1 redis.port=6379 redis.password=123456 Events: <none>
当然你也可以使用资源清单的方式进行创建
-
创建ConfigMap的资源清单:configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: redis-config data: info: | redis.host=127.0.0.1 redis.port=6379 redis.password=123456 # 创建一个名为 redis-config 的 ConfigMap [root@master /]# kubectl create -f configmap.yaml
Pod以Volume挂载的方式访问
创建一个Pod,获取刚刚配置在CongfigMap的配置数据:pod-config-map.yaml
apiVersion: v1 kind: Pod metadata: name: pod-config-map spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: # 目录挂载 - name: config # 将一个名为 config 的ConfigMap挂载到容器内 /configmap/config目录下 mountPath: /configmap/config volumes: # 定义了一个Volume指定类型为ConfigMap并指定我们刚刚创建的名为 redis-config , 对外暴露名字为 config - name: config configMap: name: redis-config
[root@master k8s]# kubectl apply -f pod-config-map.yaml # 进入我们刚刚部署的这个Pod内部 [root@master k8s]# kubectl exec -it pod-config-map /bin/bash # 进入与 redis-config 这个 ConfigMap 挂载的目录 root@pod-config-map:/# cd configmap/config/ # ConfigMap 资源清单中data键下面的 info root@pod-config-map:/configmap/config# ls info # 数据以及挂载进来了 root@pod-config-map:/configmap/config# more info redis.host=127.0.0.1 redis.port=6379 redis.password=123456
Pod以环境变量的方式访问
-
定义ConfigMap资源清单文件 :config-pod-env.yaml
-
如果值是数字的话,需要用双引号引起来
-
apiVersion: v1 kind: ConfigMap metadata: name: redis-config-env data: info: redis.host: "127.0.0.1" redis.port: "6379" redis.password: "123456"
-
定义Pod的资源清单文件:pod-config-map-env.yaml
apiVersion: v1 kind: Pod metadata: name: pod-config-map spec: containers: - name: nginx image: nginx:1.17.1 env: - name: HOST valueFrom: configMapKeyRef: name: redis-config-env key: redis.host - name: POST valueFrom: configMapKeyRef: name: redis-config-env key: redis.port - name: PASSWORD valueFrom: configMapKeyRef: name: redis-config-env key: redis.password
-
下面我们开始部署后看看效果
[root@master k8s]# kubectl apply -f config-pod-env.yaml configmap/redis-config-env created [root@master k8s]# kubectl apply -f pod-config-map-env.yaml pod/pod-config-map-env created [root@master k8s]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-config-map-env 1/1 Running 0 5s # 进入容器内部 [root@master k8s]# kubectl exec -it pod-config-map-env /bin/bash # 打印一下环境变量, 这儿port端口没有打印出来, 是因为上面资源清单文件把PORT 写成了 POST , 这里应该使用 ${POST}即可获取 root@pod-config-map-env:/# echo ${HOST} ${PORT} ${PASSWORD} 127.0.0.1 123456 root@pod-config-map-env:/# echo ${POST} 6379
Secret
相对比CongfigMap这种明文储存的机制,K8S配套的也出了一个密文的配置机制,那就是 Secret
-
其实这种加密也很鸡肋,无非就是使用base64对目标数据进行编码
-
用法和ConfigMap类似,都是在 Pod 中使用数据卷挂载或者环境变量挂载的方式进行获取
定义secret
-
定义之前先要将我们的配置数据做base64编码处理
-
echo -n 'admin' | base64 得到admin的Base64编码为 : YWRtaW4=
-
echo -n 'testPassword' | base64 得到admin的Base64编码为 : dGVzdFBhc3N3b3Jk
-
apiVersion: v1 kind: Secret metadata: name: account-secret type: Opaque data: username: YWRtaW4= password: dGVzdFBhc3N3b3Jk
-
定义Pod资源清单文件
apiVersion: v1 kind: Pod metadata: name: pod-secret spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: # 目录挂载 - name: account-secret-config # 将一个名为 account-secret-config 的 secret 挂载到容器内 /configmap/config目录下 mountPath: /configmap/config volumes: # 定义了一个Volume指定类型为secret并指定我们刚刚创建的名为 account-secret , 对外暴露名字为 account-secret-config - name: account-secret-config secret: secretName: account-secret
然后部署该资源,进入pod内部查看 /configmap/config的数据,就能获取到我们配置的数据,这里的数据是解密后的
至于通过环境变量的方式进行挂载,和ConfigMap基本就差不多了,在这里就不做更多演示了
subPath的使用
在 ConfigMap 或者 Secret 目录挂载的时候,会将容器中源目录给覆盖掉,如果只想覆盖目录中的某一个文件,可以使用 SubPath
-
配置方式
-
定义 volumes 时需要增加 items 属性,配置 key 和 path,且 path 的值不能从 / 开始
-
在容器内的 volumeMounts 中增加 subPath 属性,该值与 volumes 中 items.path 的值相同
-
volumeMounts: - mountPath: /etc/nginx/nginx.conf # 挂载到哪里 name: configMapOrSecretName subPath: etc/nginx/nginx.conf # 与 volumes.[0].items.path 相同 volumes: - configMap: name: configMapOrSecretName items: # subPath 配置 key: nginx.conf # configMap 中的文件名 path: etc/nginx/nginx.conf # subPath 路径
配置的热更新
我们通常会将项目的配置文件作为 configmap 或者 Secret 然后挂载到 pod,如果更新配置文件,是否会自动更新也是分情况的
-
默认方式:会自动更新
-
subPath : 使用subPath的方式是不会自动更新的
-
变量方式:如果 pod 中的一个变量是从 configmap 或 secret 中得到,同样也是不会更新的
对于 subPath 的方式,我们可以取消 subPath 的使用,将配置文件挂载到一个不存在的目录,避免目录的覆盖,然后再利用软连接的形式,将该文件链接到目标位置 ,但是如果目标位置原本就有文件,可能无法创建软链接,此时可以基于前面讲过的 postStart 操作执行删除命令,将默认的吻技安删除即可
配置更新的方式也就两种
-
通过 edit 命令,直接修改配置数据
-
由于 configmap 我们创建通常都是基于文件创建,并不会编写 yaml 配置文件,因此修改时我们也是直接修改配置文件,而 replace 是没有 --from-file 参数的,因此无法实现基于源配置文件的替换,此时我们可以利用下方的命令实现,该命令的重点在于 --dry-run 参数,该参数的意思打印 yaml 文件,但不会将该文件发送给 apiserver,再结合 -oyaml 输出 yaml 文件就可以得到一个配置好但是没有发给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据即可实现替换
-
kubectl create cm --from-file=nginx.conf --dry-run -oyaml | kubectl replace -f-
-
不可变的Secret和ConfigMap
对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置时可以设置 immutable: true 来禁止修改
储存
Volumes
Volume 是 Pod 中能够被多个容器访问的共享目录,Volume 与 Pod 的生命周期相同
-
Kubernetes 支持多种类型的 Volume:EmptyDir、HostPath、nfs
EmptyDir
正如其名字所示,一个 emptyDir Volume 是 Host 上的一个空目录 , 创建于pod被调度到某个宿主机上的时候,
演示说明:
-
通过下面这份配置文件可以发现:我们定义了两个pod和一个名为shared-volume的emptyDir类型的数据卷轴
-
第一个pod通过volumeMounts挂载了一个名为shared-volumn的数据卷轴
-
将其挂载在/prodecer_dir目录下
-
然后在其下创建hello文件,并写入数据 "hello world"
-
-
第二个pod通过volumeMounts挂载了一个名为shared-volumn的数据卷轴
-
将其挂载在/consumer_dir目录下
-
然后在其查看hello文件,得到"hello world"数据
-
-
两个容器通过emptyDir类型的Volume实现数据共享
HostPath
HostPath 属性的 volume 使得对应的容器能够访问当前宿主机上的指定目录 ,此时该目录会变成持久化存储目录
一旦这个 pod 离开了这个宿主机 , 但数据也不会随 pod 迁移到其他宿主机上,实际上增加了 Pod 与节点的耦合
演示说明:
-
我们创建了一个名为persistent-storage的数据卷轴,卷轴类型是hostPath,并指定为宿主机的/mydata目录
-
上面的Nginx容器挂载改了数据卷轴,并与容器内/usr/share/nginx/html这个目录做了映射
-
下面我们就可以在不修改Nginx配置文件的情况下,往宿主机的/mydata目录下存放静态文件,可通过该Nginx Pod 进行访问
NFS挂载
-
NFS即网络文件系统,其允许一个系统在网络上与它人共享目录和文件
-
NFS相信大家都知道是什么玩意了,比如win10自带功能,局域网通过NFS实现文件共享
-
k8s集群当中挂在nfs文件系统道理也是相通
-
选一台k8s集群外的同局域网的服务器 ,可相互ping通即可,下载nfs-utils
-
yum install nfs-utils
-
-
集群内所有的node节点全部下载:nfs-utils
-
yum install nfs-utils
-
-
文件系统服务器设置NFS的挂载路径以及权限等
-
vim /etc/exports
-
# /mydata:对外暴露的目录 , *:可访问的IP,*为均能访问 , rw:可读可写 , no_root-squash:登陆即有root权限,可选:root_squash , 登陆使用匿名者权限 /mydata *(rw,no_root_squash)
-
文件系统服务启动
-
systemctl start nfs
-
K8S集群部署应用使用NFS网络储存系统
-
下面这个资源清单文件表示:Nginx的静态资源目录html与192.168.217.150的/mydata目录做挂载映射
PV与PVC
PV的定义
-
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用
-
持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷挂载来实现的。
-
只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。
-
PV的状态:
-
Available : 空闲,未被绑定
-
Bound : 已经被PVC绑定
-
Released : PVC被删除后,资源已回收,但是PV尚未被重新使用
-
Failed : 自动回收失败
-
-
资源清单文件样列:
-
下面我们基于NFS定义PV的资源清单
-
apiVersion: v1 kind: PersistentVolume metadata: name: pv001 spec: capacity: storage: 5Gi # pv 的容量 accessModes: # 访问模式:ReadWriteOnce、ReadWriteMany、ReadOnlyMany - ReadWriteOnce # 访问默认依次为:单节点读写、多节点写、多节点读 persistentVolumeReclaimPolicy: Recycle # 回收策略:Retain(保留)、Recycle(回收)、Delete(删除) storageClassName: slow # 创建 PV 的存储类名,需要与 pvc 的相同 nfs: # 连接到 nfs path: /mydata # 存储路径 server: 192.168.217.150 # nfs 服务地址
PVC的定义
-
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求以及申领。
-
PVC 申领可以请求特定的大小和访问模式 (比如要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见
-
集群中已经有的 PV 无法满足 PVC 的需求,PVC 可以通过 StorageClass 实现自动创建一个 PV
-
-
资源清单文件样列:
-
他们通过下面这个资源清单文件就能找到上面制备的 PV
-
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: - ReadWriteOnce # 权限需要与对应的 pv 相同 resources: requests: storage: 5Gi # 资源可以小于 pv 的,但是不能大于,如果大于就会匹配不到 pv storageClassName: slow # 名字需要与对应的 pv 相同 # selector: # 使用选择器选择对应的 pv # matchLabels: # release: "stable" # matchExpressions: # - {key: environment, operator: In, values: [dev]}
-
当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。
-
如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV
-
若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。
-
若设置了 StorageClass ,则会动态创建 PV,与之绑定
-
Pod与PVC与PV
-
Pod 将 PVC 当作存储卷来使用,集群会通过 PVC 找到绑定的 PV,并为 Pod 挂载该卷
-
Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除
-
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。
-
PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。 目前,数据卷可以被保留、回收或删除
-
保留 (Retain) :
-
处于保留状态的PV持久卷仍然存在且不能被其他PVC申领 , 可通过下面的手段进行手动回收
-
删除PV对象 , 但PV对象位于外部的储存资产继续存在 , 然后根据情况是否手动删除所关联的储存资产
-
删除PV对象 , 可以基于存储资产的定义创建新的 PV 卷对象 , 达到重用储存资产的目的
-
-
-
回收 (Recycle):
-
目前已被废弃 , 官网建议使用动态制备
-
-
删除 (Delete):
-
从看k8s中移除改卷,且会移除所有的储存资产
-
动态制备的卷默认就是采用的这种回收策略
-
-
资源清单文件样列:
-
首先定义一个Volume , 规则为PVC(persistentVolumeClaim) , 然后给定PVC的名称
-
然后再以卷轴挂载的方式,挂载到某个容器下,容器下某个目录映射
#在 pod 的挂载容器配置中,增加 pvc 挂载
containers: ...... volumeMounts: - mountPath: /tmp/pvc name: nfs-pvc-test volumes: - name: nfs-pvc-test persistentVolumeClaim: claimName: nfs-pvc # pvc 的名称
StorageClass
-
上面的案列中,我们手动的制备了PV , 然后定义PVC时进行关联选择,最后PVC再与Pod进行挂载
-
但我们也说到了一个词语 StorageClass ,用于PVC自动创建 PV , 这样就不用我们在手动制备PV了
-
也就是说我们只需要定义PVC就行,然后PVC通过StorageClass来自动创建符合需求的PV
-
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。
下面我们来看这么一份资源清单文件
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner namespace: kube-system labels: app: nfs-client-provisioner 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 image: quay.io/external_storage/nfs-client-provisioner:latest volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: fuseim.pri/ifs - name: NFS_SERVER value: 192.168.217.150 - name: NFS_PATH value: /mydata volumes: - name: nfs-client-root nfs: server: 192.168.217.150 path: /mydata
我们在创建一个StorageClass的资源清单
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: managed-nfs-storage namespace: kube-system provisioner: fuseim.pri/ifs # 外部制备器提供者,编写为提供者的名称 parameters: archiveOnDelete: "false" # 是否存档,false 表示不存档,会删除 oldPath 下面的数据,true 表示存档,会重命名路径 reclaimPolicy: Retain # 回收策略,默认为 Delete 可以配置为 Retain volumeBindingMode: Immediate # 默认为 Immediate,表示创建 PVC 立即进行绑定,只有 azuredisk 和 AWSelasticblockstore 支持其他值
RBAC配置
-
下面这段代码或许很多人看不懂,大致就是分配账号与角色,属于权限管理的一块,K8S也是内置了RBAC的权限模块
-
上面的Deployment定义中 , 我们使用的账户是:serviceAccountName: nfs-client-provisioner
-
下面的代码就是为:nfs-client-provisioner 这个账户进行权限授权
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner namespace: kube-system --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-client-provisioner-runner namespace: kube-system 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 namespace: kube-system subjects: - kind: ServiceAccount name: nfs-client-provisioner 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 namespace: kube-system 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 namespace: kube-system subjects: - kind: ServiceAccount name: nfs-client-provisioner roleRef: kind: Role name: leader-locking-nfs-client-provisioner apiGroup: rbac.authorization.k8s.io
PVC处于pending状态:
-
在K8S版本1.20z之后,移除了一个对于SelfLink的支持,而默认上面指定的 provisioner 版本需要 SelfLink 功能,因此 PVC 无法进行自动制备。
-
配置 SelfLink,修改 Api Server 配置文件:vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec: containers: - command: - kube-apiserver - --feature-gates=RemoveSelfLink=false # 新增该行 ......
-
修改后更新部署:kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
或者将StorageClass的资源清单文件中的 provisioner 置为:任选一
-
gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0
-
registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0
然后我们就可以开始定义PVC了,并在PVC中通过 storageClassName 指定我们自己创建的 storageClass
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: auto-pv-test-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 300Mi storageClassName: managed-nfs-storage