kubernetes中数据存储

configmap

configmap的功能

  • configMap用于保存配置数据,以键值对形式存储
  • configMap 资源提供了向 Pod 注入配置数据的方法
  • 镜像和配置文件解耦,以便实现镜像的可移植性和可复用性
  • etcd 限制了文件大小不能超过1M

虽然可以在容器内进行配置,但容器可以运行在不同系统中,而不同系统中的配置不总是一样的
假如配置写进容器中,可移植性、可复用性就比较低,故使用 configmap 等

configmap的使用场景

  • 填充环境变量的值
  • 设置容器内的命令行参数
  • 填充卷的配置文件
[root@k8s-master ~]# mkdir mnt
[root@k8s-master ~]# cd mnt/

configmap创建方式

字面值创建

[root@k8s-master mnt]# kubectl create configmap user --from-literal user1=ovo --from-literal user2=ono
[root@k8s-master mnt]# kubectl describe configmaps user
Data
====
user1:
----
ovo
user2:
----
ono
# 回收
[root@k8s-master mnt]# kubectl delete configmaps user

通过文件创建

[root@k8s-master mnt]# echo "ovo" > user1.txt
[root@k8s-master mnt]# echo "ono" > user2.txt
[root@k8s-master mnt]# kubectl create configmap user --from-file ./user1.txt --from-file ./user2.txt
[root@k8s-master mnt]# kubectl describe configmaps user
Data
====
user1.txt:
----
ovo

user2.txt:
----
ono
[root@k8s-master mnt]# kubectl delete configmaps user

通过目录创建

[root@k8s-master mnt]# mkdir userspace
[root@k8s-master mnt]# mv user1.txt ./userspace/user1.txt
[root@k8s-master mnt]# mv user2.txt ./userspace/user2.txt
[root@k8s-master mnt]# kubectl create configmap user --from-file userspace/
[root@k8s-master mnt]# kubectl describe configmaps user
Data
====
user1.txt:
----
ovo

user2.txt:
----
ono
[root@k8s-master mnt]# kubectl delete configmaps user

通过Yaml文件创建

[root@k8s-master mnt]# kubectl create configmap user --from-literal name=zhang3 --from-literal age=20 --dry-run=client -o yaml > ovo.yaml
[root@k8s-master mnt]# cat ovo.yaml
apiVersion: v1
data:
  age: "20"
  name: zhang3
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: user
[root@k8s-master mnt]# kubectl apply -f ovo.yaml
[root@k8s-master mnt]# kubectl describe configmaps user
Data
====
age:
----
20
name:
----
zhang3
[root@k8s-master mnt]# kubectl delete configmaps user

configmap的使用方式

  • 通过环境变量的方式直接传递给pod
  • 通过pod的 命令行运行方式
  • 作为volume的方式挂载到pod内

使用configmap填充环境变量

[root@k8s-master mnt]# kubectl run haha --image busyboxplus --dry-run=client -o yaml > ono.yml
[root@k8s-master mnt]# vim ono.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: haha
  name: haha
spec:
  containers:
  - image: busyboxplus
    name: haha
    command:
    - /bin/sh
    - -c
    - env
    env:
    - name: key1
    # 变量名
      valueFrom:
      # 规定变量值来源
        configMapKeyRef:
          name: user
          # 使用正在运行名为 user 的 configmap 内的 name 变量及变量值
          key: name
    - name: key2
      valueFrom:
        configMapKeyRef:
          name: user
          key: age
  restartPolicy: Never
[root@k8s-master mnt]# kubectl apply -f ovo.yaml
# 不运行 configmap ,则报错 Error: configmap "ono" not found
[root@k8s-master mnt]# kubectl apply -f ono.yml
[root@k8s-master mnt]# kubectl logs pods/haha
...
key1=zhang3
key2=20
...
[root@k8s-master mnt]# kubectl delete pods haha
[root@k8s-master mnt]# kubectl delete configmaps user
------------------------------------------
# 在Pod命令行中使用变量
[root@k8s-master mnt]# vim ono.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: haha
  name: haha
