【学习笔记09】每天5分钟,玩转kubernetes-09(数据管理)

第九章 数据管理

Pod是短暂的,Pod在销毁时,保存在容器内部的文件系统各种的数据会被清除。
为了持久化保存容器中的的数据,可以使用K8s Volume。

9.1 Volume

9.1.1 emptyDir

对于容器来说是持久的,对于Pod不是。当Pod从节点删除时,Volume的内容也会被删除。但是如果只是容器被销毁而Pod存在,则volume不受影响。也就是说:emptyDir Volume的生命周期与Pod一致
Pod中的所有容器都可以共享Volume,它们可以指定各自的mount路径。
如下Pod有两个容器: producer 和 consumer,它们共享一个Volume. Producer 写, consumer 读。

vim emptyDir.yml
apiVersion: v1
kind: Pod
metadata:
  name: producer-consumer
spec:
  containers:
  - image: busybox
    name: producer
    volumeMounts:                                    # 将shared-volume mount 到 producer_dir目录
    - mountPath: /producer_dir
      name: shared-volume
    args:                                            # 将数据写入到文件hello中
    - /bin/sh
    - -c
    - echo "hello world" > /producer_dir/hello ; sleep 30000
    
  - image: busybox
    name: consumer
    volumeMounts:                                    # 将shared-volume mount 到 /consumer_dir
    - mountPath: /consumer_dir
      name: shared-volume
    args:
    - /bin/sh
    - -c
    - cat /consumer_dir/hello ; sleep 30000           # 通过cat从文件hello读数据
    
  volumes:                                            # 定义了一个emptyDir类型的Volume,名字是shared-volume.
  - name: shared-volume
    emptyDir: {}

执行

$kubectl apply -f emptyDir.yml 

检查

