一、存储卷基础

在docker容器中,为了实现数据的持久性存储,在宿主机和容器内做映射,可以保证在容器的生命周期结束,数据依旧可以实现持久性存储。

但是在k8s中,由于pod分布在各个不同的节点之上,并不能实现不同节点之间持久性数据的共享,并且,在节点故障时,可能会导致数据的永久性丢失。为此,k8s就引入了外部存储卷的功能。

1.emptyDir目录

emptyDir:Pod挂载在本地的磁盘或者内存,被称为emptyDir ,称为临时空目录,随着Pod删除,也会被删除。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod.

做一个empty数据卷示例:

示例1

vi pod-emp.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型,这里是pod
metadata:                       # 元数据
  name: empty-pod
spec:
  containers:
  - name: emptydir
    image: busybox
    command: [ "sleep", "3600" ]
    volumeMounts:
    - mountPath: /data         # 将数据卷挂载到/data下
      name: data-volume
  volumes:
  - name: data-volume
    emptyDir: {}               # 定义一个empty类型挂载

创建运行

kubectl create -f pod-emp.yaml

查看挂载描述

kubectl describe pod empty-pod

在这里插入图片描述
使用场景,容器共享文件。在一个pod中有两类容器,一个是主容器,一个是辅助容器,两个容器使用同一个存储卷。

辅助容器用来生成新的内容,主容器加载使用。

示例2

vi pod-empty.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型,这里是pod
metadata:                       # 元数据
  name: emp
  namespace: default
  labels:
   app: emp
spec:
  containers:
  - name: empapp
    image: nginx:1.9.7
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - mountPath: /usr/share/nginx/html/         # 将数据卷挂载到/usr/share/nginx/html/下
      name: html
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /data/         # 将数据卷挂载到/data/下
      name: html
    command: [ '/bin/sh','-c', "while true;do echo $(date) >> /data/index.html;sleep 2;done;" ] # 间隔2秒向/data/index.html写入date
  volumes:
  - name: html                 # 共享一个html目录
    emptyDir: {}               # 定义一个empty类型挂载

创建运行

kubectl create -f pod-empty.yaml

运行后,可看到页面内容不断增加
在这里插入图片描述

2.gitrepo目录

gitrepo实际上并不是一个新的存储类型,只是emptydir上补添一个git命令来拉取文件而已。

当pod创建时候,会拉取git(依赖于宿主机git命令驱动)仓库中数据克隆到本地,并且作为存储卷定义在pod之上。gitrepo基于emptyDir,此存储卷是emptyDir,git仓库中拉取的代码存放在emptyDir后被定义到pod。

因为拉取git仓库的动作,仅仅是在pod创建时触发一次,并不会待续更新文件,因此在工作中基本上无用处。在这里不做过多讲解。

3.hostPath目录

hostPath类型则是映射node文件系统中的文件或者目录到pod里。可实现针对某一节点的数据持久化,如果节点宕机了,那数据就丢失了

示例

vi pod-hostpath.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型,这里是pod
metadata:                       # 元数据
  name: hostpath-pod
spec:
  containers:
  - name: hostpath
    image: busybox
    command: [ "sleep", "3600" ]
    volumeMounts:
    - mountPath: /test-data    # 将数据卷挂载到/test-data下
      name: host-volume
  volumes:
  - name: host-volume
    hostPath:                  # 挂载类型为hostPath
      path: /data              # 挂载到node节点的/data下
      type: Directory

创建运行

kubectl create -f pod-hostpath.yaml

进入到容器内部

kubectl exec -it hostpath-pod /bin/sh

创建测试文件
在这里插入图片描述
查看pod的node节点
在这里插入图片描述
我们登录到work2服务器上,查看/data文件夹,可以看到文件被保存到work2节点的data中
在这里插入图片描述
此后,删除掉pod,此文件也依然存在

4.nfs共享存储卷

nfs使的我们可以挂在已经存在的共享到的我们的Pod中,nfs不会被删除,仅仅是解除挂在状态而已,这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递.并且,nfs可以同时被多个pod挂在并进行读写

4.1.节点安装配置nfs

安装nfs

yum install -y nfs-utils

配置nfs
创建文件夹

mkdir -p /data/volumes

编辑配置

vi /etc/exports

插入以下内容

/data/volumes   *(rw,no_root_squash)

启动并查看

systemctl start nfs
systemctl start rpcbind
showmount -e localhost
showmount -e

在这里插入图片描述

4.2.node节点挂载

需要在所有节点安装 nfs-utils 组件,否则当Pod被分配到没有组件的节点,会启动失败,因为没有mount.nfs

安装nfsyum install -y nfs-utils
挂载nfsmount -t nfs master1:/data/volumes /nfs(没有则先新建nfs目录)

master1为k8s-master的host名称,需要提前在work服务器配置,方式如下