spec:
  containers:
  - image: busyboxplus
    name: haha
    command:
    - /bin/sh
    - -c
    - echo ${key1} ${key2}
    env:
    - name: key1
      valueFrom:
        configMapKeyRef:
          name: user
          key: name
    - name: key2
      valueFrom:
        configMapKeyRef:
          name: user
          key: age
  restartPolicy: Never
[root@k8s-master mnt]# kubectl apply -f ovo.yaml
[root@k8s-master mnt]# kubectl apply -f ono.yml
[root@k8s-master mnt]# kubectl logs pods/haha
zhang3 20
[root@k8s-master mnt]# kubectl delete pods haha

通过数据卷使用configmap

[root@k8s-master mnt]# vim ono.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: ono
  name: ono
spec:
  containers:
  - image: busyboxplus
    name: ono
    command:
    - /bin/sh
    - -c
    - cat /config/age /config/name
    volumeMounts:
    # 被调用的卷
    - name: haha
    # 被调用卷名
      mountPath: /config
      # 卷挂载文件路径
  volumes:
  # 声明卷的配置
  - name: haha
    configMap:
    # 卷的类型
      name: user
      # 卷的名称
      # 确保有此卷在运行
  restartPolicy: Never
[root@k8s-master mnt]# kubectl apply -f ono.yml
[root@k8s-master mnt]# kubectl logs ono
20zhang3

利用configMap填充pod的配置文件

# 建立配置文件模板
[root@k8s-master mnt]# vim nginx.conf
server {
  listen 8000;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;
}
# 利用模板生成configMap
[root@k8s-master mnt]# kubectl create configmap nginx-conf --from-file nginx.conf
# 建立nginx控制器文件
[root@k8s-master mnt]# kubectl create deployment nginx --image nginx:latest --replicas 1 --dry-run=client -o yaml > nginx.yml
# 设定卷
[root@k8s-master mnt]# vim nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        name: nginx
        volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/conf.d

      volumes:
        - name: config-volume
          configMap:
            name: nginx-conf
# 测试
[root@k8s-master mnt]# kubectl get pods -o wide
NAME                       READY   STATUS      RESTARTS      AGE     IP            NODE
nginx-8487c65cfc-zpbxh     1/1     Running     0             4m27s   10.244.2.78   k8s-node2.org
[root@k8s-master mnt]# curl 10.244.2.78:8000
<h1>Welcome to nginx!</h1>

通过热更新configMap修改配置

[root@k8s-master mnt]# kubectl edit configmaps nginx-conf
...
    server {
      listen 8080;
      # 端口修改成8080
      server_name _;
...
[root@k8s-master mnt]# kubectl exec pods/nginx-8487c65cfc-zpbxh -- cat /etc/nginx/conf.d/nginx.conf
server {
  listen 8080;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;
}

配置文件修改后不会立即生效,需要删除Pod后,控制器会重建Pod,这时才会生效

[root@k8s-master mnt]# kubectl delete pods nginx-8487c65cfc-zpbxh
[root@k8s-master mnt]# kubectl get pods nginx-8487c65cfc-l64s4 -o wide
[root@k8s-master mnt]# curl 10.244.2.79:8080
<h1>Welcome to nginx!</h1>

secrets配置

secrets的功能

  • Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 ssh key

  • 敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活

  • secrets 使用方式

    • 作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里。

    • 当 kubelet 为 pod 拉取镜像时使用

  • Secret 的类型

    • Service Account:Kubernetes 自动创建包含访问 API 凭据的 secret,并自动修改 pod 以使用此类型的 secret

    • Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱

    • kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息

Secret 创建方式

通过文件创建

[root@k8s-master mnt]# mkdir secrets
[root@k8s-master mnt]# cd secrets/
[root@k8s-master secrets]# echo -n "zhang3" > username.txt
[root@k8s-master secrets]# echo -n "ovo" > password.txt
[root@k8s-master secrets]# kubectl create secret generic userlist --from-file username.txt --from-file password.txt
[root@k8s-master secrets]# kubectl describe secrets userlist
Data
====
password.txt:  3 bytes
username.txt:  6 bytes
[root@k8s-master secrets]# kubectl get secrets userlist -o yaml
data:
  password.txt: b3Zv
  username.txt: emhhbmcz
# 不安全
[root@k8s-master secrets]# echo -n "zhang3" | base64
emhhbmcz
[root@k8s-master secrets]# echo -n "ovo" | base64
b3Zv
[root@k8s-master secrets]# echo -n "emhhbmcz" | base64 -d
zhang3

通过Yaml文件创建

[root@k8s-master secrets]# kubectl create secret generic userlist --dry-run=client -o yaml > userlist.yml
[root@k8s-master secrets]# vim userlist.yml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  name: userlist
type: Opaque
data:
  username: emhhbmcz
  password: b3Zv
[root@k8s-master secrets]# kubectl delete -f userlist.yml
[root@k8s-master secrets]# kubectl apply -f userlist.yml
[root@k8s-master secrets]# kubectl describe secrets userlist
Data
====
password:  3 bytes
username:  6 bytes

Secret的使用方法

将Secret挂载到Volume中

root@k8s-master secrets]# kubectl run  nginx --image nginx --dry-run=client -o yaml > pod1.yaml
# 向固定路径映射
[root@k8s-master secrets]# vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - name: userlist
      mountPath: /secret
      # 挂载目录
      readOnly: true
  volumes:
  - name: userlist
    secret:
      secretName: userlist
