1.
PV&&PVC基础概念
PersistentVolume(PV)是外部存储系统中的一块存储空间,管理员创建和维护.PV具有独立性,生命周期独立于Pod.
PersistentVolumeClaim(PVC)是对PV的申请.PVC一般普通用户创建和维护.

需要为Pod分配资源时,用户创建一个PVC,指明存储资源的容量大小和访问模式,kubernetes会查找并提供满足条件的PV.

2.
nfs部署

实验环境,部署nfs简单快捷.
真实环境,建议使用安全性高的分布式存储,ceph等.

nfs服务需要用到的软件
nfs-utils          nfs主程序(rpc.nfsd,rbc.mountd)
rpcbind          rpc主程序

一些基础情况说明:
现在整个集群环境4个节点,    
k8s-master1   Ready         32d       v1.12.3
k8s-master2   Ready         32d       v1.12.3
k8s-master3   Ready         32d       v1.12.3
k8s-node1     Ready         32d       v1.12.3
其中k8s-node1的剩余空间最大,因此这边用的是k8s-node1来存储nfs目录和数据.

k8s-node1上部署nfs

安装软件包

[root@k8s-node1 ~]# yum install -y nfs-utils rpcbind

启动服务设置开机启动并查看当前状态

[root@k8s-node1 ~]# systemctl start rpcbind  &&systemctl enable rpcbind
[root@k8s-node1 ~]# systemctl status rpcbind
● rpcbind.service - RPC bind service
   Loaded: loaded (/usr/lib/systemd/system/rpcbind.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2019-04-01 13:46:48 CST; 3min 24s ago
Main PID: 36888 (rpcbind)
   CGroup: /system.slice/rpcbind.service
           └─36888 /sbin/rpcbind -w

Apr 01 13:46:48 k8s-node1 systemd[1]: Starting RPC bind service...
Apr 01 13:46:48 k8s-node1 systemd[1]: Started RPC bind service.
[root@k8s-node1 ~]# systemctl start nfs && systemctl enable nfs
[root@k8s-node1 ~]# systemctl status nfs
● nfs-server.service - NFS server and services
   Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; disabled; vendor preset: disabled)
   Active: active (exited) since Mon 2019-04-01 13:53:05 CST; 2s ago
  Process: 38033 ExecStartPost=/bin/sh -c if systemctl -q is-active gssproxy; then systemctl restart gssproxy ; fi (code=exited, status=0/SUCCESS)
  Process: 38016 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)
  Process: 38015 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
Main PID: 38016 (code=exited, status=0/SUCCESS)
   Memory: 0B
   CGroup: /system.slice/nfs-server.service

Apr 01 13:53:05 k8s-node1 systemd[1]: Starting NFS server and services...
Apr 01 13:53:05 k8s-node1 systemd[1]: Started NFS server and services.
[root@k8s-node1 ~]#
[root@k8s-node1 ~]# rpcinfo -p localhost
   program vers proto   port  service
    100000    4   tcp    111  portmapper
    100000    3   tcp    111  portmapper
    100000    2   tcp    111  portmapper
    100000    4   udp    111  portmapper
    100000    3   udp    111  portmapper
    100000    2   udp    111  portmapper
    100005    1   udp  20048  mountd
    100005    1   tcp  20048  mountd
    100005    2   udp  20048  mountd
    100005    2   tcp  20048  mountd
    100005    3   udp  20048  mountd
    100005    3   tcp  20048  mountd
    100003    3   tcp   2049  nfs
    100003    4   tcp   2049  nfs
    100227    3   tcp   2049  nfs_acl
    100003    3   udp   2049  nfs
    100003    4   udp   2049  nfs
    100227    3   udp   2049  nfs_acl
    100021    1   udp  53501  nlockmgr
    100021    3   udp  53501  nlockmgr
    100021    4   udp  53501  nlockmgr
    100021    1   tcp  35799  nlockmgr
    100021    3   tcp  35799  nlockmgr
    100021    4   tcp  35799  nlockmgr
[root@k8s-node1 ~]#

配置nfs服务端
/etc/exports是默认的nfs程序配置文件,默认为空
加入配置

[root@k8s-node1 ~]# cat /etc/exports
[root@k8s-node1 ~]# echo "/mnt/data 192.168.32.0/24(rw,no_root_squash,no_all_squash,sync)" >/etc/exports
[root@k8s-node1 ~]#

重启rpcbind和nfs服务,检索nfs本机挂载情况

[root@k8s-node1 ~]# showmount -e localhost
Export list for localhost:
/mnt/data 192.168.32.0/24

注意:
192.168.32.0/24(rw,no_root_squash,no_all_squash,sync)
中间不能有空格,切记,不然报错.

注意:
必须在所有node安装rpcbind并启动,确保nfs能正常挂载。
否则报错:
 mount failed: exit status 32