vi /etc/hosts

插入

192.168.23.101 master1

在这里插入图片描述
解除挂载umount /nfs
查看挂载效果mount
在这里插入图片描述
进入node节点,可查看/nfs目录下文件与master已互通

4.3.测试NFS

先在nfs服务内,加一个html测试页面

echo '<h1>NFS success</h1>' > /data/volumes/index.html

创建一个pod测试

vi vol-nfs.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型,这里是pod
metadata:                       # 元数据
  name: pod-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp
    image: nginx:1.9.7
    volumeMounts:
    - mountPath: /usr/share/nginx/html/         # 将数据卷挂载到/usr/share/nginx/html/下
      name: html
  volumes:
  - name: html
    nfs:                  # 挂载类型为nfs
      path: /data/volumes     # 引入nfs的路径
      server: 192.168.23.101  # 引入nfs的IP

运行pod后测试,可看到nginx输出页面

kubectl create -f vol-nfs.yaml

在这里插入图片描述
pod的删除重建,不影响pod服务效果

二、k8s的namespace概念

一个namespaces可以理解为kubernetes集群中的一个虚拟化集群。

在一个Kubernetes集群中可以拥有多个命名空间,它们在逻辑上彼此隔离。 这样可以方便我们区分各个团队的不同服务,增加安全甚至性能方面的帮助!

K8S默认已经为了我们初始化了三个namespace:

  • default:你的service和app默认被创建于此。
  • kube-system:kubernetes系统组件使用。
  • kube-public:公共资源使用。但实际上现在并不常用。

总而言之,namespace是一种为了你方便管理服务的辅助,你可以根据需要创建若干个namespace供业务服务分组治理。从另一方面来说,K8s的任何一个服务,都是归属于某一个namespace的。

三、PVC和PV

通过上面的存储卷的基础知识,我们大概了解了在K8S中,要持久化存储数据,最好还是存入到第三方服务里更安全,如NFS等。而在业界,这样的存储很多,使用方式五花八门,这就要求运维工程师必须要精通这些存储才能用好它。

PersistentVolume(pv)和PersistentVolumeClaim(pvc)就是k8s提供的两种API资源,用于抽象存储细节:

  • 管理员关注于如何通过pv提供存储功能而无需如何使用
  • 用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。

pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式。

接下来我们使用PV与PVC的方式,实现上面nfs模式的资源挂载效果。

定义一个PV资源

1.PV概念与使用

PersistentVolume(PV)是集群中由管理员配置的一段网络存储。 它是集群中的资源,就像节点是集群资源一样。因此PV是全局资源,不归属于任何namespace的。

vi vol-pv.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolume          # 创建的资源类型,这里是PersistentVolume
metadata:                       # 元数据
  name: pv-nfs                  # pv的名称
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi               # 存储的容量
  accessModes:                 # 访问模式
    - ReadWriteMany            # 读写权限,可以被多个节点挂载
  nfs:                         # 引入nfs服务
    path: /data/volumes        # 引入nfs的路径
    server: 192.168.23.101     # 引入nfs的IP

创建PV

kubectl create -f vol-pv.yaml

在这里插入图片描述
此处的RWX是PV资源的AccessModes (访问模式),有以下几种模式。

  • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
  • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
  • ReadWriteMany(RWX):读写权限,可以被多个节点挂载

此处的NFS资源,是支持多个pod同时挂载使用的。
同时,PV同一时间只能被一个PVC申请使用,这里有个状态变迁过程:

  • Available(可用):表示可用状态,还未被任何 PVC 绑定
  • Bound(已绑定):表示 PV 已经被 PVC 绑定
  • Released(已释放):PVC 被删除,但是资源还未被集群重新声明
  • Failed(失败): 表示该 PV 的自动回收失败

2.PVC的概念与使用

PersistentVolumeClaim(PVC)是由用户进行存储的请求。 它类似于pod。 Pod消耗节点资源,PVC消耗PV资源。因此PVC是归属于某个namespace的。

vi vol-pvc.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolumeClaim     # 创建的资源类型,这里是PersistentVolumeClaim
metadata:                       # 元数据
  name: pvc-nfs                 # pv的名称
  namespace: default
spec:
  storageClassName: manual
  accessModes:                 # 访问模式
    - ReadWriteMany            # 读写权限
  resources:
    requests:
      storage: 1Gi             # 要求的容量

创建PVC

kubectl create -f vol-pvc.yaml

在这里插入图片描述
现在查看PV的状态,发现其已经被绑定使用了
在这里插入图片描述
测试一个pod的使用效果,将上面的pvc引入这里使用

vi pod-pvc.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型,这里是Pod
metadata:                       # 元数据
  name: testpv                  
