【学习笔记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   <none>           <none>
#在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

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   <none>           <none>

与使用普通 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 的回收 \color{red}{策略设置为 Recycle,所以数据会被清除} ,但这可能不是我们想要的结果。如果我们希望保留数据,可以将策略设置为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   <none>        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   <none>           <none>
#查看详情
$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   <none>           <none>
$ 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   <none>        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'

参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容