[root@k8s-master secrets]# kubectl apply -f pod1.yaml
[root@k8s-master secrets]# kubectl exec  pods/nginx -it -- /bin/bash
root@nginx:/# cat /secret/username
zhang3
root@nginx:/# cat /secret/password
ovo

向指定路径映射 secret 密钥

[root@k8s-master secrets]# vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx1
  name: nginx1
spec:
  containers:
  - image: nginx
    name: nginx1
    volumeMounts:
    - name: secrets
      mountPath: /secret
      # 默认文件都在/secret下
      readOnly: true

  volumes:
  - name: secrets
    secret:
      secretName: userlist
      items:
      - key: username
        path: other/username
        # 将文件放到其他路径下(/secret/other/username)
      - key: password
        path: other/password
[root@k8s-master secrets]# kubectl apply -f pod2.yaml
[root@k8s-master secrets]# kubectl exec  pods/nginx1 -it -- /bin/bash
root@nginx1:/# cat /secret/other/password /secret/other/username

将Secret设置为环境变量

[root@k8s-master secrets]# vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox
spec:
  containers:
  - image: busybox
    name: busybox
    command:
    - /bin/sh
    - -c
    - env
    env:
    - name: USERNAME
      valueFrom:
        secretKeyRef:
          name: userlist
          key: username
    - name: PASS
      valueFrom:
        secretKeyRef:
          name: userlist
          key: password
  restartPolicy: Never
[root@k8s-master secrets]# kubectl apply -f pod3.yaml
[root@k8s-master secrets]# kubectl logs pods/busybox
...
USERNAME=zhang3
...
PASS=ovo
...

存储docker registry的认证信息

# 上传镜像
[root@ooovooo ~]# docker tag timinglee/game2048:latest ooovooo.org/library/game2048:latest
[root@ooovooo ~]# docker push ooovooo.org/library/game2048:latest
# 建立用于docker认证的secret
[root@k8s-master secrets]# kubectl create secret docker-registry docker-auth --docker-server ooovooo.org --docker-username admin --docker-password aaa --docker-email ooovooo.org@admin
# docker-auth:名称
# ooovooo.org:进行认证的仓库
# admin:用户名
# aaa:用户密码
# ooovooo.org@admin:邮件
[root@k8s-master secrets]# vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: game2048
  name: game2048
spec:
  containers:
  - image: ooovooo.org/library/game2048:latest
    name: game2048
  imagePullSecrets:
  # 加载认证文件
  - name: docker-auth