$kubectl  logs producer-consumer  consumer   
hello world
#
$kubectl get pods producer-consumer -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP             NODE                 NOMINATED NODE   READINESS GATES
producer-consumer   2/2     Running   0          26m   10.244.1.218   k8s-node-122132072              
#在node节点上
docker ps  |grep producer -i
#
docker inspect e2f72565c8bf
"Mounts": [
{
       "Type": "bind",
       "Source": "/var/lib/kubelet/pods/c5879c3e-8803-4d0d-a0ce-e41304853f3c/volumes/kubernetes.io~empty-dir/shared-volume",
       "Destination": "/producer_dir",
       "Mode": "",
       "RW": true,
       "Propagation": "rprivate"
   }

emptyDir是host上创建的临时目录,其优点是能够方便地为Pod中的容器提供共享存储,不需要额外的配置。它不具备持久性,如果Pod没有了,emptyDir也就没有了。所以emptyDir的使用场景是: 适合Pod中的容器需要临时共享存储空间的场景。

9.1.2 hostPath

hostPath volume的作用是将Docker Host文件系统中已经存在的目录mount给Pod的容器。大部分应用不会使用HostPath,因为它增加了Pod与节点的耦合。
应用场景: 需要访问K8s或docker内部数据(配置文件和二进制库)的应用需要使用hostPath.

9.1.2 外部Storage Provider

如果K8s部署在公有云上(比如AWS, Azure等),可以直接使用云硬盘作为Volume.
Ceph: 相对于emptyDir和hostPath,这些volume类型的最大特点就是不依赖K8s。Volume的底层基础设施由独立的存储系统管理,与K8S集群分离。

9.2 PersistentVolume & PersistentVolumeClaim

Volume在可管理上有不足: 要使用Volume,Pod必须事先知道如下信息:
(1)当前Volume来自AWS,CEPH
(2)AWS/CEPT Volume 已经提前创建, 并且知道确切的Volume-id
Pod是开发人员维护,Volume是存储系统管理员维护。二者之间耦合,不利于大规模系统的开发,沟通效率低下。
K8s给出的解决方案是:PersistentVolume(PV) 和 PersistentVolumeClaim(PVC)
PV: 外部存储系统中的一块存储空间,由管理员维护和创建。PV具有持久性,声明周期独立于Pod。
PVC: 是对PV的申请。PVC通常由普通用户创建和维护。需要为Pod分配资源时,用户可以创建一个PVC,指定存储资源容量的大小和访问模式,K8s会查找并提供满足条件的PV。
有了PVC,用户只需要告诉K8s需要什么资源,而不必关心真正的空间从哪里来、如何访问。
K8s支持多种类型的PV,比如AWS EBS、Ceph、NFS等。

9.2.1 NFS PV

  • k8s的持久化存储PV&&PVC

1. 建立NFS服务

#所有节点安装nfs
yum install -y nfs-common nfs-utils 
#在master节点创建共享目录
 mkdir /data1/nfsdata
#授权共享目录
chmod 666 /data1/nfsdata
#编辑exports文件
cat /etc/exports
/data1/nfsdata *(rw,no_root_squash,no_all_squash,sync)
配置生效
exportfs  -r
#启动rpc和nfs(注意顺序)
systemctl start rpcbind
systemctl start nfs
#systemctl status rpcbind
systemctl status nfs

exportfs命令:

exportfs [-aruv] 
  -a :全部mount或者unmount /etc/exports中的内容 
  -r :重新mount /etc/exports中分享出来的目录 
  -u :umount 目录 
  -v 在export的时候,将详细的信息输出到屏幕上 

客户端测试NFS

client:
yum install nfs-utils rpcbind
mkdir /data1/nfs_disk
service rpcbind start
service nfs start   
$showmount -e 10.122.xx.71 #nfs-server ip
Export list for 10.122.xx.71:
/data1/nfsdata 10.122.xx.0/24

mount -t nfs -o noatime,nodiratime 10.122.xx.71:/data1/nfsdata /data1/nfs_disk
$df -h |grep nfs
10.122.xx.71:/data1/nfsdata   33T  3.1G   33T   1% /data1/nfs_disk

2. 创建PV
创建一个 PV mypv1,配置文件 nfs-pv1.yml

vim nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
  namespace: default
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  storageClassName: nfs
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: "/data1/nfsdata"
    server: 10.122.132.71

创建 mypv1

$kubectl apply -f nfs-pv.yaml
persistentvolume/mypv1 created
$kubectl get pv 
NAME                 CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                     STORAGECLASS   REASON   AGE
mypv1                1Gi        RWX            Recycle          Available                             nfs                     30s

3. 创建PVC
PVC 就很简单了,只需要指定 PV 的容量,访问模式和 class。

vim nfs-pvc1.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
  namespace: default
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: "nfs"
  resources:
    requests:
      storage: 1Gi

#创建pvc
kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim/mypvc1 created
#查看
kubectl get pvc
NAME              STATUS   VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1            Bound    mypv1                1Gi        RWX            nfs            25s

4. 创建pod
上面已经创建好了pv和pvc,pod中直接使用这个pvc即可

vim nfs-pod1.yml
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
spec:
  containers:
  - name: mypod1
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 10000
    volumeMounts:
      - name: mydata
        mountPath: "/mydata"
  volumes:
    - name: mydata
      persistentVolumeClaim:
        claimName: mypvc1

##创建pod
 kubectl apply -f nfs-pod1.yml 
pod/mypod1 created
#查看 
kubectl get pod -o wide |egrep "name|mypod" -i
NAME                        READY   STATUS      RESTARTS   AGE     IP             NODE                 NOMINATED NODE   READINESS GATES
mypod1                      1/1     Running     0          42s     10.244.3.7     k8s-node-122132073              

与使用普通 Volume 的格式类似,在 volumes 中通过 persistentVolumeClaim 指定使用 mypvc1 申请的 Volume。

5. 验证

 kubectl exec -it mypod1 /bin/sh 
/ # ls mydata
/ # echo 'hello nfs'>mydata/hello.txt
/ # ls mydata/
 hello.txt

可见,在 Pod 中创建的文件 /mydata/hello 确实已经保存到了 NFS 服务器目录 /data1/nfsdata中。

9.2.2 PV的回收

如果不再需要使用 PV,可用删除 PVC 回收 PV。

$kubectl get pv
NAME                 CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   REASON   AGE
mypv1                1Gi        RWX            Recycle          Bound    default/mypvc1            nfs                     138m

$kubectl get pvc          
NAME              STATUS        VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1            Terminating   mypv1                1Gi        RWX            nfs            43m

未删除pvc之前 pv的状态是Bound.

$kubectl delete pvc mypvc1
persistentvolumeclaim "mypvc1" deleted
$kubectl get pvc      

$kubectl delete pv mypv1
$kubectl get pv

因为 PV 的回收 ,但这可能不是我们想要的结果。如果我们希望保留数据,可以将策略设置为Retain

9.2.3 PV的动态供给

前面的例子中,我们提前创建了 PV,然后通过 PVC 申请 PV 并在 Pod 中使用,这种方式叫做静态供给(Static Provision)。
与之对应的是动态供给(Dynamical Provision),即如果没有满足 PVC 条件的 PV,会动态创建 PV。相比静态供给,动态供给有明显的优势:不需要提前创建 PV,减少了管理员的工作量,效率高。
动态供给是通过 StorageClass 实现的,StorageClass 定义了如何创建 PV,下面是两个例子。
1.StorageClass standard: AWS EBS(略)

#AWS EBS略

Kubernetes 支持其他多种动态供给 PV 的 Provisioner,完整列表请参考 https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

2.glusterfs provisioner
https://kubernetes.io/docs/concepts/storage/storage-classes/#glusterfs

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://127.0.0.1:8081"
  clusterid: "630372ccdc720a92c681fb928f27b53f"
  restauthenabled: "true"
  restuser: "admin"
  secretNamespace: "default"
  secretName: "heketi-secret"
  gidMin: "40000"
  gidMax: "50000"
  volumetype: "replicate:3"

9.3 一个数据库例子

下面演示如何为 MySQL 数据库提供持久化存储,步骤为:
创建 PV 和 PVC。
部署 MySQL。
向 MySQL 添加数据。
模拟节点宕机故障,Kubernetes 将 MySQL 自动迁移到其他节点。
验证数据一致性。

首先创建 PV 和 PVC,配置如下:
mysql-pv.yml

vim mysql-pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
  namespace: default
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: nfs
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: "/data1/nfsdata/mysql-pv"
    server: 10.122.132.71

# 应用
 kubectl apply -f mysql-pv.yaml                          
persistentvolume/mysql-pv created

mysql-pvc.yml

vim mysql-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: "nfs"
  resources:
    requests:
      storage: 1Gi
#kubectl apply -f mysql-pvc.yaml 
persistentvolumeclaim/mysql-pvc created

检查

 kubectl get pv
NAME                 CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   REASON   AGE
mysql-pv             1Gi        RWO            Retain           Bound    default/mysql-pvc         nfs                     3m30s
[root@k8s-master-122132071 k8s]# kubectl get pvc
NAME              STATUS   VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pvc         Bound    mysql-pv             1Gi        RWO            nfs            51s

接下来部署 MySQL,配置文件如下:

vim mysql-service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  selector:
    app: mysql
  ports:
  - protocol: "TCP"
    port: 3306
    targetPort: 3306
  type: LoadBalancer

---

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
        - name: tz-config
          mountPath: /etc/localtime
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc
      - name: tz-config
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai

PVC mysql-pvc Bound 的 PV mysql-pv 将被 mount 到 MySQL 的数据目录 var/lib/mysql。

应用mysql-service.yaml

#
$kubectl apply -f mysql-service.yaml 
service/mysql created
deployment.apps/mysql created
#
$kubectl get pv
$kubectl get pvc
NAME              STATUS   VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pvc         Bound    mysql-pv             1Gi        RWO            nfs            16m

#
$kubectl  get svc -o wide |egrep "NAME|mysql" -i     
NAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE   SELECTOR
mysql               ClusterIP   10.10.76.104           3306/TCP   25m   app=mysql
#
$kubectl  get pods -o wide |egrep "NAME|mysql" -i
NAME                        READY   STATUS      RESTARTS   AGE     IP             NODE                 NOMINATED NODE   READINESS GATES
mysql-fd9db4d59-hjjrb       1/1     Running     0          2m11s   10.244.3.10    k8s-node-122132073              
#查看详情
$kubectl describe pod mysql-fd9db4d59-hjjrb 
Events:
  Type    Reason     Age    From                         Message
  ----    ------     ----   ----                         -------
  Normal  Scheduled  2m59s  default-scheduler            Successfully assigned default/mysql-fd9db4d59-hjjrb to k8s-node-122132073
  Normal  Pulling    2m52s  kubelet, k8s-node-122132073  Pulling image "mysql:5.6"
  Normal  Pulled     2m43s  kubelet, k8s-node-122132073  Successfully pulled image "mysql:5.6"
  Normal  Created    2m43s  kubelet, k8s-node-122132073  Created container mysql
  Normal  Started    2m43s  kubelet, k8s-node-122132073  Started container mysql
#查看日志
$kubectl logs mysql-fd9db4d59-hjjrb 
2019-10-08 23:11:21 1 [Note] Event Scheduler: Loaded 0 events
2019-10-08 23:11:21 1 [Note] mysqld: ready for connections.
Version: '5.6.45'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
#

可以看到mysql布署在k8s-node-122132073 上。下面通过客户端访问 Service mysql:

#测试mysql
mysql -h   10.10.76.104 -P3306   -ppassword -e "select version()"
或者
$kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword
If you don't see a command prompt, try pressing enter.
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.45    |
+-----------+

$kubectl get pods  |egrep  'NAME|mysql' -i  
NAME                        READY   STATUS      RESTARTS   AGE
mysql-client                1/1     Running     0          5m58s
mysql-fd9db4d59-hjjrb       1/1     Running     0          12m

更新数据

mysql> show databases;
mysql> create database test;
mysql> use test;
Database changed
mysql> create table my_id(id int(4));
Query OK, 0 rows affected (0.00 sec)

mysql> insert into my_id values(111);
Query OK, 1 row affected (0.00 sec)

#查看文件
ls -l /data1/nfsdata/mysql-pv/
total 110600
-rw-rw---- 1 systemd-bus-proxy ssh_keys 12582912 Oct  8 23:40 ibdata1
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Oct  8 23:40 ib_logfile0
-rw-rw---- 1 systemd-bus-proxy ssh_keys 50331648 Oct  8 23:40 ib_logfile1
drwx------ 2 systemd-bus-proxy ssh_keys     4096 Oct  8 23:40 mysql
drwx------ 2 systemd-bus-proxy ssh_keys     4096 Oct  8 23:40 performance_schema
drwx------ 2 systemd-bus-proxy ssh_keys       10 Oct  8 23:40 test

测试故障迁移
关闭 k8s-node-122132073 上的pod,看是否会进行故障切换。

$ kubectl get pods -o wide  |egrep "name|mysql"  -i
NAME                        READY   STATUS      RESTARTS   AGE   IP             NODE                 NOMINATED NODE   READINESS GATES
mysql-fd9db4d59-hjjrb       1/1     Running     0          19m   10.244.3.10    k8s-node-122132073              
$ kubectl get svc -o wide  |egrep "name|mysql"  -i    
NAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE   SELECTOR
mysql               ClusterIP   10.10.76.104           3306/TCP   41m   app=mysql

#容器中没有ps要手动安装
apt-get update && apt-get install procps

#k8s-node-122132073 停止pod
[root@k8s-node-122132073 ~]# docker ps |grep mysql
bc771f2776ab        mysql                                               "docker-entrypoint.s…"   5 minutes ago       Up 5 minutes                            k8s_mysql_mysql-699d897494-bmfx9_default_11530308-49e9-4f09-ab6c-a5e8e3ba36cb_0
cbc13eb6f7ee        registry.aliyuncs.com/google_containers/pause:3.1   "/pause"                 5 minutes ago       Up 5 minutes                            k8s_POD_mysql-699d897494-bmfx9_default_11530308-49e9-4f09-ab6c-a5e8e3ba36cb_0

#
$docker stop bc771f2776ab cbc13eb6f7ee 
#检查
mysql -h  10.10.76.104 -P 3306 -ppassword test -e 'select * from my_id'

参考:

  • 【目录】每天5分钟,玩转kubernetes
  • https://kubernetes.io/docs/concepts/storage/persistent-volumes/
  • https://jimmysong.io/kubernetes-handbook/concepts/persistent-volume.html
  • using-glusterfs-for-persistent-storage
  • k8s的持久化存储PV&&PVC
  • https://github.com/rootsongjc/kubernetes-handbook/tree/master/manifests/glusterfs

你可能感兴趣的:(【学习笔记09】每天5分钟,玩转kubernetes-09(数据管理))