spec:
  containers:
  - name: nginx
    image: nginx:1.9.7
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: /usr/share/nginx/html/         # 将pvc挂载到/usr/share/nginx/html/下
      name: pvc-nfs
  volumes:
  - name: pvc-nfs
    persistentVolumeClaim:
      claimName: pvc-nfs       # 申请PVC的名字

创建Pod

kubectl create -f pod-pvc.yaml

查看效果
在这里插入图片描述

3.PVC的阻塞

我们再创建一个新的pvc

vi vol-pvc2.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolumeClaim     # 创建的资源类型,这里是PersistentVolumeClaim
metadata:                       # 元数据
  name: pvc-nfs2                # pv的名称
  namespace: default
spec:
  storageClassName: manual
  accessModes:                 # 访问模式
    - ReadWriteMany            # 读写权限
  resources:
    requests:
      storage: 1Gi             # 要求的容量
kubectl create -f vol-pvc2.yaml

我们可以看到,当PVC没有足够的PV来满足时,状态为挂起pending
在这里插入图片描述
我们再新建一个PV

vi vol-pv2.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolume          # 创建的资源类型,这里是PersistentVolume
metadata:                       # 元数据
  name: pv-nfs2                 # pv的名称
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi               # 存储的容量
  accessModes:                 # 访问模式
    - ReadWriteMany            # 读写权限,可以被多个节点挂载
  nfs:                         # 引入nfs服务
    path: /data/volumes        # 引入nfs的路径
    server: 192.168.23.101     # 引入nfs的IP
kubectl create -f vol-pv2.yaml

如果有PV被释放,或者新建了PV能满足请求时,自动绑定占用
在这里插入图片描述

四、statefulset控制器

现实工作中,我们的应用,分两类,有状态和无状态

  • 无状态的, 关注的是群体,如nginx,tomcat等
  • 有状态的, 关注的是个体,如redis,mysql等

我们前面的控制器,对无状态的应用操作很好,但对有状态的应用完全不实用。

有状态应用集的特点:

①稳定且需要唯一的网络标识符;

如: Redis集群,

在Redis集群中,它是通过槽位来存储数据的,假如:第一个节点是0 ~ 1000,第二个节点是1001 ~ 2000,第三个节点2001 ~ 3000…等等,这就使得Redis集群中每个节点要通过ID来标识自己,如:

第二个节点宕机了,重建后它必须还叫第二个节点,或者说第二个节点叫R2,它必须还叫R2,这样在获取1001~2000槽位的数据时,才能找到数据,否则Redis集群将无法找到这段数据。

② 稳定且持久的存储;

可实现持久存储,新增或减少pod,存储不会随之发生变化。

针对这样的特点,k8s推出StatefulSet

1.PV资源配置

制做三个基础PV资源,需要分别在/data/volumes下添加文件夹v1,v2,v3

vi pv.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolume          # 创建的资源类型,这里是PersistentVolume
metadata:                       # 元数据
  name: pv001                   # pv的名称
  labels: 
    name: pv001
spec:
  capacity:
    storage: 1Gi               # 存储的容量
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  nfs:                         # 引入nfs服务
    path: /data/volumes/v1     # 引入nfs的路径
    server: 192.168.23.101     # 引入nfs的IP
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolume          # 创建的资源类型,这里是PersistentVolume
metadata:                       # 元数据
  name: pv002                   # pv的名称
  labels: 
    name: pv002
spec:
  capacity:
    storage: 2Gi               # 存储的容量
  accessModes: ["ReadWriteOnce"]
  nfs:                         # 引入nfs服务
    path: /data/volumes/v2     # 引入nfs的路径
    server: 192.168.23.101     # 引入nfs的IP
---
apiVersion: v1                  # 配置格式的版本
kind: PersistentVolume          # 创建的资源类型,这里是PersistentVolume
metadata:                       # 元数据
  name: pv003                   # pv的名称
  labels: 
    name: pv003
spec:
  capacity:
    storage: 3Gi               # 存储的容量
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  nfs:                         # 引入nfs服务
    path: /data/volumes/v3     # 引入nfs的路径
    server: 192.168.23.101     # 引入nfs的IP

创建PV

kubectl create -f pv.yaml

在这里插入图片描述

2.创建StatefulSet服务

StatefulSet资源清单包含三个部分:

  • headless service 用于定义网络标识(DNS)
  • StatefulSet 控制器,用于定义具体应用
  • volumeClaimTemplate 存储卷申请模板,用于创建PVC
vi stateful.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Service                   # 创建的资源类型
metadata:                       # 元数据
  name: myapp-svc               # 名称
  namespace: default
  labels: 
    name: myapp
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None               # 配置headless service,此处为没有网络头
  selector:
    app: myapp-pod              # 匹配Pod标签
---
apiVersion: apps/v1              # 配置格式的版本
kind: StatefulSet               # 控制器
metadata:                       # 元数据
  name: myapp                   # 名称