[root@k8s-master secrets]# kubectl apply -f pod3.yaml
[root@k8s-master secrets]# kubectl get pods game2048
NAME       READY   STATUS    RESTARTS   AGE
game2048   1/1     Running   0          8s

volumes配置管理

  • 容器中文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题

  • 当容器崩溃时,kubelet将重新启动容器,容器中的文件将会丢失,因为容器会以干净的状态重建

  • 当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。

  • Kubernetes 卷具有明确的生命周期与使用它的 Pod 相同

  • 卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留

  • 当一个 Pod 不再存在时,卷也将不再存在

  • Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷

  • 卷不能挂载到其他卷,也不能与其他卷有硬链接。 Pod 中的每个容器必须独立地指定每个卷的挂载位置

kubernets支持的卷的类型

官网:https://kubernetes.io/zh/docs/concepts/storage/volumes/

k8s支持的卷的类型如下:

  • awsElasticBlockStore 、azureDisk、azureFile、cephfs、cinder、configMap、csi

  • downwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker

  • gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi、local、

  • nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd

  • scaleIO、secret、storageos、vsphereVolume

emptyDir卷

功能:

当Pod指定到某个节点上时,首先创建的是一个emptyDir卷,并且只要 Pod 在该节点上运行,卷就一直存在。卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但是这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会永久删除

emptyDir 的使用场景:

  • 缓存空间,例如基于磁盘的归并排序。

  • 耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行

  • 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件

[root@k8s-master ~]# mkdir volumes
[root@k8s-master ~]# cd volumes/
[root@k8s-master volumes]# vim 001.yml
apiVersion: v1
kind: Pod
metadata:
  name: vol1
spec:
  containers:
  - image: busyboxplus:latest
    name: vm1
    command:
    - /bin/sh
    - -c
    - sleep 30000000
    volumeMounts:
    - mountPath: /cache
      name: cache-vol
  - image: nginx:latest
    name: vm2
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: cache-vol
  volumes:
  - name: cache-vol
    emptyDir:
      medium: Memory
      sizeLimit: 100Mi
[root@k8s-master volumes]# kubectl apply -f 001.yml
[root@k8s-master volumes]# kubectl describe pods vol1
[root@k8s-master volumes]# kubectl exec -it pods/vol1 -c vm1 -- /bin/sh
/ # cd cache/
/cache # curl localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
/cache # echo hello > /cache/index.html
/cache # curl localhost
hello
/cache # dd if=/dev/zero of=bigfile bs=1M count=101
dd: writing 'bigfile': No space left on device
101+0 records in
99+1 records out

hostpath卷

功能

hostPath 卷能将主机节点文件系统上的文件或目录挂载到您的 Pod 中,不会因为pod关闭而被删除

hostPath 的一些用法

  • 运行一个需要访问 Docker 引擎内部机制的容器,挂载 /var/lib/docker 路径。

  • 在容器中运行 cAdvisor(监控) 时,以 hostPath 方式挂载 /sys。

  • 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在

hostPath的安全隐患

  • 具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。
  • 当 Kubernetes 按照计划添加资源感知的调度时,这类调度机制将无法考虑由 hostPath 使用的资源。
  • 基础主机上创建的文件或目录只能由 root 用户写入。您需要在 特权容器 中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。
[root@k8s-master volumes]# vim 002.yml
apiVersion: v1
kind: Pod
metadata:
  name: vol2
spec:
  containers:
  - image: nginx
    name: vm2
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: cache-vo2
  volumes:
  - name: cache-vo2
    hostPath:
      path: /data
      # 把工作节点的/data目录挂载到镜像的/usr/share/nginx/html中
      type: DirectoryOrCreate
      # 当/data目录不存在时,自动建立
[root@k8s-master volumes]# kubectl apply -f 002.yml
[root@k8s-master volumes]# kubectl get pods vol2 -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP            NODE
vol2   1/1     Running   0          16s   10.244.2.83   k8s-node2.org
[root@k8s-master volumes]# curl 10.244.2.83
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
# 镜像内/usr/share/nginx/html的文件会同步到工作节点的/data中
# 在工作节点上自动创建/data目录
[root@k8s-node2 ~]# echo node2 > /data/index.html
[root@k8s-master volumes]# curl 10.244.2.83
node2
# 即使Pod被删除,hostPath不会被清理
[root@k8s-master volumes]# kubectl delete -f 002.yml
pod "vol2" deleted
[root@k8s-node2 ~]# ls /data/
index.html