nfs客户端配置
客户端也需要安装nfs-utils和rpcbind,同时只需要启动rpcbind.

[root@k8s-master1 ~]#  yum install -y nfs-utils rpcbind
[root@k8s-master1 ~]# showmount -e k8s-node1
Export list for k8s-node1:
/mnt/data 192.168.32.0/24
[root@k8s-master1 ~]#

3.
pv基础知识

创建pv

[root@k8s-master1 pv]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 100Mi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /mnt/data/pv1
    server: 192.168.32.131
[root@k8s-master1 pv]#

capacity指定PV的容量

accessModes指定访问模式,支持的模式有:
ReadWriteOnce  能以read-write模式mount到单个节点
ReadOnlyMany  能以read-only模式mount到多个节点
ReadWriteMany 能以read-write模式mount到多个节点

节点其实就是pod,如果多个pod必须用ReadWriteMany

persistentVolumeReclaimPolicy指定pv的回收策略
Retain 需要管理员收工回收,安全性最高
Recycle 清除PV中的数据,效果相当于执行rm -rf 
Delete 删除storage provider上对应的存储资源

storageClassName 指定PV的分类,PVC可以指定class申请相应的PV

path: /data/k8s/pv1 指定nfs上对应的目录,需手动建立

[root@k8s-master1 pv]# kubectl apply -f nfs-pv1.yml
persistentvolume "mypv1" created
[root@k8s-master1 pv]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
mypv1     100Mi      RWX            Retain           Available             nfs                      35s

PV是有状态的资源对象,有以下几种状态:
Available:空闲状态
Bound:已经绑定到某个pvc上
Released:对应的pvc已经删除,但资源还没有被回收
Failed:pv自动回收失败

4.
pvc基础知识

创建pvc

[root@k8s-master1 pv]# cat nfs-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: mypvc1
spec:
   accessModes:
     - ReadWriteMany
   resources:
     requests:
       storage: 50Mi
   storageClassName: nfs
[root@k8s-master1 pv]# kubectl get pvc,pv
NAME                           STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mypvc1   Bound     mypv1     100Mi      RWX            nfs            3m

NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM            STORAGECLASS   REASON    AGE
persistentvolume/mypv1   100Mi      RWX            Retain           Bound     default/mypvc1   nfs                      16m
[root@k8s-master1 pv]#

注意:pvc使用之后,pv的状态已经变成Bound

5.
pv和pvc是怎么对应上的呢?

上面测试的pv和pvc是通过指定   storageClassName: nfs  参数对应的.

如果不指定这个参数,会怎样呢?
根据官方的描述:
用户创建一个PVC,指明存储资源的容量大小和访问模式,kubernetes会查找并提供满足条件的PV.
也就是说,pvc会根据自己的需求去申请合适的pv.

测试见下:
pv

[root@k8s-master1 pv]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 100Mi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName:
  nfs:
    path: /mnt/data/pv1
    server: 192.168.32.131
[root@k8s-master1 pv]# cat nfs-pv2.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv2
spec:
  capacity:
    storage: 10Mi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName:
  nfs:
    path: /mnt/data/pv1
    server: 192.168.32.131
[root@k8s-master1 pv]#

pvc

[root@k8s-master1 pv]# cat nfs-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: mypvc1
spec:
   accessModes:
     - ReadWriteMany
   resources:
     requests:
       storage: 10Mi
   storageClassName:
[root@k8s-master1 pv]# cat nfs-pvc2.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: mypvc2
spec:
   accessModes:
     - ReadWriteMany
   resources:
     requests:
       storage: 50Mi
   storageClassName:
[root@k8s-master1 pv]#

执行命令,查看对应情况:

[root@k8s-master1 pv]# kubectl apply -f nfs-pv2.yml
persistentvolume "mypv2" created                                 7s
[root@k8s-master1 pv]# kubectl apply -f nfs-pv1.yml
persistentvolume "mypv1" created
[root@k8s-master1 pv]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
mypv1     100Mi      RWX            Retain           Available                                      2s
mypv2     10Mi       RWX            Retain           Available                                      16s
[root@k8s-master1 pv]# kubectl apply -f nfs-pvc2.yml
persistentvolumeclaim "mypvc2" created
[root@k8s-master1 pv]# kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim "mypvc1" created
[root@k8s-master1 pv]# kubectl get pv,pvc
NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM            STORAGECLASS   REASON    AGE
persistentvolume/mypv1   100Mi      RWX            Retain           Bound     default/mypvc2                            24s
persistentvolume/mypv2   10Mi       RWX            Retain           Bound     default/mypvc1                            38s

NAME                           STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/mypvc1   Bound     mypv2     10Mi       RWX                           5s
persistentvolumeclaim/mypvc2   Bound     mypv1     100Mi      RWX                           8s

