20

kubernetes(十一) 存储& statefulset控制器-王辉的博客

 3 years ago
source link: https://blog.51cto.com/13812615/2514194
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

数据持久化

Volume

emptyDir

创建一个空卷,挂载到Pod中的容器。POD删除,该卷也会被删除

  • 应用场景: pod间数据的共享
$ vim  pod_emptydir.yml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: write
    image: centos
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"]
    volumeMounts:
      - name: data
        mountPath: /data
  - name: read
    image: centos
    command: ["bash","-c","tail -f /data/hello"]
    volumeMounts:
      - name: data
        mountPath: /data
  volumes:
  - name: data
    emptyDir: {}
$ kubectl logs mypod -c read -f   #指定容器读
$ kubectl exec -it mypod -c read -- sh    #-c 进入指定的容器
$ kubectl get pod -o wide  #查看调度节点
$ cd /var/lib/kubelet/pods/   #进入调度节点的kubelet目录
$ docker ps | grep mypod      # 查看pod对应的数据目录
$ cd /var/lib/kubelet/pods/1e6cab01-c048-4bb2-ae47-fccf581741d4/volumes/kubernetes.io~empty-dir   #就可以看到对应的卷数据

hostPath

挂载node文件系统上的文件或者目录到Pod中的容器

  • 应用场景: pod中容器需要访问宿主机文件
$ vim vim pod_hostpath.yml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 36000
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    hostPath:
      path: /tmp
      type: Directory
$ kubectl apply -f pod_hostpath.yml
$ kubectl exec  my-pod  -- ls /data

NFS共享存储的使用

  • 环境准备
    • 准备一台nfs服务器(192.168.56.18),然后与k8s集群都安装nfs-utils
$ vim /etc/exports
/ifs/kubernetes *(rw,no_root_squash)
$ systemctl restart nfs
$ showmount -e
  • 挂载nfs到k8s集群
$ mount -t nfs 192.168.56.18:/ifs/kubernetes /mnt
$ kubectl create deploy web --image=nginx --dry-run -o yaml > deploy_nfs.yml
$ vim deploy_nfs.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  strategy: {}
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
        volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
      volumes:
      - name: data
        nfs:
          server: 192.168.56.18
          path: /ifs/kubernetes
$ kubectl apply -f deploy_nfs.yml
$ kubectl get pod  && kubectl  exec -it web-xxxx bash
root@web-587c9fb4bd-9r7r4:/# cd /usr/share/nginx/html
root@web-587c9fb4bd-9r7r4:/usr/share/nginx/html# echo "aa" > index.html

$ kubectl scale deploy web --replicas=3   #扩容
$ kubectl expose deploy web --port=80 --target-port=80 --type=NodePort 
  • PersistantVolume : 对存储资源创建和使用的抽象,使得存储作为集群资源的管理
    kubernetes(十一) 存储& statefulset控制器

pv的静态供给

kubernetes(十一) 存储& statefulset控制器

kubernetes支持持久卷的存储插件: https://kubernetes.io/docs/concepts/storage/persistent-volumes/

  • 让用户不在关心具体的Volume实现细节

定义数据卷(准备pv)

$ cd /ifs/kubernetes/ && mkdir pv01 pv02 pv03    # 这是在nfs服务器上执行(192.168.56.18)
##### 接下来在k8s master上操作
$ vim pv01.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01
spec:
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteMany
  nfs:
    path: /ifs/kubernetes/pv01
    server: 192.168.56.18
$ kubectl apply -f pv01.yml 
$ kubectl get pv   #查看pv

####### pod使用pv
$  vim pod_pv.yml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-pod
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
      - name: www
        mountPath: /usr/share/nginx/html/
  volumes:
    - name: www
      persistentVolumeClaim:
        claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
$ kubectl apply -f pod_pv.yml
$ kubectl get pv,pvc   #发现pv绑定到my-pv

#### 数据测试
$ kubectl exec -it my-pod -- bash
root@my-pod:/# cd /usr/share/nginx/html/
root@my-pod:/usr/share/nginx/html# echo "hello" > index.html
### nfs服务器查看数据
$ ls /ifs/kubernetes/pv01/
index.html

pv动态供给

kubernetes(十一) 存储& statefulset控制器

pv动态供给NFS

kubernetes(十一) 存储& statefulset控制器

由于K8S不支持NFS动态供给,还需要先安装上图中的nfs-client-provisioner插件:

# cd nfs-client
# vi deployment.yaml # 修改里面NFS地址和共享目录为你的
# kubectl apply -f .
# kubectl get pods
$ vim pod_pvc01.yml 
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
      - containerPort: 80
      volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumes:
    - name: www
      persistentVolumeClaim:
        claimName: my-pvc

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: "managed-nfs-storage"
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

这次会自动创建5GPV并与PVC绑定。

kubectl get pv,pvc

测试方法同上,进入到容器中/usr/share/nginx/html(PV挂载目录)目录下创建一个文件测试。

再切换到NFS服务器,会发现下面目录,该目录是自动创建的PV挂载点。进入到目录会发现刚在容器创建的文件。

$ ls /ifs/kubernetes/default-my-pvc-pvc-ab48e34d-398d-4f31-b46b-93ff0853b29d

有状态应用部署