nfs卷

NFS 卷允许将一个现有的 NFS 服务器上的目录挂载到 Kubernetes 中的 Pod 中。这对于在多个 Pod 之间共享数据或持久化存储数据非常有用

例如,如果有多个容器需要访问相同的数据集,或者需要将容器中的数据持久保存到外部存储,NFS 卷可以提供一种方便的解决方案。

# 部署NFS共享主机(Harbor主机)
[root@ooovooo ~]# dnf install nfs-utils -y
# 创建共享目录
[root@ooovooo ~]# mkdir /nfsdata
[root@ooovooo ~]# systemctl enable --now nfs-server
[root@ooovooo ~]# vim /etc/exports
/nfsdata   *(rw,sync,no_root_squash)
[root@ooovooo ~]# exportfs -rv
exporting *:/nfsdata

# 其他主机,均进行
dnf install nfs-utils -y
showmount -e 172.25.254.100
# 部署NFS卷
[root@k8s-master ~]# mkdir nfs
[root@k8s-master ~]# cd nfs/
[root@k8s-master nfs]# vim ovo.yml
apiVersion: v1
kind: Pod
metadata:
  name: vol1
spec:
  containers:
  - image: nginx:latest
    name: vm1
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: cache-vol
  volumes:
  - name: cache-vol
    nfs:
      server: 172.25.254.100
      path: /nfsdata
[root@k8s-master nfs]# kubectl apply -f ovo.yml
[root@k8s-master nfs]# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE    IP            NODE
vol1   1/1     Running   0          2m9s   10.244.1.21   k8s-node1.org
[root@k8s-master nfs]# curl 10.244.1.21
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
---
# 在NFS主机中
[root@ooovooo ~]# echo "hello world" > /nfsdata/index.html
# Master主机
[root@k8s-master nfs]# curl 10.244.1.21
hello world

PersistentVolume持久卷

PersistentVolume(持久卷,简称PV)

  • pv是集群内由管理员提供的网络存储的一部分

  • PV也是集群中的一种资源。是一种volume插件

  • 但是它的生命周期却是和使用它的Pod相互独立的

  • PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节

  • pv有两种提供方式:静态和动态

    • 静态PV:集群管理员创建多个PV,它们携带着真实存储的详细信息,它们存在于Kubernetes API中,并可用于存储使用

    • 动态PV:当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass

PersistentVolumeClaim(持久卷声明,简称PVC)

  • 是用户的一种存储请求

  • 它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源

  • Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式持久卷配置

  • PVC与PV的绑定是一对一的映射。没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态

volumes访问模式
  • ReadWriteOnce – 该volume只能被单个节点以读写的方式映射

  • ReadOnlyMany – 该volume可以被多个节点以只读方式映射

  • ReadWriteMany – 该volume可以被多个节点以读写的方式映射

  • 在命令行中,访问模式可以简写为:

    • RWO - ReadWriteOnce

    • ROX - ReadOnlyMany

    • RWX – ReadWriteMany

volumes回收策略

  • Retain:保留,需要手动回收

  • Recycle:回收,自动删除卷中数据(在当前版本中已经废弃)

  • Delete:删除,相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除

只有NFS和HostPath支持回收利用
AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持删除操作

volumes状态说明

  • Available 卷是一个空闲资源,尚未绑定到任何申领
  • Bound 该卷已经绑定到某申领
  • Released 所绑定的申领已被删除,但是关联存储资源尚未被集群回收
  • Failed 卷的自动回收操作失败

静态PV

