k8s 1.5版本支持了有状态应用StatefullSet,其中一个很重要的技术支撑点就是,持久化存储。

在前面介绍Statefull的时候,我把涉及到PV的地方改为使用HostPath+nodeSelector来代替了,虽然可以达到“有状态”的目的,但是应用如果要保证数据的HA,需要应用本身起多副本,并且副本之间需要能够自行完成数据的备份。

要求有点高。

更好的做法是什么呢?回过头来看,如果将应用app和数据做好解耦,那么只要做到数据本身的HA就可以了,所以,Persist Volume(持久化存储)是一剂良药。

kubernetes v1.2版本开始,引入了2个资源API:PersistentVolume(pv)和PersistentVolumeClaim(pvc)。

pv与container用的volume不同。container用的volume是与pod相同生命周期的,delete pod后,volume也会随之删除;但pv不同,其生命周期独立于pod。

有了pv,pod的需求按说就满足了,为什么还要pvc呢?原因是,生产环境上pod的提交者可能只是普通用户,而pv的创建需要admin,所以更好的做法是将pv看作一种像cpu/memory的资源,普通客户的pod需要多少存储,向k8s提交请求(pvc)就行了。k8s会根据请求和当前的pv情况,选择一个能够满足pvc要求的pv,将该pv与该pvc绑定,并挂载到pod中。pod生命周期结束后,pv还是继续存在的。

PV支持的类型:

  • GCEPersistentDisk
  • AWSElasticBlockStore
  • AzureFile
  • AzureDisk
  • FC (Fibre Channel)
  • Flocker
  • NFS
  • iSCSI
  • RBD (Ceph Block Device)
  • CephFS
  • Cinder (OpenStack block storage)
  • Glusterfs
  • VsphereVolume
  • Quobyte Volumes
  • HostPath (single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
  • VMware Photon

看着很多,但大部分都是云存储(公有云或私有云),我现在能用的其实就是NFS和HostPath,而HostPath只是单node测试用的,多节点上由于Pod重新调度后会漂移到其他node,是有问题的。

下面我会以NFS为例,简单介绍下PV/PVC的用法。当然了,NFS本身也不是可靠的,但是具备了PV/PVC的基础要求。

安装NFS

NFS server所在的机器是centos 7.2,具体的安装步骤参考这篇文章就好了,配置很简单。如果你在mount的时候,出现了“mount.nfs: access denied by server while mounting”,检查下/etc/exports的配置,这里填的IP地址是client的地址。我设置了网段,如下。

# cat /etc/exports
/home/nfs           192.168.128.*(rw,sync,no_root_squash,no_subtree_check)

创建一个5GB的PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
#  annotations:
#    volume.beta.kubernetes.io/storage-class: "slow"
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /tmp
    server: 192.168.182.1

保存成文件pv.yml,kubectl create -f pv.yml即可。你将看到一个Available状态的PV:

kubectl get pv
NAME      CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     REASON    AGE
pv0003    5Gi        RWO           Recycle         Available                       3s

创建一个带PVC的nginx RC

---
kind: ReplicationController
apiVersion: v1
metadata:
  name: nginx-controller
spec:
  replicas: 1
  selector:
    component: nginx
  template:
    metadata:
      labels:
        component: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.11
          ports:
            - containerPort: 80
          volumeMounts:
            - name: volume-root
              mountPath: /usr/share/nginx/html
      volumes:
        - name: volume-root
          persistentVolumeClaim:
            claimName: nfs-claim
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-claim
#  annotations:
#    volume.beta.kubernetes.io/storage-class: "slow"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

还是一样保存成文件用来kubectl create。之后你将看到pv/pvc的状态都转为Bound,对应关系也是一目了然。如果状态一直是Pending,那么可以kubectl describe pvc nfs-claim和kubectl describe pod nginx-controller-xxxx来看具体是什么原因,一般是PVC的请求条件不满足。

# kubectl get pvc
NAME        STATUS    VOLUME    CAPACITY   ACCESSMODES   AGE
nfs-claim   Bound     pv0001    5Gi        RWO           1h
[root@titan1 StatefulSets]#
# kubectl get pv
NAME      CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM               REASON    AGE
pv0001    5Gi        RWO           Recycle         Bound     default/nfs-claim             1h

验证

  1. 通过kubectl exec -it nginx-controller-xxxx bash进入到容器的文件系统,到/usr/share/nginx/html去创建一个文件,然后在nfs server的机器的/home/nfs目录里看有没有对应的文件,可以验证PV/PVC的功能好使。
  2. kubectl delete pod nginx-controller-xxxx,之后rc会重新拉起一个新的pod,再kubectl exec进到这个容器的文件系统里,查看/usr/share/nginx/html有没有刚刚打进去的文件。