可以看到,不指定任何匹配参数的情况下,pvc自动申请使用匹配的pv.
注意: 
如果pv指定 storageClassName:参数,pvc不指定相对应的storageClassName:参数,pvc无法自动申请使用匹配的pv.
执行命令后,pvc一直是pending状态,见下:

[root@k8s-master1 pv]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
mypv1     100Mi      RWX            Retain           Available             my1                      2m
mypv2     10Mi       RWX            Retain           Available             my2                      2m
[root@k8s-master1 pv]# kubectl get pvc
NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1    Pending                                                      2m
mypvc2    Pending                                                      2m
[root@k8s-master1 pv]#

6.
mysql使用pv和pvc

手动创建pv的存储目录

[root@k8s-node1 ~]# mkdir -p /mnt/data/mysqlpv1
[root@k8s-node1 ~]# ll /mnt/data/
total 0
drwxr-xr-x 2 root       root         6 Apr  8 17:26 mysqlpv1
drwxr-xr-x 2 root       root         6 Apr  8 10:55 pv1

pv

[root@k8s-master1 pv]# cat mysql-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv1
spec:
  capacity:
    storage: 300Mi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName:
  nfs:
    path: /mnt/data/mysqlpv1
    server: 192.168.32.131

[root@k8s-master1 pv]# kubectl apply -f mysql-pv1.yml
persistentvolume "mysql-pv1" created
[root@k8s-master1 pv]# kubectl get pv
NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
mysql-pv1   300Mi      RWX            Retain           Available                                      31s

pvc

[root@k8s-master1 pv]# cat mysql-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: mysql-pvc1
spec:
   accessModes:
     - ReadWriteMany
   resources:
     requests:
       storage: 300Mi
   storageClassName:

[root@k8s-master1 pv]# kubectl apply -f mysql-pvc1.yml
persistentvolumeclaim "mysql-pvc1" created
[root@k8s-master1 pv]# kubectl get pvc
NAME         STATUS    VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pvc1   Bound     mysql-pv1   300Mi      RWX                           5s

mysql中部署

[root@k8s-master1 pv]# cat mysql.yml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: mysql
spec:
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
      volumes:
        - name: mysql-data
          persistentVolumeClaim:
            claimName: mysql-pvc1
[root@k8s-master1 pv]#
[root@k8s-master1 pv]# kubectl apply -f mysql.yml
service "mysql" created
deployment.apps "mysql" created
[root@k8s-master1 pv]# kubectl get pod -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-5f4c86bcf9-gtstz   1/1       Running   0          9m        172.30.79.2   k8s-master2

用docker inspect检索信息看看