# 在NFS主机中建立实验目录
[root@ooovooo ~]# mkdir  /nfsdata/pv{1..3}
# 编写创建PV的yml文件,PV是集群资源,不在任何namespace中
[root@k8s-master volumes]# vim pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv1
    server: 172.25.254.100
    # 注意NFS主机的IP
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 15Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv2
    server: 172.25.254.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  capacity:
    storage: 25Gi
  volumeMode: Filesystem
  accessModes:
  - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv3
    server: 172.25.254.100
[root@k8s-master volumes]# kubectl get pv
NAME		CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   		CLAIM                STORAGECLASS
pv1		5Gi        RWO            Retain           Available    default/pvc1         nfs
pv2		15Gi       RWX            Retain           Available    default/pvc2         nfs
pv3		25Gi       ROX            Retain           Available    default/pvc3         nfs
# 建立PVC,PVC是PV使用的申请,需要保证和Pod在同一个namespace中
[root@k8s-master volumes]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
spec:
  storageClassName: nfs
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 15Gi
[root@k8s-master volumes]# kubectl get pvc
NAME         STATUS   VOLUME		CAPACITY   ACCESS MODES   STORAGECLASS
pvc1         Bound    pv1			5Gi        RWO            nfs
pvc2         Bound    pv2			15Gi       RWX            nfs
pvc3         Bound    pv3			25Gi       ROX            nfs
# 均处于Bound状态,且对应绑定
# 若类型不一致或大小超出范围,均会绑定错误
# 在其他namespace中无法应用
[root@k8s-master volumes]# kubectl -n kube-system  get pvc
No resources found in kube-system namespace.

在Pod中使用PVC

[root@k8s-master volumes]# vim pod.yml
[root@k8s-master volumes]# kubectl apply -f pod.yml
[root@k8s-master volumes]# kubectl get pods zhang3 -o wide
NAME     READY   STATUS    RESTARTS   AGE     IP            NODE
zhang3   1/1     Running   0          2m49s   10.244.2.89   k8s-node2.org
[root@k8s-master volumes]# kubectl exec -it pods/zhang3 -- /bin/bash
root@zhang3:/# curl localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
root@zhang3:/# cd /usr/share/nginx/html
root@zhang3:/usr/share/nginx/html# ls
# 到NFS主机中
[root@ooovooo nfsdata]# echo zhang3 > /nfsdata/pv1/index.html
root@zhang3:/usr/share/nginx/html# ls
index.html
root@zhang3:/usr/share/nginx/html# curl localhost
zhang3

存储类storageclass

官网:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

  • StorageClass提供了一种描述存储类(class)的方法,不同的class可能会映射到不同的服务质量等级和备份策略或其他策略等

  • 每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在StorageClass需要动态分配 PersistentVolume 时会使用到

StorageClass的属性

官网:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

Provisioner(存储分配器):用来决定使用哪个卷插件分配 PV,该字段必须指定。可以指定内部分配器,也可以指定外部分配器。外部分配器的代码地址为: kubernetes-incubator/external-storage,其中包括NFS和Ceph等

Reclaim Policy(回收策略):通过reclaimPolicy字段指定创建的Persistent Volume的回收策略,回收策略包括:Delete 或者 Retain,没有指定默认为Delete

存储分配器NFS Client Provisioner

源码地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

  • NFS Client Provisioner是一个automatic provisioner,使用NFS作为存储,自动创建PV和对应的PVC,本身不提供NFS存储,需要外部先有一套NFS存储服务

  • PV以 n a m e s p a c e − {namespace}- namespace{pvcName}-${pvName}的命名格式提供(在NFS服务器上)

  • PV回收的时候以 archieved- n a m e s p a c e − {namespace}- namespace{pvcName}-${pvName} 的命名格式(在NFS服务器上)

部署NFS Client Provisioner

创建SA并授权

# 文件可在GitHub中找到
[root@k8s-master storageclass]# vim rbac.yml
apiVersion: v1
kind: Namespace
metadata:
  name: nfs-client-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - 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
    namespace: nfs-client-provisioner
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: nfs-client-provisioner
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: nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: nfs-client-provisioner
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
[root@k8s-master storageclass]# kubectl apply -f rbac.yml
[root@k8s-master storageclass]# kubectl -n nfs-client-provisioner get sa
NAME                     SECRETS   AGE
default                  0         7s
nfs-client-provisioner   0         7s