spec:
  serviceName: myapp            # 服务名称
  replicas: 2
  selector:
    matchLabels:
      app: myapp-pod            # 匹配Pod
  template:
    metadata:
      labels:
        app: myapp-pod
    spec:
      containers:
      - name: myapp
        image: nginx:1.9.7
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: myappdata
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: myappdata          # pvc 名称
    spec:
      accessModes: ["ReadWriteOnce"] # 权限
      resources:
        requests:
          storage: 1Gi         # pvc 大小

创建

kubectl create -f stateful.yaml

在这里插入图片描述

查看pod,发现sts创建的pod集群,是有序编号的,从第0个开始,依次创建。

查看pvc列表(sts自动创建管理的),它与上面的pod是序号一一对应的。也就是说,此pod重启重建,对应的pvc都是一样的

服务扩容:
在这里插入图片描述
可以看到,pod扩展,会连带pvc一起扩容。但是缩容却不会
在这里插入图片描述
删除pod:
在这里插入图片描述
可以看到,新建出来的pod名称/node都是不变的,对应 的PVC当然也不变。

五、configmap与secret

secret和configmap可以理解为特殊的存储卷,但是它们不是给Pod提供存储功能的,而是提供了从集群外部向集群内部的应用注入配置信息的功能。ConfigMap扮演了K8S集群中配置中心的角色。

ConfigMap定义了Pod的配置信息,可以以存储卷的形式挂载至Pod中的应用程序配置文件目录,从configmap中读取配置信息;也可以基于环境变量的形式,从ConfigMap中获取变量注入到Pod容器中使用。

1.命令行方式创建ConfigMap

--from-literal=key=value格式直接赋值,示例如下

kubectl create configmap web-config --from-literal=db.host=192.168.23.80 --from-literal=db.port='3306'

查看这个configmap信息:

kubectl get configmap -o yaml

在这里插入图片描述
--from-file=path方式,将文件中信息存入configmap
先将值放入文件中

echo -n 192.168.23.80 > ./db.host
echo -n 3306 > ./db.port

在这里插入图片描述

再合建configmap

kubectl create cm config2 --from-file=./db.host --from-file=./db.port

查看信息,效果一样

 kubectl get cm config2 -o yaml

在这里插入图片描述

2.YAML 配置文件方式创建ConfigMap

直接创建一个yaml文件,配置好信息

vi db.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: ConfigMap                 # 创建的资源类型
metadata:                       # 元数据
  name: config3                 # 名称
data:
  db.host: 192.168.23.80
  db.port: "3306"
  spring.datasource.type: "com.alibaba.druid.pool.DruidDataSource"
  blog: "moonce"

创建

kubectl apply -f db.yaml

查验

kubectl get cm config3 -o yaml

在这里插入图片描述

3.环境变量方式使用configmap

使用valueFromconfigMapKeyRefnamekey指定要用的key。

vi cm-pod.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型
metadata:                       # 元数据
  name: cmpod                   # 名称
spec:
  containers:
  - name: myapp
    image: busybox
    args: ["/bin/sh","-c","sleep 3000"]
    env:
    - name: DB_HOST             # pod环境变量中的名字
      valueFrom:
        configMapKeyRef:
          name: config3         # 到config3中取值
          key: db.host          # 取config3中的db.host的值
    - name: DB_PORT             # pod环境变量中的名字
      valueFrom:
        configMapKeyRef:
          name: config3         # 到config3中取值
          key: db.port          # 取config3中的db.port的值

运行pod

kubectl create -f cm-pod.yaml

进入pod容器中,查看环境信息已经引入

kebectl exec -it cmpod /bin/sh

在这里插入图片描述

4.volume挂载使用

把configmap当作文件挂载进pod容器

vi cm-pod2.yaml
---
apiVersion: v1                  # 配置格式的版本
kind: Pod                       # 创建的资源类型
metadata:                       # 元数据
  name: cmpod2                  # 名称
spec:
  containers:
  - name: myapp
    image: busybox
    args: ["/bin/sh","-c","sleep 3000"]
    volumeMounts:
    - name: db
      mountPath: "/etc/db"     # 挂载到/etc/db路径下
      readOnly: true
  volumes:
  - name: db
    configMap:
      name: config3            # 挂载config3

运行pod

kubectl create -f cm-pod2.yaml

进入容器

kebectl exec -it cmpod2 /bin/sh

在这里插入图片描述
可以看到在挂载点/etc/db下,configMap的值被引入

相关文章

【Kubernetes】centos7搭建k8s集群(一) - 环境搭建与安装
【Kubernetes】centos7搭建k8s集群(二) - 操作命令与讲解
【Kubernetes】centos7搭建k8s集群(三) - 存储使用与讲解
【Kubernetes】centos7搭建k8s集群(四) - 网络原理讲解

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