[root@k8s-master2 ~]# docker ps
CONTAINER ID        IMAGE                                                                           COMMAND                  CREATED             STATUS              PORTS               NAMES
af22cadb3a3b        mysql@sha256:de2913a0ec53d98ced6f6bd607f487b7ad8fe8d2a86e2128308ebf4be2f92667   "docker-entrypoint..."   9 minutes ago       Up 9 minutes                            k8s_mysql_mysql-5f4c86bcf9-gtstz_default_7ff51d1a-5a71-11e9-ae21-000c291d7023_0
0dc35308a0fb        registry.access.redhat.com/rhel7/pod-infrastructure:latest                      "/usr/bin/pod"           10 minutes ago      Up 10 minutes                           k8s_POD_mysql-5f4c86bcf9-gtstz_default_7ff51d1a-5a71-11e9-ae21-000c291d7023_0
[root@k8s-master2 ~]# docker inspect af22cadb3a3b

 "Mounts": [
            {
                "Type": "bind",
                "Source": "/var/lib/kubelet/pods/7ff51d1a-5a71-11e9-ae21-000c291d7023/volumes/kubernetes.io~nfs/mysql-pv1",
                "Destination": "/var/lib/mysql",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },

可以看到使用的pv

检索pv目录生成的数据

[root@k8s-node1 ~]# ll /mnt/data/mysqlpv1/
total 110604
-rw-rw---- 1 systemd-bus-proxy ssh_keys       56 Apr  9 10:46 auto.cnf
-rw-rw---- 1 systemd-bus-proxy ssh_keys 12582912 Apr  9 10:46 ibdata1
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Apr  9 10:46 ib_logfile0
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Apr  9 10:45 ib_logfile1
drwx------ 2 systemd-bus-proxy ssh_keys     4096 Apr  9 10:46 mysql
drwx------ 2 systemd-bus-proxy ssh_keys     4096 Apr  9 10:45 performance_schema
[root@k8s-node1 ~]#

7.
操作mysql,生成数据,测试效果

创建数据库t1

[root@k8s-master2 ~]# docker exec  af22cadb3a3b mysql -uroot -ppassword -e"create database t1"
Warning: Using a password on the command line interface can be insecure.
[root@k8s-master2 ~]# docker exec  af22cadb3a3b mysql -uroot -ppassword -e"show databases;"
Warning: Using a password on the command line interface can be insecure.
Database
information_schema
mysql
performance_schema
t1

在t1中创建数据表t1

[root@k8s-master2 ~]# docker exec  af22cadb3a3b mysql -uroot -ppassword -e "use t1;create table t1(i int);"
Warning: Using a password on the command line interface can be insecure.

数据表t1插入数据并检索

[root@k8s-master2 ~]# docker exec  af22cadb3a3b mysql -uroot -ppassword -e "use t1;create table t1(i int);"
Warning: Using a password on the command line interface can be insecure.
[root@k8s-master2 ~]# docker exec  af22cadb3a3b mysql -uroot -ppassword -e "use t1;insert into t1 values(1),(3),(3);"
Warning: Using a password on the command line interface can be insecure.
[root@k8s-master2 ~]# docker exec  af22cadb3a3b mysql -uroot -ppassword -e "use t1;select * from t1;"
i
1
3
3
Warning: Using a password on the command line interface can be insecure.
[root@k8s-master2 ~]#

已经生成了数据,我们看看nfs存储目录的情况和容器存储目录的情况

nfs目录 

[root@k8s-node1 ~]# ll /mnt/data/mysqlpv1/
total 110604
-rw-rw---- 1 systemd-bus-proxy ssh_keys       56 Apr  9 10:46 auto.cnf
-rw-rw---- 1 systemd-bus-proxy ssh_keys 12582912 Apr  9 11:09 ibdata1
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Apr  9 11:09 ib_logfile0
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Apr  9 10:45 ib_logfile1
drwx------ 2 systemd-bus-proxy ssh_keys     4096 Apr  9 10:46 mysql
drwx------ 2 systemd-bus-proxy ssh_keys     4096 Apr  9 10:45 performance_schema
drwx------ 2 systemd-bus-proxy ssh_keys       48 Apr  9 11:08 t1
[root@k8s-node1 ~]# ll /mnt/data/mysqlpv1/t1
total 112
-rw-rw---- 1 systemd-bus-proxy ssh_keys    65 Apr  9 11:05 db.opt
-rw-rw---- 1 systemd-bus-proxy ssh_keys  8554 Apr  9 11:08 t1.frm
-rw-rw---- 1 systemd-bus-proxy ssh_keys 98304 Apr  9 11:09 t1.ibd
[root@k8s-node1 ~]#

容器

[root@k8s-master2 ~]# docker exec  af22cadb3a3b ls /var/lib/mysql
auto.cnf
ib_logfile0
ib_logfile1
ibdata1
mysql
performance_schema
t1
[root@k8s-master2 ~]# docker exec  af22cadb3a3b ls /var/lib/mysql/t1
db.opt
t1.frm
t1.ibd
[root@k8s-master2 ~]#

数据对比可知,pvc使用成功
现在模拟故障,k8s-master2机器死掉 

[root@k8s-master2 ~]# init 0
Connection closing...Socket close.

Connection closed by foreign host.

Disconnected from remote host(k8s-master2) at 11:14:05.

Type `help' to learn how to use Xshell prompt.
[C:\~]$
[root@k8s-master1 pv]# kubectl get pod -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-5f4c86bcf9-cz2np   1/1       Running   0          1m        172.30.27.2   k8s-master3
mysql-5f4c86bcf9-gtstz   1/1       Unknown   0          34m       172.30.79.2   k8s-master2
[root@k8s-master1 pv]#

pod自动切换到k8s-master3了

[root@k8s-master3 ~]# docker ps
CONTAINER ID        IMAGE                                                                           COMMAND                  CREATED              STATUS              PORTS               NAMES
e53dccc404b7        mysql@sha256:de2913a0ec53d98ced6f6bd607f487b7ad8fe8d2a86e2128308ebf4be2f92667   "docker-entrypoint..."   About a minute ago   Up About a minute                       k8s_mysql_mysql-5f4c86bcf9-cz2np_default_38792912-5a76-11e9-ae21-000c291d7023_0
228f7a798f7e        registry.access.redhat.com/rhel7/pod-infrastructure:latest                      "/usr/bin/pod"           2 minutes ago        Up 2 minutes                            k8s_POD_mysql-5f4c86bcf9-cz2np_default_38792912-5a76-11e9-ae21-000c291d7023_0
[root@k8s-master3 ~]#

检索数据

[root@k8s-master3 ~]# docker exec e53dccc404b7 mysql -uroot -ppassword -e "use t1;select * from t1;"
Warning: Using a password on the command line interface can be insecure.
i
1
3
3
[root@k8s-master3 ~]#

可以看到Mysql的数据已经和pod解耦。