部署应用

[root@k8s-master storageclass]# vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: 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: sig-storage/nfs-subdir-external-provisioner:v4.0.2
          # Harbor仓库需要存在此镜像
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 172.25.254.100
              # NFS主机IP
            - name: NFS_PATH
              value: /nfsdata
              # NFS主机需要存在此目录
      volumes:
        - name: nfs-client-root
          nfs:
            server: 172.25.254.100
            # NFS主机IP
            path: /nfsdata

[root@k8s-master storageclass]# kubectl apply -f deployment.yml
[root@k8s-master storageclass]# kubectl -n nfs-client-provisioner get deployments.apps nfs-client-provisioner
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
nfs-client-provisioner   1/1     1            1           13m

创建存储类

[root@k8s-master storageclass]# vim class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "false"
[root@k8s-master storageclass]# kubectl apply -f class.yaml
[root@k8s-master storageclass]# kubectl get storageclasses.storage.k8s.io
NAME         PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE
nfs-client   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate

创建PVC

[root@k8s-master storageclass]# vim pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1G
[root@k8s-master storageclass]# kubectl apply -f pvc.yml
[root@k8s-master storageclass]# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS
test-claim   Bound    pvc-57606f04-fa35-4fb8-9e4d-d03bc1f0bbef   1G         RWX            nfs-client
# 处于Bound状态,即成功

创建测试Pod

[root@k8s-master storageclass]# vim pod.yml
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim
[root@k8s-master storageclass]# kubectl apply -f pod.yml
# NFS主机
[root@ooovooo ~]# cd /nfsdata/
[root@ooovooo nfsdata]# ls default-test-claim-pvc-57606f04-fa35-4fb8-9e4d-d03bc1f0bbef
SUCCESS

一次性指定多个PVC

[root@k8s-master storageclass]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 15Gi

[root@k8s-master storageclass]# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES
pvc1         Bound    pvc-d37d233a-d89d-409a-9768-d90b8e400fe9   1Gi        RWO            nfs-client
pvc2         Bound    pvc-ed452c93-ca7f-4002-9e56-d0539cfdeada   10Gi       RWX            nfs-client
pvc3         Bound    pvc-d588d619-2a7e-4654-8d2b-d7210fed71f0   15Gi       ROX            nfs-client
test-claim   Bound    pvc-57606f04-fa35-4fb8-9e4d-d03bc1f0bbef   1G         RWX            nfs-client
# 均处于Bound状态,且对应绑定
# 若类型不一致或大小超出范围,均会绑定错误
# 需要先删除

设定默认存储类

  • 在未设定默认存储类时,PVC必须指定使用类的名称
  • 在设定默认存储类后创建PVC时,可以不用指定 storageClassName
[root@k8s-master storageclass]# kubectl edit sc nfs-client
...
metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
    # 设定默认存储类
    kubectl.kubernetes.io/last-applied-configuration: |
...

# 测试:不指定storageClassName参数	???
[root@k8s-master storageclass]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-claim
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

[root@k8s-master storageclass]# kubectl apply -f pvc.yml
[root@k8s-master storageclass]# kubectl get pvc

statefulset控制器

功能特性

  • Statefulset是为了管理有状态服务的问提设计的

  • StatefulSet将应用状态抽象成了两种情况:

    • 拓扑状态:应用实例必须按照某种顺序启动。新创建的Pod必须和原来Pod的网络标识一样

    • 存储状态:应用的多个实例分别绑定了不同存储数据。

  • StatefulSet给所有的Pod进行了编号,编号规则是: ( s t a t e f u l s e t 名称 ) − (statefulset名称)- (statefulset名称)(序号),从0开始。

  • Pod被删除后重建,重建Pod的网络标识也不会改变,Pod的拓扑状态按照Pod的“名字+编号”的方式固定下来,并且为每个Pod提供了一个固定且唯一的访问入口,Pod对应的DNS记录

StatefulSet的组成部分

  • Headless Service:用来定义pod网络标识,生成可解析的DNS记录

  • volumeClaimTemplates:创建pvc,指定pvc名称大小,自动创建pvc且pvc由存储类供应。

  • StatefulSet:管理pod的

构建方法

[root@k8s-master ~]# mkdir statefulset
[root@k8s-master ~]# cd statefulset/
# 建立无头服务
[root@k8s-master statefulset]# vim headless.yml
apiVersion: v1
kind: Service
metadata:
 name: nginx-svc
 labels:
  app: nginx
spec:
 ports:
 - port: 80
   name: web
 clusterIP: None
 selector:
  app: nginx
[root@k8s-master statefulset]# kubectl apply -f headless.yml
# 建立statefulset
[root@k8s-master statefulset]# vim statefulset.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: web
spec:
 serviceName: "nginx-svc"
 replicas: 3
 selector:
  matchLabels:
   app: nginx
 template:
  metadata:
   labels:
    app: nginx
  spec:
   containers:
   - name: nginx
     image: nginx
     volumeMounts:
       - name: www
         mountPath: /usr/share/nginx/html
 volumeClaimTemplates:
  - metadata:
     name: www
    spec:
     storageClassName: nfs-client
     accessModes:
     - ReadWriteOnce
     resources:
      requests:
       storage: 1Gi
[root@k8s-master statefulset]# kubectl apply -f statefulset.yml
[root@k8s-master statefulset]# kubectl get pods
NAME                       READY   STATUS              RESTARTS      AGE
web-0                      1/1     Running             0             13s
web-1                      1/1     Running             0             9s
web-2                      1/1     Running             0             6s
[root@ooovooo ~]# ls /nfsdata/
default-www-web-0-pvc-384bcc5e-64d1-4ea7-b641-430ca4b57b75
default-www-web-1-pvc-88cf5485-5148-45d8-912c-5818e46058e1
default-www-web-2-pvc-004675aa-e695-4f0c-bfbe-afcb5b3f902d

测试

# 为每个Pod建立index.html文件
[root@ooovooo nfsdata]# echo web-000 > default-www-web-0-pvc-384bcc5e-64d1-4ea7-b641-430ca4b57b75/index.html
[root@ooovooo nfsdata]# echo web-001 > default-www-web-1-pvc-88cf5485-5148-45d8-912c-5818e46058e1/index.html
[root@ooovooo nfsdata]# echo web-002 > default-www-web-2-pvc-004675aa-e695-4f0c-bfbe-afcb5b3f902d/index.html
# 建立测试Pod访问
[root@k8s-master statefulset]# kubectl run -it testpod --image busyboxplus
/ # curl web-0.nginx-svc
web-000
/ # curl web-1.nginx-svc
web-001
/ # curl web-2.nginx-svc
web-002
# 即使重新建立statefulset
[root@k8s-master statefulset]# kubectl delete -f statefulset.yml
[root@k8s-master statefulset]# kubectl apply -f statefulset.yml
# 访问也不会发生改变
[root@k8s-master statefulset]# kubectl attach testpod -c testpod -i -t
/ # curl  web-0.nginx-svc
web-000
/ # curl  web-1.nginx-svc
web-001
/ # curl  web-2.nginx-svc
web-002

statefulset的弹缩

首先,想要弹缩的StatefulSet. 需先清楚是否能弹缩该应用

  1. 用命令改变副本数
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
[root@k8s-master statefulset]# kubectl scale statefulset web --replicas=6
  1. 通过编辑配置改变副本数
kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
[root@k8s-master statefulset]# kubectl edit statefulsets.apps web
...
  replicas: 6
...

statefulset有序回收

[root@k8s-master statefulset]# kubectl scale statefulset web --replicas 0
[root@k8s-master statefulset]# kubectl delete -f statefulset.yml
[root@k8s-master statefulset]# kubectl delete pvc --all
persistentvolumeclaim "test-claim" deleted
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
persistentvolumeclaim "www-web-5" deleted
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值