StatefulSet控制器概述

StatefulSet:

  • 部署有状态应用

  • 解决Pod独立生命周期,保持Pod启动顺序和唯一性
  1. 稳定,唯一的网络标识符,持久存储

  2. 有序,优雅的部署和扩展、删除和终止

  3. 有序,滚动更新

应用场景:数据库主从,消息中间件kafka集群,redis集群等

稳定的网络ID

说起StatefulSet稳定的网络标识符,不得不从Headless说起了

标准Service:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

无头Service(Headless Service)

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  clusterIP: None
  selector:
    app: nginx 
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

标准Service与无头Service区别是clusterIP: None,

这表示创建Service不要为我(Headless Service)分配Cluster IP,因为我不需要。

为什么标准Service需要?

这就是无状态和有状态的控制器设计理念了,无状态的应用Pod是完全对等的,提供相同的服务,可以在飘移在任意节点,例如Web。而像一些分布式应用程序,例如zookeeper集群、etcd集群、mysql主从,每个实例都会维护着一种状态,每个实例都各自的数据,并且每个实例之间必须有固定的访问地址(组建集群),这就是有状态应用。所以有状态应用是不能像无状态应用那样,创建一个标准Service,然后访问ClusterIP负载均衡到一组Pod上。这也是为什么无头Service不需要ClusterIP的原因,它要的是能为每个Pod固定一个”身份“。

举例说明:

vim stateful_nginx.yml              
apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "headless-svc"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: web

相比之前讲的yaml,这次多了一个serviceName: “nginx”字段,这就告诉StatefulSet控制器要使用nginx这个headless service来保证Pod的身份。

[root@centos7-node4 statefulset]# kubectl get pods
NAME                                      READY   STATUS              RESTARTS   AGE
nfs-client-provisioner-75f9fd7b57-67jtk   1/1     Running             0          121m
web-0                                     1/1     Running             0          72s
web-1                                     1/1     Running             0          55s
web-2                                     0/1     ContainerCreating   0          45s

每个pod的名称就是statefulSet的序列号获取主机名称的

[root@centos7-node4 ~]# kubectl exec web-0  -- hostname
web-0
[root@centos7-node4 ~]# kubectl exec web-1  -- hostname
web-1

不过,相信你也已经注意到了,尽管 web-0.nginx 这条记录本身不会变,但它解析到的 Pod 的 IP 地址,并不是固定的。这就意味着,对于“有状态应用”实例的访问,你必须使用 DNS 记录或者 hostname 的方式,而绝不应该直接访问这些 Pod 的 IP 地址。

以下是Cluster Domain,Service name,StatefulSet名称以及它们如何影响StatefulSet的Pod的DNS名称的一些选择示例。

Cluster Domain Service (ns/name) StatefulSet (ns/name) StatefulSet Domain Pod DNS Pod Hostname
cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}
kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1}

稳定的存储

StatefulSet的存储卷使用VolumeClaimTemplate创建,称为卷申请模板,当StatefulSet使用VolumeClaimTemplate 创建一个PersistentVolume时,同样也会为每个Pod分配并创建一个编号的PVC。

$ vim  stateful_pvc.yml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx 
  serviceName: "headless-svc"
  replicas: 3 
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx 
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"
      resources:
        requests:
          storage: 1Gi
  • 查看占用存储卷的情况
 kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS          REASON   AGE
persistentvolume/pvc-3c5fc93f-e90f-45d1-992f-6589c4901d1b   1Gi        RWO            Delete           Bound    default/www-web-1   managed-nfs-storage            100s
persistentvolume/pvc-60c524ff-bae9-4195-bf57-828e20e43ac8   1Gi        RWO            Delete           Bound    default/www-web-0   managed-nfs-storage            119s
persistentvolume/pvc-83963bb0-a0fb-481b-9a00-d9dc75f3e60b   1Gi        RWO            Delete           Bound    default/www-web-2   managed-nfs-storage            86s

NAME                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
persistentvolumeclaim/www-web-0   Bound    pvc-60c524ff-bae9-4195-bf57-828e20e43ac8   1Gi        RWO            managed-nfs-storage   119s
persistentvolumeclaim/www-web-1   Bound    pvc-3c5fc93f-e90f-45d1-992f-6589c4901d1b   1Gi        RWO            managed-nfs-storage   100s
persistentvolumeclaim/www-web-2   Bound    pvc-83963bb0-a0fb-481b-9a00-d9dc75f3e60b   1Gi        RWO            managed-nfs-storage   86s

结果得知,StatefulSet为每个Pod分配专属的PVC及编号。每个PVC绑定对应的 PV,从而保证每一个 Pod 都拥有一个独立的 Volume。

在这种情况下,删除Pods或StatefulSet时,它所对应的PVC和PV不会被删除。所以,当这个Pod被重新创建出现之后,Kubernetes会为它找到同样编号的PVC,挂载这个PVC对应的Volume,从而获取到以前保存在 Volume 里的数据。

StatefulSet与Deployment区别:有身份的!

有状态应用参考 https://github.com/operator-framework/awesome-operators

身份三要素:

  • 存储(PVC)

这里为你准备了一个etcd集群,来感受下有状态部署: https://github.com/lizhenliang/k8s-statefulset/tree/master/etcd


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK